Generate Missing Changesets

Write the changesets for everything committed since the last release. A script captures the commits mechanically into `.temp/raw-changesets/`, one file per package per severity; you turn that capture into user-facing changesets in `.changeset/`, and a second script run proves nothing was lost. Handles workspace and single-package repositories. Fail-closed: stops and reports instead of fixing forward, and never touches anything outside `.changeset/`. Use when the user says "missing changesets", "generate changesets", or before a release where the changelog would otherwise be incomplete. Run while the individual commit messages still exist — before a squash merge, never after.

How to use

Use this to ensure all functional changes are captured before a release.

Prompt

Generate Missing Changesets

Commits are written for developers, changesets are read by users, and this skill
is the one place that translation happens. cebreus-release never rewrites your
wording.

commits ─script─▶ .temp/raw-changesets/ ─you─▶ .changeset/ ─script─▶ verified
         capture   one file per package   prose  same split    cited, right file,
                   per severity                                under the ceiling

Script behaviour, flags and artefacts: REFERENCE.md in this folder. Read it
when a report surprises you or you must explain what the script did — not
before step 1.

Binding rules

  1. Write every .changeset/*.md yourself, from the raw capture and nothing
    else.
    Never invent, never infer from the code.
  2. Never edit .temp/. It is the evidence --verify checks you against.
  3. Never drop a commit. Marginal work — tests, chores, docs, CI, build —
    goes in the Internal group, last, not in the bin.
  4. Never stage or commit anything outside .changeset/.
  5. Never run changeset init. It overwrites the config and drops the custom
    changelog formatter.
  6. Non-zero exit: report the message verbatim. If it names a flag that
    clears the condition, that is an instruction — decide, re-run with it, say so
    in your report. Any other non-zero exit is a STOP. Never fix forward, never
    guess a flag.
  7. Never rewrite git history. That is the user's call, never yours.
  8. The individual commit messages must still exist. On a branch that will be
    squash-merged, run before the squash; if it is already squashed, STOP and say
    so. On a trunk-based repository this is already satisfied — carry on.

1. Preflight

git status --porcelain

MUST be empty. Otherwise STOP and list the unclean paths.

2. Capture

node .agents/skills/cebreus-generate-missing-changesets/generate-changesets.mjs

Check the JSON report, in order:

  • mode matches the repository (workspace or single).
  • baseline is a commit hash. (full history) is correct only for a repo that
    has never been released — otherwise STOP.
  • commitsCaptured equals git rev-list --count --no-merges <baseline>..HEAD.
  • changesets lists one entry per package per severity, each with raw,
    target, rawBullets, scopeFloor and maxBullets. Every package you
    expect to see changed is there, with a plausible bump.

If the script aborts listing non-conventional commit subjects, re-run with
--allow-nonconventional and quote the list in your report.

3. Write the changesets

One changeset per raw file. Copy target from the report; do not compose it.
Copy the frontmatter from the raw file verbatim — same package, same bump.

Never move a bullet between files. A feat belongs in the minor changeset
and nowhere else.

Density. maxBullets is the ceiling and --verify fails above it.
scopeFloor is what the merge rules make unavoidable: one bullet per named
scope, plus one per distinct type among the scope-less commits. The ceiling is
twice that, so on average two bullets per scope — merge per scope, and split a
scope only where it genuinely holds two stories. The ceiling is always above the
floor, so the rules can never be unsatisfiable.

The body is a flat list, no headings, in the raw file's group order
(breaking, features, fixes, improvements, internal). Headings would nest inside
the ones changeset version generates; the grouping is materialised later by
regroup-changelog.mjs, from the prefixes.

- [<hash>] <type>(<scope>): <one sentence>
- [<hash>] <type>: <one sentence>
- [<hash>] [<hash>] <type>(<scope>): <one sentence>     <- merged

Prefix — copy, never compose.

  • Copy type(scope): from the raw bullet. The written prefix MUST be one that
    some cited commit carries; --verify checks it against the capture.
  • Scope is optional, exactly as on the commit. ci:, docs:, chore: keep
    none. Never invent one, and never demote a type into the scope: writing
    chore(ci): for a ci: commit is two errors at once.
  • The raw file's group titles (Breaking changes, Features, Fixes,
    Improvements, Internal) are headings, not types. A refactor(build):
    bullet under ### Improvements stays refactor(build):.
  • When merging across types, use the prefix of the merged commit from the
    highest-priority group.

Text.

  • One sentence, past tense, plain British English. Write for someone who uses
    the product and does not read code.
  • Say what changed for them and why it matters — not how it was implemented.
  • No file paths, no identifiers, no placeholder wording such as "the file".
  • Merge raw bullets that describe one user-visible change, carrying every merged
    bullet's hash. Merge across commits, not only within one: twelve content
    edits from the same admin screen are one sentence, not twelve.
  • Do not merge across scopes — the result is true of neither. Merge freely
    within one scope, and across scope-less commits of the same type.
  • Keep the raw file's order inside each group.
  • A bullet that says nothing a user could act on still survives: give it one
    honest sentence in Internal rather than deleting it.

4. Verify

node .agents/skills/cebreus-generate-missing-changesets/generate-changesets.mjs --verify

This replaces reviewing your own work. Never edit the audit file to make it
pass. On failure, fix the changesets and re-run.

Quote its density block — raw in, written out, ceiling — in your report.

5. Commit

git status --porcelain

Only paths under .changeset/ may appear; anything else is a STOP. .temp/
must be gitignored — if it appears, STOP.

.changeset/config.json is generated and temporary: it lives from here until
cebreus-release runs changeset version, which deletes it. Never stage it. If
it shows as untracked, that is not a stop — report that the repository should
add it to .gitignore to keep the window quiet.

git add .changeset/*.md
git diff --cached --name-only

Every staged path MUST end in .md under .changeset/. Otherwise unstage it,
or STOP. (git add .changeset/ would sweep in the config.)

This step executes — it does not propose. Uncommitted changesets are lost at the
next git checkout.

git commit -m $'docs(changeset): add release notes' -m $'- package-a: patch\n- package-b: minor'

Subject is fixed: docs(changeset): add release notes, verbatim, every time.
No count, no version, no paraphrase. Body: one line per changeset,
- <name>: <bump>, sorted by package then severity — a package split across two
severities gets two lines. No trailers, no attribution, no emoji.

A commit that fails is a STOP, not an obstacle. Never pass --no-verify,
never set core.hooksPath, never touch .git/config, never edit or move a
hook. A commit that skipped the repository's checks is worse than none, because
it looks finished. Report the error verbatim and stop.

What follows

cebreus-release runs changeset version, which turns the severity split into
### Major / Minor / Patch Changes, then adds the finer #### Breaking changes
/ #### Features / #### Fixes / #### Improvements / #### Internal split
and writes the release summary. It does not rewrite your bullets: if the wording
is wrong, it is wrong here.

Attachments