a recorded pid means armed, whatever route started the loop

`watcher_armed` is set on one path only — the background-job dialog's
hand-back-and-re-arm. A commander that starts its loop as an ordinary tracked
background job records its pid correctly and still reports unarmed, so Command HQ
shows nothing to stop. That is the one thing the column exists to make possible.

The pid file is the fact and the flag is a claim, so the fact wins: armed if the
row says so OR a recorded pid is alive. Verified against a live commander that
had armed itself and read as unarmed.

Deleting it already worked — that path reads the record rather than the flag —
so this closes the gap between what the platform could do and what it admitted to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 01:44:06 +00:00
co-authored by Claude Opus 5
parent edeb434233
commit 854f957058
+6 -2
View File
@@ -15,6 +15,7 @@ import {
loopPidPath,
spawnedRecordPath,
stopCommanderProcesses,
liveLoopPid,
} from './spawned';
import type { ServerMessage } from '../chat/types';
import {
@@ -255,7 +256,10 @@ commandHqRouter.get('/commanders', async (ctx) => {
categoryId: row.categoryId,
tagIds: tagsByRow.get(row.id) ?? [],
running: runningById.get(row.sessionId) ?? false,
watcherArmed: row.watcherArmed,
// The pid file is the FACT; `watcher_armed` is a claim only one path sets. A commander that started
// its loop as a plain background job recorded its pid correctly and still reported unarmed, so the
// grid offered nothing to stop — which is the one thing this column exists to make possible.
watcherArmed: row.watcherArmed || liveLoopPid(who.email, row.sessionId) !== null,
watcherArmedAt: row.watcherArmedAt,
watcherStoppedAt: row.watcherStoppedAt,
watcherLastFiredAt: lastFiredByRow.get(row.id) ?? null,
@@ -401,7 +405,7 @@ commandHqRouter.get('/commanders/:sessionId', async (ctx) => {
categoryId: row.categoryId,
tagIds: tagsByRow.get(row.id) ?? [],
running,
watcherArmed: row.watcherArmed,
watcherArmed: row.watcherArmed || liveLoopPid(who.email, row.sessionId) !== null,
watcherArmedAt: row.watcherArmedAt,
watcherStoppedAt: row.watcherStoppedAt,
watcherLastFiredAt: lastFiredByRow.get(row.id) ?? null,