residency: a session is a ledger of residents
Everything that stays alive between turns — a detached worker, a stopped
pgid group, a sub-agent, a schedule, a top-level binding — is a resident,
and a resident has exactly four facets: an identity in the session’s
ownership tree, a typed capability that reaches it, a lease, and a probe.
The session keeps one ledger of residents, in chapters that keep their own
representations, and every management surface — listing, the exit story,
cancellation cascade, /clear generations, /resources — is a fold over
that ledger, written once. Lifetime moves along one order of residency
grades, and the two halves of the system are its two traversal disciplines:
interactive work discovers its lifetime after the fact, agent work
declares it at birth. The full reasoning — the five registries that grew
independently, the wart this dissolves, why the deep fusion is refused — is
session-ledger.
The four facets
- Identity — an id, with an owner edge into the session’s tree (the agent tree, the shell that spawned the worker, the session that armed the schedule). Ownership is what cancellation cascades along.
- Capability — the typed value that reaches and controls the resident: a
Value::Handlefor a worker, a pgid job spec for a stopped pipeline, anAgentIdfor an agent, a schedule id for a wakeup, a top-level name for a binding. Capabilities are deliberately not unified — they are the honest variance between chapters, never flattened into one value. - Lease — a clock, an idle bound, a renewal signal, an optional
backstop — including the degenerate cases: “none; legibility is the
bound” (a
service) and “none; a human owns it” (a stopped job). leases-and-budgets is where the lease machinery itself lives; residency is the frame it sits inside. - Probe — what the resident costs now, for the
/resourcesfold.
Nothing session-lived escapes this: a live thing with no chapter is a review defect, the resident-shaped completion of the probe convention (probe-convention).
Resident vs. accumulator
The line between a resident and a mere accumulator (a viewport, the bus,
an inbox) is the capability: a resident can be reached and controlled by
id; an accumulator can only be measured and bounded. Both are probed — the
/resources fold spans both kinds — but only residents are listed,
cancelled, and leased. A viewport has no capability, and pretending it does
to buy uniformity would buy it at the price of a lie.
The residency order
Residency states are graded by independence from the session — a graded partial order, not a lattice: a backgrounded pgid group and a detached handle sit at the same grade with incomparable capabilities, and no join is claimed.
| grade | state | population | entered by | left by |
|---|---|---|---|---|
| 0 | foreground | the running turn | evaluation | completion, the wall, interrupt |
| 1 | stopped | pgid groups | kernel SIGTSTP | fg, bg, exit sweep |
| 2 | background | pgid groups (bg) · detached handles (spawn/&) | bg · birth | completion, cancel, lease reap, exit |
| 3 | durable | service workers | birth | completion, cancel, /clear, process exit |
| 4 | survives exit | disowned groups · future Regime 2 processes | disown · birth | outside the session’s story |
Leases are the downward pressure — everything drifts toward reclamation
unless renewed. Explicit verbs move work up: bg is promotion, and
disown is the survives-exit flip.
Two traversal disciplines, one order
- Interactive work discovers its lifetime. A human cannot know at
launch that this build wants backgrounding; intent is revealed late, so
the REPL’s verbs move work mid-flight: Ctrl-Z suspends,
bgpromotes,fgdemotes,disownpromotes to grade 4. - Agent work declares its lifetime. A model states intent at birth —
spawnis born at grade 2,serviceat grade 3 — and mid-flight movement (promote) stays deferred until a concrete need.
Birth, not promote is not a universal law; it is the declaration discipline. Promotion is not a rejected mistake; it is the discovery discipline’s native gait. Both disciplines walk the same order — they differ only in when a resident’s grade is decided.
The folds, written once
The chapters keep their own representations, locks, and homes (no-core-repr-leak-into-exarch): the worker registry beside core’s handles, the job table in the REPL binary, the agent registry and schedules in exarch. What is shared is the small resident signature — identity, population, capability kind, lease row, state label, cancel, defined once in resident.rs` — and the folds written against it:
- list — project a set of chapters into one table. The REPL’s
jobsfolds the pgidJobTableand the shell’s registered worker handles into one listing, marked by kind in a designator namespace of its own ([wN]beside[n]) so the two can never collide. - warn — the exit story: what survives, what is swept, what is named. Shell exit is warn-then-sweep: undisowned pgid groups are swept as always, and any worker handle still running is named first — it dies with the process, so naming it is its only farewell.
- cascade — cancellation follows ownership edges regardless of population. Cancelling a resident cancels what it owns: an agent’s teardown reaches its own workers through its shell’s durable root (a worker’s cancel scope is a child of that root, so cancelling it walks the ancestor chain with no extra edge), and reaches its schedules and children through the registry.
- generation —
/clearbumps one generation every chapter observes, so anything settling across a clear is rejected, uniformly — whether by checking the generation directly (an async agent’s result, a worker’s deferred surface batch) or by the stronger route of unconditional removal (a schedule, disarmed and dropped rather than tagged). - probe — the
/resourcesprojection, over residents and accumulators alike.
Enumeration is not observation is the ledger’s own restatement of a law each chapter already keeps: no fold renews a lease. Interest is naming a resident through its capability, never scanning past it.
The fusion refused
The interface is the unification; a single registry struct would be the
flattening. Fusing the chapters — one struct, one lock order, one capability
type — is rejected because the substrates genuinely differ: pgid plus
terminal versus thread plus result channel, a kernel-driven state machine
that can re-stop versus a monotone running-to-settled, tty inheritance
versus bounded capture. The deep version of this — Ctrl-Z minting a
Value::Handle, fg becoming a handle’s await — is refused for the same
reason jobs/& were kept apart in the first place
(concurrency-detached-vs-structured):
fg needs the terminal-lease, a host
affordance no eliminator should grow, and fusion would put a Stopped arm
in HandleState that only one host can produce. fg/bg/disown stay
strictly pgid-typed; a handle’s own eliminators are its analogues — await
is its fg, cancel its kill — never a shared verb pretending both
populations are one.
Not every chapter implements the resident signature, either, and that is
the same refusal at a smaller scale: exarch’s agent registry, schedules,
and binding ledger each only ever hand out a bare snapshot type for
listing, built by cloning fields out from under a lock already dropped by
the time the snapshot reaches a caller. cancel takes &self alone, by
design, and none of those three snapshots carries a live handle back to its
own registry — adding one would grow a listing snapshot into something that
either holds a lock past the call that built it or duplicates the
registry’s own cancel plumbing. The ledger is precisely what makes refusing
these fusions affordable: unity lives in the signature and the folds, so
the capabilities — and the chapters that decline to unify further — can
stay typed and distinct without the system falling into pieces.
See also
session-ledger (the ADR this page
graduates from, and its full context/alternatives),
concurrency-detached-vs-structured
(the handle model; the job-table separation this restates at the listing
layer), unify-turn-evaluation
(the deferred survivor warning), agent-binding-reaping
(the binding chapter), scheduled-wakeups
(the schedules chapter), terminal-lease
(why fg is a host affordance), no-core-repr-leak-into-exarch
(why the chapters keep separate homes), probe-convention
(the probe facet as a checkable rule), jobs,
agent, shell-state.