Repository navigation
feat(ladder): reconcile fired tranches from execution outcomes (#118 PR-C2) - #837
Conversation
…PR-C2) Add the tranche reconciler and the store pieces it needs. Nothing calls it yet (no scheduled task, no executor, flags off), so no tranche can reach fired in a running system. - migration 000114: ladder_tranches.status_reason (additive); the overdue sweep stamps 'expired'; TransitionLadderTranche takes a reason (required for failed and canceled) and gains a Tx variant. - ListFiredLadderTranches and LockLadderTrancheChainTx: lock the tranche row, follow retry_execution_id (depth cap 20, which also stops a cycle), then lock and re-read the tail execution row, the same row LinkRetryExecutionAtomic updates. - ReconcileLadderTranches: failed only when every node of the chain is failed with no_submission (or the tail is canceled/expired with proven predecessors); completed only when the commitment is visible; partially completed, unproven failures and unresolved chains stay fired and keep netting; unresolved over 6h is logged as an error. The proof is read only from failed rows. Refs #118 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 12 billable files and costs up to $3.00.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 26 minutes for your next included review. View limit details
Comment |
Gate verdict: APPROVE at 452115a (independent adversarial review, Opus 5.5
|
Refs #118
PR-C2 of the ladder tranche reconciler, stacked on the merged C1 (#828) and the executor race fixes (#831, #833; #829 closed). Nothing calls the reconciler yet: no scheduled task, no executor, ladder flags stay off, so no tranche can reach
firedin a running system. The scheduled-task wiring, the operator resolve action and the production commitment-visibility adapter stay deferred to B2.Migration number is claimed at merge.
000114is the next free number on main today (tip 000113); renumber to max+1 right before merge.What
ladder_tranches.status_reason TEXT(nullable,ADD COLUMN IF NOT EXISTS). The overdue sweep now stampsexpired.TransitionLadderTranche(..., reason)plus aTxvariant; a failed or canceled transition without a reason is an error.ListFiredLadderTranchesandLockLadderTrancheChainTx: lock the tranche row, followretry_execution_idfrom the tranche's execution (depth cap 20; a cycle hits the cap and reads as unresolved), then lock and re-read the tail execution row withFOR UPDATE.LinkRetryExecutionAtomicupdates that same row, so a Retry cannot link a successor mid-decision.purchase.ReconcileLadderTranches(store, visibility, now)(package function over a narrow store interface, so no mock or interface churn):no_submission=true, or the tail is canceled/cancelled/expired and every earlier node is proven (status_reason=no_submission/approval_canceled/approval_expired);completedandCommitmentVisiblesays so (an error or false keeps it fired);partially_completed, a failure without proof (reaper, outcome unknown), pending/running executions and an unresolved chain stay fired and keep netting;LadderFiredStaleAfter, constant) is logged as an error and counted; the tranche is never changed;Re-verified on current main: no path leaves
no_submission=trueon a row that reached the cloudWriters of the flag:
saveTerminalExecution(only on abeforeSubmissionErrorwith no rec purchased) andstampFailedBarePlanStep(pending/notified bare step). Every save goes throughSavePurchaseExecutionTx, whose upsert keeps the flag only for a failed write and clears it for any other status; since #829/#833 terminal saves are status-guarded onrunning(SaveExecutionIfStatus), so the stale approve-path save can no longer resurrect a failed row.RecoverReapedExecutionTxwrites completed/partially_completed, which clears it. Reaper,safeFailanderrAllAccountsFailednever stamp. The reconciler also requires proof on every earlier chain node, so a reaped first attempt keeps the tranche fired.Open risk carried to B2 (blocking there, not here)
A Retry that links after the reconciler commits
failedwould give a failed tranche a live approvable successor. B2 must make Retry refuse an execution whose ladder tranche is terminal (plan amendment A2(b)).Also: the visibility check runs while the reconciler holds the tranche and tail row locks; the B2 adapter must be fast or cached.
Verification
Real PostgreSQL 17.11 (throwaway local instance; Docker unavailable here; temporary helper override reverted before commit).
go test -tags integrationpasses for the fullinternal/config,internal/purchase,internal/apiand migrations suites. New real-PG tests: every mapping incl. both canceled spellings, proof flag ignored on a running row, idempotent rerun, stale alert count, cycle and over-depth chains stay fired, a 3-node chain whose first node was reaped stays fired (and fails once that node is proven), migration 000114 (existing row NULL, up idempotent, down drops), reason stored and required, sweep stampsexpired; ininternal/api: the realpersistRetrySuccessorTxblocks while the reconciler holds the tail row (and a Retry that linked first keeps the tranche fired).make lint0 issues,go test ./internal/...passes,git secrets --scanwith--register-awsunder BSD and GNU grep.🤖 Generated with Claude Code