From f4e983693ecc756619ad4db6082fa707600b5cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 25 Aug 2026 06:23:44 +0000 Subject: [PATCH] the status dot was on the wrong axis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a commander watching its folder perfectly showed AMBER, titled "Armed, not currently running", because green was keyed on `running` — which is an agent TURN in progress, not the loop. so the healthy steady state, which is what a commander is in for weeks at a time, was painted as the failure colour and the owner reasonably concluded nothing was watching. the failure amber was designed for could not happen either: watcherArmed is computed from the live pid, so a dead loop turns it off rather than leaving the pair mismatched. the server now reports `watcherRunning` separately — the loop pid is alive, right now — which is what makes the distinction available at all. green is watching, amber is a row that says armed while its loop is gone, grey is a commander that never watched. and the flow: after arming, do not wait for the backlog. the pipeline was proved on one item, so the rest working is expected rather than news — say it is armed and move to the next question. a failure in the backlog is worth saying; a success is not. then ask what comes next, because a watcher that quietly does one job is a complete answer and the conversation should offer it rather than assume it. Co-Authored-By: Claude Opus 5 Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043 --- src/servers/api/command-hq/directory.ts | 16 +++++++++++-- src/servers/api/command-hq/router.ts | 4 ++++ .../src/apps/CommandHQ/CommandHqDetail.tsx | 6 +++-- .../src/apps/CommandHQ/CommandHqGrid.tsx | 23 ++++++++++++------- .../officerdev/src/apps/CommandHQ/types.ts | 4 ++++ 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/servers/api/command-hq/directory.ts b/src/servers/api/command-hq/directory.ts index 72c12da6..c82387da 100644 --- a/src/servers/api/command-hq/directory.ts +++ b/src/servers/api/command-hq/directory.ts @@ -351,8 +351,12 @@ export function commanderClaudeMd(params: CommanderClaudeMdParams): string { 'broken on its first day.', '', 'Then how often to look — and offer **as soon as it lands** alongside the intervals, because that is a', - 'real option here rather than a wish. Categories and dispatching come later still, one at a time, and', - 'most commanders never need either.', + 'real option here rather than a wish.', + '', + 'Once it is armed, the conversation is not over: ask what should happen next. Filing what it produces', + 'into categories, doing something with a result beyond keeping it, or nothing at all — leaving it as a', + 'watcher that quietly does one job is a complete answer and often the right one. Ask, one at a time,', + 'rather than deciding for them that there is nothing more to discuss.', '', '**Never arm a watcher before the owner has agreed what it does.** Arming ahead of that is arming a', 'guess, and a wrong condition either never fires or fires constantly — either way it is found out days', @@ -367,6 +371,14 @@ export function commanderClaudeMd(params: CommanderClaudeMdParams): string { 'knows how to handle items it has not seen — that is the whole of what a watcher is — so handing it the', 'backlog costs nothing and hands the owner a running commander in the time one item takes.', '', + '**Then keep talking. Do not wait for the backlog to finish.** You proved the pipeline on one item, so', + 'the rest working is expected rather than news — say it is armed and move straight to the next', + 'question. No sleeping, no polling a log, no "I will confirm once it is done". The owner does not need a', + 'progress report on work they already know succeeds.', + '', + 'If something DOES go wrong in the backlog, that is worth saying — in the conversation if it is still', + 'open, or as a notification if it is not. A failure is news. A success is not.', + '', 'Once the job is settled, write it into this file, replacing this section: what you watch for, what you', 'do about it, and which numbers were guesses. The next session should read the answer, not the', 'questions.', diff --git a/src/servers/api/command-hq/router.ts b/src/servers/api/command-hq/router.ts index 92e7d0f5..74301230 100644 --- a/src/servers/api/command-hq/router.ts +++ b/src/servers/api/command-hq/router.ts @@ -274,6 +274,9 @@ commandHqRouter.get('/commanders', async (ctx) => { // on `dirName`, not the transcript id: the pid file lives in this commander's OWN `state/`, not // beside the transcript. watcherArmed: row.watcherArmed || liveLoopPid(who.email, row.dirName) !== null, + // Whether the LOOP is alive right now, separate from `running` (an agent turn in progress) and from + // `watcherArmed` (which is also true for a row that says armed but whose loop has gone). + watcherRunning: liveLoopPid(who.email, row.dirName) !== null, watcherArmedAt: row.watcherArmedAt, watcherStoppedAt: row.watcherStoppedAt, watcherLastFiredAt: lastFiredByRow.get(row.id) ?? null, @@ -457,6 +460,7 @@ commandHqRouter.get('/commanders/:sessionId', async (ctx) => { tagIds: tagsByRow.get(row.id) ?? [], running, watcherArmed: row.watcherArmed || liveLoopPid(who.email, row.dirName) !== null, + watcherRunning: liveLoopPid(who.email, row.dirName) !== null, watcherArmedAt: row.watcherArmedAt, watcherStoppedAt: row.watcherStoppedAt, watcherLastFiredAt: lastFiredByRow.get(row.id) ?? null, diff --git a/src/workspaces/officerdev/src/apps/CommandHQ/CommandHqDetail.tsx b/src/workspaces/officerdev/src/apps/CommandHQ/CommandHqDetail.tsx index 658f2372..90c2843c 100644 --- a/src/workspaces/officerdev/src/apps/CommandHQ/CommandHqDetail.tsx +++ b/src/workspaces/officerdev/src/apps/CommandHQ/CommandHqDetail.tsx @@ -608,8 +608,10 @@ function MetaBar({ sessionId, title, isConnected, isGenerating, onDisconnect }: {commander.watcherArmed ? ( - - watcher armed + + {commander.watcherRunning ? 'watching' : 'armed, loop not running'}