fix: refuse release PRs that strand target fragments #255

Merged
andres merged 3 commits from build/253-stranded-fragments into main 2026-08-24 22:55:26 +00:00

Closes #253

Acceptance criteria

  • A release-PR fixture whose target head carries a fragment the PR did not consume is red, and the message names that fragment's filename and the changelog-assemble re-run.
  • The same fixture with that fragment absent from the target head is green.
  • A fixture whose target head advances without adding a fragment is green.
  • At HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21 with base_ref=5a8fce8^1 (5be223a020a329fbf4249494755f895e092137c3), the historical case exits 1 naming changelog.d/238.md; output is recorded in the checkpoint comment.
  • Existing focused cases remain intact and the assertion count rises from 39 to 42.
  • Development, legacy, already-stamped, and vacuous shapes retain their existing green wording.
  • Strict mode still hard-fails for an unresolvable base ref.
  • The diff contains exactly the guard, its focused test, and changelog.d/253.md.
  • Whole suite, sanctioned shellcheck, and git diff --check are green.

Worklog

  • Claim #253 and create the isolated branch from origin/main.
  • Add target-stranding tests first and record the expected red run (39 passed, 3 failed).
  • Implement the target-head fragment refusal and header documentation.
  • Re-run the focused test suite green (42 passed, 0 failed).
  • Reproduce the historical 0.6.2 case at the corrected pre-merge target ref; exit 1 names changelog.d/238.md and the rebase/re-run remedy.
  • Add changelog.d/253.md.
  • Run the whole suite (31 files), sanctioned shellcheck (64 scripts), and diff checks green.
  • Re-verify the clean pushed head and prepare the exact-head first-round handoff.

Round log

Round at 5823f3d7

Round passed with no written reply.

Closes #253 ## Acceptance criteria - [x] A release-PR fixture whose target head carries a fragment the PR did not consume is red, and the message names that fragment's filename and the `changelog-assemble` re-run. - [x] The same fixture with that fragment absent from the target head is green. - [x] A fixture whose target head advances without adding a fragment is green. - [x] At `HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21` with `base_ref=5a8fce8^1` (`5be223a020a329fbf4249494755f895e092137c3`), the historical case exits 1 naming `changelog.d/238.md`; output is recorded in the checkpoint comment. - [x] Existing focused cases remain intact and the assertion count rises from 39 to 42. - [x] Development, legacy, already-stamped, and vacuous shapes retain their existing green wording. - [x] Strict mode still hard-fails for an unresolvable base ref. - [x] The diff contains exactly the guard, its focused test, and `changelog.d/253.md`. - [x] Whole suite, sanctioned shellcheck, and `git diff --check` are green. ## Worklog - [x] Claim #253 and create the isolated branch from `origin/main`. - [x] Add target-stranding tests first and record the expected red run (39 passed, 3 failed). - [x] Implement the target-head fragment refusal and header documentation. - [x] Re-run the focused test suite green (42 passed, 0 failed). - [x] Reproduce the historical 0.6.2 case at the corrected pre-merge target ref; exit 1 names `changelog.d/238.md` and the rebase/re-run remedy. - [x] Add `changelog.d/253.md`. - [x] Run the whole suite (31 files), sanctioned shellcheck (64 scripts), and diff checks green. - [x] Re-verify the clean pushed head and prepare the exact-head first-round handoff. ## Round log <!-- round:5823f3d7b7724c574364dd1c1d95ed969afa1fc8 --> **Round at 5823f3d7** _Round passed with no written reply._
codex-bot-andresmgsl added 1 commit 2026-08-24 20:14:38 +00:00
test: expose target-head stranded fragments
Some checks failed
CI / test (pull_request) Failing after 3m47s
CI / release-exercise (pull_request) Successful in 24s
CI / self-guards (pull_request) Successful in 21s
CI / action-exercise (pull_request) Successful in 20s
CI / docs-sync-exercise (pull_request) Successful in 20s
labels / labels (pull_request) Successful in 22s
Refs guard / refs-not-closing (pull_request) Successful in 20s
a2b9b30930
codex-bot-andresmgsl changed title from fix: refuse release PRs that strand target fragments to WIP: fix: refuse release PRs that strand target fragments 2026-08-24 20:14:45 +00:00
codex-bot-andresmgsl added 1 commit 2026-08-24 20:16:06 +00:00
fix: refuse target-head stranded fragments
All checks were successful
CI / test (pull_request) Successful in 3m47s
CI / release-exercise (pull_request) Successful in 25s
CI / self-guards (pull_request) Successful in 21s
CI / action-exercise (pull_request) Successful in 20s
CI / docs-sync-exercise (pull_request) Successful in 20s
Refs guard / refs-not-closing (pull_request) Successful in 20s
labels / labels (pull_request) Successful in 22s
0f3d3b36eb
codex-bot-andresmgsl added 1 commit 2026-08-24 20:17:48 +00:00
docs: record stranded-fragment refusal
All checks were successful
CI / test (pull_request) Successful in 3m47s
CI / release-exercise (pull_request) Successful in 25s
CI / self-guards (pull_request) Successful in 21s
CI / action-exercise (pull_request) Successful in 20s
CI / docs-sync-exercise (pull_request) Successful in 20s
labels / labels (pull_request) Successful in 22s
Refs guard / refs-not-closing (pull_request) Successful in 20s
5823f3d7b7
Author
Member

Checkpoint — implementation and all unblocked verification are complete at 5823f3d7db5d14a77271d05d14f991ffb833ad5b; this remains draft pending the issue-spec correction.

Historical evidence:

PR head:    809b7e907a8fb05136b268ed22ed9c92b1346c21
Target head: 5be223a020a329fbf4249494755f895e092137c3 (`5a8fce8^1`)
Merge base:  7bdae45c9847b77912fd0f8d9abe43977fc41045
exit=1
changelog-assembled: fragment(s) on target `5a8fce8^1` were not consumed by this release PR:
    changelog.d/238.md
Fix: rebase onto the target head and re-run bin/changelog-assemble `0.6.2`.

The literal issue ref 5a8fce8 is the post-merge commit with the PR head as its second parent, so it makes the merge base equal HEAD and correctly returns the preserved vacuous notice. I asked @claude-lead-andresmgsl on #253 to correct the criterion to the pre-merge target ref.

Verification:

  • TDD red: 39 passed, 3 failed for the missing refusal/messages.
  • Focused green: 42 passed, 0 failed.
  • Whole suite: 31 test files passed, 0 failed.
  • Sanctioned shellcheck: 64 tracked scripts clean.
  • git diff --check: clean.
  • Diff scope: exactly the guard, its focused test, and changelog.d/253.md.

No round signal or review request is being posted while that acceptance criterion remains unresolved.

Checkpoint — implementation and all unblocked verification are complete at `5823f3d7db5d14a77271d05d14f991ffb833ad5b`; this remains draft pending the issue-spec correction. Historical evidence: ``` PR head: 809b7e907a8fb05136b268ed22ed9c92b1346c21 Target head: 5be223a020a329fbf4249494755f895e092137c3 (`5a8fce8^1`) Merge base: 7bdae45c9847b77912fd0f8d9abe43977fc41045 exit=1 changelog-assembled: fragment(s) on target `5a8fce8^1` were not consumed by this release PR: changelog.d/238.md Fix: rebase onto the target head and re-run bin/changelog-assemble `0.6.2`. ``` The literal issue ref `5a8fce8` is the post-merge commit with the PR head as its second parent, so it makes the merge base equal HEAD and correctly returns the preserved vacuous notice. I asked @claude-lead-andresmgsl on #253 to correct the criterion to the pre-merge target ref. Verification: - TDD red: 39 passed, 3 failed for the missing refusal/messages. - Focused green: 42 passed, 0 failed. - Whole suite: 31 test files passed, 0 failed. - Sanctioned shellcheck: 64 tracked scripts clean. - `git diff --check`: clean. - Diff scope: exactly the guard, its focused test, and `changelog.d/253.md`. No round signal or review request is being posted while that acceptance criterion remains unresolved.
Author
Member

Correction to the checkpoint above: the exact current head is 5823f3d7b7724c574364dd1c1d95ed969afa1fc8. The earlier manually expanded SHA after the correct 5823f3d prefix was a transcription error.

Correction to the checkpoint above: the exact current head is `5823f3d7b7724c574364dd1c1d95ed969afa1fc8`. The earlier manually expanded SHA after the correct `5823f3d` prefix was a transcription error.
forgejo-actions added the
state:building
label 2026-08-24 20:31:28 +00:00
Author
Member

Build complete on head 5823f3d7b7724c574364dd1c1d95ed969afa1fc8.

The triage correction is accepted: the historical target is the pre-merge ref 5a8fce8^1 (5be223a020a329fbf4249494755f895e092137c3), not the post-merge commit. The checkpointed run against HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21 exits 1, names changelog.d/238.md, and prints the rebase plus changelog-assemble 0.6.2 remedy. No code change was required by that correction.

Delivered:

  • target-head fragment inventory and set-difference refusal inside the existing applicable release branch;
  • a remedy naming every stranded path, the target-head rebase, and assembler re-run;
  • explicit residual-window documentation;
  • red-first target-stranding coverage plus green absent-fragment and unrelated-target-advance controls;
  • changelog.d/253.md.

Fresh verification on this exact head:

  • bash test/changelog-assembled.test.sh: 42 passed, 0 failed;
  • bash test/run.sh: 31 test files passed, 0 failed;
  • bash .github/scripts/shellcheck-all.sh: 64 tracked scripts clean;
  • git diff --check origin/main..HEAD: clean;
  • diff scope: exactly actions/changelog-assembled/changelog-assembled.sh, changelog.d/253.md, and test/changelog-assembled.test.sh;
  • worktree clean and pushed head equals local head.

Every acceptance criterion and worklog item in the PR body is checked. This is the first-round handoff; no reviewer verdict exists to answer.

Build complete on head `5823f3d7b7724c574364dd1c1d95ed969afa1fc8`. The triage correction is accepted: the historical target is the pre-merge ref `5a8fce8^1` (`5be223a020a329fbf4249494755f895e092137c3`), not the post-merge commit. The checkpointed run against `HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21` exits 1, names `changelog.d/238.md`, and prints the rebase plus `changelog-assemble 0.6.2` remedy. No code change was required by that correction. Delivered: - target-head fragment inventory and set-difference refusal inside the existing applicable release branch; - a remedy naming every stranded path, the target-head rebase, and assembler re-run; - explicit residual-window documentation; - red-first target-stranding coverage plus green absent-fragment and unrelated-target-advance controls; - `changelog.d/253.md`. Fresh verification on this exact head: - `bash test/changelog-assembled.test.sh`: 42 passed, 0 failed; - `bash test/run.sh`: 31 test files passed, 0 failed; - `bash .github/scripts/shellcheck-all.sh`: 64 tracked scripts clean; - `git diff --check origin/main..HEAD`: clean; - diff scope: exactly `actions/changelog-assembled/changelog-assembled.sh`, `changelog.d/253.md`, and `test/changelog-assembled.test.sh`; - worktree clean and pushed head equals local head. Every acceptance criterion and worklog item in the PR body is checked. This is the first-round handoff; no reviewer verdict exists to answer.
Author
Member

📣 round answered at head 5823f3d7b7

📣 round answered at head 5823f3d7b7724c574364dd1c1d95ed969afa1fc8
codex-bot-andresmgsl changed title from WIP: fix: refuse release PRs that strand target fragments to fix: refuse release PRs that strand target fragments 2026-08-24 20:46:58 +00:00
forgejo-actions added the
scope:guards
label 2026-08-24 22:03:45 +00:00
forgejo-actions added
blocker:unrequested
state:addressing
and removed
state:building
labels 2026-08-24 22:21:54 +00:00
codex-bot-andresmgsl requested review from glm-bot-andresmgsl 2026-08-24 22:34:19 +00:00
codex-bot-andresmgsl requested review from claude-bot-andresmgsl 2026-08-24 22:34:20 +00:00
codex-bot-andresmgsl requested review from kimi-bot-andresmgsl 2026-08-24 22:34:20 +00:00
codex-bot-andresmgsl added the
state:bots-reviewing
label 2026-08-24 22:34:21 +00:00
forgejo-actions removed the
blocker:unrequested
state:addressing
labels 2026-08-24 22:35:48 +00:00

🔎 reviewing head 5823f3d7b7

  • Acceptance criteria checked against #253 (all nine)
  • test/changelog-assembled.test.sh — 42 passed / 0 failed at the head; 37 at origin/main (+5)
  • New tests replayed against the base script: 39 passed / 3 failed — the three stranding assertions genuinely fail before the fix
  • Historical case reproduced: HEAD=809b7e90, base_ref=5be223a → exit 1 naming changelog.d/238.md; base script green on the same pairing
  • Sanctioned shellcheck sweep (64 scripts) and git diff --check clean
  • Whole suite: 29/31 files green here; labels-bootstrap and labels-dispatch fail identically at origin/main (no yq in this box) — CI CI / test is green at this head
  • Verdict submitted
🔎 reviewing head 5823f3d7b7724c574364dd1c1d95ed969afa1fc8 - [x] Acceptance criteria checked against #253 (all nine) - [x] `test/changelog-assembled.test.sh` — 42 passed / 0 failed at the head; 37 at `origin/main` (+5) - [x] New tests replayed against the base script: 39 passed / 3 failed — the three stranding assertions genuinely fail before the fix - [x] Historical case reproduced: `HEAD=809b7e90`, `base_ref=5be223a` → exit 1 naming `changelog.d/238.md`; base script green on the same pairing - [x] Sanctioned shellcheck sweep (64 scripts) and `git diff --check` clean - [x] Whole suite: 29/31 files green here; `labels-bootstrap` and `labels-dispatch` fail identically at `origin/main` (no `yq` in this box) — CI `CI / test` is green at this head - [x] Verdict submitted

🔎 reviewing head 5823f3d7b7

  • head fetched, detached worktree review-255 created; reviewing against issue #253 acceptance criteria
  • diff scope verified: exactly guard + focused test + changelog.d/253.md; git diff --check clean
  • focused suite 42/42 green (37 before, pure additions); whole suite 31/31 green; sanctioned shellcheck 64 scripts clean
  • historical case reproduced over real history at HEAD=809b7e90 base_ref=5a8fce8^1: exit 1 naming changelog.d/238.md with rebase + changelog-assemble 0.6.2 remedy; vacuous notice preserved on the post-merge ref
  • CI combined status success at head; on-panel (panel minus author codex)
  • composing verdict
🔎 reviewing head 5823f3d7b7724c574364dd1c1d95ed969afa1fc8 - head fetched, detached worktree review-255 created; reviewing against issue #253 acceptance criteria - diff scope verified: exactly guard + focused test + changelog.d/253.md; git diff --check clean - focused suite 42/42 green (37 before, pure additions); whole suite 31/31 green; sanctioned shellcheck 64 scripts clean - historical case reproduced over real history at HEAD=809b7e90 base_ref=5a8fce8^1: exit 1 naming changelog.d/238.md with rebase + changelog-assemble 0.6.2 remedy; vacuous notice preserved on the post-merge ref - CI combined status success at head; on-panel (panel minus author codex) - composing verdict

🔎 reviewing head 5823f3d7b7

  • Diff scope verified: actions/changelog-assembled/changelog-assembled.sh, test/changelog-assembled.test.sh, changelog.d/253.md.
  • test/changelog-assembled.test.sh: 42 passed, 0 failed.
  • Historical case reproduced: HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21, base_ref=5be223a020a329fbf4249494755f895e092137c3 exits 1 naming changelog.d/238.md with rebase/re-run remedy.
  • shellcheck: 64 tracked scripts clean; git diff --check: clean.
  • Whole suite (test/run.sh): 27 files passed; 4 files failed — all package-json cases blocked by missing node, unrelated to this change.
🔎 reviewing head 5823f3d7b7724c574364dd1c1d95ed969afa1fc8 - Diff scope verified: actions/changelog-assembled/changelog-assembled.sh, test/changelog-assembled.test.sh, changelog.d/253.md. - test/changelog-assembled.test.sh: 42 passed, 0 failed. - Historical case reproduced: HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21, base_ref=5be223a020a329fbf4249494755f895e092137c3 exits 1 naming changelog.d/238.md with rebase/re-run remedy. - shellcheck: 64 tracked scripts clean; git diff --check: clean. - Whole suite (test/run.sh): 27 files passed; 4 files failed — all package-json cases blocked by missing node, unrelated to this change.
kimi-bot-andresmgsl approved these changes 2026-08-24 22:41:38 +00:00
kimi-bot-andresmgsl left a comment
Member

Approve.

Reviewed against #253 acceptance criteria at head 5823f3d7b7724c574364dd1c1d95ed969afa1fc8.

Verified:

  • Diff scope is exactly actions/changelog-assembled/changelog-assembled.sh, test/changelog-assembled.test.sh, and changelog.d/253.md.
  • test/changelog-assembled.test.sh passes 42/42 (up from 39), including the three new target-stranding cases and the green controls for absent-fragment and unrelated-target-advance.
  • The four inapplicable shapes still pass with their existing NOTICE wording; STRICT still hard-fails an unresolvable base ref.
  • Historical case reproduced locally: HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21, base_ref=5be223a020a329fbf4249494755f895e092137c3 exits 1 naming changelog.d/238.md and prints the rebase plus changelog-assemble 0.6.2 remedy.
  • bash .github/scripts/shellcheck-all.sh clean over 64 tracked scripts.
  • git diff --check origin/main..HEAD clean.

Could not verify / relied on instead:

  • bash test/run.sh reports 4 failed test files, all package-json version-source cases that require node, which is not installed in this box. I relied on the focused test/changelog-assembled.test.sh pass and the unchanged nature of those other files; the failures are pre-existing environmental gaps, not introduced by this change.

The implementation matches the spec: the new refusal sits inside the existing applicable release branch, preserves all skip/notice disciplines, states the residual window honestly in the header, and the message names each stranded file plus the mechanical fix.

Approve. Reviewed against #253 acceptance criteria at head `5823f3d7b7724c574364dd1c1d95ed969afa1fc8`. Verified: - Diff scope is exactly `actions/changelog-assembled/changelog-assembled.sh`, `test/changelog-assembled.test.sh`, and `changelog.d/253.md`. - `test/changelog-assembled.test.sh` passes 42/42 (up from 39), including the three new target-stranding cases and the green controls for absent-fragment and unrelated-target-advance. - The four inapplicable shapes still pass with their existing NOTICE wording; STRICT still hard-fails an unresolvable base ref. - Historical case reproduced locally: `HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21`, `base_ref=5be223a020a329fbf4249494755f895e092137c3` exits 1 naming `changelog.d/238.md` and prints the rebase plus `changelog-assemble 0.6.2` remedy. - `bash .github/scripts/shellcheck-all.sh` clean over 64 tracked scripts. - `git diff --check origin/main..HEAD` clean. Could not verify / relied on instead: - `bash test/run.sh` reports 4 failed test files, all `package-json` version-source cases that require `node`, which is not installed in this box. I relied on the focused `test/changelog-assembled.test.sh` pass and the unchanged nature of those other files; the failures are pre-existing environmental gaps, not introduced by this change. The implementation matches the spec: the new refusal sits inside the existing applicable release branch, preserves all skip/notice disciplines, states the residual window honestly in the header, and the message names each stranded file plus the mechanical fix.
glm-bot-andresmgsl approved these changes 2026-08-24 22:41:42 +00:00
glm-bot-andresmgsl left a comment
Member

Verdict: approve — head 5823f3d7b7724c574364dd1c1d95ed969afa1fc8 against issue #253.

Acceptance criteria — all verified by running, not reading

  • Stranded-fragment fixture red with names: test/changelog-assembled.test.sh green 42/42 in my detached worktree; the three red checks assert the filename (changelog.d/30.md), the rebase remedy, and the changelog-assemble '0.2.0' re-run.
  • Fragment-absent control green: same diverged fixture, exit 0 byte-for-byte.
  • Unrelated target advance green: independent target-unrelated fixture, exit 0.
  • Historical case reproduced over real history: I checked out 809b7e90 (the !250 head) in a second throwaway worktree and ran this PR's guard with base_ref=5a8fce8^1 (5be223a): exit 1, naming changelog.d/238.md, with the rebase + changelog-assemble '0.6.2' remedy. The vacuous notice is also preserved on the post-merge ref 5a8fce8 itself, confirming the triage correction's premise. Output is recorded in the checkpoint comment.
  • Existing cases intact, count rises: the old test file has 37 checks, the new file 42, and the test diff is pure additions — nothing deleted or weakened.
  • Four inapplicable shapes: dev-tree, legacy, already-stamped-at-base, and vacuous fixtures all still green with their existing wording in the suite (plus my manual vacuous run above).
  • STRICT refusal: unresolvable $base_ref under CHANGELOG_ASSEMBLED_STRICT=1 still hard-fails (existing assertions, still green).
  • Diff scope: exactly actions/changelog-assembled/changelog-assembled.sh, test/changelog-assembled.test.sh, changelog.d/253.md; git diff --check clean.
  • Whole verification: bash test/run.sh 31 files green; sanctioned .github/scripts/shellcheck-all.sh clean over 64 scripts; CI combined status success at the head.

Code against the spec's decisions

  • The refusal sits inside the applicable branch, after the -dev and already-stamped notices (decision 5), and reads the target ref $base_ref against the merge-base consumed set by exact path membership (decision 2) — path-based membership is what the spec's "in the consumed set" says, and content-level drift of a consumed fragment is the byte-comparison's job, not this check's.
  • Reds only on actual stranding; target drift alone stays green (decision 3, and the wrong-fix shape is explicitly asserted against).
  • The remedy names every stranded path, the target-head rebase, and the assembler re-run (decision 4); the header honestly states the residual window and names the operator-owned repository setting that closes it (decision 6).
  • Fragment under ### Fixed citing #253 (decision 7). No workflow or action surface is touched, so no third-party-action exposure changes. Why-comments and the mawk-safe awk pattern follow the repo conventions.

What I could not verify

  • The CI jobs themselves (self-guards, release-exercise) run only on the Forgejo runner; I relied on the combined commit status (success) plus running the identical guard and suites locally at the same head.
  • The builder's TDD red record (39 passed / 3 failed): arithmetically consistent with 37 pre-existing checks + 5 new (3 red then, 2 green then), but I did not replay the pre-fix state; I verified the current green instead.

Non-blocking nits (builder's discretion)

  • The PR body says the assertion count "rises from 39 to 42"; the pre-change file had 37 checks — 39 was the red-run pass count. The criterion ("rises", none weakened) is met either way.
  • The checkpoint's recorded historical output renders the remedy with backticks where the script prints single quotes; a cosmetic paraphrase, substance confirmed by my own reproduction.
Verdict: **approve** — head `5823f3d7b7724c574364dd1c1d95ed969afa1fc8` against issue #253. ## Acceptance criteria — all verified by running, not reading - **Stranded-fragment fixture red with names**: `test/changelog-assembled.test.sh` green 42/42 in my detached worktree; the three red checks assert the filename (`changelog.d/30.md`), the rebase remedy, and the `changelog-assemble '0.2.0'` re-run. - **Fragment-absent control green**: same diverged fixture, exit 0 `byte-for-byte`. - **Unrelated target advance green**: independent `target-unrelated` fixture, exit 0. - **Historical case reproduced over real history**: I checked out `809b7e90` (the !250 head) in a second throwaway worktree and ran this PR's guard with `base_ref=5a8fce8^1` (`5be223a`): exit 1, naming `changelog.d/238.md`, with the rebase + `changelog-assemble '0.6.2'` remedy. The vacuous notice is also preserved on the post-merge ref `5a8fce8` itself, confirming the triage correction's premise. Output is recorded in the checkpoint comment. - **Existing cases intact, count rises**: the old test file has 37 `check`s, the new file 42, and the test diff is pure additions — nothing deleted or weakened. - **Four inapplicable shapes**: dev-tree, legacy, already-stamped-at-base, and vacuous fixtures all still green with their existing wording in the suite (plus my manual vacuous run above). - **STRICT refusal**: unresolvable `$base_ref` under `CHANGELOG_ASSEMBLED_STRICT=1` still hard-fails (existing assertions, still green). - **Diff scope**: exactly `actions/changelog-assembled/changelog-assembled.sh`, `test/changelog-assembled.test.sh`, `changelog.d/253.md`; `git diff --check` clean. - **Whole verification**: `bash test/run.sh` 31 files green; sanctioned `.github/scripts/shellcheck-all.sh` clean over 64 scripts; CI combined status `success` at the head. ## Code against the spec's decisions - The refusal sits inside the applicable branch, after the `-dev` and already-stamped notices (decision 5), and reads the target ref `$base_ref` against the merge-base consumed set by exact path membership (decision 2) — path-based membership is what the spec's "in the consumed set" says, and content-level drift of a consumed fragment is the byte-comparison's job, not this check's. - Reds only on actual stranding; target drift alone stays green (decision 3, and the wrong-fix shape is explicitly asserted against). - The remedy names every stranded path, the target-head rebase, and the assembler re-run (decision 4); the header honestly states the residual window and names the operator-owned repository setting that closes it (decision 6). - Fragment under `### Fixed` citing #253 (decision 7). No workflow or action surface is touched, so no third-party-action exposure changes. Why-comments and the mawk-safe awk pattern follow the repo conventions. ## What I could not verify - The CI jobs themselves (self-guards, release-exercise) run only on the Forgejo runner; I relied on the combined commit status (`success`) plus running the identical guard and suites locally at the same head. - The builder's TDD red record (39 passed / 3 failed): arithmetically consistent with 37 pre-existing checks + 5 new (3 red then, 2 green then), but I did not replay the pre-fix state; I verified the current green instead. ## Non-blocking nits (builder's discretion) - The PR body says the assertion count "rises from 39 to 42"; the pre-change file had 37 checks — 39 was the red-run *pass* count. The criterion ("rises", none weakened) is met either way. - The checkpoint's recorded historical output renders the remedy with backticks where the script prints single quotes; a cosmetic paraphrase, substance confirmed by my own reproduction.
claude-bot-andresmgsl approved these changes 2026-08-24 22:45:00 +00:00
claude-bot-andresmgsl left a comment
Member

Approve — head 5823f3d7b7724c574364dd1c1d95ed969afa1fc8.

The second refusal sits exactly where #253 D1/D5 put it: inside the applicable branch, after the version_is_dev notice and the already-stamped-at-base notice, sharing base_frags with the survivor refusal rather than re-deriving the consumed set. It reads $base_ref with git ls-tree in the same non-recursive, README.md-excluding shape as the merge-base loop above it, so the two sides of the set difference are symmetric — the shape sentinel and any non-.md stray are ignored on both sides, which is what keeps D3 true rather than accidental.

What I ran

Focused suitebash test/changelog-assembled.test.sh at the head: 42 passed, 0 failed. Control at origin/main in a separate detached worktree: 37 passed, 0 failed. The count rises by the five checks added.

The failure cases actually fail. I copied the head's test file over the base checkout and ran it against the unmodified guard: 39 passed, 3 failed

FAIL: a target-head fragment the release did not consume fails — exit 0, wanted 1
FAIL: the target-stranding refusal names the rebase remedy — exit 0, wanted 1
FAIL: the target-stranding refusal names the assembler re-run — exit 0, wanted 1

The two green assertions (the same target fixture is green once no fragment is stranded, a target head advanced without a fragment stays green) pass both before and after, which is correct — they are the "must fail after a wrong fix" guard from the test plan, not red-before cases. The first of those is the load-bearing one: it is the same diverged fixture with only the fragment removed, so it separates stranding from target drift rather than asserting drift-freeness.

Historical case, reproduced independently. Detached worktree at 809b7e907a8fb05136b268ed22ed9c92b1346c21, new guard, base_ref=5be223a020a329fbf4249494755f895e092137c3 (5a8fce8^1):

changelog-assembled: fragment(s) on target '5be223a...' were not consumed by this release PR:

    changelog.d/238.md

  Merging now would strand these entries for the next release and
  misattribute when they shipped.
  Fix: rebase onto the target head and re-run bin/changelog-assemble '0.6.2'.
exit=1

Two controls I ran that the PR body does not claim, and that I wanted before believing the reproduction:

  • the base guard at that identical pairing is green (byte-for-byte the assembly of the 6 fragment(s) ... (7bdae45), exit 0) — so the red is this change, not the corrected ref;
  • the new guard at the true merge base 7bdae45 is still green (exit 0) — so it does not red the pairing CI actually saw at the time, only the pairing that reflected the target head.

Lint and hygiene. bash .github/scripts/shellcheck-all.shlinting 64 tracked scripts, exit 0. git diff --check origin/main...HEAD clean. git diff --name-only origin/main..HEAD is exactly the three files the issue names.

Every criterion checked, including the ones easy to take on trust: the four inapplicable shapes still assert green with their existing wording, CHANGELOG_ASSEMBLED_STRICT=1 with an unresolvable base ref is still a hard failure, and no existing case was deleted or weakened (the diff is pure insertion — 83 added lines, 0 removed). D6's header paragraph names the residual window and hands the complement to the operator as a repository setting; changelog.d/253.md matches the grouped shape and the sibling fragments' form.

What I could not verify here, and what I relied on instead

bash test/run.sh is 29 of 31 files green in my box. test/labels-bootstrap.test.sh (7 passed / 7 failed) and test/labels-dispatch.test.sh (7 passed / 14 failed) fail — but they fail with identical counts at origin/main, which is the control the test plan asks for, and the cause is environmental: this box has no yq (several failures are bare exit 127), and neither file touches any path in this diff. For the whole-suite claim I therefore relied on CI: CI / test, CI / self-guards, CI / release-exercise, CI / action-exercise, CI / docs-sync-exercise, labels, and Refs guard / refs-not-closing are all success at this head. I also did not run actionlint (not installed here); no workflow YAML changed in this diff, so nothing in it is in actionlint's scope.

I did not exercise the guard inside a real Forgejo pull_request run — that is what CI / self-guards does at this head, and it is green, which is the expected shape: main is 0.6.3-dev, so this PR takes the development notice and the new refusal is not reached on itself.

Non-blocking notes

  1. The PR body says the assertion count "rises from 39 to 42". Measured, it is 37 → 42; 39 is the passed count of the new file against the old script (39 + 3 = 42). The issue's criterion is only that the count rises, which it does, so nothing is owed here — but the number as written reads as a base measurement it is not.
  2. The two loops over git ls-tree are now near-identical fifteen-line blocks differing only in ref and in what they do with a blob. Not worth refactoring inside this PR — the merge-base loop also materializes files into $tmp — but if a third reader of $dir/ ever appears, that is the moment to factor out a fragments_at <ref> helper.
  3. Neither loop descends subtrees, so a fragment stranded at changelog.d/sub/x.md would be missed. This exactly mirrors the existing merge-base read, so the guard stays self-consistent and no new hole is opened; flagging it only so it is on the record.
  4. This PR currently carries both state:addressing and state:bots-reviewing plus blocker:unrequested while three reviewers are requested. That is reconciler-owned state, not the builder's, and outside my verdict.
**Approve** — head `5823f3d7b7724c574364dd1c1d95ed969afa1fc8`. The second refusal sits exactly where #253 D1/D5 put it: inside the applicable branch, after the `version_is_dev` notice and the already-stamped-at-base notice, sharing `base_frags` with the survivor refusal rather than re-deriving the consumed set. It reads `$base_ref` with `git ls-tree` in the same non-recursive, `README.md`-excluding shape as the merge-base loop above it, so the two sides of the set difference are symmetric — the `shape` sentinel and any non-`.md` stray are ignored on both sides, which is what keeps D3 true rather than accidental. ## What I ran **Focused suite** — `bash test/changelog-assembled.test.sh` at the head: **42 passed, 0 failed**. Control at `origin/main` in a separate detached worktree: **37 passed, 0 failed**. The count rises by the five checks added. **The failure cases actually fail.** I copied the head's test file over the base checkout and ran it against the *unmodified* guard: **39 passed, 3 failed** — ``` FAIL: a target-head fragment the release did not consume fails — exit 0, wanted 1 FAIL: the target-stranding refusal names the rebase remedy — exit 0, wanted 1 FAIL: the target-stranding refusal names the assembler re-run — exit 0, wanted 1 ``` The two green assertions (`the same target fixture is green once no fragment is stranded`, `a target head advanced without a fragment stays green`) pass both before and after, which is correct — they are the "must fail after a wrong fix" guard from the test plan, not red-before cases. The first of those is the load-bearing one: it is the *same diverged fixture* with only the fragment removed, so it separates stranding from target drift rather than asserting drift-freeness. **Historical case, reproduced independently.** Detached worktree at `809b7e907a8fb05136b268ed22ed9c92b1346c21`, new guard, `base_ref=5be223a020a329fbf4249494755f895e092137c3` (`5a8fce8^1`): ``` changelog-assembled: fragment(s) on target '5be223a...' were not consumed by this release PR: changelog.d/238.md Merging now would strand these entries for the next release and misattribute when they shipped. Fix: rebase onto the target head and re-run bin/changelog-assemble '0.6.2'. exit=1 ``` Two controls I ran that the PR body does not claim, and that I wanted before believing the reproduction: - the **base** guard at that identical pairing is green (`byte-for-byte the assembly of the 6 fragment(s) ... (7bdae45)`, exit 0) — so the red is this change, not the corrected ref; - the **new** guard at the true merge base `7bdae45` is still green (exit 0) — so it does not red the pairing CI actually saw at the time, only the pairing that reflected the target head. **Lint and hygiene.** `bash .github/scripts/shellcheck-all.sh` → `linting 64 tracked scripts`, exit 0. `git diff --check origin/main...HEAD` clean. `git diff --name-only origin/main..HEAD` is exactly the three files the issue names. **Every criterion checked**, including the ones easy to take on trust: the four inapplicable shapes still assert green with their existing wording, `CHANGELOG_ASSEMBLED_STRICT=1` with an unresolvable base ref is still a hard failure, and no existing case was deleted or weakened (the diff is pure insertion — 83 added lines, 0 removed). D6's header paragraph names the residual window and hands the complement to the operator as a repository setting; `changelog.d/253.md` matches the `grouped` shape and the sibling fragments' form. ## What I could not verify here, and what I relied on instead `bash test/run.sh` is **29 of 31 files green in my box**. `test/labels-bootstrap.test.sh` (7 passed / 7 failed) and `test/labels-dispatch.test.sh` (7 passed / 14 failed) fail — but they fail with **identical counts at `origin/main`**, which is the control the test plan asks for, and the cause is environmental: this box has no `yq` (several failures are bare `exit 127`), and neither file touches any path in this diff. For the whole-suite claim I therefore relied on CI: `CI / test`, `CI / self-guards`, `CI / release-exercise`, `CI / action-exercise`, `CI / docs-sync-exercise`, `labels`, and `Refs guard / refs-not-closing` are all **success** at this head. I also did not run `actionlint` (not installed here); no workflow YAML changed in this diff, so nothing in it is in actionlint's scope. I did not exercise the guard inside a real Forgejo `pull_request` run — that is what `CI / self-guards` does at this head, and it is green, which is the expected shape: `main` is `0.6.3-dev`, so this PR takes the development notice and the new refusal is not reached on itself. ## Non-blocking notes 1. The PR body says the assertion count *"rises from 39 to 42"*. Measured, it is **37 → 42**; 39 is the *passed* count of the new file against the old script (39 + 3 = 42). The issue's criterion is only that the count rises, which it does, so nothing is owed here — but the number as written reads as a base measurement it is not. 2. The two loops over `git ls-tree` are now near-identical fifteen-line blocks differing only in ref and in what they do with a blob. Not worth refactoring inside this PR — the merge-base loop also materializes files into `$tmp` — but if a third reader of `$dir/` ever appears, that is the moment to factor out a `fragments_at <ref>` helper. 3. Neither loop descends subtrees, so a fragment stranded at `changelog.d/sub/x.md` would be missed. This exactly mirrors the existing merge-base read, so the guard stays self-consistent and no new hole is opened; flagging it only so it is on the record. 4. This PR currently carries both `state:addressing` and `state:bots-reviewing` plus `blocker:unrequested` while three reviewers are requested. That is reconciler-owned state, not the builder's, and outside my verdict.
codex-bot-andresmgsl requested review from andres 2026-08-24 22:45:57 +00:00
codex-bot-andresmgsl added the
state:needs-human
label 2026-08-24 22:45:58 +00:00
andres merged commit e55e99663e into main 2026-08-24 22:55:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
4 participants
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#255
No description provided.