There is no shared convention
Connect more than one kind of agent CLI and session resume is the first wall you hit. There is no shared convention. Each CLI has its own, several of them kill the process on the wrong input, and the flag names look similar enough that you will assume they behave alike. They do not.
The measured table
Versions are included on purpose. This behavior changes between releases, and a table without versions becomes a lie in six months.
| CLI (version measured) | Flag | Behavior |
|---|---|---|
| Codex CLI 0.144.5 | resume <unknown-uuid> | Exit 1, immediately. Error wording differs between TUI and exec modes — do not assert on the message string |
| Codex CLI 0.144.5 | resume --last | Graceful. Boots a fresh session even in a home directory with zero sessions. This is the correct default |
| Grok CLI 0.2.112 | --session-id <uuid> | New sessions only. If that ID already exists: already in use, instant death |
| Grok CLI 0.2.112 | --resume <uuid> | Searches the whole home, cwd-independent. An unknown ID hits a remote registry and 404s → instant death |
| Grok CLI 0.2.112 | --continue | Most recent session for the current directory. In a directory with no session: instant death |
| Claude Code | --resume <uuid> | Keyed off its own project-scoped session store |
Three rules fall out of that table.
Rule 1 — never pass one CLI another CLI's session ID
This sounds too obvious to write down. It was the single most expensive bug we hit in this area.
A resume-ID resolver written for one harness was called without model awareness, and fed that harness's UUIDs to a different harness. The result: death during boot on every restart.
What made it confusing to diagnose: it presented as "restarts are broken but switching works." The switch path happened to be model-aware; the restart path was not.
Whenever you add a resume path, find every other place that already resolves a session ID and confirm each one is model-aware. Copy the shape of the one that already got it right.
Rule 2 — gate every resume flag on its silent precondition
Each flag quietly requires something different.
--continue→ a session must exist in this cwd--session-id→ the ID must not exist--resume→ the ID must exist and be resolvable
A failed precondition is process death, not a warning. So check before you spawn. Handling the failure after launch is already too late.
Rule 3 — session keying is by literal working directory
At least one CLI stores sessions under an encoding of the actual cwd. Under git worktrees that means each worktree keeps its own session history.
There is a trap here that cost us a day. Do not confuse this with folder trust, which is a different rule inside the same CLI. Trust decisions can normalize a worktree back to the main checkout, so registering the worktree path alone leaves it untrusted and local tooling silently fails to start.
Two rules, two different path semantics, one CLI.
One more: a prompt_history file sitting next to the session directories is not
a session. Counting it makes "does this directory have a session?" return true
when the answer is no — and then --continue kills the process.
An assumption that turned out wrong
Worth recording a hypothesis we expected to be dangerous that measurement disproved.
We assumed agents sharing a machine would steal each other's "most recent session." They do not, provided each spawn gets an isolated home directory. "Most recent" is then always scoped to that agent.
We tested it specifically because it seemed obviously dangerous. The hypothesis was
false, and --last is the right answer for the CLI that offers it.
Summary
| Concern | Practical rule |
|---|---|
| Session ID origin | Isolate per harness. Resolvers must be model-aware |
| Preconditions | Check before spawn. Failure is death, not a warning |
| Error detection | Use exit codes, not message strings |
| Worktrees | Sessions are per-worktree. Trust is a separate rule |
| Home isolation | Do it, and --last becomes safe |
Rather than handling this yourself, Marblo binds a harness and session to each ticket, and the restart and switch paths share one resolver.
The full write-up is public in the Fleet Operations knowledge pack, including what was measured and what had problems.
More from the same fleet
- How Do You Know an Agent Is Working? — why PTY output is wrong in both directions
- Your Cost Table Is Empty and Both the Parser and the Router Are Correct — where cost attribution breaks silently