# How to Store Corrections and Manage Candidate Versions with distilly_correct

> Learn to store corrections and manage candidate versions with distilly_correct in titanwings/distilly. Normalize JSON, edit markdown, and track changes efficiently.

- Repository: [Tianyi Zhou/distilly](https://github.com/titanwings/distilly)
- Tags: how-to-guide
- Published: 2026-09-10

---

**distilly_correct is the built-in correction system in titanwings/distilly that normalizes JSON payloads, applies edits to persona markdown files, and tracks changes via a `corrections_count` metadata field while maintaining versioned candidate directories.**

The distilly_correct system enables iterative refinement of AI skill personas after initial generation. When you supply a correction payload via the CLI, the system processes structured feedback through [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) and maintains a complete version history via [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py).

## The distilly_correct Processing Pipeline

The correction pipeline implemented in [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) processes feedback through three distinct phases before the Version Manager creates candidate snapshots.

### Normalizing Correction Payloads

When you pass a `--correction-json` argument to the CLI, the `normalize_corrections()` function at line 375 in [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) flattens nested structures into a standardized format. Whether your payload uses top-level keys like `persona_corrections` or `corrections`, the normalizer extracts a flat list of dictionaries.

Each dictionary must contain `wrong` and `correct` entries specifying the exact text to find and replace. The function handles nested structures automatically, ensuring consistent processing regardless of input format variations.

### Applying Corrections to Persona Markdown

The `apply_correction()` function at line 352 in [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) inserts formatted correction lines into the persona markdown file. The system appends each correction either at the end of the file or immediately before an existing **Correction Log** heading.

This preservation of history creates human-readable audit trails within the skill documentation. The function formats entries consistently, ensuring that subsequent corrections maintain chronological order without breaking existing markdown structure.

### Tracking Correction Count in Metadata

After applying all corrections, the writer increments the `generation.corrections_count` field at line 432 in [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py). This counter is defined in the skill schema at lines 219–223 of [`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py), providing downstream tools with immediate visibility into edit frequency.

The metadata update synchronizes with the skill's JSON schema, enabling programmatic queries about refinement depth across your skill library.

## Managing Candidate Versions with the Version Manager

The Version Manager module in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) handles the lifecycle of candidate releases, from initial creation to promotion or deletion, while preserving correction history in each snapshot.

### Generating Deterministic Version Slugs

Candidate versions receive deterministic identifiers through the `slugify()` function at line 28 of [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py). This creates consistent, URL-safe slugs derived from skill names, ensuring reproducible directory naming across environments and preventing duplicate version collisions.

### Creating Version Directories and Manifests

The `update_skill()` function in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) creates new directories under the `versions/` folder containing updated **SKILL.md** files and associated artifacts. Each candidate receives a [`manifest.json`](https://github.com/titanwings/distilly/blob/main/manifest.json) file recording:

- The current `corrections_count` from the metadata
- The deterministic slug generated for that version
- An ISO timestamp generated via `now_iso()` from line 34 of [`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py)

This manifest enables precise comparison between candidates based on when corrections were applied and how many edits separate different versions.

### Promoting and Pruning Candidates

Two helper functions manage candidate lifecycle within [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py):

- **`promote_candidate()`**: Elevates a specific candidate version to "current" status, making it the active skill version
- **`prune_old_candidates()`**: Removes stale versions to conserve storage while preserving historical integrity

Both functions operate through the Version Manager API, allowing automated cleanup scripts or manual version selection based on correction quality.

## CLI Usage and Code Examples

### Running the Writer with Corrections

Execute the correction pipeline by specifying your correction JSON file using the `--correction-json` flag:

```bash
distilly write ./skills/colleague/example_zhangsan \
    --work-json work.json \
    --persona-json persona.json \
    --correction-json corrections.json

```

This command triggers the full distilly_correct workflow including normalization, application, and metadata updates before creating a new candidate version.

### Creating a Correction JSON File

Structure your corrections as an array of objects with `wrong` and `correct` fields:

```json
{
  "persona_corrections": [
    {
      "scene": "intro",
      "wrong": "I'm a junior engineer",
      "correct": "I'm a senior engineer"
    },
    {
      "scene": "closing",
      "wrong": "I'll get back to you tomorrow",
      "correct": "I'll get back to you within the hour"
    }
  ]
}

```

The system accepts nested structures under `persona_corrections` or flat `corrections` arrays, normalizing them automatically during processing.

### Inspecting Correction Metadata Programmatically

Verify applied corrections by reading the skill's metadata file:

```python
from pathlib import Path
import json

meta = json.loads(Path("./skills/colleague/example_zhangsan/meta.json").read_text())
print(meta["generation"]["corrections_count"])   # → 2

```

This reads the counter updated by [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) after each correction batch.

### Listing and Promoting Candidates

View available candidates in the versions directory:

```bash
ls ./skills/colleague/example_zhangsan/versions

#   v001  v002  v003

```

Promote a specific candidate using the Python API from [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py):

```python
from tools.version_manager import promote_candidate

promote_candidate(
    skill_dir=Path("./skills/colleague/example_zhangsan"),
    candidate_slug="v003"
)

```

## Summary

- **distilly_correct** normalizes JSON payloads via `normalize_corrections()` in [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) to handle various input structures consistently
- The `apply_correction()` function at line 352 preserves history by inserting formatted lines before the **Correction Log** heading or at file end
- The `generation.corrections_count` metadata field in [`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py) tracks total edits for each skill version
- **Version Manager** creates deterministic candidate directories under `versions/` with [`manifest.json`](https://github.com/titanwings/distilly/blob/main/manifest.json) files containing timestamps and correction counts
- Use `promote_candidate()` and `prune_old_candidates()` from [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) to manage candidate lifecycle and storage

## Frequently Asked Questions

### What format should the correction JSON file use for distilly_correct?

The correction JSON should contain either a `persona_corrections` or `corrections` key containing an array of objects. Each object requires `wrong` and `correct` string fields specifying the text to find and replace. The `normalize_corrections()` function in [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) automatically flattens nested structures into the standard format expected by the application logic.

### How does distilly_correct track how many corrections have been applied?

The system maintains a counter in the skill's metadata at `generation.corrections_count`. After each correction batch is processed, the writer increments this value at line 432 of [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) according to the schema defined at lines 219–223 of [`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py). This count is also recorded in each candidate's [`manifest.json`](https://github.com/titanwings/distilly/blob/main/manifest.json) file, allowing quantitative comparison between different versions.

### Can I revert to a previous candidate version after promoting another?

Yes. The Version Manager preserves all candidate directories under the `versions/` folder until explicitly pruned. You can promote any existing candidate by calling `promote_candidate()` with the specific slug (such as "v001" or "v002"). The function updates the current skill pointer without deleting the version history, enabling rapid rollback to previous correction states stored in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py).

### Where are candidate versions stored in the skill directory structure?

Candidate versions reside in the `versions/` subdirectory within each skill folder. Each candidate receives a deterministic slug directory (generated by `slugify()` at line 28 of [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py)) containing the updated **SKILL.md**, associated artifacts, and a [`manifest.json`](https://github.com/titanwings/distilly/blob/main/manifest.json) file. The `update_skill()` function in [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) manages these directories, while `now_iso()` from line 34 of [`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py) timestamps each creation for chronological sorting.