chore(changelog): land entries as per-change fragments in changelog.d/ - #16
Conversation
Concurrent PRs editing the shared '## Unreleased' section of CHANGELOG.md conflict whenever one PR outlives another merge. Entries now land as uniquely-named fragment files (changelog.d/<slug>.<category>.md), which git merges without conflict; the npm-version hook folds them into the release section and deletes them. Direct '## Unreleased' edits remain supported and are merged at the same point, so in-flight PRs need no rework. Tag-time publish guard and github-release job are unchanged. Part of the ecosystem-wide rollout of the fragment scheme; reference implementation and full rationale in agent-framework PR #128. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nts in CI Two findings from Greptile's review of the membrane companion PR (antra-tess/membrane#49), applied wave-wide: a fragment whose first line was a bullet could smuggle top-level prose or a rogue '## ' heading (a fake section boundary) into the released changelog, and the CI check's path-only grep let a deleted or renamed fragment satisfy the entry requirement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Greptile's review of the membrane companion (antra-tess/membrane#49) found two issues in the shared code, fixed here in 0479dcb: fragment validation now checks every line (top-level prose or a rogue |
…egression tests Codex QA pass on the membrane companion (antra-tess/membrane#49) found that a slug taken verbatim from a 'fix/foo' branch name — which the docs invited — created changelog.d/fix/foo.fixed.md, and the scanner skipped the 'fix' directory without a word: a silent release-note data-loss path that PR CI catches but direct pushes bypass. The scanner now aborts on any entry that is not README.md or a well-formed fragment file, and the docs prescribe filesystem-safe slugs ('/' -> '-'). The continuation-line check also treated any whitespace-prefixed line as valid, so an indented '## ' heading still passed. Continuations must now be indented two or more spaces, and headings/rules are refused at any indentation. The new regression test runs the real script in a throwaway directory: fragment-only and mixed assembly, canonical ordering, audience-qualified headings, deletion + README preservation, and fourteen refusal cases asserting nothing is modified on failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
A Codex QA pass on the membrane companion (antra-tess/membrane#49, blocking) found two more shared-code issues, fixed here in ab31cdf: a fragment slug taken verbatim from a |
… and spaced rules Second Codex QA round on the membrane companion (antra-tess/membrane#49, head f72e00a): - Refusals used console.error + process.exit(1). Where stderr is a pipe (asynchronous on macOS), the exit can pre-empt the write, so callers — including the new tests — saw exit 1 with empty stderr. Refusals now throw a ReleaseError caught at the entry point, which prints and sets process.exitCode = 1 so the process drains stdio on its own. - The block-construct check missed thematic breaks with interior whitespace ('- - -') and headings used as item content ('- ## x'). Both are now refused; content after the bullet marker is inspected. Six regression cases added, plus one guarding against over-refusal of bullets that merely resemble rules or headings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Round 2 of the Codex QA pass on membrane (antra-tess/membrane#49) — fixed here in 4e104c0: refusals reported via |
Problem
The changelog policy is a structural conflict generator: every PR inserts prose at the same textual location (the top of
## UnreleasedinCHANGELOG.md), so any PR that outlives another merge conflicts there. Full rationale and design discussion in anima-research/agent-framework#128 — this PR is the chronicle leg of the same ecosystem-wide rollout (same shape as the July policy rollout).Changes
Entries now land as one uniquely-named file per change —
changelog.d/<slug>.<breaking|added|changed|fixed>.md, containing the entry's markdown bullets verbatim (slug = PR number or branch name). Distinct files never conflict in git.scripts/release-changelog.mjs(thenpm versionhook) folds pending fragments plus anything filed directly under## Unreleasedinto the new## X.Y.Z — datesection (subsections in Breaking/Added/Changed/Fixed order; fragments join an existing matching subsection, including audience-qualified### Breaking (on-disk format)headings), deletes the consumed fragments, and keeps all existing refusals. New refusals: unrecognized category suffix and non-bullet fragment content. Chronicle's script was byte-identical to agent-framework's (it is repo-agnostic — readspackage.jsonfor name-free version handling), so the new version drops in verbatim.changelog.ymlsoft check now passes on a well-formed new fragment or aCHANGELOG.mdedit (transition-friendly for in-flight PRs;no-changeloglabel escape unchanged).changelog.d/README.mdand badly-named fragments do not satisfy it. Chronicle's existing^src/path condition (which here matches the Rust crate'ssrc/) is kept as-is.publish.ymlis untouched — the tag-time section guard and thegithub-releasemirror job both read the assembled versioned section, which exists at tag time exactly as before because assembly runs pre-tag (verified below).## Unreleasededits remain a supported input, merged at release time. (Chronicle's## Unreleasedis currently empty, so nothing even needs carrying over.)## Changelogsection rewritten; chronicle-specific prose kept verbatim (what-needs-an-entry list: napi surface, store/branch/state semantics, on-disk/wire format, recovery, packaging; the "say plainly whether existing stores keep opening" breaking guidance; the five-target cross-build in the release ritual). Breaking fragments open the bullet naming who needs to act — chronicle's example adapted to- **On-disk format:** …, matching its existing### Breaking (on-disk format)heading style, which remains available via a direct Unreleased edit.changelog.d/README.mddocuments the format in-place (example slug10-torn-tail.fixed.md), and this PR dogfoods the scheme with its own fragment (changelog-fragments.changed.md), so the next release exercises the pipeline.Companion to anima-research/agent-framework#128 and siblings in membrane, context-manager, and connectome-host; safe to merge in any order — each repo's scheme is self-contained.
Tests
Release script exercised on copies in a scratch dir through all eight legs (outputs pasted from the runs):
### Breaking (on-disk format)heading): breaking fragment joined the qualified heading, canonical subsection order, continuation lines preserved, fragments deleted,README.mdkept.CHANGELOG.md: released '## 0.4.0 — 2026-08-24' from 4 fragment(s) plus the Unreleased section.Exit 0.a '## 0.3.0' section already exists.Exit 1.nothing to release as 0.4.0 — no fragments in changelog.d/ and no entries under '## Unreleased'.Exit 1.### Changedsection, fresh empty## Unreleasedkept above, exit 0.oops.md) — exit 1 with naming guidance.github-releaseawk (the exact commands frompublish.yml) run on leg 1's assembled output withGITHUB_REF_NAME=v0.4.0: guard passes, awk extracts the 26-line section, non-empty.changelog.ymlgrep logic simulated on six changed-file sets: src+fragment PASS, src+CHANGELOG PASS (legacy), src-alone FAIL, src+changelog.d/README.md-only FAIL, src+badly-named-fragment FAIL, docs-only PASS.Compatibility
No format change — docs, CI, and release tooling only; no Rust or napi code touched.
Not verified
A real
npm versionrun end-to-end (the next actual release is the live exercise — same status as the existing tag-time guards, unexercised since #14 landed). Thechangelogcheck will run live on this PR itself.changelog.d/changelog-fragments.changed.md.🤖 Generated with Claude Code