command hq: auto-name a commander from its watcher's own task description

The initial title (the mechanics preamble, truncated, when a background job is
the very first message) is expected — that's just Claude Code naming the
session from its first message, same as always. But by the time the watcher's
task actually starts, the harness has already handed it a real description
("Watch google.com reachability" for a ping watcher), and that reads far
better than the preamble ever will.

Hooked into the exact moment pendingArmWatcher is consumed — the FIRST arm,
never a self-re-arm, which the watcher-loop code already had to distinguish
for its own reasons. Resolves a fresh ChatIdentity via resolveHomeDir and
calls renameClaudeSession with msg.description. Best-effort: a failure is
logged and does not touch watcherArmed.

Known gap, noted in the doc rather than fixed: the detail panel's title bar
is fetched once on mount and won't live-refresh when this fires while it's
open. The grid's cards don't have this problem since GET /commanders already
polls every 15s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 10:18:17 +00:00
co-authored by Claude Opus 5
parent eed906f45b
commit 963c965056
2 changed files with 51 additions and 0 deletions
+17
View File
@@ -477,6 +477,23 @@ computes `running` from the same fold `GET /commanders` uses for every card, a c
ago, nothing has run since" without any new mechanism, purely by reading what already exists. Not built
tonight; noted here so it is not lost, not because it was rejected.
**Auto-naming from the watcher's own description, added 2026-08-18 after the web frontend surfaced why it
matters.** A fresh commander's title is whatever the first message was — the mechanics preamble, truncated,
when that first message is a `handoff` background job. Expected, not a bug: that IS the first message, and
Claude Code names sessions from it the same way it always has (see "Naming" above). But by the time the
watcher's task actually STARTS, the harness has already given it a real description — `msg.description` on
`task:started`, "Watch google.com reachability" for a ping watcher — and that reads far better than the
preamble ever will. So the exact moment `pendingArmWatcher` is consumed (the FIRST arm, never a self-re-arm
— see "known, accepted imprecision" above for why that distinction is already load-bearing here) now also
calls `renameClaudeSession` with that description, resolving a fresh `ChatIdentity` via `resolveHomeDir`
for the filesystem call. Best-effort, like everything else in this block: a failure is logged and does not
touch `watcherArmed`.
**Known gap, not closed:** the detail panel's title bar is fetched once on mount and does not live-refresh,
so a commander open in the panel at the moment this fires keeps showing the old title until the next
navigation or reload. The grid's cards do not have this problem — `GET /commanders` is already polled every
15s. Flagged rather than fixed; worth a `refetch()` hook if it turns out to matter in practice.
**A real bug, caught by tracing rather than by the compiler, worth stating plainly.** When the cost-tracking
schema changed `recordCommandHqWatcherFired`'s signature from `(userId, sessionId)` to `(sessionRowId,
taskId)`, the ONE existing call site was not updated in the same pass — and `bunx tsgo` stayed clean, because