fix(issues): let allowlisted coordination-metadata PATCH clear the recovery guard (BLO-19951) - #979
Conversation
1 similar comment
|
@ally please review at head Focus, in priority order:
|
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
|
CEO — answering review focus #1 with owner authority, not adding a review. The author asked Ally to check rather than accept the reasoning on the widening. The semantic being appealed to belongs to BLO-19119, which is mine, so here is the authoritative answer rather than leaving it to inference.
This is the intended behavior, not a tolerated side effect. Measured on BLO-19119 (2026-08-02): of 119 recovery actions still active from before the 07-30 drain, 74 have not fired since — 51 last fired 2026-07-26, all 74 with So the alternative matters: a carve-out that clears the stale The author's safety argument holds and is the right frame: Reviewer guidance: please still scrutinize focus #2 (that Separately, for the author: the |
Ally — Consolidated PR ReviewLenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex. Looks good. I found no Critical or Important issues in the authorization change. Suggestions (1)
Strengths
Recommended Action
|
…covery guard (BLO-19951) The BLO-18289 coordination-metadata allowlist computes `coordinationMetadataDecision` and passes it into `assertAgentIssueMutationAllowed`, but the recovery-action guard further down the same handler never consulted it. Any `blockedByIssueIds` array sets `recoveryRelevantSourceMutationRequested`, so an allowlist-confined patch still 403'd with "Agent cannot resolve another owner's recovery action" whenever the target issue carried an active recovery action owned outside the actor's chain. That is precisely the stranded-recovery population the gate exists to let us curate (BLO-19119), so the gate missed the subset where a stale blocker edge is most likely to have accumulated. Thread the already-computed decision into the guard condition rather than recomputing it. The carve-out is narrow by construction: a non-null decision means the body contained only allowlisted fields, and `status`, `assigneeAgentId`, `executionPolicy`, `reopen` and `resume` are all outside the allowlist, so `blockedByIssueIds` is the only trigger that can reach the guard with a decision in hand. Any non-allowlisted field nulls the decision and restores the guard verbatim. Reported by Ally on PR #795 at 31bc713; verified to transfer to the code that merged in #870. Co-Authored-By: Claude <noreply@anthropic.com>
dea8b22 to
412826e
Compare
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
Ally — Consolidated PR ReviewLenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex. Looks good. I found no Critical or Important issues in the authorization change. Suggestions (1)
Strengths
Recommended Action
|
kkroo
left a comment
There was a problem hiding this comment.
Reviewed narrow coordination-metadata recovery guard carve-out. PR body repaired and review gate rerun.
Thinking Path
Linked Issues or Issue Description
Fixes BLO-19951. Follow-up to BLO-18289 / #870; Ally raised this against #795 at
31bc713e, and it transferred to the code that merged in #870.The coordination-metadata allowlist computes
coordinationMetadataDecisioninroutes/issues.tsand passes it intoassertAgentIssueMutationAllowedasallowCoordinationMetadata, but the recovery-action guard further down the same handler never consulted it. Since anyblockedByIssueIdsarray setsrecoveryRelevantSourceMutationRequested, an allowlist-confined patch still got 403 whenever the target issue carried an active recovery action owned outside the actor's chain.What Changed
coordinationMetadataDecisionin the recovery-action guard.!coordinationMetadataDecisionto the existing guard condition instead of recomputing the allowlist decision.status,assigneeAgentId,executionPolicy,reopen, andresumestill null the decision and restore the existing guard.POST /issues/:id/recovery-actions/resolveuntouched because that route explicitly resolves a recovery action.Verification
describeinissue-agent-mutation-ownership-routes.test.tsthat builds a real active recovery action owned by a third agent rather than stubbingaccess.decide.issue-agent-mutation-ownership-routes.test.ts: 170/170 passing.tsc --noEmitonserver: clean.issue-recovery-actions,issue-blocked-by-update-routes,issue-blocker-attention,issue-liveness,issue-force-release,issue-execution-policy-routes, andissue-comment-reopen-routes: 260 passed.Risks
revalidateActiveSourceRecoveryAfterCommittedWrite, which can cancel a foreign-owned recovery action. The classifier is state-derived: it re-reads dependency readiness, owner, review participant, and monitor state, and does not trust the actor's assertion.priority-only patch is a control, not a regression test, because it never reached the recovery guard before this change.Model Used
Claude Code, exact model/version not recorded in the original PR body.
Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue template