The server owns session membership and shared arrangements. Clients project that organization into tabs and panes; they do not independently decide where a resource belongs. Start with concepts for user vocabulary, protocol for commands, and troubleshooting for recovery operations.
Records, handles, and arrangements
Use the identity terms consistently. Clients must validate authority, record key and activation/incarnation when reconnecting, rather than adopting whichever object now has the same number.
Every resource belongs to exactly one server-owned session. Moving it preserves its identity and execution; removing the last member of an Ephemeral session deletes that session in the same transition. The designated Intake is persistent and cannot be deleted. An arrangement is presentation, not membership: it may name only members of its session, and moving a member prunes its old arrangement entries atomically. Splits are binary, with finite ratios strictly between zero and one. Focus, selection, scroll and zoom are client-local; a shared layout edit must not take over human focus.
The pure core apply function owns these invariants; arrangement validation owns tree validity. Expected revisions, operation ids and live activation fences are concurrency/authority checks, not optional UI bookkeeping. The legacy TUI layout metadata adapter translates into this model; it does not supplant it.
Commit before publication
The dataflow is command + actor → pure apply → durable commit → mirror → effects/events/reply. A refusal changes nothing. Commit the transition, operation receipt and journal position in one store transaction before mirroring memory, acknowledging success or performing its effects. Replaying a retained identical operation returns its receipt; changing the payload under that id is refused. Do not copy the transient protocol dedupe cache’s lifetime into this durable receipt model; each store owns its retention.
The server commit seam separates
persist_command_from from mirror_persisted; its
a_failed_store_write_refuses_and_leaves_memory_unchanged test is the
regression boundary. The per-socket SQLite organization store
uses WAL and full synchronization. It stores organization/launch intent,
receipts and journal position, not PTY output, scrollback or a recoverable
terminal process. Event payloads remain a bounded in-memory journal: a gap
requires a level-state reread, not invented continuity.
Activations and reconciliation
Hot handoff can re-adopt inherited live PTYs. A cold restart cannot recreate them. At startup the server reconciles recorded activations against the boot identity, PID and process start time, or a vouched-for live handoff. It never adopts an arbitrary matching PID or implicitly relaunches work. A surviving recorded process without its owning engine becomes Lost; a process no longer matching becomes Completed with an Unobserved outcome. Lost means uncertainty, not an inactive pane: removal/relaunch is refused while it may still run. The current CLI cannot resolve every Lost activation; follow the recovery limitation and escalation path, not an assumed PID-kill or relaunch sequence.
ExitPolicy::Remove removes a normally exited resource (and an empty
Ephemeral session). Retain keeps its outcome inspectable until purge or
retention expiry. Exit is not close: purging a Terminal closes its bound
AgentSession children; a retained parent can outlive its process.
Reconciliation,
activation reports and policy tests
and core refusal rules own these decisions.
Work and missions
Organization records own execution, not task scheduling. The separate work store has one keeper writer per store, on the host that runs it. Mission documents compose over its client and never open SQLite directly. Operation ids deduplicate work/mail mutations and expected revisions refuse stale edits. Mail/path claims are advisory host-local coordination, not filesystem enforcement or an authenticated execution identity; see claims and acquisition fences.
The shipping mission start creates
a task worktree, brief, session and configured harness worker, with a bounded
agent cap and a revision-checked keeper commit. Queued is not launched. Its
CLI implementation,
pure launch planning and
keeper start transaction own the sequence.
The keeper projects each bound agent’s state into the task. The binding’s
terminal id is the source of truth. On start, and after journal_gap or
source_gap, the keeper re-reads level state and appends one event. The
journal is a wake-up, not the task’s history. Replaying the same derived
operation id writes nothing.
| Observed fact | Task state |
|---|---|
| the keeper launches a queued task | working |
agent working | working |
agent blocked | needs_you |
agent done | done_unchecked |
process exit 0 after done | done_unchecked |
process exit non-zero, or exit with no done | failed |
| bound terminal missing from a complete level read | failed (terminal_lost) |
unresolved credential_lost | needs_you |
done is reserved for merge cleanup and is not written from agent state.
needs_you keeps the agent slot. done_unchecked and failed free it, and
the keeper then starts the oldest queued task through the same worktree and
brief path as mission start. The keeper stays up while any task is
working or queued. On exit it waits a short bounded grace and re-reads
that terminal’s agent record before recording failed, so a done that
lands in the same moment becomes done_unchecked.
done_unchecked, done, and failed stamp the work store at
user_version 2 and add no tables. An older binary refuses the file at
open with an unsupported-schema error instead of failing on a task row
in one of those states.
The transition is pure in state.rs and applied by the keeper projector. Journal wake-ups follow ADR-0123 and ADR-0130. The single keeper writer is ADR-0092. Agent records are ADR-0040 and ADR-0046. Delegated grants are ADR-0163. Agents coordinate through phux per ADR-0187. The org’s one home keeper, and the cross-host work that waits until after launch, is the substrate decision. A multi-host coordinator, an autonomous Lead, and a durable grant ledger are not shipping contracts. Do not infer them from historical design names.
Restore and executable authority
Importing dormant organization records is not permission to execute them. Explicit restore/relaunch creates a new execution; it never resurrects the old PTY or its scrollback. There are two different agent restore lanes:
- Terminal
agent_sessionresume: stores bounded opaque native identity and owning plugin/integration ids. Resume argv is resolved from the currently enabled, trusted integration template. Ownership mismatch refuses before creation; other unresolved terminal integrations may warn and fall back to a plain shell. Option-shaped native ids are rejected. - ACP
native_launchrestore: archives a completeSpawnNativeAgentexecutable recipe, including command argv, cwd and environment. Explicit restore/relaunch currently executes that saved recipe without resolving current integration ownership/templates. Inspect and trust the archive as executable input before restoring it; disabling/changing its plugin does not invalidate the saved command. The provider process starts before theloadSessioncapability check, so a later restore failure does not mean no executable ran.loadSessionis still required to resume the provider conversation, not an executable-trust check.
The saved recipe path and ACP process startup define that current limitation; ACP usage covers readiness and operation receipts.
The archive resolver and live-session bridge tests own this trust seam; CLI reference owns the current restore/import options.