From a9b48320a37ea41d882f6664f310ca4d54be97e3 Mon Sep 17 00:00:00 2001 From: Michael Grundberg Date: Fri, 25 Sep 2026 07:47:03 +0200 Subject: [PATCH] fix(session): alert for an agent waiting on artifact comments An agent that publishes an artifact arms a monitor for its comments and ends its turn. Claude Code shows that on the footer as `1 Artifact comment monitor`, and #473 put that chip on the list of background work, so the session counted as watching and its idle prompt opened already acknowledged. Unlike every other chip on the list, that monitor waits on the user: the agent hears nothing until somebody comments. Claude's `watchingLine` now refuses any footer that carries the chip, through a lookahead over the whole row, so a shell running beside the monitor cannot report the session as watching either. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/cli-registry.md | 3 ++ docs/wiki/Notifications-And-Approvals.md | 4 +++ src/config/cli-registry/stock.ts | 10 ++++++- test/session-watching.test.ts | 37 +++++++++++++++++++++++- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/docs/cli-registry.md b/docs/cli-registry.md index 48cfee37..7298f385 100644 --- a/docs/cli-registry.md +++ b/docs/cli-registry.md @@ -66,6 +66,9 @@ agents` while a monitor, a backgrounded shell or a cloud session is live. Codema into `Session.watching`, and an idle prompt from such a session opens already acknowledged, so a pane waiting for its own background work never raises an alert a human cannot answer. Group 1 is the label, and a CLI that declares no pattern reports no background work. +Claude's Artifact comment monitor is the one chip that does not count. It waits for a human +to comment on a page the agent published, so Claude's pattern refuses any footer that +carries it, and the idle alert goes out as usual. Two CLIs declare such a row today, and they put it in different places. Claude writes its chip on the last row of the screen, so it keeps the default one-row window and anchors on diff --git a/docs/wiki/Notifications-And-Approvals.md b/docs/wiki/Notifications-And-Approvals.md index 41472d1a..e7964f6b 100644 --- a/docs/wiki/Notifications-And-Approvals.md +++ b/docs/wiki/Notifications-And-Approvals.md @@ -127,6 +127,10 @@ plain prose is not a dialog, so an agent that starts a monitor and then writes " should I target?" is quiet along with the rest — check a watching session yourself if it has been quiet longer than the work it is waiting for should take. +An agent waiting for your comments on an artifact it published never counts as watching. +Claude shows that as "1 Artifact comment monitor", but the agent hears nothing until you +comment, so the session alerts you like any other quiet session. + ## The phone overview On phones, tapping the "C" logo gives a session overview with **NEEDS YOU** first, then diff --git a/src/config/cli-registry/stock.ts b/src/config/cli-registry/stock.ts index 94d2e3b7..f491f5ee 100644 --- a/src/config/cli-registry/stock.ts +++ b/src/config/cli-registry/stock.ts @@ -237,7 +237,15 @@ const CLAUDE: CliEntry = { // carry a count. A footer that ever drew the chip as its only item would report no // watching rather than open that door. See `watchingLabel()` in // `session-activity.ts`. - watchingLine: String.raw`·\s*(\d+ (?:monitors?|shells?|teams?|local agents?|cloud sessions?|MCP tasks?|background tasks?|(?:background|remote) dynamic workflows?|Artifact comment monitors?))`, + // ⚠️ An Artifact comment monitor is the one chip that waits on the user. The agent + // has published a page and hears nothing until somebody comments on it, so the + // lookahead refuses the whole row while that chip is on it, whatever else is + // running beside it. The `^` is what makes the lookahead judge the row once: + // without it the engine retries from each later position, and a start past the + // chip reports the shell beside it. The lookahead stops short of "monitor", so a + // footer cut off mid-chip still counts. Counting the chip as watching kept the + // idle alert quiet for a session that was waiting for a human. + watchingLine: String.raw`^(?!.*Artifact comment).*?·\s*(\d+ (?:monitors?|shells?|teams?|local agents?|cloud sessions?|MCP tasks?|background tasks?|(?:background|remote) dynamic workflows?))`, }, requiresMux: false, // Claude installs Codeman's own hooks block into every workspace it runs in, so its diff --git a/test/session-watching.test.ts b/test/session-watching.test.ts index d520ed32..44b940c4 100644 --- a/test/session-watching.test.ts +++ b/test/session-watching.test.ts @@ -133,7 +133,6 @@ describe('watchingLabel', () => { '1 MCP task', '1 background dynamic workflow', '2 remote dynamic workflows', - '1 Artifact comment monitor', '2 teams', ]; for (const label of labels) { @@ -141,6 +140,42 @@ describe('watchingLabel', () => { } }); + it('reports no watching while the agent waits for comments on an artifact', () => { + // An agent that publishes an artifact arms a monitor for its comments and ends its + // turn. That monitor waits on the user, so the idle alert has to reach them. The + // singular footer is a live capture from 2026-09-25; the plural is assumed. + expect( + watchingLabel(pane('⏵⏵ bypass permissions on · 1 Artifact comment monitor · ← for agents'), CLAUDE_WATCHING) + ).toBeNull(); + expect( + watchingLabel(pane('⏵⏵ bypass permissions on · 2 Artifact comment monitors · ← for agents'), CLAUDE_WATCHING) + ).toBeNull(); + }); + + it('lets a comment monitor outrank other background work on the same row', () => { + // A shell beside the monitor is still running, but the agent needs the user all the + // same, and the chip order on the footer must not decide that. The second row is + // the one that needs the `^` in front of the lookahead. + expect( + watchingLabel( + pane('⏵⏵ bypass permissions on · 1 shell · 1 Artifact comment monitor · ← for agents'), + CLAUDE_WATCHING + ) + ).toBeNull(); + expect( + watchingLabel( + pane('⏵⏵ bypass permissions on · 1 Artifact comment monitor · 1 shell · ← for agents'), + CLAUDE_WATCHING + ) + ).toBeNull(); + }); + + it('still refuses a footer cut off in the middle of the comment monitor', () => { + expect( + watchingLabel(pane('⏵⏵ bypass permissions on · 1 shell · 1 Artifact comment moni…'), CLAUDE_WATCHING) + ).toBeNull(); + }); + it('says nothing about a pane that is running nothing', () => { expect(watchingLabel(NOTHING_RUNNING, CLAUDE_WATCHING)).toBeNull(); expect(watchingLabel('', CLAUDE_WATCHING)).toBeNull();