Start a stopped session's process for a command too

Same reasoning as the message path a commit ago, and the same objection
to leaving it out: a command is something somebody asked the session to
do, and answering "its process has exited" hands back the work of
starting one. `/compact` on a stopped session is the case that shows it
-- what is being asked for is exactly what a stopped session needs
before it is useful again.

`POST /sessions/{id}/command` and `/compact` now go through
`SessionManager::run_command`. A rename is deliberately not one of them:
it is persisted and listed whether or not a process ever hears about it,
so starting a CLI to tell it a name would be spending a resume on
nothing. It stays a forward to a process that happens to be there.

A command needs one thing a message did not. `Commands::submit` refuses
on `Exited`, and a driver that has just started a process announces
`Idle` through the sink rather than writing it -- so a command judged
against the session's own status would be refused by the word the start
had just replaced, in a window narrow enough that only a test reliably
hits it. `start_if_exited` returning `Exited` is what says a process was
started, so the status the command is judged against comes from there
rather than from a re-read the pump may not have caught up with. The
test fails without it.

`LiveSession::compact` went with this: `/compact` the route and
"/compact" the typed command were two ways to the same command, and now
there is one.

Verified over the API against a stand-in CLI: with the session reporting
`exited`, both `/clear` and `POST /compact` started the process and were
delivered -- transcript order `idle`, `commandSent`, `running`, `idle`,
with no "this session's process has exited" anywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-08-30 14:06:59 -04:00
1 parent 4a122f7b25
commit 1a132b4de3
4 files changed
+115 -36

No files matched your search

+18 -7
View File
@@ -375,19 +375,30 @@ Two rules come out of it, and neither is optional:
existed for the backend going away — and it is the whole of what a driver
whose process has exited is owed.
**Sending a message starts the process if there isn't one** (decided
**A message or a command starts the process if there isn't one** (decided
2026-08-30). Refusing was work handed back: read the status word, find the
other button, press it, type the message again. Sending plainly means "do
this now", and `--resume` puts the new process on the same conversation, so
nothing about the message changes — only whether there was anything there to
read it. The manager's `send_message` and the Start button ask one function
other button, press it, type the thing again. Both plainly mean "do this
now", and `--resume` puts the new process on the same conversation, so
nothing about what was typed changes — only whether there was anything there
to read it. A rename is deliberately not one of them: it is persisted and
listed whether or not a process ever hears about it, so starting a CLI to
tell it a name would be spending a resume on nothing. The manager's
`send_message` and `run_command` and the Start button ask one function
(`start_if_exited`) and want opposite answers from it: "there is already a
process" is a refusal worth showing to somebody who pressed Start, and
nothing at all to a message. Deciding it in one place under one write lock is
also what stops two requests arriving together from starting two CLIs. Only
`Exited` starts anything, for the reason above — `Unknown` has a process that
may well be reading its fifo, and the message goes to the driver as it always
did.
may well be reading its fifo, and what was typed goes to the driver as it
always did.
A command needs one thing a message does not. `Commands::submit` refuses on
`Exited`, and a driver that has just started a process announces `Idle`
through the sink rather than writing it — so a command judged against the
session's own status would be refused by the word the start had just
replaced. `start_if_exited` returning `Exited` is what says a process was
started, so `run_command` judges against `Idle` from there rather than
re-reading a status the pump may not have caught up with.
The phone's half is that the process button is disabled while its own request
is in flight, so a second press cannot be decided against a status the first