Expand description
Answer “will this pane resume its conversation, or start a new one?” at pane-open time, before anything is spawned.
§Why this exists
The persistent controller spawns lazily, on the first message — so every
signal that reports what actually happened to a resume
(agentmux_session_outcome, session:resume_failed,
SPEC_AGENT_PANE_HISTORY_ALIGNMENT_2026_08_05.md Part A) can only speak
after the user has already typed. The lived experience that produces is
the one this module exists to remove: the pane opens showing a long prior
conversation, the user types into it, and only then does the transcript
clear and announce a new session. The information was knowable the whole
time; nothing asked for it.
§Why it can be known in advance
--resume <sid> fails for exactly one reason in practice: the session’s
.jsonl isn’t under the CLAUDE_CONFIG_DIR the CLI is about to run with.
That is a file-existence question, and
session_backfill::session_is_reachable answers it using the same path
scheme and the same home-dir expansion the spawn path itself uses. So the
preflight is not a heuristic or a guess about CLI behaviour — it evaluates
the same condition the CLI will, just earlier.
§Faithfulness to the real spawn path
preflight deliberately mirrors the decision sequence in
persistent/spawn.rs and persistent/resume_retry.rs rather than
modelling an idealized one, so its verdict and the eventual outcome
can’t disagree:
| Pane state | Spawn does | Verdict |
|---|---|---|
| sid held, transcript present | --resume <sid> succeeds | Verdict::Resume |
| sid held, transcript missing, another session on disk | rejected → retry_after_resume_failure → find_recovery_session_id resumes that one | Verdict::Recover |
| sid held, transcript missing, nothing else on disk | rejected → retry finds nothing → blank | Verdict::Fresh |
| no sid, pane renders history whose session is reachable | first spawn continues it (find_continuation_session_id) | Verdict::Resume |
| no sid otherwise | spawns with no --resume; no disk scan runs on this path | Verdict::Fresh |
| provider has no resume flag | resume isn’t a concept here | Verdict::Unknown |
The no-sid rows are the case
docs/status/STATUS_CROSS_CHANNEL_RESUME_STALE_SESSION_ID_2026_08_20.md
recorded: a pane opened in a new channel or version shows the agent’s
whole prior conversation, but its spawn held no id. Since
SPEC_DURABLE_CONVERSATION_MEMORY_2026_09_23.md §5 P0a the spawn continues
the session of that rendered history. It still never scans the provider’s
dir for some other session: one may be sitting there —
Preflight::recoverable_session_id reports it when so — but “largest
on disk” can be an archived conversation or a subagent’s, so it stays
evidence, not a verdict.
Structs§
- Preflight
- The full answer, including the trail of how it was reached.
- Preflight
Input - Everything
preflightneeds, lifted out of block meta by the caller so this stays a pure-ish function over plain values (oneis_fileand at most oneread_dirof a single directory — no store, no block, no RPC types). - Step
- One line in the pane’s progress list while the preflight runs. Mirrors the
launcher splash’s
StageEntryrow (crates/launcher/src/splash_core.rs) so the two read as the same idea in two places.
Enums§
- Verdict
- What will happen to this pane’s conversation on its next spawn.
Functions§
- gate 🔒
- The spawn’s resume gate (
PersistentSubprocessController::apply_resume_gate_with), mirrored: a candidate that isn’t the chain head is redirected to the head when reachable here, else refused.Nonewhen the candidate stands. - preflight
- Decide, without spawning anything, what the next spawn will do to this pane’s conversation. See the module doc for the mapping this mirrors.
- step 🔒