Commit Graph
1 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 b7112f6e5d a commander records what it is and what it spawned, so deleting one means it
Without this record a commander cannot be stopped or fully deleted, and the delete route's comment claimed
otherwise: "the running background process (if any) dies on its own". It does not. Nothing in that route
touched a process, so a deleted commander left its loop running — still polling, still able to fire, still
able to spawn a deploying agent, with no chip to stop it from and no row left to disarm.

THE RECORD, and why the loop has to write it. Three ways to recover the link were tried and none works: the
PROCESS holds nothing (no open transcript descriptor, nothing identifying in its environment — tested); the
TRANSCRIPT is indistinguishable, because queue-operation/ai-title/last-prompt look like platform markers and
are the harness's own, appearing in every session equally; CONTENT matching finds ordinary chats that quote a
procedure as readily as the agent following it. So the loop records, because it is the only party that knows.

Two files under DATA_PATH/<email>/commander-state/<session>/: `loop-pid` and `spawned`. The reader is
deliberately tolerant — a truncated write or a stray line is dropped, not fatal, because a record that failed
closed would make a commander undeletable over one bad line.

STOPPING needs no new mechanism, only the pid: descendantPids + SIGTERM from the loop takes the loop and any
agent under it, which is exactly what that helper was written for. A commander with no recorded pid stops
nothing and SAYS so — the honest answer for one armed before this existed.

DELETE now cascades in a deliberate order: stop the processes, then the children's transcripts, then the
commander. Stopping first because a live agent writing to a transcript being deleted is a race, and because
the loop could otherwise spawn a replacement between the last two steps. It returns counts rather than a bare
ok — this is the one irreversible action here.

The children's transcripts are deleted with the EXISTING deleteClaudeSession: its cwd argument is documented
as "a hint, not an authority", so it locates a child that ran in the platform checkout rather than the
commanders directory. No new function.

THE CHAIN'S WEAK LINK was that the loop cannot know which commander it belongs to. The paths are now handed
to it in the background-job prompt, fetched from a new endpoint because they are rooted in DATA_PATH — a
server fact. Paths rather than an upload endpoint on purpose: a shell redirect needs no credential, and a
background job has no business holding one.

The prompt rule is numbered properly rather than landing on 7.5, and the output rule shifts down one when it
is present. Verified both shapes produce consecutive numbers.

The manager now exposes claudeSessionId, because a commander is keyed on the TRANSCRIPT id everywhere and
officer's own session handle is not interchangeable — that mismatch is what once rendered a working commander
as an empty pane.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 17:18:30 +00:00