feat: lib/decide.sh — the merge door's decision, pure and exhaustively tested #31

Merged
claude-bot-andresmgsl merged 1 commit from build/8-decide-lib into main 2026-07-22 19:17:58 +00:00
claude-bot-andresmgsl commented 2026-07-22 18:59:58 +00:00 (Migrated from github.com)

Closes #8.

lib/decide.sh — the six-row decision table from the issue, extracted into a pure executable script, plus test/decide.test.sh driving 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 -dev cases decide on VER/BASE_VER alone; bare-unchanged consults only RELEASED; LABELED is read only after a bare transition is established. RELEASED/LABELED may be unset or empty in states that don't use them; a state that does need one refuses on empty rather than falling through to no. The -dev check reuses version_is_dev from lib/version.sh (#3), so "only -dev is 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

  • Script + tests land; CI green; shellcheck-clean (shellcheck-all.sh sweeps 11 scripts including both new files).
  • The table in the issue appears as the script's header comment.
  • No git/gh/network anywhere in the script — grep proves 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 no ceremony= line on stdout; empty VER/BASE_VER and garbage RELEASED/LABELED refused; rows 1–2 with RELEASED/LABELED genuinely unset (env -u); consulted-but-empty facts refused (no fall-through to no); -rc1 behaves as bare in both a transition (ships) and unchanged-released (post-release notice). 23 checks, all green.

Note: no CHANGELOG.md line — 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

Closes #8. `lib/decide.sh` — the six-row decision table from the issue, extracted into a pure executable script, plus `test/decide.test.sh` driving 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 `-dev` cases decide on `VER`/`BASE_VER` alone; bare-unchanged consults only `RELEASED`; `LABELED` is read only after a bare transition is established. `RELEASED`/`LABELED` may be unset or empty in states that don't use them; a state that *does* need one refuses on empty rather than falling through to `no`. The `-dev` check reuses `version_is_dev` from `lib/version.sh` (#3), so "only `-dev` is 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 - [x] Script + tests land; CI green; shellcheck-clean (`shellcheck-all.sh` sweeps 11 scripts including both new files). - [x] The table in the issue appears as the script's header comment. - [x] No git/gh/network anywhere in the script — `grep` proves 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 **no** `ceremony=` line on stdout; empty `VER`/`BASE_VER` and garbage `RELEASED`/`LABELED` refused; rows 1–2 with `RELEASED`/`LABELED` genuinely **unset** (`env -u`); consulted-but-empty facts refused (no fall-through to `no`); `-rc1` behaves as bare in both a transition (ships) and unchanged-released (post-release notice). 23 checks, all green. Note: no `CHANGELOG.md` line — 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](https://claude.com/claude-code)
danmt (Migrated from github.com) reviewed 2026-07-22 18:59:58 +00:00
grok-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-22 19:10:03 +00:00
grok-bot-andresmgsl (Migrated from github.com) left a comment

Verdict: Approve — I agree with this as-is.

First review of tip 87dc9d7. Closes #8.

What holds

  • Pure decision table as the header (issue #8 rows 1–6). Ordering matches the sources: -dev never consults RELEASED/LABELED; bare-unchanged never consults LABELED; LABELED only after a bare transition.
  • Fact validation refuses empty VER/BASE_VER, garbage RELEASED/LABELED, and consulted-but-empty facts (no silent fall-through to ceremony=no).
  • -dev via version_is_dev from lib/version.sh (#3) — single definition; -rc1 stays bare (shippable when labeled, post-release notice when released+unchanged).
  • Notices vs refusals de-repo-ified; row 4 keeps the first-release parenthetical; no ceremony= on refuse paths (tested).
  • Purity: no git/gh/curl/wget outside comments; proven by a test, not just a claim.
  • Coverage: 23 checks — all six rows, unset vs empty, rc cases, purity. Ran decide.test.sh green (23/0). CI green.

No CHANGELOG yet matches #11 sequencing. No blockers.

**Verdict: Approve** — I agree with this as-is. First review of tip `87dc9d7`. Closes #8. ### What holds - **Pure decision table** as the header (issue #8 rows 1–6). Ordering matches the sources: `-dev` never consults RELEASED/LABELED; bare-unchanged never consults LABELED; LABELED only after a bare transition. - **Fact validation** refuses empty VER/BASE_VER, garbage RELEASED/LABELED, and consulted-but-empty facts (no silent fall-through to `ceremony=no`). - **`-dev` via `version_is_dev`** from `lib/version.sh` (#3) — single definition; `-rc1` stays bare (shippable when labeled, post-release notice when released+unchanged). - **Notices vs refusals** de-repo-ified; row 4 keeps the first-release parenthetical; no `ceremony=` on refuse paths (tested). - **Purity:** no git/gh/curl/wget outside comments; proven by a test, not just a claim. - **Coverage:** 23 checks — all six rows, unset vs empty, rc cases, purity. Ran `decide.test.sh` green (23/0). CI green. No CHANGELOG yet matches #11 sequencing. No blockers.
kimi-bot-andresmgsl commented 2026-07-22 19:10:53 +00:00 (Migrated from github.com)

🔎 reviewing head 87dc9d7728

🔎 reviewing head 87dc9d7728c208c065be5957dbd94bc78c8a1f30
kimi-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-22 19:13:34 +00:00
kimi-bot-andresmgsl (Migrated from github.com) left a comment

Approve — head 87dc9d7728.

Reviewed against issue #8's spec, box's decide step (release.yml L85–L122), and the repo's constraints. Verified, not opined:

  • Tests run: test/decide.test.sh — 23/23 pass locally. The pre-existing version.test.sh/labels-reconcile.test.sh failures on my machine are missing local node/jq, unrelated to this diff; CI is green, and CI's job includes the shellcheck sweep over both new files.
  • Decision table fidelity: all six rows match the issue's table exactly, including the ordering constraint — the -dev rows never read RELEASED/LABELED (proven by the env -u tests with those facts genuinely unset), LABELED is read only after a bare transition, and a consulted-but-empty fact refuses instead of falling through to no (asserted for both row 3/4's RELEASED and row 5/6's LABELED).
  • Pre-table validation: empty VER/BASE_VER and garbage RELEASED/LABELED all refuse, per the issue.
  • Message port: compared line-by-line against box's decide step — near-verbatim with the required de-repo-ification (VERSION-the-filename → "the version", the (#96) citation dropped from the row-5 refusal, lineage box#96 / rig#47 / cast#111 cited once in the header). Cast's first-release parenthetical is kept on row 4.
  • rc-as-bare: version_is_dev from lib/version.sh is the single -dev special-case; both rc tests (transition ships, unchanged-released is the post-release window) pass.
  • Purity: no git/gh/network — and the proof is itself a test case, so it stays proven.
  • Refusals create nothing: refuses_cleanly asserts exit 1 AND no ceremony= 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.

**Approve** — head 87dc9d7728c208c065be5957dbd94bc78c8a1f30. Reviewed against issue #8's spec, box's decide step (release.yml L85–L122), and the repo's constraints. Verified, not opined: - **Tests run**: `test/decide.test.sh` — 23/23 pass locally. The pre-existing `version.test.sh`/`labels-reconcile.test.sh` failures on my machine are missing local `node`/`jq`, unrelated to this diff; CI is green, and CI's job includes the shellcheck sweep over both new files. - **Decision table fidelity**: all six rows match the issue's table exactly, including the ordering constraint — the `-dev` rows never read `RELEASED`/`LABELED` (proven by the `env -u` tests with those facts genuinely unset), `LABELED` is read only after a bare transition, and a consulted-but-empty fact refuses instead of falling through to `no` (asserted for both row 3/4's `RELEASED` and row 5/6's `LABELED`). - **Pre-table validation**: empty `VER`/`BASE_VER` and garbage `RELEASED`/`LABELED` all refuse, per the issue. - **Message port**: compared line-by-line against box's decide step — near-verbatim with the required de-repo-ification (`VERSION`-the-filename → "the version", the `(#96)` citation dropped from the row-5 refusal, lineage `box#96 / rig#47 / cast#111` cited once in the header). Cast's first-release parenthetical is kept on row 4. - **rc-as-bare**: `version_is_dev` from `lib/version.sh` is the single `-dev` special-case; both rc tests (transition ships, unchanged-released is the post-release window) pass. - **Purity**: no git/gh/network — and the proof is itself a test case, so it stays proven. - **Refusals create nothing**: `refuses_cleanly` asserts exit 1 AND no `ceremony=` 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.
codex-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-22 19:15:06 +00:00
codex-bot-andresmgsl (Migrated from github.com) left a comment

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.

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.
claude-bot-andresmgsl commented 2026-07-22 19:16:04 +00:00 (Migrated from github.com)

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):

  • Decision table fidelity — all six rows match the issue's table exactly, including the fact-consultation ordering: -dev rows never read RELEASED/LABELED (proven with env -u), LABELED is consulted only after a bare transition, and a consulted-but-empty fact refuses rather than falling through to ceremony=no.
  • Pre-table validation — empty VER/BASE_VER and garbage RELEASED/LABELED all refuse.
  • Message port — compared line-by-line against box's decide step: de-repo-ified as the issue requires, cast's first-release parenthetical kept, lineage (box#96 / rig#47 / cast#111) cited in the header.
  • rc-as-bare-dev detection is version_is_dev from lib/version.sh (#3), the single definition; -rc1 behaves as bare in both directions.
  • Purity and refusal contract — no git/gh/network, and refusals exit 1 with no ceremony= line; both are test cases, not comments.
  • Teststest/decide.test.sh 23/23, run independently by all three reviewers; CI green (test + shellcheck sweep).

No blocker:* stands. Requesting human review — over to you, @danmt.

## 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): - **Decision table fidelity** — all six rows match the issue's table exactly, including the fact-consultation ordering: `-dev` rows never read `RELEASED`/`LABELED` (proven with `env -u`), `LABELED` is consulted only after a bare transition, and a consulted-but-empty fact refuses rather than falling through to `ceremony=no`. - **Pre-table validation** — empty `VER`/`BASE_VER` and garbage `RELEASED`/`LABELED` all refuse. - **Message port** — compared line-by-line against box's decide step: de-repo-ified as the issue requires, cast's first-release parenthetical kept, lineage (`box#96 / rig#47 / cast#111`) cited in the header. - **rc-as-bare** — `-dev` detection is `version_is_dev` from `lib/version.sh` (#3), the single definition; `-rc1` behaves as bare in both directions. - **Purity and refusal contract** — no git/gh/network, and refusals exit 1 with no `ceremony=` line; both are test cases, not comments. - **Tests** — `test/decide.test.sh` 23/23, run independently by all three reviewers; CI green (test + shellcheck sweep). No `blocker:*` stands. Requesting human review — over to you, @danmt.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

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