fix: one bounded recovery review pass after a receipt-invalidating write (2.6.3) - #758
Open
apackeer wants to merge 1 commit into
Open
fix: one bounded recovery review pass after a receipt-invalidating write (2.6.3)#758apackeer wants to merge 1 commit into
apackeer wants to merge 1 commit into
Conversation
…ite (2.6.3) A terminal review receipt voided by a later write to a declared produces[] artifact used to deadlock the stage: completion demanded a fresh receipt while the review budget refused another pass, and the two refusals pointed at each other. The engine now exposes the invalidated-receipt state from the fresh- receipt scan, permits exactly one recovery REVIEW_REQUESTED at the next ordinal when the budget was consumed and nothing is pending, and rewrites the completion, retry-pending, and budget refusals to name the actual recovery path (recovery pass first, human Request Changes at the gate once it is spent). Intact receipts keep their existing budgets; ordinary over-budget requests are still refused. Wedge reported from Kiro IDE (#755), where the review-freeze hook cannot run, and from Copilot (#742). Regression coverage: t271 recovery cases and the new t291 end-to-end reproduction; t115/t263 assertions moved to the stale-specific message.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes #755. Refs #742.
A stage that declares a reviewer deadlocks when a declared
produces[]artifact is written after the terminal review receipt:freshReviewReceiptsinvalidates the receipt (fingerprint mismatch, or a laterARTIFACT_CREATED/ARTIFACT_UPDATEDrow), so completion refuses: no fresh REVIEW_COMPLETED is recorded... Invoke the reviewer and record the verdict.reviewAttemptSummary) never resets on that invalidation, so a new request refuses: review request 2 exceeds this stage's review budget (1)... do not re-invoke the reviewer; quote its findings at the approval gate.--retry-pendingand--verdictalso refuse (the pair was already matched).The two guards instruct each other's forbidden action - a circular loop with no tool path forward. #698/#702 added the review-freeze PreToolUse hook to prevent the invalidating write, but Kiro IDE cannot run it (IDE hook payloads carry empty
tool_input, so a targeted pre-write freeze has no path to inspect) - it is the only harness without that backstop, which is where #755 was hit. The gate-revision backstop does not cover this either: it requires the write to land after gate-open and after aHUMAN_TURN.Change
One bounded, deterministic recovery pass, plus refusal messages that name the real way out:
core/tools/aidlc-lib.ts-freshReviewReceiptsnow reportsstageStale/unitStale: a terminal receipt existed in the current attempt window but was invalidated. Set only for terminal verdicts with a usable recorded fingerprint (or an event-clear of an actually-set verdict); cleared when a later terminal receipt lands; floored by the same attempt boundaries as everything else.core/tools/aidlc-log.ts- the request path permits exactly ONE recoveryREVIEW_REQUESTEDat ordinal budget+1 when the scope is stale, nothing is pending, and the budget was consumed by real passes (requestCount > budgetproves the recovery slot is already spent, so no extra bookkeeping). The emitted row carriesRecovery: stale-receipt. A second invalidation is refused with guidance to stop editing past receipts and present the situation at the human gate; the--retry-pendingrefusal in the stale state now names the recovery command with the correct ordinal.--singlerows are excluded (invisible to the fresh-receipt scan by design).core/tools/aidlc-state.ts- completion refusals distinguish an invalidated receipt from a review that never ran, per-unit refusals list invalidated vs never-reviewed units, both with the recovery instruction and the human Request Changes reset.docs/reference/04-stage-protocol.md(the budget-enforcement paragraph gains the bounded exception).Nothing is weakened for intact receipts: the ordinal contract, the advisory single pass, the adversarial cap, and every existing refusal string are unchanged when no terminal receipt was invalidated. Wave
review_stateplumbing is deliberately untouched; a stale per-unit receipt still surfaces asoutstanding/iteration 1 and the out-of-sequence refusal self-corrects the ordinal.Tests
tests/unit/t291-review-receipt-recovery.test.ts- end-to-end reproduction of the [Bug]: Workflow stucks at "Intent-capture" stage (scope Enterprise) #755 wedge through the real CLIs: request+verdict, invalidating write, stale-specific completion refusal, recovery pair, successful approval, and the bounded second invalidation.tests/unit/t271-review-iteration-ceiling.test.ts- recovery cases: advisory wedge (retry-pending guidance, out-of-sequence self-correction, recovery row + JSON marker, retry during pending recovery, recovery-spent refusal) and the adversarial per-unit variant; the existing intact-receipt budget assertions are unchanged.tests/unit/t115.test.ts,tests/unit/t263-reviewer-terminal-ordering.test.ts- assertions for the write-after-receipt scenario moved to the new stale-specific message.Verification
bun run typecheck,bun run check(dist parity + lint),bun scripts/package.ts --check: clean.t238-user-stories-mob.sdk(known red on clean v2 at 74a51a1) andt145-packaging-parity(box-level bun module-resolution artifact: the identicalCannot find package 'smol-toml'failure reproduces with the pristine v2emit.tscopied to /tmp; every input to that test is byte-identical to v2 on this branch).Field workaround (pre-fix dists)
A wedged session recovers today by choosing Request Changes at the approval gate: the
GATE_REJECTEDresets both the receipt-freshness floor and the review budget, after which the revision path re-runs the section 12a review.