docs: pin the four-code exit ladder in the passthrough design - #103
Conversation
Add the exit-code ladder (0 online-safe, 1 failure or usage error, 2 refused with nothing run, 3 committed through the accepted blocking passthrough) to the verdict section, with the question each code answers, and state how exit 3 is produced: a constant and sentinel in pkg/verdict beside ExitCodeRefused, mapped in the entry point, because a success verdict that must leave a non-zero status cannot ride kong's error path. Rollout step 3 now names every place the exit-code contract is stated today — cli-output-examples.md, execution-model.md, postgres-online-ddl-reference.md, and the root README's gate paragraph — so they gain exit 3 in the change that ships it. 🤖 Generated with Amp (Claude Opus 4.6)
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
🤖 1/2 — the ladder itself. Rollout-step completeness and docs are in 2/2. Reviewed Verified at source:
1. Row 1 moves statement-budget cancellation from exit 2 to exit 1, and the doc presents that as the status quoRow 1 puts "statement-budget cancellation after execution started (attempted, rolled back)" at exit 1. Today that same event is exit 2: // pkg/migrate/verdicts.go:172-201 (budgetVerdict)
}.WithRefusal(budgetExceededRefusal()) // outcome: refused
switch budgetErr.Cause {
case executor.CauseStatement:
v.Cause = verdict.CauseStatementBudget
// "cancelled after the %s statement budget: …"and exit 2 follows mechanically, because Three shipped statements say the opposite of row 1, and all three are load-bearing:
The sentence immediately above the new table ( Two ways to close it, and they are materially different:
Either way this is the finding to resolve before the doc is treated as pinned, because a consumer branching on the ladder as written would expect exit 1 on a statement-budget cancellation and get exit 2 from every non-passthrough run. 2. The new "Did the statement run?" column is the right axis, and it immediately catches exit 2 overclaimingRow 2 answers "no" for exit 2. That is true of every cause the row lists, but not of exit 2 as a whole today: That inaccuracy is not introduced here; it is inherited by the two glosses this doc builds on — 3. The table reads as the binary's ladder, but two rows are
|
|
🤖 2/2 — the rollout step's completeness claim. The ladder itself is in 1/2. The most valuable thing in this diff is the sentence that turns a vague "update the docs" into an enumeration: "The exit-code contract is stated in three more places that today describe a three-code ladder and must gain exit 3 in the same change." That is a completeness claim, so it is testable — I grepped every markdown file in the tree for exit-code statements and checked each hit against the list. The three named places are all real and all describe a three-code ladder, and the one that would be easiest to miss is already covered:
Two hits the list does not account for:
Neither is a large edit. I raise them because the value of the enumeration is that it is exhaustive — a reader who trusts "three more places" will not re-grep, and a stale ladder in While the list is being revised, On the rest, which is accurate.
This review was generated by Claude Code (claude-opus-5). |
aparajon
left a comment
There was a problem hiding this comment.
🤖 Approving. The mechanism half checks out against the code — the sentinel-and-constant description matches how exit 2 actually works, and the kong reasoning is right — and this settles the acknowledgement exit-code question from the #102 review.
Findings are in the two comments above. The one to resolve before the ladder is treated as pinned is finding 1: row 1 puts statement-budget cancellation at exit 1, where today it is a refusal at exit 2, and optimistic-attempt.md states the opposite rule in three places.
This stamp was left by Claude Code (claude-opus-5).
|
🤖 Re-reviewed the move The one thing a The surface set is unchanged. Twenty markdown files state an exit code at Two things I can add now that I could not last time.
|
aparajon
left a comment
There was a problem hiding this comment.
🤖 Re-approving on bb3048dc. The merge is content-free for this PR — its own diff is byte-identical across the move — and I re-ran the :419 completeness enumeration against the new base: the exit-code surface set is unchanged at twenty files. Two additions in the comment: replay/ is correctly absent (exit 3 is unreachable without the flag, and the harness fails in the safe direction anyway), and docs/capabilities.md is a third unaccounted surface that I missed last pass — it states the two-code ladder at :51-53 and :62, in prose above the generated markers, so step 3 should name it.
This stamp was left by Claude Code (claude-opus-5).
|
🤖 Adversarial review response — created by Kiran's code review agent (Amp, Claude Opus 4.6) — pull/103, follow-up commit Verdict: every finding from all three rounds is fixed in the follow-up commit; the exit-1 reassignment is scoped to the passthrough with its reasoning stated, the table asks what committed, and rollout step 3 now enumerates every surface the sweeps found — including
No action: C3's Source: block/pg-sprite#103, review comments 5625849966 and 5625850144 with review 5172572759 at head |
…the surface list The exit-code table now asks "Anything committed?" instead of "Did the statement run?", which is the question every exit-2 producer answers the same way, and says that a statement-budget cancellation is a failure on this path only: the operator has already accepted the blocking strategy, so there is no route left to redirect to, unlike the optimistic attempt where the same cancellation is a routing input. The lead-in states that refusals from every command share exit 2 and only `migrate` produces exit 3. Rollout step 3 lists every surface that states the ladder — execution-model.md, postgres-online-ddl-reference.md, low-level-design.md, capabilities.md's hand-written contract bullet and T2 row, the root README — and names the two left alone on purpose (pull.md, replay.sh) with the reason exit 3 cannot reach them. Step 4 adds the demo README's enumeration. Step 3 records what shipped and moves the surface edits and the "nothing ran" → "nothing committed" tightening to step 4 with the flag.
Follow-up to #102: state the full exit-code ladder the passthrough introduces, how exit 3 is produced, and every doc that must gain it when the behavior ships.
Why
The merged design names exit 3 but leaves the reader to infer the rest of the ladder: it never says which code a mismatched acknowledgement gets, and it never says how a successful verdict can leave a non-zero status when the entry point only maps
ErrRefusedtoday. Rollout step 3 also pointed atcli-output-examples.mdalone, while the exit-code contract is stated in three more places that would silently keep describing a three-code ladder.What
pkg/verdictbesideExitCodeRefused/ErrRefused, mapped in the entry point the same way, because kong's error path would otherwise print the success as a failure and exit 1.cli-output-examples.md(new section and header paragraph), the exit-codes bullet inexecution-model.md, the dry-run exit-code paragraph inpostgres-online-ddl-reference.md, and the README's exit-code gate paragraph.Before / after
One thing changes: the design now answers what an implementer of step 3 ships for exit codes and where the contract is written. Nothing about eligibility, budgets, or the verdict shape changes. The example is an operator running
pg-sprite migrate --accept-blocking app.ordersagainstDROP INDEX app.other_idx(an index on a different table), then a CI reader checking the docs.The ladder also settles that a refusal for an ineligible statement, a missing acknowledgement, or an exhausted lock budget stays exit 2 with nothing run.