feat: lib/decide.sh — the merge door's decision, pure and exhaustively tested #31
No reviewers
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#31
Loading…
Reference in a new issue
No description provided.
Delete branch "build/8-decide-lib"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #8.
lib/decide.sh— the six-row decision table from the issue, extracted into a pure executable script, plustest/decide.test.shdriving every state offline. The header comment carries the table and the "why the decision is subtle" context; the notices/refusals are ported near-verbatim from box's decide step, de-repo-ified ("the version", no single-repo issue numbers), with the design lineage (box#96 / rig#47 / cast#111) cited once in the header.Structure follows the sources exactly: the
-devcases decide onVER/BASE_VERalone; bare-unchanged consults onlyRELEASED;LABELEDis read only after a bare transition is established.RELEASED/LABELEDmay be unset or empty in states that don't use them; a state that does need one refuses on empty rather than falling through tono. The-devcheck reusesversion_is_devfromlib/version.sh(#3), so "only-devis special-cased" has exactly one definition.Row 4 keeps cast's first-release parenthetical ("if this PR was mislabeled, drop the label; if it was meant to release, it forgot the bump"), de-repo-ified.
Acceptance criteria
shellcheck-all.shsweeps 11 scripts including both new files).grepproves it, and the proof is a test case (decide.sh calls no git/gh/network tools), so it stays proven.Test coverage
All six table rows (exit code +
ceremony=line + a distinguishing message substring each), refusals additionally asserted to emit noceremony=line on stdout; emptyVER/BASE_VERand garbageRELEASED/LABELEDrefused; rows 1–2 withRELEASED/LABELEDgenuinely unset (env -u); consulted-but-empty facts refused (no fall-through tono);-rc1behaves as bare in both a transition (ships) and unchanged-released (post-release notice). 23 checks, all green.Note: no
CHANGELOG.mdline — the file doesn't exist in this repo yet (same as PRs #27/#28; it presumably arrives with the #11 dogfood ceremony).🤖 Generated with Claude Code
Verdict: Approve — I agree with this as-is.
First review of tip
87dc9d7. Closes #8.What holds
-devnever consults RELEASED/LABELED; bare-unchanged never consults LABELED; LABELED only after a bare transition.ceremony=no).-devviaversion_is_devfromlib/version.sh(#3) — single definition;-rc1stays bare (shippable when labeled, post-release notice when released+unchanged).ceremony=on refuse paths (tested).decide.test.shgreen (23/0). CI green.No CHANGELOG yet matches #11 sequencing. No blockers.
🔎 reviewing head
87dc9d7728Approve — head
87dc9d7728.Reviewed against issue #8's spec, box's decide step (release.yml L85–L122), and the repo's constraints. Verified, not opined:
test/decide.test.sh— 23/23 pass locally. The pre-existingversion.test.sh/labels-reconcile.test.shfailures on my machine are missing localnode/jq, unrelated to this diff; CI is green, and CI's job includes the shellcheck sweep over both new files.-devrows never readRELEASED/LABELED(proven by theenv -utests with those facts genuinely unset),LABELEDis read only after a bare transition, and a consulted-but-empty fact refuses instead of falling through tono(asserted for both row 3/4'sRELEASEDand row 5/6'sLABELED).VER/BASE_VERand garbageRELEASED/LABELEDall refuse, per the issue.VERSION-the-filename → "the version", the(#96)citation dropped from the row-5 refusal, lineagebox#96 / rig#47 / cast#111cited once in the header). Cast's first-release parenthetical is kept on row 4.version_is_devfromlib/version.shis the single-devspecial-case; both rc tests (transition ships, unchanged-released is the post-release window) pass.refuses_cleanlyasserts exit 1 AND noceremony=line on stdout — the half-ceremony guarantee is contract-tested, not just commented.Non-blocking nits, builder's discretion: none worth a round. The header table is the doc a debugger reads at 2am, and it matches the issue verbatim. Acceptance criteria all met.
Verdict: I agree with everything and have no additional feedback.
Reviewed the complete current diff against issue #8 and repository guidance. The six decision rows, validation and fact-consultation ordering match the specification; refusals emit no ceremony output; and the script remains pure. I ran test/decide.test.sh at head
87dc9d7: 23 passed, 0 failed. The repository test check is also successful.Round summary — handoff
Round 1 (head
87dc9d7, unchanged since ready-for-review): all three panel verdicts approve — @grok-bot-andresmgsl, @kimi-bot-andresmgsl, @codex-bot-andresmgsl. No changes were requested, so the head the panel approved is the head being handed off.What the round verified (each reviewer independently, against issue #8 and box's decide step):
-devrows never readRELEASED/LABELED(proven withenv -u),LABELEDis consulted only after a bare transition, and a consulted-but-empty fact refuses rather than falling through toceremony=no.VER/BASE_VERand garbageRELEASED/LABELEDall refuse.box#96 / rig#47 / cast#111) cited in the header.-devdetection isversion_is_devfromlib/version.sh(#3), the single definition;-rc1behaves as bare in both directions.ceremony=line; both are test cases, not comments.test/decide.test.sh23/23, run independently by all three reviewers; CI green (test + shellcheck sweep).No
blocker:*stands. Requesting human review — over to you, @danmt.