Install in Claude Code
Copygit clone --depth 1 https://github.com/Pipelex/pipelex /tmp/add-migration && cp -r /tmp/add-migration/.claude/skills/add-migration ~/.claude/skills/add-migrationThen start a new Claude Code session; the skill loads automatically.
Definition
SKILL.md
# Add a Migration Entry A configuration **surface** — `pipelex.toml` and its tiers, `telemetry.toml`, `pipelex_service.toml`, the inference backend definitions in `inference/backends/` — ships a checked-in ledger at `pipelex/migration/ledgers/<surface-id>.toml` recording, as data, every shape change it has ever undergone. A user's existing file is repaired by replaying that ledger over it, so a schema change with no entry is a schema change that breaks every machine in the field. `docs/migration-ledger.md` is the normative contract. **Read it before writing an entry** — this skill is the procedure, that document is the law, and where the two disagree the contract wins. ## The one rule **Derive the entry from the gate's refusal, never from memory.** The coverage check recomputes the surface's fingerprint and names every path, every enumerated spelling and every value domain the change moved. That output is the fingerprint diff, already rendered. Composing an entry by hand from your own reading of the diff is how a rename gets misspelled and every user's file is migrated onto a key the schema rejects, with the tool reporting success. ## Step 1: See what the gate says `make check-migration-schemas` (alias `cmig`) is deliberately **not** in `make agent-check` — it is a golden check, and a fail-regenerate-fail cycle in the loop agents run constantly is how a gate goes permanently green while catching nothing. So run both gates explicitly: ```bash make cl # check-ledger: is the ledger legal, and is replaying it harmless? make cmig # check-migration-schemas: is every schema change accounted for? ``` Each failure names the surface and a `kind`, and every message says what to do. The table below is not the whole list of kinds — it is the map of which ones mean **an entry is owed**, and what it owes: | Kind | What the change did | What the entry owes | |---|---|---| | `removal_needs_a_bump` | a path, an enumerated spelling or a value domain is gone from the models but still in the head golden | a version bump plus the operation that removes or repairs it | | `unaccounted_path` | a path the diff removed that no operation in the entry acts on | an operation naming that path | | `enum_member_not_remapped` | an enumerated spelling disappeared | a `remap_value` from the old spelling to the new one | | `value_domain_narrowed` | the type or a bound got stricter — nothing was removed, but a legal value stopped being legal | a `remap_value` per narrowed path, or an `unsafe` entry naming them in `declared_narrowed_paths` | | `dead_op` | an operation whose source no schema version ever removed, or a `delete_table` pointed at a key | fix the path or the kind — the applier would skip it forever while looking like an accounting | | `destination_occupied` / `destination_not_in_new_shape` | an operation's destination is wrong | fix the destination — this is usually a typo, and it is the defect the transform goldens exist to catch | | `over_deletion` | the entry removes a path the schema still has | narrow the operation; a parent is deleted only when the parent itself retires | | `required_path_without_default` | an added required path has no value in the defaults layer | give it a default — an added key is absorbable only because the defaults layer carries it | | `snapshot_pending` / `fingerprint_drifted` | the goldens predate the models | usually just step 5; if it is the only failure, no entry is owed | | `reserved_path_reused` / `reserved_value_reused` | the new name was retired by an earlier version | **pick another name** — see step 4 | | `convergence_broken` | replaying the ledger over a healthy reference document is not a no-op | the operation acts on live material, or on the wrong path | **A gate that only asks for a regeneration is not asking for an entry.** An additive change — a new optional key, a widened type, a relaxed bound — costs a `make umig` and nothing else. Stop at step 5 in that case. ## Step 2: Decide the safety `safety` governs whether the applier may act, and it is not a matter of taste: - **`safe`** — the operations mechanically complete the repair. The file is rewritten after one confirmation. Everything structural is safe: renames, moves, deletions, and a `remap_value` whose old spelling is genuinely no longer legal. - **`unsafe`** — no operation in the vocabulary can repair the file, so the entry is *reported and never applied*. This is the form for a tightened numeric bound, a completeness rule a validator expresses, anything where the tool would have to choose a value on the user's behalf. An `unsafe` entry may legitimately carry no operations at all — and then `declared_narrowed_paths` is **mandatory**, because the engine questions a document before reporting an entry, and an entry with neither operations nor a declaration answers "nothing to say" for every file there will ever be. The declaration is spelled as the fingerprint at the entry's own version records the paths, `*` segments included, and `make cl` refuses a path that version does not have. `guidance` is independent of `safety`: any entry may carry it, and it is the explanation a person reads, **never the mechanism**. Anything expressible as operations must be operations. ## Step 3: Write the entry Two edits to `pipelex/migration/ledgers/<surface-id>.toml`, in one change: 1. In `[surface]`, raise `current_schema_version` to N. 2. Append a `[[migration]]` block. Entries are contiguous and named for their version — the id **must** be `<surface-id>@<N>` and `to_schema_version` **must** be N, or the file is refused when it is parsed. ```toml [[migration]] id = "example-config@2" to_schema_version = 2 introduced_in = "0.46.0" breaking = true safety = "safe" title = "Short imperative summary of the shape change" description = "One or two sentences a release note can quote." guidance = """ What a user should understand o
More from this repository