phux docs

Organization and execution

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 factTask state
the keeper launches a queued taskworking
agent workingworking
agent blockedneeds_you
agent donedone_unchecked
process exit 0 after donedone_unchecked
process exit non-zero, or exit with no donefailed
bound terminal missing from a complete level readfailed (terminal_lost)
unresolved credential_lostneeds_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_session resume: 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_launch restore: archives a complete SpawnNativeAgent executable 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 the loadSession capability check, so a later restore failure does not mean no executable ran. loadSession is 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.