lib/changelog.sh + changelog-armed — the shape anchor becomes declarable, and ceremony's changelog flips to grouped #182
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#182
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
danmt ruled on discussion #170 (2026-07-25 12:30Z): ceremony's own changelog goes grouped. The reason is doctrinal, not cosmetic — ceremony flat while every consumer groups teaches the next reader that consumers shouldn't group either. The mechanism is the example.
Ceremony's published sections are flat back to 0.1.0, recorded as the repo's shape in #112 D3. Since #159/#163, the one-shape rule anchors the fragment set to the newest published section —
changelog_shape_problem, the one definition shared by the PR guard (changelog-armed L99) and the assembler (L270). That anchor makes the flip inexpressible today: a grouped fragment over flat0.3.0is red on its own PR by design (that guard is #159 doing its job), and a grouped section can only ever be assembled from grouped fragments. Deliberate flips need a door; drift must stay red. This issue builds the door and walks ceremony through it.Prior art: #158 stripped headings from four drifted fragments with every bullet byte-identical. This is that operation, inverted, plus the mechanism that makes it legal.
Spec
###headings; the next## X.Y.Zsection is the first grouped one. Published flat sections stay byte-identical;changelog-monotonicis untouched (#112 D10).changelog.d/shape, containing exactlyflatorgrouped(one line). Present →changelog_shape_problemholds the set to it, outranking the newest-published-section inference. Absent → today's inference binds unchanged, so no consumer sees any behavior change until it opts in (the #112 D8 posture). Any other content — empty, trailing junk, unknown word — is a red diagnosis naming the file, never a silent fallback.changelog_shape_problemonly; guard and assembler cannot disagree. It binds in both callers because it lives in the fragments dir — note the assembler calls withchangelog="". The sentinel is not a fragment:changelog_fragments' glob only matches*.md, soshape(no extension) never enters the list; a test asserts that, not just the glob's current shape.changelog.d/shape=grouped, and convert the five pending fragments in place —167.md,173.md,175.md,178.md,180.mdgain###headings, every bullet byte-identical. Group assignment per bullet is the builder's judgment, stated in the PR body (the existing type labels on the source issues are the obvious guide). Canonical order and per-group sorting are #112 D5, already implemented — nothing to build there.changelog.d/README.mdgains one line naming the sentinel. Ceremony stops being described as flat anywhere the shape is stated.Tasks
lib/changelog.sh—changelog_shape_problemreads<dir>/shapeper D2; diagnosis strings follow the existing "one shape or the other" voice.test/changelog.test.sh— new rows in the #159 shape-predicate block (see test plan), including the D3 fragment-list assertion.changelog.d/shape=grouped; convert the five pending fragments per D4.changelog.d/<this issue>.md— grouped, necessarily: it lands atop the sentinel it ships.Acceptance criteria
changelog.d/shape=grouped, an all-grouped fragment set over flat newest published0.3.0passeschangelog_shape_problem, andchangelog_assembleemits the grouped section in canonical order.0.3.0is still refused — the #159/#163 drift guard is intact, verbatim.groupedsentinel is refused; a malformed sentinel is refused with a diagnosis namingchangelog.d/shape.shapenever appears inchangelog_fragmentsoutput (asserted by test, not by inspection).mainversions only by added###lines — every bullet byte-identical, shown in the PR (the #158 bar, inverted).changelog-armedis green on the flip PR itself;test/run.shgreen on a clean checkout.changelog.d/README.mdname the sentinel and the flip procedure.Test plan
Must-pass: grouped set +
groupedsentinel + flat newest published section; empty fragment dir with a sentinel present; absent sentinel reproducing every existing shape-predicate row untouched. Must-fail: flat set undergroupedsentinel; mixed set regardless of sentinel; sentinel containingGrouped, empty string, or two lines; grouped set with no sentinel over flat newest section (the existing red row, unchanged); a probe flat fragment added atop the flip PR's tree goes red inchangelog-armed.Dependencies
None — builds on
mainat4debf53. Extends the #112 mechanism but is not a child of that epic: the epic's remaining scope is consumer adoption, and this changes no consumer's behavior (D2). Ruling provenance: discussion #170.Claiming — starting now. Branch
build/182-shape-sentinel, draft PR to follow shortly.Draft built and marked ready: PR #183, CI green, whole panel requested (codex, grok, kimi). Claim is parked (shape 2 — live review round): the next move is the panel's first verdicts. Worklog and acceptance checklist live in the PR body.
Parked (round live): PR #183 answered round 1 whole on head
0b158a6— the shared codex/grok blocker ($(cat) laundering trailing newlines past the sentinel's one-line contract) is fixed with the check on the file's own line count, regression rows in both suites. All three panel reviewers re-requested (kimi's approval was of the prior head). Next move belongs to the panel.