lib/decide.sh — the merge door's decision, pure and exhaustively tested #8
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#8
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of #1. Blocked by #3. Blocks #9.
Goal
lib/decide.sh— the merge door's decision, extracted into a pure, exhaustively-tested script. This is the subtlest logic in the whole ceremony; in the source repos it lives inline in the workflow (boxrelease.ymlL85–L122, rig L123–L163, cast L110–L157) where it can only be proven by releasing. Here, the workflow (#9) gathers facts and this script makes the decision, so every state is contract-tested offline.Why the decision is subtle (keep this in the header)
The merge-door job runs on every push to main, and the
releaselabel carries two legitimate meanings (per each repo's LABELS.md: "release flow and version/packaging work"): the ceremony PR that ships a version, and ordinary work on the release machinery — the PR that adds the ceremony to a repo included. The version transition tells them apart. Every ambiguous middle state is a half-ceremony and must die loudly, creating nothing; every legitimate non-ceremony state must be a green notice no-op, not a red run on main per infra PR.Interface
Pure: no git, no
gh, no network. The caller (workflow #9) establishes four facts and passes them as environment variables:Output:
ceremony=yesorceremony=noon stdout (the workflow appends it to$GITHUB_OUTPUT), notices to stdout, refusals to stderr, exit 1 on refusal.The decision table (exact — this IS the spec)
-devceremony=no, NOTICE: work under the label / ordinary merge — nothing to publish-devceremony=no, NOTICE: still a dev tree — the post-release bump or a renumber; "a dev tree is by definition not a release"ceremony=no, NOTICE: post-release window (ceremony landed,-devbump hasn't) — nothing to publishceremony=yesOrdering matters and must match the sources: the
-devcases never consult RELEASED or LABELED; the bare-unchanged cases never consult LABELED; LABELED is only read after a bare transition is established. (RELEASED and LABELED may therefore be passed as empty in the states that don't use them — the script must not require them there; this keeps the workflow free to skip API calls it doesn't need.)Also refuse, before the table: empty/missing VER or BASE_VER (a fact-gathering bug upstream must not fall through to "no"), and RELEASED/LABELED values other than yes/no/empty.
State 4 doubles as the known first-release edge (cast#111): a repo whose first version never carried
-devships its first release by the tag door. Keep cast's parenthetical in the refusal ("if this PR was mislabeled, drop the label; if it was meant to release, it forgot the bump"). Consumers that bootstrap atX.Y.Z-dev(the #12 guide says to) never hit this.Message text
Port the notices/refusals from box's decide step near-verbatim (they are the operator's debugging surface — each one names the state AND the way out), de-repo-ified (no
VERSION-the-filename assumptions; say "the version"; no issue numbers of a single repo — citebox#96 / rig#47 / cast#111as the design lineage once, in the header).Tests (
test/decide.test.sh)ceremony=line, and a distinguishing message substring per row.-rc1version behaves as bare (not-dev) — an rc transition with a label is a shippable ceremony (this matches the sources, where only*-devis special-cased).Acceptance criteria
grepproves it.Triage: the only named blocker, #3, landed (PR #28 merged) — flipping
blocked→ready. Spec unchanged; the decide table in the body is the contract.Claiming — starting on
lib/decide.sh+test/decide.test.shper the decision table in the body. Draft PR soon.