fix: resume a stranded merge-door release #274

Merged
andres merged 8 commits from build/273-resume-merge-door into main 2026-08-31 16:57:10 +00:00

Closes #273

Acceptance criteria

  • The door resumes with RELEASED=no and a tag line equal to MERGE_SHA; paste stdout.
  • A completed ceremony refuses for empty, matching, and foreign TAG_SHAS; paste all three.
  • A foreign tag refuses and names both the tag SHA and MERGE_SHA; paste stderr.
  • The ordinary first run returns resume=no.
  • Empty VER, MERGE_SHA, or RELEASED and malformed RELEASED each refuse with their own message.
  • The extracted merge-door step executes and refuses unreadable release state and non-2 ls-remote failures.
  • The tag step condition contains steps.preflight.outputs.resume; paste it.
  • The release-on-tag job is untouched and its no-tag-exists guard test stays green.
  • The old already-happened-or-manual-tag disjunction is absent from release.yml and README.md.
  • README describes the three actual outcomes and carries a correct renamed-step anchor; paste the anchor and span.
  • release-path.sh names lib/preflight.sh and test/release-path.test.sh is green.
  • The changed set is exactly the nine paths authorized by amended #273, with no forge backend diff; paste the stat.
  • bash test/run.sh is green; git diff --check, shellcheck, and actionlint are clean.

Worklog

  • Claim #273 and publish the plan of record.
  • Establish the pure preflight contract RED and push the first checkpoint.
  • Implement lib/preflight.sh and make the pure contract green.
  • Add executable workflow-step coverage and rewire release-on-merge.
  • Update the release path manifest, scope mapping, README, and changelog fragment.
  • Run focused and full verification; record acceptance evidence.
  • Apply triage’s nine-path amendment: derive preflight in every synthetic release-path fixture and require each mutation to report exactly its intended path.
  • Reproduce and repair the grouped-fragment self-guard failure; all five self-guards pass locally.
  • Verify the draft head and prepare the exact-head handoff; the signal comment is the record.

Acceptance evidence

  • Resume stdout: NOTICE names the previous failed publish, followed by resume=yes.
  • Completed release: TAG_SHAS empty, matching, and foreign each exit 1 with: release 1.2.3 already exists — this release already happened; refusing to re-release, creating nothing.
  • Foreign tag: exit 1 names 2222222222222222222222222222222222222222 and merge 1111111111111111111111111111111111111111.
  • Tag condition: steps.decide.outputs.ceremony == yes && steps.preflight.outputs.resume != yes.
  • Workflow diff hunks are confined to original lines 208–232; release-on-tag is untouched, and its no-tag-exists guard test passes.
  • README points to release.yml lines 208–239; the renamed step begins at 208 and its output write ends at 239.
  • Changed stat: exactly 9 authorized paths, 354 insertions, 64 deletions; both forge backend files are unchanged.
  • Focused results: preflight 22/22, forge-backends 177/177, changelog 103/103, labels 46/46; ShellCheck, actionlint, and git diff --check clean.
  • Release-path fixtures: 7/7, with every mutation reporting exactly its intended single missing or stale path.
  • Full suite: 32 test files passed, 0 failed; ShellCheck (66 scripts), actionlint (9 workflows), and git diff --check are clean.

Review round at 715663cf53

  • Re-point every README release.yml anchor affected by the nine-line merge-door shift.
  • Verify the repaired anchors against current release.yml spans and confirm the changed set remains authorized.
  • Run focused and full verification, push the fix, and record the whole-round answer.

Round log

Round at 715663cf

🔧 addressing round on head 715663cf53

Round analysis:

  • claude-bot — blocking README anchor regression: agree. The merge-door step added nine lines, and the README anchors below that insertion were not all moved with their targets. I will re-point every listed anchor, checking the nine formerly exact anchors against the actual step/job spans and moving the four already-approximate anchors by the same +9 so they retain their prior referents.
  • claude-bot — non-blocking multi-line check behavior in the release-path harness: agree, out of scope for this round. It is pre-existing, the neighboring transitive-dependency test supplies the required regression coverage, and #273 authorizes only the release-resume work. I will not broaden this PR.
  • glm-bot — approval with no blocking or non-blocking findings: agree. No change is required; I will preserve the behavior and gates it verified.
  • glm-bot — live-door and per-check-detail verification limits: agree. No live release window exists, so I will retain the issue-authorized extracted-step evidence and rerun the local gates after the documentation-only fix.
  • kimi-bot — approval with no blocking findings: agree. No implementation change is required beyond the anchor repair.
  • kimi-bot — actionlint unavailable and full-suite package-json cases unavailable in that reviewer box: agree as reviewer-environment limits, not product findings. I will run the repository’s available full verification in this builder worktree and report exact results.

Plan: append round-fix Worklog items, repair the README anchors only, verify every changed anchor against the current release.yml, run focused/full gates, push the completed fix, answer the whole round in one reply, then signal the final head while still draft and mark ready with no commit between.

Whole-round answer for head d944bddecc:

  • Claude blocker — fixed. Re-pointed every README anchor below the nine-line merge-door insertion. The merge-door whole-job links now end at L310; re-arm links now target L302–L310 / L276–L310 / L275; the tag-door job and its three refusal spans now target L325–L410, L350–L361, L362–L374, and L375–L390, with the narrower message links moved to L356–L359 and L368–L374. A pre-fix occurrence probe failed on all expected new targets; the same probe now passes with the exact expected occurrence counts.
  • Claude non-blocking note — no change. The multi-line check behavior predates #273, and the neighboring transitive-dependency test already supplies the issue-required manifest regression coverage. Changing that harness would widen this nine-path release-resume PR, so it remains proposal material.
  • GLM approval and verification limits — preserved and rechecked. The implementation and extracted-step evidence are unchanged. No live release window exists, so no live-door claim is added; the offline contract and workflow-step tests remain the evidence authorized by #273.
  • Kimi approval and environment limits — rechecked in the builder worktree. The builder environment has the required tools, and the full repository gates completed successfully.

Verification at this exact head:

  • Anchor regression probe: PASS for every shifted target and occurrence count.
  • Scope: exactly the nine authorized paths; lib/forge-forgejo.sh and lib/forge-github.sh have no diff.
  • bash test/run.sh: 32 test files passed, 0 failed.
  • bash .github/scripts/shellcheck-all.sh: clean.
  • bash .github/scripts/actionlint-all.sh: clean.
  • git diff --check origin/main..HEAD: clean.
  • Final stat: 9 files, 354 insertions, 64 deletions.

The only fix commit is d944bddecc (docs: repair release workflow anchors after preflight shift).

📣 round answered at head d944bddecc

Round at d944bdde

Round passed with no written reply.

Closes #273 ## Acceptance criteria - [x] The door resumes with RELEASED=no and a tag line equal to MERGE_SHA; paste stdout. - [x] A completed ceremony refuses for empty, matching, and foreign TAG_SHAS; paste all three. - [x] A foreign tag refuses and names both the tag SHA and MERGE_SHA; paste stderr. - [x] The ordinary first run returns resume=no. - [x] Empty VER, MERGE_SHA, or RELEASED and malformed RELEASED each refuse with their own message. - [x] The extracted merge-door step executes and refuses unreadable release state and non-2 ls-remote failures. - [x] The tag step condition contains steps.preflight.outputs.resume; paste it. - [x] The release-on-tag job is untouched and its no-tag-exists guard test stays green. - [x] The old already-happened-or-manual-tag disjunction is absent from release.yml and README.md. - [x] README describes the three actual outcomes and carries a correct renamed-step anchor; paste the anchor and span. - [x] release-path.sh names lib/preflight.sh and test/release-path.test.sh is green. - [x] The changed set is exactly the nine paths authorized by amended #273, with no forge backend diff; paste the stat. - [x] bash test/run.sh is green; git diff --check, shellcheck, and actionlint are clean. ## Worklog - [x] Claim #273 and publish the plan of record. - [x] Establish the pure preflight contract RED and push the first checkpoint. - [x] Implement lib/preflight.sh and make the pure contract green. - [x] Add executable workflow-step coverage and rewire release-on-merge. - [x] Update the release path manifest, scope mapping, README, and changelog fragment. - [x] Run focused and full verification; record acceptance evidence. - [x] Apply triage’s nine-path amendment: derive preflight in every synthetic release-path fixture and require each mutation to report exactly its intended path. - [x] Reproduce and repair the grouped-fragment self-guard failure; all five self-guards pass locally. - [x] Verify the draft head and prepare the exact-head handoff; the signal comment is the record. ## Acceptance evidence - Resume stdout: NOTICE names the previous failed publish, followed by resume=yes. - Completed release: TAG_SHAS empty, matching, and foreign each exit 1 with: release 1.2.3 already exists — this release already happened; refusing to re-release, creating nothing. - Foreign tag: exit 1 names 2222222222222222222222222222222222222222 and merge 1111111111111111111111111111111111111111. - Tag condition: steps.decide.outputs.ceremony == yes && steps.preflight.outputs.resume != yes. - Workflow diff hunks are confined to original lines 208–232; release-on-tag is untouched, and its no-tag-exists guard test passes. - README points to release.yml lines 208–239; the renamed step begins at 208 and its output write ends at 239. - Changed stat: exactly 9 authorized paths, 354 insertions, 64 deletions; both forge backend files are unchanged. - Focused results: preflight 22/22, forge-backends 177/177, changelog 103/103, labels 46/46; ShellCheck, actionlint, and git diff --check clean. - Release-path fixtures: 7/7, with every mutation reporting exactly its intended single missing or stale path. - Full suite: 32 test files passed, 0 failed; ShellCheck (66 scripts), actionlint (9 workflows), and git diff --check are clean. ### Review round at 715663cf53472f48d907cc9609eaca967b79b52f - [x] Re-point every README release.yml anchor affected by the nine-line merge-door shift. - [x] Verify the repaired anchors against current release.yml spans and confirm the changed set remains authorized. - [x] Run focused and full verification, push the fix, and record the whole-round answer. ## Round log <!-- round:715663cf53472f48d907cc9609eaca967b79b52f --> **Round at 715663cf** 🔧 addressing round on head 715663cf53472f48d907cc9609eaca967b79b52f Round analysis: - claude-bot — blocking README anchor regression: **agree**. The merge-door step added nine lines, and the README anchors below that insertion were not all moved with their targets. I will re-point every listed anchor, checking the nine formerly exact anchors against the actual step/job spans and moving the four already-approximate anchors by the same +9 so they retain their prior referents. - claude-bot — non-blocking multi-line `check` behavior in the release-path harness: **agree, out of scope for this round**. It is pre-existing, the neighboring transitive-dependency test supplies the required regression coverage, and #273 authorizes only the release-resume work. I will not broaden this PR. - glm-bot — approval with no blocking or non-blocking findings: **agree**. No change is required; I will preserve the behavior and gates it verified. - glm-bot — live-door and per-check-detail verification limits: **agree**. No live release window exists, so I will retain the issue-authorized extracted-step evidence and rerun the local gates after the documentation-only fix. - kimi-bot — approval with no blocking findings: **agree**. No implementation change is required beyond the anchor repair. - kimi-bot — actionlint unavailable and full-suite package-json cases unavailable in that reviewer box: **agree as reviewer-environment limits, not product findings**. I will run the repository’s available full verification in this builder worktree and report exact results. Plan: append round-fix Worklog items, repair the README anchors only, verify every changed anchor against the current `release.yml`, run focused/full gates, push the completed fix, answer the whole round in one reply, then signal the final head while still draft and mark ready with no commit between. Whole-round answer for head d944bddeccba60a0978e8565c1237a68bcaf10e6: - **Claude blocker — fixed.** Re-pointed every README anchor below the nine-line merge-door insertion. The merge-door whole-job links now end at L310; re-arm links now target L302–L310 / L276–L310 / L275; the tag-door job and its three refusal spans now target L325–L410, L350–L361, L362–L374, and L375–L390, with the narrower message links moved to L356–L359 and L368–L374. A pre-fix occurrence probe failed on all expected new targets; the same probe now passes with the exact expected occurrence counts. - **Claude non-blocking note — no change.** The multi-line `check` behavior predates #273, and the neighboring transitive-dependency test already supplies the issue-required manifest regression coverage. Changing that harness would widen this nine-path release-resume PR, so it remains proposal material. - **GLM approval and verification limits — preserved and rechecked.** The implementation and extracted-step evidence are unchanged. No live release window exists, so no live-door claim is added; the offline contract and workflow-step tests remain the evidence authorized by #273. - **Kimi approval and environment limits — rechecked in the builder worktree.** The builder environment has the required tools, and the full repository gates completed successfully. Verification at this exact head: - Anchor regression probe: PASS for every shifted target and occurrence count. - Scope: exactly the nine authorized paths; `lib/forge-forgejo.sh` and `lib/forge-github.sh` have no diff. - `bash test/run.sh`: 32 test files passed, 0 failed. - `bash .github/scripts/shellcheck-all.sh`: clean. - `bash .github/scripts/actionlint-all.sh`: clean. - `git diff --check origin/main..HEAD`: clean. - Final stat: 9 files, 354 insertions, 64 deletions. The only fix commit is d944bddeccba60a0978e8565c1237a68bcaf10e6 (`docs: repair release workflow anchors after preflight shift`). 📣 round answered at head d944bddeccba60a0978e8565c1237a68bcaf10e6 <!-- round:d944bddeccba60a0978e8565c1237a68bcaf10e6 --> **Round at d944bdde** _Round passed with no written reply._
codex-bot-andresmgsl added 1 commit 2026-08-31 11:13:07 +00:00
test: specify release preflight contract
Some checks failed
CI / release-exercise (pull_request) Successful in 13s
CI / self-guards (pull_request) Successful in 9s
CI / action-exercise (pull_request) Successful in 8s
CI / docs-sync-exercise (pull_request) Successful in 8s
labels / labels (pull_request) Successful in 10s
Refs guard / refs-not-closing (pull_request) Successful in 8s
CI / test (pull_request) Failing after 44s
3104aac6f3
codex-bot-andresmgsl changed title from fix: resume a stranded merge-door release to WIP: fix: resume a stranded merge-door release 2026-08-31 11:13:22 +00:00
forgejo-actions added the
state:building
label 2026-08-31 11:13:48 +00:00
codex-bot-andresmgsl added 1 commit 2026-08-31 11:15:12 +00:00
feat: decide when merge-door releases resume
Some checks failed
CI / self-guards (pull_request) Successful in 10s
CI / action-exercise (pull_request) Successful in 8s
CI / release-exercise (pull_request) Successful in 14s
CI / docs-sync-exercise (pull_request) Successful in 8s
Refs guard / refs-not-closing (pull_request) Successful in 8s
labels / labels (pull_request) Successful in 10s
CI / test (pull_request) Failing after 43s
153a408e10
forgejo-actions added the
scope:release-flow
blocker:ci-red
labels 2026-08-31 11:15:57 +00:00
codex-bot-andresmgsl added 1 commit 2026-08-31 11:17:59 +00:00
fix: resume stranded merge-door publishes
Some checks failed
CI / action-exercise (pull_request) Successful in 12s
CI / self-guards (pull_request) Successful in 16s
CI / release-exercise (pull_request) Successful in 20s
CI / docs-sync-exercise (pull_request) Successful in 12s
labels / labels (pull_request) Successful in 13s
Refs guard / refs-not-closing (pull_request) Successful in 10s
CI / test (pull_request) Failing after 58s
7bd331a44d
forgejo-actions removed the
blocker:ci-red
label 2026-08-31 11:18:39 +00:00
codex-bot-andresmgsl added 1 commit 2026-08-31 11:21:20 +00:00
docs: describe merge-door resume recovery
Some checks failed
CI / action-exercise (pull_request) Successful in 29s
CI / self-guards (pull_request) Failing after 32s
CI / release-exercise (pull_request) Successful in 39s
CI / docs-sync-exercise (pull_request) Successful in 12s
labels / labels (pull_request) Successful in 14s
Refs guard / refs-not-closing (pull_request) Successful in 11s
CI / test (pull_request) Failing after 1m11s
79e747b163
forgejo-actions added the
scope:docs
scope:labels
blocker:ci-red
labels 2026-08-31 11:22:24 +00:00
codex-bot-andresmgsl added 1 commit 2026-08-31 11:25:03 +00:00
test: isolate preflight fact environments
Some checks failed
CI / self-guards (pull_request) Failing after 17s
CI / action-exercise (pull_request) Successful in 16s
CI / release-exercise (pull_request) Successful in 24s
CI / docs-sync-exercise (pull_request) Successful in 13s
labels / labels (pull_request) Successful in 14s
CI / test (pull_request) Failing after 1m8s
Refs guard / refs-not-closing (pull_request) Successful in 8s
f19658ea82
codex-bot-andresmgsl added 1 commit 2026-08-31 13:55:35 +00:00
test: derive preflight in release path fixtures
Some checks failed
CI / self-guards (pull_request) Failing after 9s
CI / action-exercise (pull_request) Successful in 8s
CI / release-exercise (pull_request) Successful in 14s
CI / docs-sync-exercise (pull_request) Successful in 8s
labels / labels (pull_request) Successful in 10s
CI / test (pull_request) Successful in 3m47s
Refs guard / refs-not-closing (pull_request) Successful in 9s
e2aa834601
codex-bot-andresmgsl added 1 commit 2026-08-31 14:02:20 +00:00
docs: group release recovery changelog entry
All checks were successful
CI / action-exercise (pull_request) Successful in 10s
CI / self-guards (pull_request) Successful in 13s
CI / release-exercise (pull_request) Successful in 16s
CI / docs-sync-exercise (pull_request) Successful in 9s
labels / labels (pull_request) Successful in 11s
CI / test (pull_request) Successful in 4m7s
Refs guard / refs-not-closing (pull_request) Successful in 8s
715663cf53
forgejo-actions removed the
blocker:ci-red
label 2026-08-31 14:02:53 +00:00
Author
Member

📣 round answered at head 715663cf53

📣 round answered at head 715663cf53472f48d907cc9609eaca967b79b52f
codex-bot-andresmgsl changed title from WIP: fix: resume a stranded merge-door release to fix: resume a stranded merge-door release 2026-08-31 14:05:13 +00:00
forgejo-actions added
blocker:unrequested
state:addressing
and removed
state:building
labels 2026-08-31 15:00:19 +00:00
codex-bot-andresmgsl requested review from glm-bot-andresmgsl 2026-08-31 15:39:10 +00:00
codex-bot-andresmgsl requested review from claude-bot-andresmgsl 2026-08-31 15:39:11 +00:00
codex-bot-andresmgsl requested review from kimi-bot-andresmgsl 2026-08-31 15:39:11 +00:00
codex-bot-andresmgsl added the
state:bots-reviewing
label 2026-08-31 15:39:12 +00:00
forgejo-actions removed the
blocker:unrequested
state:addressing
labels 2026-08-31 15:39:28 +00:00

🔎 reviewing head d944bddecc

Progress (whole PR re-checked at this head, in a detached worktree):

  • The blocker from 715663c is fixed. All 17 release.yml#L… anchors in README.md resolve at this head; the 13 that had rotted now name what they did at main, and the 4 that were already approximate carry the same +9 shift.
  • Pure table: test/preflight.test.sh 22/22, mutation-tested (wrong RELEASED precedence → 3 fail, substring match → 1 fail, NOTICE to stderr → 2 fail).
  • Extracted step re-run against a real remote at this head: annotated/lightweight/absent/foreign tags, ls-remote exit 2 vs 128, unreadable release state — all as specified.
  • Workflow contract: forge-backends 177/177; dropping the resume condition and collapsing the ls-remote case each red a guard.
  • Manifest: release-path 7/7; deleting lib/preflight.sh reds 5/7.
  • Changed set is exactly the nine authorized paths (354+/64−), no forge-backend diff; release-on-tag untouched; disjunction absent.
  • bash test/run.sh 32/32, shellcheck clean, actionlint 1.7.12 clean, git diff --check clean.

Verdict: approve.

🔎 reviewing head d944bddeccba60a0978e8565c1237a68bcaf10e6 Progress (whole PR re-checked at this head, in a detached worktree): - ✅ **The blocker from `715663c` is fixed.** All 17 `release.yml#L…` anchors in `README.md` resolve at this head; the 13 that had rotted now name what they did at `main`, and the 4 that were already approximate carry the same +9 shift. - ✅ Pure table: `test/preflight.test.sh` 22/22, mutation-tested (wrong `RELEASED` precedence → 3 fail, substring match → 1 fail, NOTICE to stderr → 2 fail). - ✅ Extracted step re-run against a real remote at this head: annotated/lightweight/absent/foreign tags, `ls-remote` exit 2 vs 128, unreadable release state — all as specified. - ✅ Workflow contract: `forge-backends` 177/177; dropping the `resume` condition and collapsing the `ls-remote` case each red a guard. - ✅ Manifest: `release-path` 7/7; deleting `lib/preflight.sh` reds 5/7. - ✅ Changed set is exactly the nine authorized paths (354+/64−), no forge-backend diff; release-on-tag untouched; disjunction absent. - ✅ `bash test/run.sh` 32/32, shellcheck clean, actionlint 1.7.12 clean, `git diff --check` clean. Verdict: approve.

🔎 reviewing head d944bddecc

  • delta since my approved head 715663cf: README.md only (16 anchors re-pointed) — claude-bot's blocker fixed
  • all 20 release.yml#L… anchors in README verified against actual spans at this head (nine exact-blocker values match claude-bot's table; four approximate repaired by the sanctioned +9)
  • pure table re-driven live: rows 1-4 + substring non-match + 4 fact refusals — streams and precedence correct
  • tag-step condition, disjunction gone, single hunk confined to release-on-merge, manifest names lib/preflight.sh
  • gates: run.sh 32/32 files, shellcheck 0.10.0 clean (66 scripts), actionlint 1.7.7 clean (9 workflows), git diff --check clean; CI combined 7/7 success at this head
  • changed set exactly the nine authorized paths, 354+/64-, no forge backend diff
  • verdict: approve — submitting via submit-verdict.sh
🔎 reviewing head d944bddeccba60a0978e8565c1237a68bcaf10e6 - delta since my approved head 715663cf: README.md only (16 anchors re-pointed) — claude-bot's blocker fixed - all 20 release.yml#L… anchors in README verified against actual spans at this head (nine exact-blocker values match claude-bot's table; four approximate repaired by the sanctioned +9) - pure table re-driven live: rows 1-4 + substring non-match + 4 fact refusals — streams and precedence correct - tag-step condition, disjunction gone, single hunk confined to release-on-merge, manifest names lib/preflight.sh - gates: run.sh 32/32 files, shellcheck 0.10.0 clean (66 scripts), actionlint 1.7.7 clean (9 workflows), git diff --check clean; CI combined 7/7 success at this head - changed set exactly the nine authorized paths, 354+/64-, no forge backend diff - verdict: approve — submitting via submit-verdict.sh

🔎 reviewing head d944bddecc

  • focused suites green: preflight 22/22, forge-backends 177/177, release-path 7/7
  • gates green: shellcheck-all, actionlint 1.7.12, self-ref, marker, vendored, git diff --check
  • README release.yml anchors verified; disjunction absent; tag step condition contains preflight output; changed set exactly 9 paths
  • composing approve verdict
🔎 reviewing head d944bddeccba60a0978e8565c1237a68bcaf10e6 - focused suites green: preflight 22/22, forge-backends 177/177, release-path 7/7 - gates green: shellcheck-all, actionlint 1.7.12, self-ref, marker, vendored, git diff --check - README release.yml anchors verified; disjunction absent; tag step condition contains preflight output; changed set exactly 9 paths - composing approve verdict
glm-bot-andresmgsl approved these changes 2026-08-31 15:47:31 +00:00
Dismissed
glm-bot-andresmgsl left a comment
Member

Verdict: approve — reviewed the whole PR at head 715663cf53472f48d907cc9609eaca967b79b52f in a detached throwaway worktree against issue #273 including the 2026-08-31 nine-path amendment.

Acceptance criteria — all verified at the head

  • The door resumes: VER=1.2.3 MERGE_SHA=111…111 TAG_SHAS=111…111 RELEASED=no → stdout NOTICE: tag '1.2.3' already stands at this merge commit and no release exists — a previous run of this door tagged and then failed to publish. Resuming: the tag is not recreated; the artifact hook and the publish run. + resume=yes, exit 0.
  • Completed ceremony refuses, all three TAG_SHAS: empty, matching (111…111), and foreign (222…222) each exit 1 with release '1.2.3' already exists — this release already happened; refusing to re-release, creating nothing. — precedence correct; the wrong-precedence republish row is also pinned by check "row 1: a published release refuses with the matching tag".
  • Foreign tag refuses naming both SHAs: stderr tag '1.2.3' already exists at 2222222222222222222222222222222222222222 but this run would tag 1111111111111111111111111111111111111111 — a manual tag won the race, or it names a different commit; refusing to re-release, creating nothing. Delete that tag, or re-tag the merge commit., exit 1.
  • Ordinary first run: resume=no, exit 0.
  • Missing/malformed facts: empty VER, empty MERGE_SHA, empty RELEASED, RELEASED=maybe each exit 1 with their own distinct message; none falls through to resume=no.
  • Extracted step executed: test/forge-backends.test.sh (177/177 green here) yq-extracts the real id: preflight step and drives it — unreadable release state exits 1 with "refusing rather than assuming" (#191 rule survives), ls-remote exit 128 exits 1, exit 2 is read as absence and proceeds, and the resume= line reaches $GITHUB_OUTPUT as key=value for both the lightweight and annotated (peeled) shapes.
  • Tag step conditioned: yq -r '.jobs.release-on-merge.steps[] | select(.name | test("tag the merge commit")) | .if'steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'.
  • Tag door untouched: the release.yml diff is one hunk, @@ -205,31 +205,40 @@, confined to release-on-merge; no hunk inside release-on-tag; check "the tag door carries no tag-exists guard" green in the 177/177.
  • Disjunction gone: grep -n "already happened, or a manual tag" over .github/workflows/release.yml and README.md → no matches.
  • README true of the merged code: the L131–134 sentence now credits the merge-door preflight with the tag door as fallback; the refusal entry quotes exactly the three messages the step can print; anchor re-pointed to release.yml#L208-L239 — verified at the head: step name begins at 208, >> "$GITHUB_OUTPUT" write ends at 239; the companion forge_release_create anchor L264–L277 also lands exactly on the publish step.
  • Manifest moved: .github/scripts/release-path.sh names lib/preflight.sh (ordered after lib/decide.sh); bash test/release-path.test.sh 7/7 green, with the amended exact-output assertions (path_check_reports_only) requiring each mutation to report only its intended path — stronger than the substring check the issue warned about.
  • Changed set exactly nine paths: stat shows the nine authorized files, 339 insertions / 49 deletions; lib/forge-forgejo.sh and lib/forge-github.sh have no diff.
  • Whole-suite + gates: bash test/run.sh → 32 test files passed, 0 failed; git diff --check clean; shellcheck over the 66 tracked scripts (v0.10.0, the exact release CI installs) clean; actionlint over the 9 workflows (v1.7.7, likewise) clean.

Constraints and conventions

mawk-safe awk (NF { print $1 }, no \x); whole-line SHA matching (prefix/substring non-match pinned by tests); the decision is a pure sourced lib with the wiring in the workflow, per the every-logic-its-own-file rule; comments carry why with the issue number; changelog fragment present; labeler row adds test/preflight.test.sh under scope:release-flow (#302 D3).

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

  • The live doors. No release window is open (#268, option C) and release-exercise.yml is dry wiring by design — the issue's own test plan declares this gap. I relied on the extracted-step tests driving the real workflow body against PATH-stubbed git and a stubbed forge_release_exists, plus the pure table driven directly.
  • CI's per-check detail. The Forgejo combined commit status for the head reads success, but the per-check rows return null states through this API. I relied on the combined state plus reproducing every gate locally with the same upstream tool versions CI installs (results above).

No blocking findings; no non-blocking nits worth the builder's time.

Verdict: **approve** — reviewed the whole PR at head `715663cf53472f48d907cc9609eaca967b79b52f` in a detached throwaway worktree against issue #273 including the 2026-08-31 nine-path amendment. ## Acceptance criteria — all verified at the head - **The door resumes:** `VER=1.2.3 MERGE_SHA=111…111 TAG_SHAS=111…111 RELEASED=no` → stdout `NOTICE: tag '1.2.3' already stands at this merge commit and no release exists — a previous run of this door tagged and then failed to publish. Resuming: the tag is not recreated; the artifact hook and the publish run.` + `resume=yes`, exit 0. - **Completed ceremony refuses, all three TAG_SHAS:** empty, matching (`111…111`), and foreign (`222…222`) each exit 1 with `release '1.2.3' already exists — this release already happened; refusing to re-release, creating nothing.` — precedence correct; the wrong-precedence republish row is also pinned by `check "row 1: a published release refuses with the matching tag"`. - **Foreign tag refuses naming both SHAs:** stderr `tag '1.2.3' already exists at 2222222222222222222222222222222222222222 but this run would tag 1111111111111111111111111111111111111111 — a manual tag won the race, or it names a different commit; refusing to re-release, creating nothing. Delete that tag, or re-tag the merge commit.`, exit 1. - **Ordinary first run:** `resume=no`, exit 0. - **Missing/malformed facts:** empty `VER`, empty `MERGE_SHA`, empty `RELEASED`, `RELEASED=maybe` each exit 1 with their own distinct message; none falls through to `resume=no`. - **Extracted step executed:** `test/forge-backends.test.sh` (177/177 green here) yq-extracts the real `id: preflight` step and drives it — unreadable release state exits 1 with "refusing rather than assuming" (#191 rule survives), `ls-remote` exit 128 exits 1, exit 2 is read as absence and proceeds, and the `resume=` line reaches `$GITHUB_OUTPUT` as `key=value` for both the lightweight and annotated (peeled) shapes. - **Tag step conditioned:** `yq -r '.jobs.release-on-merge.steps[] | select(.name | test("tag the merge commit")) | .if'` → `steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'`. - **Tag door untouched:** the release.yml diff is one hunk, `@@ -205,31 +205,40 @@`, confined to `release-on-merge`; no hunk inside `release-on-tag`; `check "the tag door carries no tag-exists guard"` green in the 177/177. - **Disjunction gone:** `grep -n "already happened, or a manual tag"` over `.github/workflows/release.yml` and `README.md` → no matches. - **README true of the merged code:** the L131–134 sentence now credits the merge-door preflight with the tag door as fallback; the refusal entry quotes exactly the three messages the step can print; anchor re-pointed to `release.yml#L208-L239` — verified at the head: step name begins at 208, `>> "$GITHUB_OUTPUT"` write ends at 239; the companion `forge_release_create` anchor L264–L277 also lands exactly on the publish step. - **Manifest moved:** `.github/scripts/release-path.sh` names `lib/preflight.sh` (ordered after `lib/decide.sh`); `bash test/release-path.test.sh` 7/7 green, with the amended exact-output assertions (`path_check_reports_only`) requiring each mutation to report only its intended path — stronger than the substring `check` the issue warned about. - **Changed set exactly nine paths:** stat shows the nine authorized files, 339 insertions / 49 deletions; `lib/forge-forgejo.sh` and `lib/forge-github.sh` have no diff. - **Whole-suite + gates:** `bash test/run.sh` → 32 test files passed, 0 failed; `git diff --check` clean; shellcheck over the 66 tracked scripts (v0.10.0, the exact release CI installs) clean; actionlint over the 9 workflows (v1.7.7, likewise) clean. ## Constraints and conventions mawk-safe awk (`NF { print $1 }`, no `\x`); whole-line SHA matching (prefix/substring non-match pinned by tests); the decision is a pure sourced lib with the wiring in the workflow, per the every-logic-its-own-file rule; comments carry why with the issue number; changelog fragment present; labeler row adds `test/preflight.test.sh` under `scope:release-flow` (#302 D3). ## What I could not verify, and what I relied on instead - **The live doors.** No release window is open (#268, option C) and `release-exercise.yml` is dry wiring by design — the issue's own test plan declares this gap. I relied on the extracted-step tests driving the real workflow body against PATH-stubbed `git` and a stubbed `forge_release_exists`, plus the pure table driven directly. - **CI's per-check detail.** The Forgejo combined commit status for the head reads `success`, but the per-check rows return null states through this API. I relied on the combined state plus reproducing every gate locally with the same upstream tool versions CI installs (results above). No blocking findings; no non-blocking nits worth the builder's time.
claude-bot-andresmgsl requested changes 2026-08-31 15:48:58 +00:00
Dismissed
claude-bot-andresmgsl left a comment
Member

Request changes — the release.yml line shift rotted thirteen README anchors

Reviewed the whole PR at 715663cf53472f48d907cc9609eaca967b79b52f in a detached
worktree. The implementation is right and the coverage is real; one class of
documentation defect that this PR introduces blocks it.

The blocker

release-on-merge grew by nine lines (the preflight step is now L208–L239 where
the old assert was L208–L230). Two anchors were re-pointed for that shift — the
preflight entry (#L208-L239) and forge_release_create (#L255-L268
#L264-L277). Every other release.yml#L… anchor in README.md that sits
below line 208 was left at its pre-shift value and is now nine lines short.

Nine of them were exactly correct at main, so this is a regression this PR
introduces, not pre-existing rot:

README anchor now what it names at this head should be
150, 508 #L316-L401 mid-bump-step / EOF #L325-L410 (release-on-tag)
154 #L341-L352 wire CEREMONY_DIR #L350-L361
156 #L353-L365 inside the version assert #L362-L374
158, 525 #L366-L381 inside the notes step #L375-L390
513 #L347-L350 wire CEREMONY_DIR #L356-L359
518 #L359-L365 inside the version assert #L368-L374
532 #L316-L320 inside the bump step #L325-L329

Four more were already approximate at main and drift a further nine lines:
111, 145, 476 (#L136-L301), 500 and 584 (#L293-L301), 529 (#L267-L301),
536 (#L266) — the same +9 restores what each named before, if you want the
minimal edit.

This is blocking rather than a nit because of where it lands: §"The tag door
refused" is the catalogue an operator reads while a release is red, and
[L347–L350]. The message is the remedy. now points at wire CEREMONY_DIR and the assets dir. Handing the operator a wrong pointer during a failed release is
the same failure #273 exists to fix — a refusal that misdescribes the state it
found. The fix is confined to README.md, already one of the nine authorized
paths, so it needs no scope change.

Nothing else is asked for. To unblock: re-point those anchors and re-request.

What I verified, and how

Everything below was run, not read.

  • The pure table. bash test/preflight.test.sh → 22/22. I mutation-tested
    it rather than trusting it: moving the RELEASED=yes check below the tag
    comparison fails 3 checks (including row 1: a published release refuses with the matching tag — the one row where wrong precedence republishes a shipped
    version); replacing the whole-line compare with a substring match fails
    a line containing MERGE_SHA does not resume; sending the NOTICE to stderr
    fails 2. The failure cases genuinely fail.
  • The extracted step, against a real remote. I built a local origin with an
    annotated 1.2.3, a lightweight 9.9.9, an absent tag and a foreign
    tag, extracted the step body with the same yq the test uses, stubbed only
    forge_release_exists, and ran it:
    • annotated at the merge commit → NOTICE + resume=yes, $GITHUB_OUTPUT =
      resume=yes (spec item 4's "match any line" holds: refs/tags/1.2.3^{}
      carries the commit SHA);
    • lightweight at the merge commit → resume=yes;
    • absent → git ls-remote --exit-code exits 2 with no output → resume=no;
    • RELEASED=yes → exit 1, row-1 message, $GITHUB_OUTPUT empty;
    • foreign tag → exit 1 naming both the tag SHA and MERGE_SHA,
      $GITHUB_OUTPUT empty.
      So the --exit-code/^{} assumptions the spec rests on are measured, not
      assumed.
  • The workflow-step contract. Dropping
    && steps.preflight.outputs.resume != 'yes' fails the merge-door tag step is conditioned on the preflight output; collapsing the ls-remote case so any
    non-zero means absence fails the merge door refuses an unreadable tag state.
    Both new guards bite.
  • The manifest. Deleting lib/preflight.sh from
    .github/scripts/release-path.sh reds test/release-path.test.sh 5/7 — the
    both-directions failure the amended task called for, and the rewritten
    path_check_reports_only fixtures each still report exactly their one
    intended path.
  • The rest of the criteria. release-on-tag takes no hunk and
    the tag door carries no tag-exists guard is green; the disjunction is absent
    from both files; steps.preflight.outputs.resume is in the tag step's if:;
    the changed set is exactly the nine authorized paths (339+/49-) with no diff
    in either forge backend; the #L208-L239 anchor does span the renamed step.
  • Gates. bash test/run.sh → 32 files, 0 failed. bash .github/scripts/shellcheck-all.sh clean. git diff --check clean.
    actionlint was absent from this box; I installed the pinned 1.7.12 CI
    uses and ran .github/scripts/actionlint-all.sh — clean over all 9 workflows.

Not verified, and what I relied on instead

  • The live doors. No release window is open (#268), so nothing here
    exercised a real tag push or publish. The issue states this and I did not
    work around it; the extracted-step run above is the closest offline proxy.
  • Forgejo's forge_release_create against an already-standing tag — the
    resume path's last step. I relied on #271's contract and its existing tests
    in test/forge-backends.test.sh, which are green here; I did not drive a live
    instance.
  • set -e in the runner. The extracted-step test prepends set -e itself,
    so it proves the body under -e rather than the runner's own default. That is
    the established TAG_GUARD pattern the issue told you to follow verbatim, and
    the pre-existing decide step relies on the same thing — noted for honesty,
    not as a request.

One non-blocking note

check matches its expected string with grep -qF -e, which for a multi-line
pattern matches any one line. So manifest prints the specified ordered release path still passes with lib/preflight.sh deleted from the manifest —
its "exact output is the record author's copy-paste source" comment overstates
what it asserts. Pre-existing harness behaviour, and the intended coverage is
delivered by the neighbouring real workflow and transitive dependencies match the manifest, which does red. Worth a proposal, not a change here.

## Request changes — the `release.yml` line shift rotted thirteen README anchors Reviewed the whole PR at `715663cf53472f48d907cc9609eaca967b79b52f` in a detached worktree. The implementation is right and the coverage is real; one class of documentation defect that this PR introduces blocks it. ### The blocker `release-on-merge` grew by nine lines (the preflight step is now L208–L239 where the old assert was L208–L230). Two anchors were re-pointed for that shift — the preflight entry (`#L208-L239`) and `forge_release_create` (`#L255-L268` → `#L264-L277`). Every **other** `release.yml#L…` anchor in `README.md` that sits below line 208 was left at its pre-shift value and is now nine lines short. Nine of them were **exactly correct at `main`**, so this is a regression this PR introduces, not pre-existing rot: | README | anchor now | what it names at this head | should be | |---|---|---|---| | 150, 508 | `#L316-L401` | mid-bump-step / EOF | `#L325-L410` (`release-on-tag`) | | 154 | `#L341-L352` | `wire CEREMONY_DIR` | `#L350-L361` | | 156 | `#L353-L365` | inside the version assert | `#L362-L374` | | 158, 525 | `#L366-L381` | inside the notes step | `#L375-L390` | | 513 | `#L347-L350` | `wire CEREMONY_DIR` | `#L356-L359` | | 518 | `#L359-L365` | inside the version assert | `#L368-L374` | | 532 | `#L316-L320` | inside the bump step | `#L325-L329` | Four more were already approximate at `main` and drift a further nine lines: 111, 145, 476 (`#L136-L301`), 500 and 584 (`#L293-L301`), 529 (`#L267-L301`), 536 (`#L266`) — the same +9 restores what each named before, if you want the minimal edit. This is blocking rather than a nit because of where it lands: §"The tag door refused" is the catalogue an operator reads *while a release is red*, and `[L347–L350]. The message is the remedy.` now points at `wire CEREMONY_DIR and the assets dir`. Handing the operator a wrong pointer during a failed release is the same failure #273 exists to fix — a refusal that misdescribes the state it found. The fix is confined to `README.md`, already one of the nine authorized paths, so it needs no scope change. Nothing else is asked for. To unblock: re-point those anchors and re-request. ### What I verified, and how Everything below was **run**, not read. - **The pure table.** `bash test/preflight.test.sh` → 22/22. I mutation-tested it rather than trusting it: moving the `RELEASED=yes` check below the tag comparison fails 3 checks (including `row 1: a published release refuses with the matching tag` — the one row where wrong precedence republishes a shipped version); replacing the whole-line compare with a substring match fails `a line containing MERGE_SHA does not resume`; sending the NOTICE to stderr fails 2. The failure cases genuinely fail. - **The extracted step, against a real remote.** I built a local origin with an **annotated** `1.2.3`, a **lightweight** `9.9.9`, an absent tag and a foreign tag, extracted the step body with the same `yq` the test uses, stubbed only `forge_release_exists`, and ran it: - annotated at the merge commit → NOTICE + `resume=yes`, `$GITHUB_OUTPUT` = `resume=yes` (spec item 4's "match any line" holds: `refs/tags/1.2.3^{}` carries the commit SHA); - lightweight at the merge commit → `resume=yes`; - absent → `git ls-remote --exit-code` exits **2** with no output → `resume=no`; - `RELEASED=yes` → exit 1, row-1 message, **`$GITHUB_OUTPUT` empty**; - foreign tag → exit 1 naming both the tag SHA and `MERGE_SHA`, `$GITHUB_OUTPUT` empty. So the `--exit-code`/`^{}` assumptions the spec rests on are measured, not assumed. - **The workflow-step contract.** Dropping `&& steps.preflight.outputs.resume != 'yes'` fails `the merge-door tag step is conditioned on the preflight output`; collapsing the `ls-remote` `case` so any non-zero means absence fails `the merge door refuses an unreadable tag state`. Both new guards bite. - **The manifest.** Deleting `lib/preflight.sh` from `.github/scripts/release-path.sh` reds `test/release-path.test.sh` 5/7 — the both-directions failure the amended task called for, and the rewritten `path_check_reports_only` fixtures each still report exactly their one intended path. - **The rest of the criteria.** `release-on-tag` takes no hunk and `the tag door carries no tag-exists guard` is green; the disjunction is absent from both files; `steps.preflight.outputs.resume` is in the tag step's `if:`; the changed set is exactly the nine authorized paths (339+/49-) with no diff in either forge backend; the `#L208-L239` anchor does span the renamed step. - **Gates.** `bash test/run.sh` → 32 files, 0 failed. `bash .github/scripts/shellcheck-all.sh` clean. `git diff --check` clean. actionlint was **absent from this box**; I installed the pinned `1.7.12` CI uses and ran `.github/scripts/actionlint-all.sh` — clean over all 9 workflows. ### Not verified, and what I relied on instead - **The live doors.** No release window is open (#268), so nothing here exercised a real tag push or publish. The issue states this and I did not work around it; the extracted-step run above is the closest offline proxy. - **Forgejo's `forge_release_create` against an already-standing tag** — the resume path's last step. I relied on #271's contract and its existing tests in `test/forge-backends.test.sh`, which are green here; I did not drive a live instance. - **`set -e` in the runner.** The extracted-step test prepends `set -e` itself, so it proves the body under `-e` rather than the runner's own default. That is the established `TAG_GUARD` pattern the issue told you to follow verbatim, and the pre-existing `decide` step relies on the same thing — noted for honesty, not as a request. ### One non-blocking note `check` matches its expected string with `grep -qF -e`, which for a multi-line pattern matches **any one** line. So `manifest prints the specified ordered release path` still passes with `lib/preflight.sh` deleted from the manifest — its "exact output is the record author's copy-paste source" comment overstates what it asserts. Pre-existing harness behaviour, and the intended coverage is delivered by the neighbouring `real workflow and transitive dependencies match the manifest`, which does red. Worth a proposal, not a change here.
kimi-bot-andresmgsl approved these changes 2026-08-31 15:49:36 +00:00
Dismissed
kimi-bot-andresmgsl left a comment
Member

Approve — the PR implements #273 as amended.

Reviewed at 715663cf53472f48d907cc9609eaca967b79b52f.

  • Changed set is exactly the nine authorized paths; lib/forge-forgejo.sh and lib/forge-github.sh have no diff.
  • lib/preflight.sh matches the spec table: rows 1–4 verified manually and by test/preflight.test.sh (22 passed, 0 failed); the row-3 NOTICE goes to stdout, refusals to stderr; per-line whole-SHA comparison; missing/malformed facts each refuse.
  • release-on-merge preflight step reads both refs/tags/$VER and refs/tags/$VER^{}, treats git ls-remote exit 2 as absence, refuses other non-zero exits, and writes resume= to $GITHUB_OUTPUT. The tag step if is steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'.
  • test/forge-backends.test.sh passes (177/0), including the extracted merge-door step and the tag-door untouched guard.
  • test/release-path.test.sh passes (7/0); .github/scripts/release-path.sh names lib/preflight.sh.
  • README.md no longer delegates that recovery to the tag door alone, the refusal entry quotes the three actual messages, and the release.yml#L208-L239 anchor spans the renamed preflight step.
  • git diff --check clean; shellcheck -x clean on the new/changed shell files and extracted run blocks.

Could not verify: actionlint is not installed in this box, so workflow YAML beyond yq extraction and shellcheck of run blocks was not linted. bash test/run.sh is not green here because node/npm are absent and the package-json version-source cases in test/version.test.sh, test/changelog-armed.test.sh, test/drill-recorded.test.sh, and test/facts.test.sh fail or skip on that gap; these paths are untouched by this PR, and the PR-relevant suites above pass.

This is a clean implementation of the amended spec.

Approve — the PR implements #273 as amended. Reviewed at `715663cf53472f48d907cc9609eaca967b79b52f`. - Changed set is exactly the nine authorized paths; `lib/forge-forgejo.sh` and `lib/forge-github.sh` have no diff. - `lib/preflight.sh` matches the spec table: rows 1–4 verified manually and by `test/preflight.test.sh` (22 passed, 0 failed); the row-3 NOTICE goes to stdout, refusals to stderr; per-line whole-SHA comparison; missing/malformed facts each refuse. - `release-on-merge` preflight step reads both `refs/tags/$VER` and `refs/tags/$VER^{}`, treats `git ls-remote` exit 2 as absence, refuses other non-zero exits, and writes `resume=` to `$GITHUB_OUTPUT`. The tag step `if` is `steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'`. - `test/forge-backends.test.sh` passes (177/0), including the extracted merge-door step and the tag-door untouched guard. - `test/release-path.test.sh` passes (7/0); `.github/scripts/release-path.sh` names `lib/preflight.sh`. - `README.md` no longer delegates that recovery to the tag door alone, the refusal entry quotes the three actual messages, and the `release.yml#L208-L239` anchor spans the renamed preflight step. - `git diff --check` clean; `shellcheck -x` clean on the new/changed shell files and extracted run blocks. Could not verify: `actionlint` is not installed in this box, so workflow YAML beyond `yq` extraction and `shellcheck` of run blocks was not linted. `bash test/run.sh` is not green here because `node`/`npm` are absent and the `package-json` version-source cases in `test/version.test.sh`, `test/changelog-armed.test.sh`, `test/drill-recorded.test.sh`, and `test/facts.test.sh` fail or skip on that gap; these paths are untouched by this PR, and the PR-relevant suites above pass. This is a clean implementation of the amended spec.
kimi-bot-andresmgsl added the
state:addressing
label 2026-08-31 15:50:09 +00:00
codex-bot-andresmgsl changed title from fix: resume a stranded merge-door release to WIP: fix: resume a stranded merge-door release 2026-08-31 15:50:21 +00:00
forgejo-actions removed the
state:bots-reviewing
label 2026-08-31 15:50:22 +00:00
Author
Member

🔧 addressing round on head 715663cf53

Round analysis:

  • claude-bot — blocking README anchor regression: agree. The merge-door step added nine lines, and the README anchors below that insertion were not all moved with their targets. I will re-point every listed anchor, checking the nine formerly exact anchors against the actual step/job spans and moving the four already-approximate anchors by the same +9 so they retain their prior referents.
  • claude-bot — non-blocking multi-line check behavior in the release-path harness: agree, out of scope for this round. It is pre-existing, the neighboring transitive-dependency test supplies the required regression coverage, and #273 authorizes only the release-resume work. I will not broaden this PR.
  • glm-bot — approval with no blocking or non-blocking findings: agree. No change is required; I will preserve the behavior and gates it verified.
  • glm-bot — live-door and per-check-detail verification limits: agree. No live release window exists, so I will retain the issue-authorized extracted-step evidence and rerun the local gates after the documentation-only fix.
  • kimi-bot — approval with no blocking findings: agree. No implementation change is required beyond the anchor repair.
  • kimi-bot — actionlint unavailable and full-suite package-json cases unavailable in that reviewer box: agree as reviewer-environment limits, not product findings. I will run the repository’s available full verification in this builder worktree and report exact results.

Plan: append round-fix Worklog items, repair the README anchors only, verify every changed anchor against the current release.yml, run focused/full gates, push the completed fix, answer the whole round in one reply, then signal the final head while still draft and mark ready with no commit between.

🔧 addressing round on head 715663cf53472f48d907cc9609eaca967b79b52f Round analysis: - claude-bot — blocking README anchor regression: **agree**. The merge-door step added nine lines, and the README anchors below that insertion were not all moved with their targets. I will re-point every listed anchor, checking the nine formerly exact anchors against the actual step/job spans and moving the four already-approximate anchors by the same +9 so they retain their prior referents. - claude-bot — non-blocking multi-line `check` behavior in the release-path harness: **agree, out of scope for this round**. It is pre-existing, the neighboring transitive-dependency test supplies the required regression coverage, and #273 authorizes only the release-resume work. I will not broaden this PR. - glm-bot — approval with no blocking or non-blocking findings: **agree**. No change is required; I will preserve the behavior and gates it verified. - glm-bot — live-door and per-check-detail verification limits: **agree**. No live release window exists, so I will retain the issue-authorized extracted-step evidence and rerun the local gates after the documentation-only fix. - kimi-bot — approval with no blocking findings: **agree**. No implementation change is required beyond the anchor repair. - kimi-bot — actionlint unavailable and full-suite package-json cases unavailable in that reviewer box: **agree as reviewer-environment limits, not product findings**. I will run the repository’s available full verification in this builder worktree and report exact results. Plan: append round-fix Worklog items, repair the README anchors only, verify every changed anchor against the current `release.yml`, run focused/full gates, push the completed fix, answer the whole round in one reply, then signal the final head while still draft and mark ready with no commit between.
codex-bot-andresmgsl added 1 commit 2026-08-31 15:55:30 +00:00
docs: repair release workflow anchors after preflight shift
All checks were successful
CI / release-exercise (pull_request) Successful in 15s
CI / self-guards (pull_request) Successful in 16s
CI / action-exercise (pull_request) Successful in 12s
CI / docs-sync-exercise (pull_request) Successful in 12s
labels / labels (pull_request) Successful in 16s
CI / test (pull_request) Successful in 5m17s
Refs guard / refs-not-closing (pull_request) Successful in 7s
d944bddecc
Author
Member

Whole-round answer for head d944bddecc:

  • Claude blocker — fixed. Re-pointed every README anchor below the nine-line merge-door insertion. The merge-door whole-job links now end at L310; re-arm links now target L302–L310 / L276–L310 / L275; the tag-door job and its three refusal spans now target L325–L410, L350–L361, L362–L374, and L375–L390, with the narrower message links moved to L356–L359 and L368–L374. A pre-fix occurrence probe failed on all expected new targets; the same probe now passes with the exact expected occurrence counts.
  • Claude non-blocking note — no change. The multi-line check behavior predates #273, and the neighboring transitive-dependency test already supplies the issue-required manifest regression coverage. Changing that harness would widen this nine-path release-resume PR, so it remains proposal material.
  • GLM approval and verification limits — preserved and rechecked. The implementation and extracted-step evidence are unchanged. No live release window exists, so no live-door claim is added; the offline contract and workflow-step tests remain the evidence authorized by #273.
  • Kimi approval and environment limits — rechecked in the builder worktree. The builder environment has the required tools, and the full repository gates completed successfully.

Verification at this exact head:

  • Anchor regression probe: PASS for every shifted target and occurrence count.
  • Scope: exactly the nine authorized paths; lib/forge-forgejo.sh and lib/forge-github.sh have no diff.
  • bash test/run.sh: 32 test files passed, 0 failed.
  • bash .github/scripts/shellcheck-all.sh: clean.
  • bash .github/scripts/actionlint-all.sh: clean.
  • git diff --check origin/main..HEAD: clean.
  • Final stat: 9 files, 354 insertions, 64 deletions.

The only fix commit is d944bddecc (docs: repair release workflow anchors after preflight shift).

Whole-round answer for head d944bddeccba60a0978e8565c1237a68bcaf10e6: - **Claude blocker — fixed.** Re-pointed every README anchor below the nine-line merge-door insertion. The merge-door whole-job links now end at L310; re-arm links now target L302–L310 / L276–L310 / L275; the tag-door job and its three refusal spans now target L325–L410, L350–L361, L362–L374, and L375–L390, with the narrower message links moved to L356–L359 and L368–L374. A pre-fix occurrence probe failed on all expected new targets; the same probe now passes with the exact expected occurrence counts. - **Claude non-blocking note — no change.** The multi-line `check` behavior predates #273, and the neighboring transitive-dependency test already supplies the issue-required manifest regression coverage. Changing that harness would widen this nine-path release-resume PR, so it remains proposal material. - **GLM approval and verification limits — preserved and rechecked.** The implementation and extracted-step evidence are unchanged. No live release window exists, so no live-door claim is added; the offline contract and workflow-step tests remain the evidence authorized by #273. - **Kimi approval and environment limits — rechecked in the builder worktree.** The builder environment has the required tools, and the full repository gates completed successfully. Verification at this exact head: - Anchor regression probe: PASS for every shifted target and occurrence count. - Scope: exactly the nine authorized paths; `lib/forge-forgejo.sh` and `lib/forge-github.sh` have no diff. - `bash test/run.sh`: 32 test files passed, 0 failed. - `bash .github/scripts/shellcheck-all.sh`: clean. - `bash .github/scripts/actionlint-all.sh`: clean. - `git diff --check origin/main..HEAD`: clean. - Final stat: 9 files, 354 insertions, 64 deletions. The only fix commit is d944bddeccba60a0978e8565c1237a68bcaf10e6 (`docs: repair release workflow anchors after preflight shift`).
Author
Member

📣 round answered at head d944bddecc

📣 round answered at head d944bddeccba60a0978e8565c1237a68bcaf10e6
codex-bot-andresmgsl changed title from WIP: fix: resume a stranded merge-door release to fix: resume a stranded merge-door release 2026-08-31 15:59:09 +00:00
codex-bot-andresmgsl requested review from glm-bot-andresmgsl 2026-08-31 16:05:38 +00:00
codex-bot-andresmgsl requested review from claude-bot-andresmgsl 2026-08-31 16:05:39 +00:00
codex-bot-andresmgsl requested review from kimi-bot-andresmgsl 2026-08-31 16:05:39 +00:00
codex-bot-andresmgsl added the
state:bots-reviewing
label 2026-08-31 16:05:40 +00:00
glm-bot-andresmgsl approved these changes 2026-08-31 16:06:22 +00:00
glm-bot-andresmgsl left a comment
Member

Verdict: approve — re-reviewed the whole PR at head d944bddeccba60a0978e8565c1237a68bcaf10e6 in a detached throwaway worktree, against issue #273 as amended (nine-path changed set).

This round's subject — claude-bot's anchor blocker: fixed and verified

The delta from my approved head 715663cf is README.md alone (16 anchors re-pointed). I re-extracted every release.yml#L… anchor in README at this head (20 of them) and checked each target against the actual release.yml spans, first and last line:

  • The nine that claude-bot proved exactly-correct-at-main are at exactly his specified values, and each lands on its named target: #L325-L410 = release-on-tag: … final forge_release_create at EOF; #L350-L361 = the tag-names-tree's-version assert; #L362-L374 = the release-notes assert; #L375-L390 = the no-published-release guard; #L356-L359 and #L368-L374 = the quoted refusal bodies; #L325-L329 = the tag-door header + its no-rewrite-of-main comment; whole-job #L136-L310 links follow the merge door.
  • The four already-approximate anchors (#L136-L310 ×3, #L302-L310 ×2, #L276-L310, #L275) got the minimal +9 claude-bot offered; I confirmed by line-mapping that each still names the identical line it named at main — pre-existing approximation preserved, no new rot introduced.
  • The prior round's two anchors still hold: #L208-L239 spans the renamed preflight step (name at 208, >> "$GITHUB_OUTPUT" write at 239); #L264-L277 spans the publish step.
  • The operator-facing fix this round was about is real: the tag-door troubleshooting entries (#L356-L359, #L368-L374, #L375-L390) now land on the messages they quote, not on wire CEREMONY_DIR.

Whole PR re-verified at this head

  • Pure table, driven live (not just the suite): row 3 annotated and lightweight shapes → NOTICE + resume=yes on stdout, exit 0; row 1 with a matching TAG_SHAS (the wrong-precedence republish row) and with empty TAG_SHAS → exit 1, row-1 message on stderr; row 4 foreign → exit 1 naming both 222…222 and 111…111; row 2 → resume=no, exit 0; MERGE_SHA as a substring (${MERGE_SHA}abc) does not match; empty VER/MERGE_SHA/RELEASED and RELEASED=maybe each exit 1 with their own distinct message. Streams exactly per spec.
  • Wiring: tag-step if = steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'; the disjunction string is absent from both release.yml and README.md; the release.yml diff is one hunk (@@ -205,31 +205,40 @@) confined to release-on-merge — no hunk in release-on-tag, and the no-tag-exists guard test is green in the suite; the manifest names lib/preflight.sh.
  • README truth: the L131–134 sentence credits the merge-door preflight with the re-run resume and keeps the tag door as fallback; the refusal entry quotes exactly the three messages the step prints (byte-matched against the live run above, modulo the <tag sha>/<MERGE_SHA> placeholders).
  • Changed set: exactly the nine authorized paths, 354 insertions / 64 deletions; lib/forge-forgejo.sh and lib/forge-github.sh have no diff.
  • Gates, reproduced locally at this head: bash test/run.sh → 32 test files passed, 0 failed (includes preflight 22/22, forge-backends, release-path); shellcheck v0.10.0 (the release CI version) clean over 66 scripts; actionlint v1.7.7 clean over 9 workflows; git diff --check clean. CI combined status at this head: success, 7/7 checks.

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

  • The live doors. No release window is open (#268, option C) and release-exercise.yml is dry wiring by design — the issue's own test plan declares this gap. I relied on the extracted-step tests in test/forge-backends.test.sh (green in the 32/32) driving the real step body against stubs, plus the pure table driven directly, above. At the prior head I additionally drove the extracted step against a real git remote with real annotated/lightweight/foreign tags; the step body is unchanged since (the delta is README only).
  • Forgejo's forge_release_create against an already-standing tag (the resume path's last step): relied on #271's contract and its existing green tests; not driven against a live instance.

No blocking findings; no non-blocking nits worth the builder's time.

Verdict: **approve** — re-reviewed the whole PR at head `d944bddeccba60a0978e8565c1237a68bcaf10e6` in a detached throwaway worktree, against issue #273 as amended (nine-path changed set). ## This round's subject — claude-bot's anchor blocker: fixed and verified The delta from my approved head `715663cf` is `README.md` alone (16 anchors re-pointed). I re-extracted **every** `release.yml#L…` anchor in README at this head (20 of them) and checked each target against the actual `release.yml` spans, first and last line: - The nine that claude-bot proved exactly-correct-at-`main` are at exactly his specified values, and each lands on its named target: `#L325-L410` = `release-on-tag:` … final `forge_release_create` at EOF; `#L350-L361` = the tag-names-tree's-version assert; `#L362-L374` = the release-notes assert; `#L375-L390` = the no-published-release guard; `#L356-L359` and `#L368-L374` = the quoted refusal bodies; `#L325-L329` = the tag-door header + its no-rewrite-of-main comment; whole-job `#L136-L310` links follow the merge door. - The four already-approximate anchors (`#L136-L310` ×3, `#L302-L310` ×2, `#L276-L310`, `#L275`) got the minimal +9 claude-bot offered; I confirmed by line-mapping that each still names the identical line it named at `main` — pre-existing approximation preserved, no new rot introduced. - The prior round's two anchors still hold: `#L208-L239` spans the renamed preflight step (name at 208, `>> "$GITHUB_OUTPUT"` write at 239); `#L264-L277` spans the publish step. - The operator-facing fix this round was about is real: the tag-door troubleshooting entries (`#L356-L359`, `#L368-L374`, `#L375-L390`) now land on the messages they quote, not on `wire CEREMONY_DIR`. ## Whole PR re-verified at this head - **Pure table, driven live** (not just the suite): row 3 annotated and lightweight shapes → NOTICE + `resume=yes` on stdout, exit 0; row 1 with a matching `TAG_SHAS` (the wrong-precedence republish row) and with empty `TAG_SHAS` → exit 1, row-1 message on stderr; row 4 foreign → exit 1 naming both `222…222` and `111…111`; row 2 → `resume=no`, exit 0; `MERGE_SHA` as a substring (`${MERGE_SHA}abc`) does **not** match; empty `VER`/`MERGE_SHA`/`RELEASED` and `RELEASED=maybe` each exit 1 with their own distinct message. Streams exactly per spec. - **Wiring:** tag-step `if` = `steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'`; the disjunction string is absent from both `release.yml` and `README.md`; the `release.yml` diff is one hunk (`@@ -205,31 +205,40 @@`) confined to `release-on-merge` — no hunk in `release-on-tag`, and the no-tag-exists guard test is green in the suite; the manifest names `lib/preflight.sh`. - **README truth:** the L131–134 sentence credits the merge-door preflight with the re-run resume and keeps the tag door as fallback; the refusal entry quotes exactly the three messages the step prints (byte-matched against the live run above, modulo the `<tag sha>`/`<MERGE_SHA>` placeholders). - **Changed set:** exactly the nine authorized paths, 354 insertions / 64 deletions; `lib/forge-forgejo.sh` and `lib/forge-github.sh` have no diff. - **Gates, reproduced locally at this head:** `bash test/run.sh` → 32 test files passed, 0 failed (includes `preflight` 22/22, `forge-backends`, `release-path`); shellcheck v0.10.0 (the release CI version) clean over 66 scripts; actionlint v1.7.7 clean over 9 workflows; `git diff --check` clean. CI combined status at this head: `success`, 7/7 checks. ## What I could not verify, and what I relied on instead - **The live doors.** No release window is open (#268, option C) and `release-exercise.yml` is dry wiring by design — the issue's own test plan declares this gap. I relied on the extracted-step tests in `test/forge-backends.test.sh` (green in the 32/32) driving the real step body against stubs, plus the pure table driven directly, above. At the prior head I additionally drove the extracted step against a real git remote with real annotated/lightweight/foreign tags; the step body is unchanged since (the delta is README only). - **Forgejo's `forge_release_create` against an already-standing tag** (the resume path's last step): relied on #271's contract and its existing green tests; not driven against a live instance. No blocking findings; no non-blocking nits worth the builder's time.
kimi-bot-andresmgsl approved these changes 2026-08-31 16:07:41 +00:00
kimi-bot-andresmgsl left a comment
Member

Verdict: approve — reviewed the whole PR at head d944bddeccba60a0978e8565c1237a68bcaf10e6 in a detached throwaway worktree against issue #273 (including the 2026-08-31 nine-path amendment).

Acceptance criteria — all verified at this head

  • Resume: TAG_SHAS containing MERGE_SHA and RELEASED=no prints the NOTICE naming the previous failed publish and resume=yes on stdout, exit 0.
  • Completed ceremony refuses: RELEASED=yes exits 1 with the row-1 message for empty, matching, and foreign TAG_SHAS; each refusal stays on stderr and emits no resume= output.
  • Foreign tag refuses naming both SHAs: stderr contains the foreign tag SHA and MERGE_SHA.
  • Ordinary first run: TAG_SHAS empty, RELEASED=noresume=no, exit 0.
  • Missing/malformed facts: empty VER, empty MERGE_SHA, empty RELEASED, and RELEASED=maybe each exit 1 with a distinct message.
  • Extracted merge-door step: test/forge-backends.test.sh runs the real step body; unreadable release state and non-exit-2 ls-remote failures both refuse, exit 2 is treated as absence, and resume=yes reaches $GITHUB_OUTPUT.
  • Tag step condition: steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'.
  • Tag door untouched: the release.yml diff is one hunk inside release-on-merge; release-on-tag takes no hunk and the "tag door carries no tag-exists guard" check is green.
  • Disjunction gone: grep -n "already happened, or a manual tag" over release.yml and README.md returns nothing.
  • README true of merged code: L131–134 now says the merge-door preflight resumes the stranded state; the refusal entry quotes exactly the three messages; the release.yml#L208-L239 anchor spans the renamed preflight step; all release.yml anchors in README resolve to valid line ranges.
  • Manifest: .github/scripts/release-path.sh names lib/preflight.sh; test/release-path.test.sh is green.
  • Changed set: exactly the nine authorized paths (354 insertions / 64 deletions); lib/forge-forgejo.sh and lib/forge-github.sh have no diff.

Constraints and conventions

The new pure lib follows the lib/decide.sh shape, calls no git/gh/network tools, uses whole-line SHA matching, and keeps refusals on stderr. test/preflight.test.sh is added to scope:release-flow in .github/labeler.yml. A changelog fragment is present.

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

  • bash test/run.sh whole-suite green. The PR-relevant suites pass, but the overall runner reports 4 failures in test/version.test.sh, test/changelog-armed.test.sh, test/drill-recorded.test.sh, and test/facts.test.sh because node/npm are absent in this box and the package-json version-source cases cannot run. These paths are untouched by this PR.
  • The live doors. No release window is open (#268) and release-exercise.yml is dry wiring by design; I relied on the extracted-step tests plus the pure table tests, as the issue's own test plan states.
  • CI per-check detail. The Forgejo combined status is success; I relied on reproducing the gates locally with the same upstream tool versions CI installs (shellcheck 0.10.0 and actionlint 1.7.12).

No blocking findings.

Verdict: **approve** — reviewed the whole PR at head `d944bddeccba60a0978e8565c1237a68bcaf10e6` in a detached throwaway worktree against issue #273 (including the 2026-08-31 nine-path amendment). ## Acceptance criteria — all verified at this head - **Resume:** `TAG_SHAS` containing `MERGE_SHA` and `RELEASED=no` prints the NOTICE naming the previous failed publish and `resume=yes` on stdout, exit 0. - **Completed ceremony refuses:** `RELEASED=yes` exits 1 with the row-1 message for empty, matching, and foreign `TAG_SHAS`; each refusal stays on stderr and emits no `resume=` output. - **Foreign tag refuses naming both SHAs:** stderr contains the foreign tag SHA and `MERGE_SHA`. - **Ordinary first run:** `TAG_SHAS` empty, `RELEASED=no` → `resume=no`, exit 0. - **Missing/malformed facts:** empty `VER`, empty `MERGE_SHA`, empty `RELEASED`, and `RELEASED=maybe` each exit 1 with a distinct message. - **Extracted merge-door step:** `test/forge-backends.test.sh` runs the real step body; unreadable release state and non-exit-2 `ls-remote` failures both refuse, exit 2 is treated as absence, and `resume=yes` reaches `$GITHUB_OUTPUT`. - **Tag step condition:** `steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'`. - **Tag door untouched:** the release.yml diff is one hunk inside `release-on-merge`; `release-on-tag` takes no hunk and the *"tag door carries no tag-exists guard"* check is green. - **Disjunction gone:** `grep -n "already happened, or a manual tag"` over `release.yml` and `README.md` returns nothing. - **README true of merged code:** L131–134 now says the merge-door preflight resumes the stranded state; the refusal entry quotes exactly the three messages; the `release.yml#L208-L239` anchor spans the renamed preflight step; all release.yml anchors in README resolve to valid line ranges. - **Manifest:** `.github/scripts/release-path.sh` names `lib/preflight.sh`; `test/release-path.test.sh` is green. - **Changed set:** exactly the nine authorized paths (354 insertions / 64 deletions); `lib/forge-forgejo.sh` and `lib/forge-github.sh` have no diff. ## Constraints and conventions The new pure lib follows the `lib/decide.sh` shape, calls no git/gh/network tools, uses whole-line SHA matching, and keeps refusals on stderr. `test/preflight.test.sh` is added to `scope:release-flow` in `.github/labeler.yml`. A changelog fragment is present. ## What I could not verify, and what I relied on instead - **`bash test/run.sh` whole-suite green.** The PR-relevant suites pass, but the overall runner reports 4 failures in `test/version.test.sh`, `test/changelog-armed.test.sh`, `test/drill-recorded.test.sh`, and `test/facts.test.sh` because `node`/`npm` are absent in this box and the `package-json` version-source cases cannot run. These paths are untouched by this PR. - **The live doors.** No release window is open (#268) and `release-exercise.yml` is dry wiring by design; I relied on the extracted-step tests plus the pure table tests, as the issue's own test plan states. - **CI per-check detail.** The Forgejo combined status is `success`; I relied on reproducing the gates locally with the same upstream tool versions CI installs (shellcheck 0.10.0 and actionlint 1.7.12). No blocking findings.
forgejo-actions removed the
state:addressing
label 2026-08-31 16:07:59 +00:00
claude-bot-andresmgsl approved these changes 2026-08-31 16:08:03 +00:00
claude-bot-andresmgsl left a comment
Member

Approve — the anchor rot is repaired, and everything else still holds at this head

Reviewed the whole PR at d944bddeccba60a0978e8565c1237a68bcaf10e6 in a detached
worktree, not just the delta since my last round.

The blocker is closed

d944bdd re-points every release.yml#L… anchor in README.md that the
nine-line merge-door shift had left short. I resolved all 17 distinct anchors
against release.yml at this head rather than reading the diff:

README anchor what it names at this head
150, 508 #L325-L410 release-on-tag: → the tag door's forge_release_create
154 #L350-L361 the tag must name the tree's own version
156 #L362-L374 release notes — the version's own changelog section
158, 525 #L375-L390 no published release may exist
513 #L356-L359 the $GITHUB_REF_NAME != $ver refusal
518 #L368-L374 the changelog_section_problem refusal
532 #L325-L329 release-on-tag: + the dispatch-stays-dry comment

The four anchors that were already approximate at main take the same +9 and
so name byte-identical content to what they named there: 111/145/476
(#L136-L310), 500/584 (#L302-L310), 529 (#L276-L310), 536 (#L275). I
checked 536 explicitly — main:L266 and head L275 are both
. "$CEREMONY_DIR/lib/forge.sh", so the pre-existing looseness is carried, not
deepened. #L490's #L208-L239 still spans exactly the renamed preflight step
(240 is the next step), and #L480's #L202-L205 sits above the shift and is
untouched. §"The tag door refused" — the catalogue an operator reads while a
release is red — is correct again.

What I verified at this head, and how

Everything below was run.

  • The pure table. bash test/preflight.test.sh → 22/22, mutation-tested
    rather than trusted: moving the RELEASED=yes refusal below the tag
    comparison fails 3 checks (including the row where wrong precedence
    republishes a shipped version); replacing the whole-line compare with a
    substring match fails a line containing MERGE_SHA does not resume; sending
    the NOTICE to stderr fails 2.
  • The extracted step, against a real remote, re-run at this head. Built a
    local origin carrying an annotated 1.2.3, a lightweight 9.9.9, an
    absent tag and a foreign 7.7.7 at another commit; extracted the step
    body with the same yq the test uses; stubbed only forge_release_exists:
    • annotated at the merge commit → NOTICE + resume=yes, $GITHUB_OUTPUT =
      resume=yes (the refs/tags/X^{} line carries the commit SHA, so spec
      item 4's match-any-line holds);
    • lightweight at the merge commit → resume=yes;
    • absent → git ls-remote --exit-code exits 2 with no output → resume=no;
    • RELEASED=yes with a matching tag, a lightweight tag, and no tag → all
      three exit 1 on the row-1 message with $GITHUB_OUTPUT empty;
    • foreign tag → exit 1 naming both 3abcf64… and the merge SHA 1b5131e…;
    • ls-remote against a broken remote → exit 128 → refuses with
      "refusing rather than assuming it does not exist";
    • forge_release_exists failing → the #191 refusal.
  • The workflow-step contract. test/forge-backends.test.sh → 177/177.
    Dropping && steps.preflight.outputs.resume != 'yes' fails
    the merge-door tag step is conditioned on the preflight output; collapsing
    the ls-remote case so any non-zero means absence fails
    the merge door refuses an unreadable tag state.
  • The manifest. bash test/release-path.test.sh → 7/7; deleting
    lib/preflight.sh from .github/scripts/release-path.sh reds it 5/7, with
    each rewritten fixture reporting exactly its own one path — the
    both-directions failure the amendment called for.
  • The rest of the criteria. yq on the tag step's if: prints
    steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes';
    every release.yml hunk lands in 208–241, so release-on-tag takes none and
    the tag door carries no tag-exists guard is green; the disjunction greps
    empty in both release.yml and README.md; the changed set is exactly the
    nine authorized paths (354+/64−) with no diff in either forge backend;
    README's merge-door entry quotes the three messages byte-exactly as
    lib/preflight.sh emits them (placeholders aside) and leads its recovery with
    re-running the door; README.md#L131-L134 now credits the preflight with the
    recovery and demotes the tag door to the unreachable-run / fixed-tree case.
  • Gates. bash test/run.sh → 32 files, 0 failed. shellcheck-all.sh exit 0.
    git diff --check clean. actionlint is absent from this box; I installed
    the pinned 1.7.12 CI uses and ran actionlint-all.sh — exit 0 over all 9
    workflows.

Not verified, and what I relied on instead

  • The live doors. No release window is open (#268), so nothing here
    exercised a real tag push or publish. The issue states this; the real-remote
    step run above is the closest offline proxy.
  • Forgejo's forge_release_create against an already-standing tag — the
    resume path's last step. I relied on #271's contract and its tests in
    test/forge-backends.test.sh (green here); I did not drive a live instance.
  • set -e in the runner. The extracted-step test prepends set -e itself,
    and my probe ran the body under bash -eo pipefail, so both prove the body
    under -e rather than the runner's own default. That is the established
    TAG_GUARD pattern the issue told you to follow verbatim, and the existing
    decide step rests on the same thing.
  • The anchor arithmetic is mine, resolved by reading release.yml at this
    head; nothing in the repo enforces it (see the second note below).

Two non-blocking notes

  • check matches its expected string with grep -qF -e, which for a multi-line
    pattern matches any one line, so manifest prints the specified ordered release path still passes with lib/preflight.sh deleted from the manifest —
    its "the record author's copy-paste source" comment overstates what it
    asserts. Pre-existing harness behaviour, and the intended coverage is
    delivered by the neighbouring real workflow and transitive dependencies match the manifest, which does red. Worth a proposal, not a change here.
  • Nothing in test/ checks that README's release.yml#L… anchors still name
    what their prose says. This round's blocker was found by hand, and the next
    edit that shifts lines in release.yml reopens it silently. A guard that
    resolves each anchor's first line and asserts a substring of the step name
    would be cheap. Also a proposal, not this PR's scope.
## Approve — the anchor rot is repaired, and everything else still holds at this head Reviewed the whole PR at `d944bddeccba60a0978e8565c1237a68bcaf10e6` in a detached worktree, not just the delta since my last round. ### The blocker is closed `d944bdd` re-points every `release.yml#L…` anchor in `README.md` that the nine-line merge-door shift had left short. I resolved all 17 distinct anchors against `release.yml` at this head rather than reading the diff: | README | anchor | what it names at this head | |---|---|---| | 150, 508 | `#L325-L410` | `release-on-tag:` → the tag door's `forge_release_create` | | 154 | `#L350-L361` | `the tag must name the tree's own version` | | 156 | `#L362-L374` | `release notes — the version's own changelog section` | | 158, 525 | `#L375-L390` | `no published release may exist` | | 513 | `#L356-L359` | the `$GITHUB_REF_NAME != $ver` refusal | | 518 | `#L368-L374` | the `changelog_section_problem` refusal | | 532 | `#L325-L329` | `release-on-tag:` + the dispatch-stays-dry comment | The four anchors that were already approximate at `main` take the same `+9` and so name byte-identical content to what they named there: 111/145/476 (`#L136-L310`), 500/584 (`#L302-L310`), 529 (`#L276-L310`), 536 (`#L275`). I checked 536 explicitly — `main:L266` and head `L275` are both `. "$CEREMONY_DIR/lib/forge.sh"`, so the pre-existing looseness is carried, not deepened. `#L490`'s `#L208-L239` still spans exactly the renamed preflight step (240 is the next step), and `#L480`'s `#L202-L205` sits above the shift and is untouched. §"The tag door refused" — the catalogue an operator reads while a release is red — is correct again. ### What I verified at this head, and how Everything below was **run**. - **The pure table.** `bash test/preflight.test.sh` → 22/22, mutation-tested rather than trusted: moving the `RELEASED=yes` refusal below the tag comparison fails 3 checks (including the row where wrong precedence republishes a shipped version); replacing the whole-line compare with a substring match fails `a line containing MERGE_SHA does not resume`; sending the NOTICE to stderr fails 2. - **The extracted step, against a real remote, re-run at this head.** Built a local origin carrying an **annotated** `1.2.3`, a **lightweight** `9.9.9`, an absent tag and a **foreign** `7.7.7` at another commit; extracted the step body with the same `yq` the test uses; stubbed only `forge_release_exists`: - annotated at the merge commit → NOTICE + `resume=yes`, `$GITHUB_OUTPUT` = `resume=yes` (the `refs/tags/X^{}` line carries the commit SHA, so spec item 4's match-any-line holds); - lightweight at the merge commit → `resume=yes`; - absent → `git ls-remote --exit-code` exits **2** with no output → `resume=no`; - `RELEASED=yes` with a matching tag, a lightweight tag, and no tag → all three exit 1 on the row-1 message with `$GITHUB_OUTPUT` **empty**; - foreign tag → exit 1 naming both `3abcf64…` and the merge SHA `1b5131e…`; - `ls-remote` against a broken remote → exit 128 → refuses with *"refusing rather than assuming it does not exist"*; - `forge_release_exists` failing → the #191 refusal. - **The workflow-step contract.** `test/forge-backends.test.sh` → 177/177. Dropping `&& steps.preflight.outputs.resume != 'yes'` fails `the merge-door tag step is conditioned on the preflight output`; collapsing the `ls-remote` `case` so any non-zero means absence fails `the merge door refuses an unreadable tag state`. - **The manifest.** `bash test/release-path.test.sh` → 7/7; deleting `lib/preflight.sh` from `.github/scripts/release-path.sh` reds it 5/7, with each rewritten fixture reporting exactly its own one path — the both-directions failure the amendment called for. - **The rest of the criteria.** `yq` on the tag step's `if:` prints `steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'`; every `release.yml` hunk lands in 208–241, so `release-on-tag` takes none and `the tag door carries no tag-exists guard` is green; the disjunction greps empty in both `release.yml` and `README.md`; the changed set is exactly the nine authorized paths (354+/64−) with no diff in either forge backend; README's merge-door entry quotes the three messages **byte-exactly** as `lib/preflight.sh` emits them (placeholders aside) and leads its recovery with re-running the door; `README.md#L131-L134` now credits the preflight with the recovery and demotes the tag door to the unreachable-run / fixed-tree case. - **Gates.** `bash test/run.sh` → 32 files, 0 failed. `shellcheck-all.sh` exit 0. `git diff --check` clean. actionlint is **absent from this box**; I installed the pinned `1.7.12` CI uses and ran `actionlint-all.sh` — exit 0 over all 9 workflows. ### Not verified, and what I relied on instead - **The live doors.** No release window is open (#268), so nothing here exercised a real tag push or publish. The issue states this; the real-remote step run above is the closest offline proxy. - **Forgejo's `forge_release_create` against an already-standing tag** — the resume path's last step. I relied on #271's contract and its tests in `test/forge-backends.test.sh` (green here); I did not drive a live instance. - **`set -e` in the runner.** The extracted-step test prepends `set -e` itself, and my probe ran the body under `bash -eo pipefail`, so both prove the body under `-e` rather than the runner's own default. That is the established `TAG_GUARD` pattern the issue told you to follow verbatim, and the existing `decide` step rests on the same thing. - **The anchor arithmetic** is mine, resolved by reading `release.yml` at this head; nothing in the repo enforces it (see the second note below). ### Two non-blocking notes - `check` matches its expected string with `grep -qF -e`, which for a multi-line pattern matches **any one** line, so `manifest prints the specified ordered release path` still passes with `lib/preflight.sh` deleted from the manifest — its "the record author's copy-paste source" comment overstates what it asserts. Pre-existing harness behaviour, and the intended coverage is delivered by the neighbouring `real workflow and transitive dependencies match the manifest`, which does red. Worth a proposal, not a change here. - Nothing in `test/` checks that README's `release.yml#L…` anchors still name what their prose says. This round's blocker was found by hand, and the next edit that shifts lines in `release.yml` reopens it silently. A guard that resolves each anchor's first line and asserts a substring of the step name would be cheap. Also a proposal, not this PR's scope.
forgejo-actions added
state:needs-human
and removed
state:bots-reviewing
labels 2026-08-31 16:08:14 +00:00
codex-bot-andresmgsl requested review from andres 2026-08-31 16:35:47 +00:00
andres merged commit 85290031b2 into main 2026-08-31 16:57:10 +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#274
No description provided.