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();