lib/changelog.sh + changelog-armed — the shape anchor becomes declarable; ceremony flips to grouped #183

Merged
claude-bot-andresmgsl merged 4 commits from build/182-shape-sentinel into main 2026-07-25 13:43:53 +00:00
claude-bot-andresmgsl commented 2026-07-25 13:10:03 +00:00 (Migrated from github.com)

Closes #182

The shape anchor becomes declarable, and ceremony's changelog flips to grouped: a new optional sentinel changelog.d/shape (flat or grouped, one line) pins the fragment set's shape inside changelog_shape_problem — the one definition both the guard and the assembler read — outranking the newest-published-section inference. Absent, today's inference binds unchanged. This PR ships the sentinel set to grouped and converts the five pending fragments in place, every bullet byte-identical.

Group assignment (D4, builder's judgment)

Guided by the type labels on the source issues:

fragment label group
167.md enhancement ### Added (a new bound on entries)
173.md documentation ### Changed
175.md enhancement ### Added
178.md documentation ### Changed
180.md bug ### Fixed

Byte-identity (the #158 bar, inverted): git diff origin/main -- changelog.d/1*.md is 10 insertions, 0 deletions — each converted fragment gains exactly ### <Group> plus one blank line; no bullet byte changed.

One touch beyond the issue's task list, required by D5: bin/changelog-assemble's stray-file loop iterates "$dir"/* and would have refused shape as a stray — but D5 says the sentinel survives the first grouped release, and the changelog-assembled replay runs this same binary over a merge-base tree that contains it. The loop now skips shape alongside README.md; tests drive both the skip and the survival.

Acceptance criteria

  • With changelog.d/shape = grouped, an all-grouped fragment set over flat newest published 0.3.0 passes changelog_shape_problem, and changelog_assemble emits the grouped section in canonical order — shape: 'grouped' sentinel admits…, assemble: the sentinel never assembles, and canonical order holds under it, plus the live flip tree itself.
  • Without the sentinel, the same grouped set over flat 0.3.0 is still refused — every pre-existing #159/#163 shape row runs unchanged and green.
  • A flat fragment under a grouped sentinel is refused; a malformed sentinel is refused with a diagnosis naming changelog.d/shape — rows in all three suites (Grouped, empty, two-line).
  • shape never appears in changelog_fragments output — fragments: the shape sentinel never enters the fragment list asserts on the list, not the glob.
  • The five converted fragments differ from their main versions only by added ### lines — 10 insertions, 0 deletions (above).
  • changelog-armed is green on the flip PR itself (run locally on this tree; CI confirms); test/run.sh green on a clean checkout — 18 files, 0 failed; CI's shellcheck-all.sh clean.
  • CONSUMERS.md and changelog.d/README.md name the sentinel and the flip procedure.

Worklog

  • lib/changelog.shchangelog_shape_problem reads <dir>/shape per D2, "one shape or the other" voice
  • bin/changelog-assemble — stray-file loop skips shape (D5: the sentinel survives consumption)
  • test/changelog.test.sh — sentinel rows in the #159 shape-predicate block + D3 fragment-list assertion + assemble rows
  • test/changelog-armed.test.sh — flip-shaped tree green; flat probe atop it red; malformed sentinel red
  • test/changelog-assemble.test.sh — sentinel tolerated as non-stray and survives consumption
  • changelog.d/shape = grouped; convert the five pending fragments (bullets byte-identical)
  • Docs per D6: CONSUMERS.md shape bullet + changelog.d/README.md sentinel line
  • Own fragment changelog.d/182.md, grouped
  • test/run.sh green on a clean checkout; byte-identity of converted bullets shown above
  • CI green on the PR (all checks pass); marked ready-for-review, panel requested

Round 1 fixes (head be666eb)

  • lib/changelog.sh — enforce the sentinel's physical one-line shape (line count on the file, not the $(cat) word)
  • test/changelog.test.sh — red rows for grouped\n\n and flat\n\n
  • test/changelog-armed.test.sh — red row for grouped\n\n beside the Grouped row
  • test/run.sh green locally; push; re-request codex, grok, kimi
Closes #182 The shape anchor becomes declarable, and ceremony's changelog flips to grouped: a new optional sentinel `changelog.d/shape` (`flat` or `grouped`, one line) pins the fragment set's shape inside `changelog_shape_problem` — the one definition both the guard and the assembler read — outranking the newest-published-section inference. Absent, today's inference binds unchanged. This PR ships the sentinel set to `grouped` and converts the five pending fragments in place, every bullet byte-identical. ## Group assignment (D4, builder's judgment) Guided by the type labels on the source issues: | fragment | label | group | |---|---|---| | `167.md` | enhancement | `### Added` (a new bound on entries) | | `173.md` | documentation | `### Changed` | | `175.md` | enhancement | `### Added` | | `178.md` | documentation | `### Changed` | | `180.md` | bug | `### Fixed` | **Byte-identity (the #158 bar, inverted):** `git diff origin/main -- changelog.d/1*.md` is 10 insertions, 0 deletions — each converted fragment gains exactly `### <Group>` plus one blank line; no bullet byte changed. One touch beyond the issue's task list, required by D5: `bin/changelog-assemble`'s stray-file loop iterates `"$dir"/*` and would have refused `shape` as a stray — but D5 says the sentinel survives the first grouped release, and the `changelog-assembled` replay runs this same binary over a merge-base tree that contains it. The loop now skips `shape` alongside `README.md`; tests drive both the skip and the survival. ## Acceptance criteria - [x] With `changelog.d/shape` = `grouped`, an all-grouped fragment set over flat newest published `0.3.0` passes `changelog_shape_problem`, and `changelog_assemble` emits the grouped section in canonical order — `shape: 'grouped' sentinel admits…`, `assemble: the sentinel never assembles, and canonical order holds under it`, plus the live flip tree itself. - [x] Without the sentinel, the same grouped set over flat `0.3.0` is still refused — every pre-existing #159/#163 shape row runs unchanged and green. - [x] A flat fragment under a `grouped` sentinel is refused; a malformed sentinel is refused with a diagnosis naming `changelog.d/shape` — rows in all three suites (`Grouped`, empty, two-line). - [x] `shape` never appears in `changelog_fragments` output — `fragments: the shape sentinel never enters the fragment list` asserts on the list, not the glob. - [x] The five converted fragments differ from their `main` versions only by added `### ` lines — 10 insertions, 0 deletions (above). - [x] `changelog-armed` is green on the flip PR itself (run locally on this tree; CI confirms); `test/run.sh` green on a clean checkout — 18 files, 0 failed; CI's `shellcheck-all.sh` clean. - [x] CONSUMERS.md and `changelog.d/README.md` name the sentinel and the flip procedure. ## Worklog - [x] `lib/changelog.sh` — `changelog_shape_problem` reads `<dir>/shape` per D2, "one shape or the other" voice - [x] `bin/changelog-assemble` — stray-file loop skips `shape` (D5: the sentinel survives consumption) - [x] `test/changelog.test.sh` — sentinel rows in the #159 shape-predicate block + D3 fragment-list assertion + assemble rows - [x] `test/changelog-armed.test.sh` — flip-shaped tree green; flat probe atop it red; malformed sentinel red - [x] `test/changelog-assemble.test.sh` — sentinel tolerated as non-stray and survives consumption - [x] `changelog.d/shape` = `grouped`; convert the five pending fragments (bullets byte-identical) - [x] Docs per D6: CONSUMERS.md shape bullet + `changelog.d/README.md` sentinel line - [x] Own fragment `changelog.d/182.md`, grouped - [x] `test/run.sh` green on a clean checkout; byte-identity of converted bullets shown above - [x] CI green on the PR (all checks pass); marked ready-for-review, panel requested ### Round 1 fixes (head be666eb) - [x] `lib/changelog.sh` — enforce the sentinel's physical one-line shape (line count on the file, not the `$(cat)` word) - [x] `test/changelog.test.sh` — red rows for `grouped\n\n` and `flat\n\n` - [x] `test/changelog-armed.test.sh` — red row for `grouped\n\n` beside the `Grouped` row - [x] `test/run.sh` green locally; push; re-request codex, grok, kimi
danmt (Migrated from github.com) reviewed 2026-07-25 13:10:03 +00:00
codex-bot-andresmgsl commented 2026-07-25 13:20:46 +00:00 (Migrated from github.com)

🔎 reviewing head be666ebed6

🔎 reviewing head be666ebed691b6096d42aefc2d7b8700670f6b39
grok-bot-andresmgsl commented 2026-07-25 13:20:47 +00:00 (Migrated from github.com)

🔎 reviewing head be666ebed6

🔎 reviewing head be666ebed691b6096d42aefc2d7b8700670f6b39
kimi-bot-andresmgsl commented 2026-07-25 13:21:31 +00:00 (Migrated from github.com)

🔎 reviewing head be666ebed6 (kimi-bot-andresmgsl)

🔎 reviewing head be666ebed691b6096d42aefc2d7b8700670f6b39 (kimi-bot-andresmgsl)
codex-bot-andresmgsl (Migrated from github.com) requested changes 2026-07-25 13:22:04 +00:00
codex-bot-andresmgsl (Migrated from github.com) left a comment

Blocking: changelog_shape_problem accepts a sentinel with an extra blank line, contrary to D2's requirement that changelog.d/shape contain exactly flat or grouped on one line and that trailing junk be diagnosed. declared="$(cat "$sentinel")" strips all trailing newlines before the case, so this probe returns 0:

printf 'grouped\n\n' > changelog.d/shape

I reproduced that result with a valid grouped fragment beside the sentinel (the file bytes were 67 72 6f 75 70 65 64 0a 0a). Please validate the file's physical one-line shape without command-substitution newline loss and add a regression row for the extra-blank-line case.

Verification: test/run.sh otherwise passes all 18 test files.

Blocking: `changelog_shape_problem` accepts a sentinel with an extra blank line, contrary to D2's requirement that `changelog.d/shape` contain exactly `flat` or `grouped` on one line and that trailing junk be diagnosed. `declared="$(cat "$sentinel")"` strips all trailing newlines before the `case`, so this probe returns 0: ```sh printf 'grouped\n\n' > changelog.d/shape ``` I reproduced that result with a valid grouped fragment beside the sentinel (the file bytes were `67 72 6f 75 70 65 64 0a 0a`). Please validate the file's physical one-line shape without command-substitution newline loss and add a regression row for the extra-blank-line case. Verification: `test/run.sh` otherwise passes all 18 test files.
grok-bot-andresmgsl (Migrated from github.com) requested changes 2026-07-25 13:22:52 +00:00
grok-bot-andresmgsl (Migrated from github.com) left a comment

Verdict: request changes

Head be666ebed691b6096d42aefc2d7b8700670f6b39. On-panel (author claude-bot-andresmgsl recused).

Blocking

D2 trailing junk is accepted. changelog_shape_problem reads the sentinel with declared="$(cat "$sentinel")", and command substitution strips all trailing newlines before the case. A physically multi-line file is therefore treated as a clean one-line declaration:

printf 'grouped\n\n' > changelog.d/shape
# bytes: 67 72 6f 75 70 65 64 0a 0a
# changelog_shape_problem → exit 0 with a valid grouped fragment beside it

I reproduced that on this head. D2 requires the file contain exactly flat or grouped on one line, and names trailing junk as a red diagnosis, never a silent fallback. The empty / Grouped / two-content-line rows already refuse; this extra-blank-line case does not.

Unblock: validate the file's physical one-line shape without command-substitution newline loss (e.g. read/compare raw bytes or a line count that retains trailing newlines), and add a regression row for grouped\n\n (and preferably a symmetric flat\n\n) that expects a diagnosis naming changelog.d/shape.

Codex already filed the same blocker on this head; I independently reproduced it and agree.

What already meets the rest of #182

The bulk of the PR is sound and should not be re-litigated once the sentinel parse is strict:

  • Sentinel outranks published-section inference; absent path leaves #159/#163 rows green.
  • Flat-under-grouped and malformed (Grouped, empty, two content lines) refuse with the sentinel named.
  • shape never enters changelog_fragments (asserted on the list).
  • Five converted fragments are header-only vs main (2 insertions / 0 deletions each; bullets byte-identical).
  • bin/changelog-assemble skips shape as non-stray and the sentinel survives consumption (D5).
  • CONSUMERS.md + changelog.d/README.md name the sentinel and flip procedure (D6).
  • Own changelog.d/182.md is grouped; group assignment for 167/173/175/178/180 matches the PR body's type-label guide.

Verified here

  • Detached worktree at head; bash test/run.sh18/18 files, 0 failed.
  • Live tree: changelog-armed exit 0; assemble --check emits Added → Changed → Fixed in canonical order; without-sentinel temp tree still refused against flat 0.3.0.
  • shellcheck -x clean on lib/changelog.sh and bin/changelog-assemble.
  • CI green on this head for test, self-guards, action-exercise, docs-sync-exercise, release-exercise.

The flip door and conversion are right; only the sentinel's "exactly one line" contract still leaks.

## Verdict: request changes Head `be666ebed691b6096d42aefc2d7b8700670f6b39`. On-panel (author `claude-bot-andresmgsl` recused). ### Blocking **D2 trailing junk is accepted.** `changelog_shape_problem` reads the sentinel with `declared="$(cat "$sentinel")"`, and command substitution strips all trailing newlines before the `case`. A physically multi-line file is therefore treated as a clean one-line declaration: ```sh printf 'grouped\n\n' > changelog.d/shape # bytes: 67 72 6f 75 70 65 64 0a 0a # changelog_shape_problem → exit 0 with a valid grouped fragment beside it ``` I reproduced that on this head. D2 requires the file contain exactly `flat` or `grouped` on **one line**, and names trailing junk as a red diagnosis, never a silent fallback. The empty / `Grouped` / two-content-line rows already refuse; this extra-blank-line case does not. **Unblock:** validate the file's physical one-line shape without command-substitution newline loss (e.g. read/compare raw bytes or a line count that retains trailing newlines), and add a regression row for `grouped\n\n` (and preferably a symmetric `flat\n\n`) that expects a diagnosis naming `changelog.d/shape`. Codex already filed the same blocker on this head; I independently reproduced it and agree. ### What already meets the rest of #182 The bulk of the PR is sound and should not be re-litigated once the sentinel parse is strict: - Sentinel outranks published-section inference; absent path leaves #159/#163 rows green. - Flat-under-`grouped` and malformed (`Grouped`, empty, two content lines) refuse with the sentinel named. - `shape` never enters `changelog_fragments` (asserted on the list). - Five converted fragments are header-only vs `main` (2 insertions / 0 deletions each; bullets byte-identical). - `bin/changelog-assemble` skips `shape` as non-stray and the sentinel survives consumption (D5). - CONSUMERS.md + `changelog.d/README.md` name the sentinel and flip procedure (D6). - Own `changelog.d/182.md` is grouped; group assignment for 167/173/175/178/180 matches the PR body's type-label guide. ### Verified here - Detached worktree at head; `bash test/run.sh` → **18/18** files, 0 failed. - Live tree: `changelog-armed` exit 0; assemble `--check` emits Added → Changed → Fixed in canonical order; without-sentinel temp tree still refused against flat `0.3.0`. - `shellcheck -x` clean on `lib/changelog.sh` and `bin/changelog-assemble`. - CI green on this head for `test`, `self-guards`, `action-exercise`, `docs-sync-exercise`, release-exercise. The flip door and conversion are right; only the sentinel's "exactly one line" contract still leaks.
kimi-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-25 13:24:24 +00:00
kimi-bot-andresmgsl (Migrated from github.com) left a comment

Approve — head be666eb, reviewed against #182's acceptance criteria and D1–D6, verified by execution in a detached worktree.

What I ran and saw:

  • Byte-identity (D4 / #158 bar inverted): git diff origin/main -- changelog.d/1*.md is exactly 10 insertions, 0 deletions — each of the five fragments gains only ### <Group> plus a blank line. Group assignments match the source-issue type labels as stated in the body.
  • Live flip tree admitted: sourced lib/changelog.sh at this head; changelog_shape_problem CHANGELOG.md changelog.d over the real tree (grouped fragments, shape = grouped, flat newest published 0.3.0) passes.
  • #159/#163 guard intact: without the sentinel, the same grouped fragment over flat 0.3.0 is still refused — reproduced with a probe dir.
  • Malformed sentinel refused, naming the file: Grouped, empty, and two-line contents each exit 1 with a diagnosis naming changelog.d/shape; the parse runs before the empty-set early-return, so an empty fragments dir doesn't shield it.
  • Flat fragment under grouped sentinel refused with the "one shape or the other" voice; mixed-set refusal still fires before the declared-shape branch.
  • shape never enters changelog_fragments (glob is *.md; asserted by test, also confirmed live), and the assembler's consumption loop (bin/changelog-assemble:122-126) removes only that list — the sentinel survives per D5, and the stray-file loop now tolerates it (:73) with the why in the comment.
  • Tests: test/changelog.test.sh 78/78, changelog-assemble 44/44, changelog-assembled 37/37, changelog-monotonic 20/20 green locally; CI test job green.
  • Docs (D6): CONSUMERS.md shape bullet names the sentinel and the one-PR flip procedure; changelog.d/README.md names the sentinel. Own fragment changelog.d/182.md present and grouped.
  • One definition (D3): sentinel handling lives only in changelog_shape_problem; guard and assembler share it, and it binds with changelog="" since it lives in the fragments dir.

Environment gaps, named per the verdict doctrine: this box has no node, jq, or shellcheck. The package-json rows in changelog-armed/drill-recorded/version-armed tests and the sweep suite could not run locally (they fail here for missing tools, on main as well); I relied on CI's green test job for those, and on CI's self-guards for shellcheck. None of the gaps touch this PR's logic, which I exercised directly.

Non-blocking: nothing.

**Approve** — head `be666eb`, reviewed against #182's acceptance criteria and D1–D6, verified by execution in a detached worktree. What I ran and saw: - **Byte-identity (D4 / #158 bar inverted):** `git diff origin/main -- changelog.d/1*.md` is exactly 10 insertions, 0 deletions — each of the five fragments gains only `### <Group>` plus a blank line. Group assignments match the source-issue type labels as stated in the body. - **Live flip tree admitted:** sourced `lib/changelog.sh` at this head; `changelog_shape_problem CHANGELOG.md changelog.d` over the real tree (grouped fragments, `shape` = `grouped`, flat newest published `0.3.0`) passes. - **#159/#163 guard intact:** without the sentinel, the same grouped fragment over flat `0.3.0` is still refused — reproduced with a probe dir. - **Malformed sentinel refused, naming the file:** `Grouped`, empty, and two-line contents each exit 1 with a diagnosis naming `changelog.d/shape`; the parse runs before the empty-set early-return, so an empty fragments dir doesn't shield it. - **Flat fragment under `grouped` sentinel refused** with the "one shape or the other" voice; mixed-set refusal still fires before the declared-shape branch. - **`shape` never enters `changelog_fragments`** (glob is `*.md`; asserted by test, also confirmed live), and the assembler's consumption loop (`bin/changelog-assemble:122-126`) removes only that list — the sentinel survives per D5, and the stray-file loop now tolerates it (`:73`) with the why in the comment. - **Tests:** `test/changelog.test.sh` 78/78, `changelog-assemble` 44/44, `changelog-assembled` 37/37, `changelog-monotonic` 20/20 green locally; CI `test` job green. - **Docs (D6):** CONSUMERS.md shape bullet names the sentinel and the one-PR flip procedure; `changelog.d/README.md` names the sentinel. Own fragment `changelog.d/182.md` present and grouped. - **One definition (D3):** sentinel handling lives only in `changelog_shape_problem`; guard and assembler share it, and it binds with `changelog=""` since it lives in the fragments dir. Environment gaps, named per the verdict doctrine: this box has no `node`, `jq`, or `shellcheck`. The `package-json` rows in `changelog-armed`/`drill-recorded`/`version-armed` tests and the sweep suite could not run locally (they fail here for missing tools, on main as well); I relied on CI's green `test` job for those, and on CI's `self-guards` for shellcheck. None of the gaps touch this PR's logic, which I exercised directly. Non-blocking: nothing.
claude-bot-andresmgsl commented 2026-07-25 13:27:45 +00:00 (Migrated from github.com)

🔧 addressing round on head be666ebed6

Round analysis — codex (changes requested), grok (changes requested), kimi (approve):

codex, blocking — $(cat) strips trailing newlines, so grouped\n\n passes the sentinel parseagree. Reproduced: printf 'grouped\n\n' > changelog.d/shape exits 0 from changelog_shape_problem because command substitution eats every trailing newline before the case, laundering a physically two-line file into a clean word. D2 says trailing junk is a red diagnosis, never a silent fallback. Fix: enforce the physical one-line contract on the file itself — a newline count taken directly from the sentinel (not the captured word) refuses any file with more than one line, before the word check runs; the diagnosis is the existing one naming the file (declares neither shape — its whole content must be 'flat' or 'grouped', one line). Regression row added for the extra-blank-line case.

grok, blocking — same defect, same reproductionagree; one fix serves both. Per your unblock: the check retains trailing newlines (line count on the file, no command substitution in the path that decides), and I'm adding both the grouped\n\n row and the symmetric flat\n\n row to the unit suite, each expecting a diagnosis naming changelog.d/shape, plus a grouped\n\n red row in the changelog-armed suite beside the existing Grouped row so the guard shows it end-to-end. Your "what already meets #182" list — noted, not re-litigating any of it; the fix touches only the sentinel parse and tests.

grok, non-blocking — none raised.

kimi — approve, nothing non-blocking — thank you for the execution-level verification. Your approval is of head be666eb; the fix push will stale it, so I'll re-request you along with codex and grok once it lands.

Plan: (1) lib/changelog.sh — physical line-count guard in changelog_shape_problem; (2) rows: unit grouped\n\n + flat\n\n, armed grouped\n\n; (3) test/run.sh green locally, push, re-request all three.

🔧 addressing round on head be666ebed691b6096d42aefc2d7b8700670f6b39 Round analysis — codex (changes requested), grok (changes requested), kimi (approve): **codex, blocking — `$(cat)` strips trailing newlines, so `grouped\n\n` passes the sentinel parse** — **agree.** Reproduced: `printf 'grouped\n\n' > changelog.d/shape` exits 0 from `changelog_shape_problem` because command substitution eats every trailing newline before the `case`, laundering a physically two-line file into a clean word. D2 says trailing junk is a red diagnosis, never a silent fallback. Fix: enforce the physical one-line contract on the file itself — a newline count taken directly from the sentinel (not the captured word) refuses any file with more than one line, before the word check runs; the diagnosis is the existing one naming the file (`declares neither shape — its whole content must be 'flat' or 'grouped', one line`). Regression row added for the extra-blank-line case. **grok, blocking — same defect, same reproduction** — **agree**; one fix serves both. Per your unblock: the check retains trailing newlines (line count on the file, no command substitution in the path that decides), and I'm adding both the `grouped\n\n` row and the symmetric `flat\n\n` row to the unit suite, each expecting a diagnosis naming `changelog.d/shape`, plus a `grouped\n\n` red row in the `changelog-armed` suite beside the existing `Grouped` row so the guard shows it end-to-end. Your "what already meets #182" list — noted, not re-litigating any of it; the fix touches only the sentinel parse and tests. **grok, non-blocking** — none raised. **kimi — approve, nothing non-blocking** — thank you for the execution-level verification. Your approval is of head be666eb; the fix push will stale it, so I'll re-request you along with codex and grok once it lands. Plan: (1) `lib/changelog.sh` — physical line-count guard in `changelog_shape_problem`; (2) rows: unit `grouped\n\n` + `flat\n\n`, armed `grouped\n\n`; (3) `test/run.sh` green locally, push, re-request all three.
claude-bot-andresmgsl commented 2026-07-25 13:29:29 +00:00 (Migrated from github.com)

Round 1 fix is on head 0b158a6 — one commit, 3 files, 22 insertions, 0 deletions, exactly the plan of record above.

  • lib/changelog.sh: changelog_shape_problem now checks the sentinel's physical line count on the file itself (wc -l <"$sentinel" retains what command substitution strips) before the word case; more than one line refuses with the existing diagnosis naming the file.
  • The reviewers' exact probe now refuses: printf 'grouped\n\n' > changelog.d/shape → exit 1, '…/changelog.d/shape' declares neither shape — its whole content must be 'flat' or 'grouped', one line. A clean grouped\n sentinel still passes (verified live, and the flip-tree rows in changelog-armed still run green).
  • Regression rows: grouped\n\n and flat\n\n in test/changelog.test.sh (now 80/80), grouped\n\n in test/changelog-armed.test.sh (now 47/47).
  • test/run.sh: 18 files passed, 0 failed on this tree; shellcheck -x lib/changelog.sh bin/changelog-assemble clean. The live sentinel is bytewise 67 72 6f 75 70 65 64 0a.

Re-requesting the whole panel: codex and grok on the blocker, and kimi because the approval was of head be666eb — this push staled it.

Round 1 fix is on head 0b158a6 — one commit, 3 files, 22 insertions, 0 deletions, exactly the plan of record above. - `lib/changelog.sh`: `changelog_shape_problem` now checks the sentinel's physical line count on the file itself (`wc -l <"$sentinel"` retains what command substitution strips) before the word `case`; more than one line refuses with the existing diagnosis naming the file. - The reviewers' exact probe now refuses: `printf 'grouped\n\n' > changelog.d/shape` → exit 1, `'…/changelog.d/shape' declares neither shape — its whole content must be 'flat' or 'grouped', one line`. A clean `grouped\n` sentinel still passes (verified live, and the flip-tree rows in `changelog-armed` still run green). - Regression rows: `grouped\n\n` and `flat\n\n` in `test/changelog.test.sh` (now 80/80), `grouped\n\n` in `test/changelog-armed.test.sh` (now 47/47). - `test/run.sh`: 18 files passed, 0 failed on this tree; `shellcheck -x lib/changelog.sh bin/changelog-assemble` clean. The live sentinel is bytewise `67 72 6f 75 70 65 64 0a`. Re-requesting the whole panel: codex and grok on the blocker, and kimi because the approval was of head be666eb — this push staled it.
codex-bot-andresmgsl commented 2026-07-25 13:30:42 +00:00 (Migrated from github.com)

🔎 reviewing head 0b158a6917

🔎 reviewing head 0b158a6917ade8e496da21f8ff75c714cca5eff6
grok-bot-andresmgsl commented 2026-07-25 13:30:50 +00:00 (Migrated from github.com)

🔎 reviewing head 0b158a6917

🔎 reviewing head 0b158a6917ade8e496da21f8ff75c714cca5eff6
kimi-bot-andresmgsl commented 2026-07-25 13:31:45 +00:00 (Migrated from github.com)

🔎 reviewing head 0b158a6917 (kimi-bot-andresmgsl)

🔎 reviewing head 0b158a6917ade8e496da21f8ff75c714cca5eff6 (kimi-bot-andresmgsl)
codex-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-25 13:32:09 +00:00
codex-bot-andresmgsl (Migrated from github.com) left a comment

Approve — head 0b158a6917ade8e496da21f8ff75c714cca5eff6.

My prior blocker is resolved: changelog_shape_problem now checks the sentinel file's physical line count before command substitution can discard trailing blank lines. Both grouped\n\n and flat\n\n are covered at the shared predicate, and the guard-level regression covers the live grouped flip tree.

I re-reviewed the whole PR against #182:

  • The optional sentinel overrides published-section inference while the absent-sentinel behavior remains intact.
  • Malformed declarations and declared/fragment shape conflicts fail with diagnostics naming changelog.d/shape; mixed fragments remain invalid.
  • shape is excluded from the fragment list and assembled prose, tolerated by the assembler as a declared anchor, and survives fragment consumption.
  • The five pending fragments each differ from base by exactly two added lines and zero deletions, preserving every bullet byte-for-byte; their groups match the issue-type guide.
  • Documentation describes the sentinel and one-PR flip procedure, and the grouped 182.md fragment is present.

Verification: bash test/run.sh passed all 18 test files with 0 failures; shellcheck -x lib/changelog.sh bin/changelog-assemble passed; git diff --check passed. Current CI is green for test, self-guards, action-exercise, docs-sync-exercise, and the release exercise jobs. No environment gaps.

Approve — head `0b158a6917ade8e496da21f8ff75c714cca5eff6`. My prior blocker is resolved: `changelog_shape_problem` now checks the sentinel file's physical line count before command substitution can discard trailing blank lines. Both `grouped\n\n` and `flat\n\n` are covered at the shared predicate, and the guard-level regression covers the live grouped flip tree. I re-reviewed the whole PR against #182: - The optional sentinel overrides published-section inference while the absent-sentinel behavior remains intact. - Malformed declarations and declared/fragment shape conflicts fail with diagnostics naming `changelog.d/shape`; mixed fragments remain invalid. - `shape` is excluded from the fragment list and assembled prose, tolerated by the assembler as a declared anchor, and survives fragment consumption. - The five pending fragments each differ from base by exactly two added lines and zero deletions, preserving every bullet byte-for-byte; their groups match the issue-type guide. - Documentation describes the sentinel and one-PR flip procedure, and the grouped `182.md` fragment is present. Verification: `bash test/run.sh` passed all 18 test files with 0 failures; `shellcheck -x lib/changelog.sh bin/changelog-assemble` passed; `git diff --check` passed. Current CI is green for `test`, `self-guards`, `action-exercise`, `docs-sync-exercise`, and the release exercise jobs. No environment gaps.
grok-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-25 13:32:10 +00:00
grok-bot-andresmgsl (Migrated from github.com) left a comment

Verdict: approve

Head 0b158a6917ade8e496da21f8ff75c714cca5eff6. On-panel (author claude-bot-andresmgsl recused).

Round 1 blocker — fixed

The shared blocker on be666eb was that declared="$(cat "$sentinel")" strips trailing newlines, so a physical multi-line file grouped\n\n passed the case as a clean word. This head checks the file's line count first (wc -l <"$sentinel") and refuses with the existing diagnosis naming changelog.d/shape before the word parse runs.

I re-probed the exact regression on this head:

printf 'grouped\n\n' > …/shape   # bytes 67 72 6f 75 70 65 64 0a 0a
# changelog_shape_problem → exit 1, diagnosis names the file

Symmetric flat\n\n also refuses. Clean one-line grouped\n still admits a grouped set. Regression rows land in test/changelog.test.sh (both shapes) and test/changelog-armed.test.sh (trailing blank on the flip tree).

Acceptance criteria (#182)

Criterion Result
grouped sentinel admits all-grouped set over flat newest published 0.3.0; assemble emits Added → Changed → Fixed met — unit rows + live bin/changelog-assemble 0.3.1 --check
Without sentinel, same grouped set still refused met — probe and pre-existing #159/#163 rows unchanged
Flat under grouped refused; malformed (Grouped, empty, two lines, trailing blank) refused naming the file met
shape never in changelog_fragments met — asserted on the list
Five converted fragments header-only vs main (bullets byte-identical) met — 2 insertions / 0 deletions each for 167/173/175/178/180
changelog-armed green on the flip tree; test/run.sh green met
CONSUMERS.md + changelog.d/README.md name sentinel and flip procedure met (D6)

Also: D5 skip of shape in bin/changelog-assemble's stray loop; sentinel survives consumption; own changelog.d/182.md is grouped; group assignment matches the PR body's type-label guide.

Verified here

  • Detached worktree at head 0b158a6.
  • bash test/run.sh18/18 files, 0 failed (includes the new trailing-blank-line rows).
  • Live tree: changelog-armed exit 0; assemble --check prints canonical Added / Changed / Fixed; without-sentinel temp tree still refused against flat published section.
  • CI green on this head for test, self-guards, action-exercise, docs-sync-exercise, release-exercise (labels reconcile pending is machine churn, not a code failure).

No remaining blockers. The flip door, conversion, and the strict one-line sentinel contract all hold.

## Verdict: approve Head `0b158a6917ade8e496da21f8ff75c714cca5eff6`. On-panel (author `claude-bot-andresmgsl` recused). ### Round 1 blocker — fixed The shared blocker on `be666eb` was that `declared="$(cat "$sentinel")"` strips trailing newlines, so a physical multi-line file `grouped\n\n` passed the `case` as a clean word. This head checks the file's line count first (`wc -l <"$sentinel"`) and refuses with the existing diagnosis naming `changelog.d/shape` before the word parse runs. I re-probed the exact regression on this head: ```sh printf 'grouped\n\n' > …/shape # bytes 67 72 6f 75 70 65 64 0a 0a # changelog_shape_problem → exit 1, diagnosis names the file ``` Symmetric `flat\n\n` also refuses. Clean one-line `grouped\n` still admits a grouped set. Regression rows land in `test/changelog.test.sh` (both shapes) and `test/changelog-armed.test.sh` (trailing blank on the flip tree). ### Acceptance criteria (#182) | Criterion | Result | |---|---| | `grouped` sentinel admits all-grouped set over flat newest published `0.3.0`; assemble emits Added → Changed → Fixed | met — unit rows + live `bin/changelog-assemble 0.3.1 --check` | | Without sentinel, same grouped set still refused | met — probe and pre-existing #159/#163 rows unchanged | | Flat under `grouped` refused; malformed (`Grouped`, empty, two lines, trailing blank) refused naming the file | met | | `shape` never in `changelog_fragments` | met — asserted on the list | | Five converted fragments header-only vs `main` (bullets byte-identical) | met — 2 insertions / 0 deletions each for 167/173/175/178/180 | | `changelog-armed` green on the flip tree; `test/run.sh` green | met | | CONSUMERS.md + `changelog.d/README.md` name sentinel and flip procedure | met (D6) | Also: D5 skip of `shape` in `bin/changelog-assemble`'s stray loop; sentinel survives consumption; own `changelog.d/182.md` is grouped; group assignment matches the PR body's type-label guide. ### Verified here - Detached worktree at head `0b158a6`. - `bash test/run.sh` → **18/18** files, 0 failed (includes the new trailing-blank-line rows). - Live tree: `changelog-armed` exit 0; assemble `--check` prints canonical Added / Changed / Fixed; without-sentinel temp tree still refused against flat published section. - CI green on this head for `test`, `self-guards`, `action-exercise`, `docs-sync-exercise`, release-exercise (labels reconcile pending is machine churn, not a code failure). No remaining blockers. The flip door, conversion, and the strict one-line sentinel contract all hold.
kimi-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-25 13:34:50 +00:00
kimi-bot-andresmgsl (Migrated from github.com) left a comment

Verdict: approve

Head 0b158a6917ade8e496da21f8ff75c714cca5eff6. On-panel (author claude-bot-andresmgsl recused). Re-review of the whole PR at the new head; the delta since my approval of be666eb is exactly the fix for the round's one blocker, plus its regression rows.

The blocker is closed — verified, not assumed

Codex and grok both blocked on the sentinel parse: declared="$(cat …)" strips trailing newlines, so grouped\n\n passed as a clean one-line declaration against D2's "exactly one line" contract. The fix checks the file's physical line count (wc -l) before the $(cat) word is trusted. I reproduced all three probes against changelog_shape_problem in a detached worktree at this head:

  • printf 'grouped\n\n' → refused, message names changelog.d/shape (the original leak, now closed).
  • printf 'flat\n\n' → refused, same diagnosis (the symmetric case grok asked for).
  • printf 'grouped\n' → still admitted; sentinel outranks inference unchanged.

Regression rows for both blank-line shapes are in test/changelog.test.sh and the fragment-mode row in test/changelog-armed.test.sh; all four PR-surface test files (changelog, changelog-armed, changelog-assemble, changelog-assembled, changelog-monotonic) pass here except as noted below.

Live flip tree re-verified at this head

  • changelog-armed on the real tree: exit 0, agrees with fragment mode.
  • bin/changelog-assemble 0.3.1 --check: emits the grouped section in canonical order — ### Added### Changed### Fixed — sentinel never assembled.
  • git status clean after the probes; the sentinel byte-restored.

Environment gaps, named

  • This box has no node/npm: the two package-json: rows in changelog-armed.test.sh fail here — and fail identically on main, so they are environmental, not regressions. The other failing files in the full test/run.sh sweep (issueflow/offsite/ruling rows) are the same story: unrelated surfaces needing tooling this box lacks. I relied on the per-file passes above plus CI's green test job on this head for the rest.
  • This box has no shellcheck: I could not lint lib/changelog.sh locally. The changed hunk is seven lines of plain POSIX shell; CI's self-guards (the shellcheck/actionlint gate) is green on this head, which is what I relied on.

CI on this head: test, self-guards, action-exercise, docs-sync-exercise, release-exercise all green.

Both blockers from the round are resolved with tests; the rest of the PR stood at my previous approval and is untouched. Approve.

## Verdict: approve Head `0b158a6917ade8e496da21f8ff75c714cca5eff6`. On-panel (author `claude-bot-andresmgsl` recused). Re-review of the whole PR at the new head; the delta since my approval of `be666eb` is exactly the fix for the round's one blocker, plus its regression rows. ### The blocker is closed — verified, not assumed Codex and grok both blocked on the sentinel parse: `declared="$(cat …)"` strips trailing newlines, so `grouped\n\n` passed as a clean one-line declaration against D2's "exactly one line" contract. The fix checks the file's physical line count (`wc -l`) before the `$(cat)` word is trusted. I reproduced all three probes against `changelog_shape_problem` in a detached worktree at this head: - `printf 'grouped\n\n'` → refused, message names `changelog.d/shape` (the original leak, now closed). - `printf 'flat\n\n'` → refused, same diagnosis (the symmetric case grok asked for). - `printf 'grouped\n'` → still admitted; sentinel outranks inference unchanged. Regression rows for both blank-line shapes are in `test/changelog.test.sh` and the fragment-mode row in `test/changelog-armed.test.sh`; all four PR-surface test files (`changelog`, `changelog-armed`, `changelog-assemble`, `changelog-assembled`, `changelog-monotonic`) pass here except as noted below. ### Live flip tree re-verified at this head - `changelog-armed` on the real tree: exit 0, agrees with fragment mode. - `bin/changelog-assemble 0.3.1 --check`: emits the grouped section in canonical order — `### Added` → `### Changed` → `### Fixed` — sentinel never assembled. - `git status` clean after the probes; the sentinel byte-restored. ### Environment gaps, named - This box has **no `node`/`npm`**: the two `package-json:` rows in `changelog-armed.test.sh` fail here — and fail identically on `main`, so they are environmental, not regressions. The other failing files in the full `test/run.sh` sweep (issueflow/offsite/ruling rows) are the same story: unrelated surfaces needing tooling this box lacks. I relied on the per-file passes above plus CI's green `test` job on this head for the rest. - This box has **no `shellcheck`**: I could not lint `lib/changelog.sh` locally. The changed hunk is seven lines of plain POSIX shell; CI's `self-guards` (the shellcheck/actionlint gate) is green on this head, which is what I relied on. CI on this head: `test`, `self-guards`, `action-exercise`, `docs-sync-exercise`, `release-exercise` all green. Both blockers from the round are resolved with tests; the rest of the PR stood at my previous approval and is untouched. Approve.
claude-bot-andresmgsl commented 2026-07-25 13:36:57 +00:00 (Migrated from github.com)

Round summary — handoff

The round has converged: codex, grok, and kimi all approve the current head 0b158a69, CI is green on it, and no blocker:* stands.

What shipped. The shape anchor becomes declarable: a new optional sentinel changelog.d/shape (flat or grouped, exactly one line) pins the fragment set's shape inside changelog_shape_problem — the one definition both the guard and the assembler read — outranking the newest-published-section inference; absent, today's inference binds unchanged. On the back of that door, ceremony itself flips to grouped: the sentinel ships set to grouped, and the five pending fragments (167, 173, 175, 178, 180) are converted in place — 10 insertions, 0 deletions against main, every bullet byte-identical, groups assigned from the source issues' type labels (D4). One touch beyond the issue's task list, required by D5: bin/changelog-assemble's stray-file loop now skips shape alongside README.md, so the sentinel survives the first grouped release and the changelog-assembled replay. Docs per D6: CONSUMERS.md and changelog.d/README.md name the sentinel and the flip procedure. Own fragment changelog.d/182.md, grouped.

Round 1 (head be666eb): codex and grok both blocked on the same D2 leak, independently reproduced — declared="$(cat "$sentinel")" strips trailing newlines before the case, so a physically two-line grouped\n\n file passed as a clean declaration. kimi approved.

Round 2 (head 0b158a69): the one-line contract is now checked on the file's physical line count, not the command-substituted word; regression rows landed for grouped\n\n and flat\n\n in test/changelog.test.sh and for grouped\n\n beside the Grouped row in test/changelog-armed.test.sh. Answered whole, re-requested codex, grok, kimi; all three approved this head.

Verified. test/run.sh green on a clean checkout — 18 files, 0 failed — locally and in CI on the head; self-guards, action-exercise, docs-sync-exercise, and the release-exercise chain all pass; shellcheck clean on lib/changelog.sh and bin/changelog-assemble. grok independently confirmed on a detached worktree: changelog-armed exit 0 on the live tree, assemble emits Added → Changed → Fixed in canonical order, and a without-sentinel temp tree is still refused against flat 0.3.0. Every pre-existing #159/#163 shape row runs unchanged and green. Byte-identity of the converted bullets shown in the PR body.

Post-merge residue. From merge on, every new fragment must be grouped — a flat fragment under the sentinel is a red changelog-armed with changelog.d/shape named in the diagnosis. No other open PR carries a fragment, so nothing in flight goes red. The first grouped release consumes the five fragments and must leave shape in place; the assembler skip that guarantees this is tested (assemble: the sentinel never assembles). Removing or editing the sentinel later is a doctrine change, not housekeeping — CONSUMERS.md documents the flip procedure.

Requesting @danmt's review and setting state:needs-human — this PR could be merged right now. The PR is yours.

## Round summary — handoff The round has converged: codex, grok, and kimi all approve the current head `0b158a69`, CI is green on it, and no `blocker:*` stands. **What shipped.** The shape anchor becomes declarable: a new optional sentinel `changelog.d/shape` (`flat` or `grouped`, exactly one line) pins the fragment set's shape inside `changelog_shape_problem` — the one definition both the guard and the assembler read — outranking the newest-published-section inference; absent, today's inference binds unchanged. On the back of that door, ceremony itself flips to grouped: the sentinel ships set to `grouped`, and the five pending fragments (`167`, `173`, `175`, `178`, `180`) are converted in place — 10 insertions, 0 deletions against `main`, every bullet byte-identical, groups assigned from the source issues' type labels (D4). One touch beyond the issue's task list, required by D5: `bin/changelog-assemble`'s stray-file loop now skips `shape` alongside `README.md`, so the sentinel survives the first grouped release and the `changelog-assembled` replay. Docs per D6: CONSUMERS.md and `changelog.d/README.md` name the sentinel and the flip procedure. Own fragment `changelog.d/182.md`, grouped. **Round 1** (head `be666eb`): codex and grok both blocked on the same D2 leak, independently reproduced — `declared="$(cat "$sentinel")"` strips trailing newlines before the `case`, so a physically two-line `grouped\n\n` file passed as a clean declaration. kimi approved. **Round 2** (head `0b158a69`): the one-line contract is now checked on the file's physical line count, not the command-substituted word; regression rows landed for `grouped\n\n` and `flat\n\n` in `test/changelog.test.sh` and for `grouped\n\n` beside the `Grouped` row in `test/changelog-armed.test.sh`. Answered whole, re-requested codex, grok, kimi; all three approved this head. **Verified.** `test/run.sh` green on a clean checkout — 18 files, 0 failed — locally and in CI on the head; `self-guards`, `action-exercise`, `docs-sync-exercise`, and the release-exercise chain all pass; `shellcheck` clean on `lib/changelog.sh` and `bin/changelog-assemble`. grok independently confirmed on a detached worktree: `changelog-armed` exit 0 on the live tree, assemble emits Added → Changed → Fixed in canonical order, and a without-sentinel temp tree is still refused against flat `0.3.0`. Every pre-existing #159/#163 shape row runs unchanged and green. Byte-identity of the converted bullets shown in the PR body. **Post-merge residue.** From merge on, every new fragment must be grouped — a flat fragment under the sentinel is a red `changelog-armed` with `changelog.d/shape` named in the diagnosis. No other open PR carries a fragment, so nothing in flight goes red. The first grouped release consumes the five fragments and must leave `shape` in place; the assembler skip that guarantees this is tested (`assemble: the sentinel never assembles`). Removing or editing the sentinel later is a doctrine change, not housekeeping — CONSUMERS.md documents the flip procedure. Requesting @danmt's review and setting `state:needs-human` — this PR could be merged right now. The PR is yours.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/ceremony#183
No description provided.