Development record
The changelog composer cannot see the previous release surface, so its `Changed`
The changelog composer cannot see the previous release surface, so its Changed
and Removed sections rest on trust. Its inputs are added[], removed[] and
the record bodies at their paths. None of those is the shipped surface of
base_tag, yet three of the six Keep a Changelog sections are claims about
exactly that: Changed, Deprecated and Removed each assert that something a
user could reach in the previous release is now different, deprecated or gone.
The composer has no way to check any of them.
The failure is not hypothetical. In the v0.7.0 cut the composer wrote three lines about a verb that did not exist in v0.6.9:
CHANGELOG.md:39— "an invocation carrying only--positionand--target— anything scripted against the previous release — stops working until a scope is added" (itd-199)CHANGELOG.md:40— "abcd reading ingesttakes--reading-jsonwhere it took--output-json. The old spelling is gone rather than aliased" (iss-2608311725218136)CHANGELOG.md:44, under### Removed— "The comparative position no longer assembles." (itd-199)
Verified against the tag: git show v0.6.9:internal/surface/cli/reading.go and
git show v0.6.9:commands/reading.md are both absent, and output-json appears
zero times in the CLI reference at v0.6.0, v0.6.5 and v0.6.9. The whole abcd reading verb ships for the first time in 0.7.0. The same section's ### Added
announces it as new (itd-183, itd-184, itd-185), so the release record
contradicts itself and instructs a user to migrate a surface they never had.
The records are not at fault and must not be edited to compensate. itd-199's
impact: fix is a deliberate maintainer ruling whose fidelity verdict records
cond-2608312031020321 as survived. iss-2608311725218136's body states the
disambiguating fact outright — "the verb shipped today and has no users" — in
the very text the composer read before writing that scripts against the previous
release stop working. Every fact needed was present; the prompt asked no question
that would have used them.
Grounds
- pursued: the prompt-side half is fixed in this same change, adding a
base_tagbaseline rule to the section-choice guidance, because a release record that fabricates a migration is falsifiable by anyone who reads the previous tag
- pursued: the prompt-side half is fixed in this same change, adding a
base_tag
What is still open
The prompt rule is a trusted instruction, not a checkable one: the composer is
told to ask whether a capability was reachable in base_tag while still having
no way to look. Two candidate rungs, both binary-side:
- hand the composer the previous tag's surface as cut input, so the question it is asked is one it can answer from evidence rather than inference
- validate at ingest instead: refuse a payload whose
Changed/Deprecated/Removedline cites only records absent frombase_tag, which needs no new agent input and fails closed in the binary where the bijection already lives
The second is closer to the existing shape — launch ship --changelog-json
already re-derives the cut and proves the prose against it, so a baseline check
would join the completeness bijection rather than form a new gate. Deciding
between them is the work this issue holds.
Frontmatter
| id | iss-2609011207114761 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609011207114761-the-changelog-composer-cannot-see-the.md
open on GitHub ↗ · commit history ↗
mentions6
- dcn-26090100000000022026-09-01 — The changelog composer writes only Added and Fixed until it can see the previous release (maintainer, ruling on iss-2609011207114761). The composer's inputs are the records that shipped and their bodies, never the base tag's surface, yet Changed, Deprecated and Removed are claims about exactly that surface, and in v0.7.0 it wrote three such lines about a verb that did not exist in v0.6.9. From here the composer emits no Changed, Deprecated or Removed section, the ingest refuses a composed changelog that carries one, and the release notes say so once so the absence reads as a rule rather than an oversight. Rejected: handing the composer the base tag's command reference (the right long-term input, deferred until the release surface is exported in a form the composer can diff); leaving it to the pre-tag review (a review is not a gate).
- iss-2608311725218136The ingest verb's payload flag is named for its role rather than its content, breaking the house idiom its four siblings follow and colliding with the global rendering flag. The repository's output-contract pattern names the flag after what the JSON CONTAINS: review-json, verdict-json twice, changelog-json. This one is output-json, which names the role instead, and role is exactly the ambiguous axis here because the global json flag means the opposite direction of travel: one is how abcd renders its own result, the other is a path to what an agent returned. The verb's own example shows both on one line, which is where a reader trips. Renaming it to reading-json makes it unambiguous about whose output it is and consistent with the four siblings. The window is now: the verb shipped today and has no users, so the change costs a flag rename and a page regeneration, and the cost rises the moment anything scripts against it.
- itd-183The cold reading sees exactly what the assembler passes — positive inclusion, field projection, and a per-run manifest
- itd-184Four cold-reading definitions, one blindness core — each position licenses a different output, and none may hold another's licence
- itd-185One ingest verb validates every cold-reading output — including what the reading was licensed to produce, not only what it saw
- itd-199A reading is about something narrower than everything, and its scope is a committed, adjustable choice