From 301c1879b2591f3c8c7c80b0808a98f592e65c3e Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Tue, 18 Aug 2026 10:41:40 -0400 Subject: [PATCH] fix: blocker advisory always steers a new turn, immune window exempts it The post-interrupt immune-window downgraded end-of-turn blockers to non-interrupting asides, so a blocker that means the agent handed off broken work never woke a new turn. Concerns keep the cooldown; blockers now bypass it and steer a triggered turn, consistent with the #5628 blocker-after-terminal-answer exception. --- packages/coding-agent/src/advisor/advise-tool.ts | 8 +++++--- packages/coding-agent/test/advisor/advisor.test.ts | 11 ++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/src/advisor/advise-tool.ts b/packages/coding-agent/src/advisor/advise-tool.ts index d9cb954e2..51835b9a9 100644 --- a/packages/coding-agent/src/advisor/advise-tool.ts +++ b/packages/coding-agent/src/advisor/advise-tool.ts @@ -110,8 +110,10 @@ export function isAdvisorInterruptImmuneTurnActive(opts: { * auto-resume anything, so it is delivered live. Parking it during an active * run instead strands it (it never reaches the running agent) and the withheld * notes dump as one burst at the next user prompt — the bug this guards. - * - During the post-interrupt immune-turn window, further `concern`/`blocker` - * notes are downgraded to asides; preservation still wins. + * - During the post-interrupt immune-turn window, further `concern` notes are + * downgraded to asides; preservation still wins. A `blocker` is exempt: it + * means the agent handed off broken or unexercised work, so it still steers a + * triggered turn even right after a prior interrupt (#5628). */ export function resolveAdvisorDeliveryChannel(opts: { severity: AdvisorSeverity | undefined; @@ -127,7 +129,7 @@ export function resolveAdvisorDeliveryChannel(opts: { if (opts.autoResumeSuppressed && (opts.aborting || !opts.streaming)) return "preserve"; if (opts.terminalAnswerNoQueuedWork && opts.severity !== "blocker" && !opts.streaming && !opts.aborting) return "preserve"; - if (opts.interruptImmuneTurnActive) return "aside"; + if (opts.interruptImmuneTurnActive && opts.severity !== "blocker") return "aside"; return "steer"; } diff --git a/packages/coding-agent/test/advisor/advisor.test.ts b/packages/coding-agent/test/advisor/advisor.test.ts index 598a7247e..5dc94b86f 100644 --- a/packages/coding-agent/test/advisor/advisor.test.ts +++ b/packages/coding-agent/test/advisor/advisor.test.ts @@ -5509,7 +5509,7 @@ describe("advisor", () => { ).toBe("steer"); }); - it("routes interrupting notes to the aside queue during immune turns without overriding preservation", () => { + it("downgrades concern to aside during immune turns, but still steers a blocker (#5628)", () => { expect( resolveAdvisorDeliveryChannel({ severity: "concern", @@ -5519,6 +5519,15 @@ describe("advisor", () => { interruptImmuneTurnActive: true, }), ).toBe("aside"); + expect( + resolveAdvisorDeliveryChannel({ + severity: "blocker", + autoResumeSuppressed: false, + streaming: false, + aborting: false, + interruptImmuneTurnActive: true, + }), + ).toBe("steer"); expect( resolveAdvisorDeliveryChannel({ severity: "blocker",