How to Store Corrections and Manage Candidate Versions with distilly_correct
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 and maintains a complete version history via tools/version_manager.py.
The distilly_correct Processing Pipeline
The correction pipeline implemented in 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 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 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. This counter is defined in the skill schema at lines 219–223 of 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 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. 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 creates new directories under the versions/ folder containing updated SKILL.md files and associated artifacts. Each candidate receives a manifest.json file recording:
- The current
corrections_countfrom the metadata - The deterministic slug generated for that version
- An ISO timestamp generated via
now_iso()from line 34 oftools/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:
promote_candidate(): Elevates a specific candidate version to "current" status, making it the active skill versionprune_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:
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:
{
"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:
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 after each correction batch.
Listing and Promoting Candidates
View available candidates in the versions directory:
ls ./skills/colleague/example_zhangsan/versions
# v001 v002 v003
Promote a specific candidate using the Python API from tools/version_manager.py:
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()intools/skill_writer.pyto 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_countmetadata field intools/skill_schema.pytracks total edits for each skill version - Version Manager creates deterministic candidate directories under
versions/withmanifest.jsonfiles containing timestamps and correction counts - Use
promote_candidate()andprune_old_candidates()fromtools/version_manager.pyto 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 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 according to the schema defined at lines 219–223 of tools/skill_schema.py. This count is also recorded in each candidate's 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.
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) containing the updated SKILL.md, associated artifacts, and a manifest.json file. The update_skill() function in tools/version_manager.py manages these directories, while now_iso() from line 34 of tools/skill_schema.py timestamps each creation for chronological sorting.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →