Skip to content
Blume
Esc
↑↓navigate↵open⌘Jpreview

Updated

On this page

Changes

Skillset change records capture source-change reasons, verify coverage, and preserve evidence for releases.

Feature Feature status claude codex cursor
changes implemented not_applicable not_applicable not_applicable

Support vocabulary: Feature Reference

Changes record why source units changed. They provide committed coverage, changelog text, and release-planning evidence; they are neither generated provider output nor npm package Changesets.

Record a Change

Pending reasons live as Markdown under .skillset/changes/. Let the command create the stable id and evidence:

skillset change add \
  --scope skill:review-notes \
  --bump patch \
  --reason "Clarify the review checklist."

The Markdown body is the authored reason. Readable directives such as Bump:, Group:, and Scope: preserve the chosen release impact and selection. Skillset derives ids, source hashes, and coverage into the change ledger instead of requiring hand-authored frontmatter. Each command that records ledger events writes them to one new pending file under .skillset/changes/events/; skillset release apply folds pending files into .skillset/changes/ledger.jsonl.

Ids are twelve lowercase hexadecimal characters. CLI references use @<prefix> with a minimum of six characters and must be unambiguous. A group is a filtering/reporting aid, not a release boundary.

The generated change reference owns exact options. The publishing guide shows the normal change-to-release workflow.

Check Coverage

skillset change status
skillset change status --since origin/main
skillset change check --since origin/main

status reports changed source units and generated-output drift separately. check validates the reason body, scopes, ids, current source-hash evidence, and coverage. Both are read-only.

After skillset move, status reports the old selector removed and the new one added. A reason whose scope names the new selector also covers the old selector’s removal when the ledger records that move, and releasing it retires the old selector. The moved unit’s source hash changes with its identity, so refresh the reason’s evidence after a move.

The baseline is selected in this order: an explicit --since; ledger-derived release state; compatibility state.json; source-inventory locks; then the Git merge base. If a failed check used --since, use the same baseline when refreshing evidence:

skillset change refresh @a1b2c3 --since origin/main
skillset change refresh @a1b2c3 --since origin/main --yes

Refresh previews by default and appends coverage events only with --yes. It does not edit the reason.

Correct or Disposition a Reason

Intent Command Write behavior
Edit a pending reason skillset change reason @a1b2c3 --reason "…" Updates reason evidence while preserving the id
Append to a pending reason skillset change reason @a1b2c3 --append --reason "…" Appends after validation
Intentionally exclude a pending reason from release skillset change ignore @a1b2c3 Previews; appends ignore evidence with --yes
Correct applied change wording skillset change amend @a1b2c3 --reason "…" Appends an amendment; original history remains
Correct release-event notes skillset release amend @a1b2c3 --reason "…" Appends release amendment evidence

An ignored reason remains visible but contributes no bump or changelog entry. Applying a release records its current source hash so status does not repeatedly report the intentionally ignored edit.

Legacy Pending Entries

Older YAML-frontmatter entries remain readable for recovery. They are not the current authoring shape:

skillset change migrate
skillset change migrate --yes

The first command previews conversion to reason-only Markdown and equivalent ledger events. The second writes it. Valid legacy entries produce a cleanup warning until migrated.

Errors and Recovery

Problem Result Recovery
Missing or stale source evidence change check fails Run change refresh with the same baseline, review, then confirm
Unknown scope or empty reason Validation fails Correct the source-unit selector or reason body
Ambiguous short ref Command lists candidates and fails Supply a longer @ prefix
Two stacked reasons cover one current hash Both remain valid and a stacked-evidence note is shown Keep both when attribution is intentional
One stacked reason has an old hash That reason remains stale Refresh it; evidence is never borrowed from another reason
bump: none covers a structural or severity-bearing change Check warns Choose the intended bump or explicitly review the warning
Generated file was edited Change commands do not reverse-map it Use reconciliation

Build --scope is rejected for change status/check because a destination filter cannot safely limit source coverage.

Writes and Exit Behavior

Read commands (status, check, list, show, and history) write nothing. add, reason, and amendments write their named source-side records. refresh, ignore, and migration are plan-first and require --yes to apply. Validation or uncovered changes produce a nonzero check result.

Package-facing edits can require both ledgers: .skillset/changes/ records workspace source intent, while .changeset/*.md records npm release intent. Generated schemas and examples are evidence, not substitutes for either source record.

Provenance

ledger.jsonl is the schema-versioned event stream for reason lifecycle, coverage, ignores, and release projection. Events recorded since the last release live in pending files, .skillset/changes/events/<YYYYMMDDTHHMMSSmmmZ>-<event-id>.jsonl, one per mutation. Readers fold ledger.jsonl first, then pending files by name, then each file’s records by line, so the ledger reads the same before and after a release folds them in. Readers take no lock: a read that overlaps a fold reads pending files before ledger.jsonl, counts a record the fold already copied once, and retries when the set of pending files changed under it. Applied reasons remain in history.jsonl; release records remain in releases.jsonl; corrections append to amendment files. Nearby skillset.lock files continue to own generated paths and hashes.

See Source Change, Release, and Dependency Provenance and Reason-Only Change Ledger and Derived State for the durable design.

Merging Change Streams

Ordinary change commands never append to a tracked stream. change add, reason, refresh, ignore, migrate, and the applying draft, promote, move, and new writes each create one new pending event file under .skillset/changes/events/. The name starts with the mutation’s UTC stamp and ends with its first event id, so two branches add two different files and git merges them without a conflict, in either direction. Pending files are write-once: never edit, rename, or delete one by hand. skillset release apply is the only writer that removes them, when it folds them into ledger.jsonl; a release with no entry to apply leaves them pending.

The trunk streams (ledger.jsonl, history.jsonl, releases.jsonl, and amendment files) only gain records at the end, and only a release appends to them. Declare the built-in union merge strategy for them in .gitattributes so two release branches still merge:

.skillset/changes/*.jsonl merge=union

union keeps both sides’ appended records without a conflict, and being built in it needs no per-clone git config registration, so it applies the same way in CI, fresh clones, and worktrees. The pattern does not match events/, so pending files keep the default text merge, which is enough for files that only ever get added.

Because only a release appends to a trunk stream, a branch that records change events can be updated either way, by restacking onto trunk or by merging trunk into it. A branch that ran skillset release apply still appends to trunk streams: restack it onto trunk (git rebase, or gh stack rebase for a stack) so union replays its appended block after trunk’s records. Merging trunk into such a branch puts trunk’s new records after the branch’s, and the guard below rejects that result by design.

Union is line-level and cannot validate the result. Two invariants still need checking after a merge: record ids stay unique, and trunk’s records keep their order. File order is the only fold order for trunk streams: derived state walks records top to bottom and later records win. createdAt, appliedAt, and amendedAt are event time, not sequence, so global chronological order is not an invariant. What is not fine is changing what trunk already has, so bun run change-stream:guard reads each stream at the merge-base with the remote trunk and requires those lines to be an unchanged, in-order prefix of the working copy, naming the first divergent line when they are not. It also requires every pending file to be well named and, if it exists at the merge-base, unedited since; a release may delete it. Event ids stay unique across ledger.jsonl and every pending file. Live writers serialize on the owner-fenced ledger lock and stamp max(now, latest) across ledger.jsonl and every pending file, so event time stays non-decreasing in fold order. Never sort a stream to fix a failure; restore trunk’s records and move the branch’s records after them. See ADR-0038 and ADR-0045.

Last updated on

Was this page helpful?