Context
src/queue/review-evasion.ts's shared evaluateCloseEnforcementGate helper (lines 82-207) also takes an optional paused: { detail, metadata } | null argument (declared at line 97): when the resolved agent-action mode is not "live" (the repo or the whole fleet is paused/frozen), the gate records a denied audit event describing the stand-down using the caller-supplied paused.detail/paused.metadata (lines 149-163) — unless the caller passes paused: null, in which case it stands down with no audit event at all.
4 of the 5 close-enforcement guards pass a real paused object:
closeReviewEvasionSelfCloseIfReviewed — line 625
closeReviewEvasionDraftConversionIfReviewed — line 831
closeRepeatedDraftCyclingIfDetected — line 998
recloseDisallowedReopenIfNeeded — line 430
closeDraftDodgeAttemptIfBlocked (the implementation behind the exported maybeCloseDraftDodgeAttempt) passes paused: null at line 281. The surrounding comment (lines 279-280) explicitly documents this as a known, deliberately-preserved gap: "Draft-dodge is the one guard of the 5 that records NO audit event on a paused/frozen repo -- a pre-existing gap this extraction preserves rather than fixes (a refactor must not change behavior)." The shared helper's own doc comment (line 80) repeats the same acknowledgment: "paused: null records NO audit event on a paused/frozen repo -- draft-dodge's existing, preserved gap (every other guard here DOES audit a paused stand-down; draft-dodge simply never has)."
Practical effect: an operator inspecting audit_events to understand why a stale, gate-failed PR wasn't auto-closed while the fleet was paused/frozen sees a real, informative "paused" record for 4 of the 5 enforcement types, and total silence for the draft-dodge case — indistinguishable from the guard never having evaluated the PR at all.
Requirements
- In
closeDraftDodgeAttemptIfBlocked (src/queue/review-evasion.ts, around lines 264-294), change the evaluateCloseEnforcementGate call's paused argument from null to a real object, following the same shape as the 4 sibling call sites (e.g. line 625-629: `agent actions paused -- review-evasion self-close not enforced for ${pr.authorLogin}`).
- The new
detail string must name the draftDodgeAuthor variable and the "draft-dodge close" action, e.g. `agent actions paused -- draft-dodge close not enforced for ${draftDodgeAuthor}`. Reuse the existing gateMetadata object already built at line 263 for metadata.
- Update the doc comment at line 80 (on
evaluateCloseEnforcementGate) and the doc comment at lines 279-280 (at the call site) so neither still claims this gap exists once it is fixed.
- Do not change the behavior of any of the other 4 guards, and do not change
evaluateCloseEnforcementGate's own signature or the shape of its paused parameter.
Deliverables
Test Coverage Requirements
Touches src/queue/review-evasion.ts, which is under src/** — this repo's Codecov patch gate (99%+ on changed lines) applies to every changed line, including the new test. Existing guard tests exercising maybeCloseDraftDodgeAttempt (via src/queue/processors.ts's re-export) live in test/unit/queue-2.test.ts and test/unit/queue-lifecycle-guards.test.ts — extend one of those with a paused-audit-event assertion for the draft-dodge guard, mirroring the equivalent test already present for one of the other 4 guards.
Expected Outcome
All 5 close-enforcement guards in src/queue/review-evasion.ts behave identically with respect to a paused/frozen repo: every one records a denied audit event describing the stand-down, not just 4 of them.
Links & Resources
src/queue/review-evasion.ts — evaluateCloseEnforcementGate (L82-207), closeDraftDodgeAttemptIfBlocked (L228-320)
- Sibling call sites passing a real
paused object: L625, L831, L998, L430
test/unit/queue-2.test.ts, test/unit/queue-lifecycle-guards.test.ts
Context
src/queue/review-evasion.ts's sharedevaluateCloseEnforcementGatehelper (lines 82-207) also takes an optionalpaused: { detail, metadata } | nullargument (declared at line 97): when the resolved agent-action mode is not"live"(the repo or the whole fleet is paused/frozen), the gate records adeniedaudit event describing the stand-down using the caller-suppliedpaused.detail/paused.metadata(lines 149-163) — unless the caller passespaused: null, in which case it stands down with no audit event at all.4 of the 5 close-enforcement guards pass a real
pausedobject:closeReviewEvasionSelfCloseIfReviewed— line 625closeReviewEvasionDraftConversionIfReviewed— line 831closeRepeatedDraftCyclingIfDetected— line 998recloseDisallowedReopenIfNeeded— line 430closeDraftDodgeAttemptIfBlocked(the implementation behind the exportedmaybeCloseDraftDodgeAttempt) passespaused: nullat line 281. The surrounding comment (lines 279-280) explicitly documents this as a known, deliberately-preserved gap: "Draft-dodge is the one guard of the 5 that records NO audit event on a paused/frozen repo -- a pre-existing gap this extraction preserves rather than fixes (a refactor must not change behavior)." The shared helper's own doc comment (line 80) repeats the same acknowledgment: "paused: nullrecords NO audit event on a paused/frozen repo -- draft-dodge's existing, preserved gap (every other guard here DOES audit a paused stand-down; draft-dodge simply never has)."Practical effect: an operator inspecting
audit_eventsto understand why a stale, gate-failed PR wasn't auto-closed while the fleet was paused/frozen sees a real, informative "paused" record for 4 of the 5 enforcement types, and total silence for the draft-dodge case — indistinguishable from the guard never having evaluated the PR at all.Requirements
closeDraftDodgeAttemptIfBlocked(src/queue/review-evasion.ts, around lines 264-294), change theevaluateCloseEnforcementGatecall'spausedargument fromnullto a real object, following the same shape as the 4 sibling call sites (e.g. line 625-629:`agent actions paused -- review-evasion self-close not enforced for ${pr.authorLogin}`).detailstring must name thedraftDodgeAuthorvariable and the "draft-dodge close" action, e.g.`agent actions paused -- draft-dodge close not enforced for ${draftDodgeAuthor}`. Reuse the existinggateMetadataobject already built at line 263 formetadata.evaluateCloseEnforcementGate) and the doc comment at lines 279-280 (at the call site) so neither still claims this gap exists once it is fixed.evaluateCloseEnforcementGate's own signature or the shape of itspausedparameter.Deliverables
closeDraftDodgeAttemptIfBlockedpasses a realpausedobject toevaluateCloseEnforcementGate, matching its 4 siblingsdenied/paused-outcome audit event is recorded for a draft-dodge close attempt while the agent is paused or frozen, mirroring the existing coverage pattern for the other 4 guardsTest Coverage Requirements
Touches
src/queue/review-evasion.ts, which is undersrc/**— this repo's Codecov patch gate (99%+ on changed lines) applies to every changed line, including the new test. Existing guard tests exercisingmaybeCloseDraftDodgeAttempt(viasrc/queue/processors.ts's re-export) live intest/unit/queue-2.test.tsandtest/unit/queue-lifecycle-guards.test.ts— extend one of those with a paused-audit-event assertion for the draft-dodge guard, mirroring the equivalent test already present for one of the other 4 guards.Expected Outcome
All 5 close-enforcement guards in
src/queue/review-evasion.tsbehave identically with respect to a paused/frozen repo: every one records adeniedaudit event describing the stand-down, not just 4 of them.Links & Resources
src/queue/review-evasion.ts—evaluateCloseEnforcementGate(L82-207),closeDraftDodgeAttemptIfBlocked(L228-320)pausedobject: L625, L831, L998, L430test/unit/queue-2.test.ts,test/unit/queue-lifecycle-guards.test.ts