Wiki index
Catalog of every page. Read this first, then drill into the pages. How pages are written and kept honest is the maintainer contract.
How this is laid out
Five layers, each with a different relationship to staleness:
design/— why the design is what it is: durable theory and cross-cutting concepts.internals/— how it runs: narratives threading several subsystems into one flow.invariants/— the hard rules, one per page.decisions/— why it changed: one dated ADR per decision, status in its frontmatter.map/— where things live: thin pointers into the code, stamped with the commit they track.
Start here
A guided path through the system, why before how: syscalls-are-effects → cbpv → types → the compilation ladder → the evaluator machine → grant → capability enforcement → exarch-architecture → a turn, end to end.
design/ — the why
- syscalls-are-effects — the foundational principle: a system call is an algebraic-effect operation, separated from its OS interpretation; handlers and reified continuations are orthogonal layers on top.
- cbpv — call-by-push-value: values vs commands, the two sigils, thunks, immutable bindings.
- types — Hindley–Milner with byte-mode computation types and let-polymorphism; pipeline modes.
- effects-handlers — algebraic effects; deep, self-masking, tail-resumptive handlers; no first-class
resume. - grant — capabilities; authority attenuated by intersection; exarch’s sandbox.
- capability-freeze — one always-frozen
Capabilities, resolved at decode time; the freeze pass is the one-way door and the xdg-escape guard a per-profile invariant. - two-enforcers — why capability checks gate in-process and in the OS sandbox: each enforces where the other is blind, “all sandbox” is strictly weaker.
- capability-carriers — why authority needs three forms, not one: the rule you write, the live act of judging, and the flat checklist for the blunt OS guard.
- row-types — open-row-polymorphic records with scoped labels and spread shadowing.
- records-and-maps — one runtime carrier, two static types: heterogeneous static-label records vs homogeneous runtime-key maps; the key space, the indexing split, the forgetful Record→Map coercion.
- pipelines —
|as dataflow; the edge type selects byte-process vs value-fold execution. - name-resolution — where a capability lives: the layering of head names; effects→coreutils, queries→builtins, conveniences→prelude.
- builtins — the structured primitive set: the three kinds of irreducibility (reaches a syscall/shell state · base computation · undelivable type), the families, the
Sig/Scheme/Reducerrules, reification through the name. - codecs —
from-X/to-Xas the typed byte↔value crossing:decodeF[Bytes,∅]vsencodeF[∅,Bytes]; strict structured decode vs lossy line streams. - control-operators — the five reserved operators vs control flow as library; audit ownership.
- failure — failure is status, not truth;
?fallback chains,tryas recovery and the only||, aBoolis data. - scoping — lexical scoping for data, dynamic scoping for ambient authority.
- audit — execution as a structural tree; lexical ownership, processes only transport fragments.
- exarch-architecture — the agent as a provider loop over one
shelltool, each turn a grant-framed ral top-level turn. - hash-addressed-editing — exarch addresses a line by an adaptive-context witness: a digest of the line plus the smallest window of neighbours (≥ ±5, grown only where a line repeats) that names it uniquely, no line number.
view-text,view-text-around,witnesses, andedit-hashare all path-based, sharing one witness function and one read door, so the hash a read shows is always a valid handle;edit-hash path [[hash: …, line: …], …]resolves every hash against one read before writing one atomic batch. Closes with a comparison against the two anchor-based edit harnesses it descends from — Bölük’s hashline (disambiguates by line number) and Dirac’s stateful single-token anchors (by construction) — reaching their ambiguity-free editing statelessly, by growing the window. - pins — a pin is state a kit publishes to a keyed register slot, overwritten in place — the model-authored dual of the agent matrix.
surface ``pin [key, body]writes acard `` to the focused session's reserved right-hand column; ``unpin “ drops it. Neither logged nor landed in scrollback (what is, not what happened); the register mirrorstasks/goalfor the nudge that keeps the model restless about what it has pinned once it is actionable, and the single host-ownedservicesslot is the one pin ordinarysurfacecalls cannot write or clear. - exarch-config-dir — exarch’s trusted configuration lives in the XDG config home (
$XDG_CONFIG_HOME/exarch/), structurally outside the working tree the sandboxed agent can write, and distinct from the per-project state home (model selection + run logs) and the cache home (model catalog). Holdsconfig.ral(custom providers) and globalAGENTS.md; trusted because the grant cannot reach it, yetconfig.ralis still evaluated no-authority because a redirected endpoint is an exfiltration channel. - agents-md-injection — exarch injects
AGENTS.mdfiles as aWorkspacesystem-prompt section, discovered outermost-first (operator’s<config>/AGENTS.md, then every repoAGENTS.mdfrom the git root down to cwd; the walk stops at the first.gitentry). They steer behaviour but add only prompt text, never capabilities — so a cwdAGENTS.mdis untrusted yet harmless: it cannot widen the grant, unlike trustedconfig.ral, which can cause effects. - agents — the sub-agent model: a run is a tree of uniform
Agentnodes under one shareddriveloop, distinguished only by position — the parent-less trunk (conversing when a human is attached, withholdingreply) versus every returning agent, keyed on a construction-fixedreturnsbit, never anis_rootflag. Spawning is universal but bounded byfuel, a per-agent budget each fork hands one less unit of to its child; one record-specagentverb forks a value-snapshot of the parent shell onto a detached thread and gives the child a name that is its fleet-unique identity; the spec’stypefield picks the child’s memory —amnemonblank,mnemoninheriting the parent context with a fresh prompt; live agents may send marked peer messages by name; returning is the deliberatereply, which cancels unfinished descendants before the node settles; focus is the dynamic human attachment, andEsc/agent-cancelcascade a subtree; self-scheduling authority is inherited; a spawn’s mandatorygrantbase bounds the child toparent ⊓ base— a lattice meet that narrows but never escalates. - residency — 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 with four facets (identity, capability, lease, probe); the ledger is an interface each chapter answers through its own representation, never a fused struct, and lifetime moves along one graded residency order, from foreground through stopped/background/durable to survives-exit, discovered mid-flight by interactive work and declared at birth by agent work.
internals/ — how it runs
- compilation-ladder — source → tokens → flat AST → CBPV IR → annotated typed IR; the two
compileverbs, per-turn seeding, the annotated prelude bake. - surface-syntax — context-free lexing, recursive-descent + Pratt parsing, the flat AST, three-stage head classification.
- type-inference — the HM algorithm: the Inferencer, the Unifier (types, rows by Rémy rewrite, byte modes), generalisation at
Bind, the verdict written onto the IR to survive into the next turn. - evaluator-machine — the trampolined CBPV machine, its
pub(crate)verbs reached only through framed turn doors, the four-wayShelllifetime split, same-thread bodies sharing the caller’s session, tail calls bounded bypub(crate), the two exit channels. - builtins-registry — the six-facet
builtin_registry!, seeding vs dispatch, bundled coreutils, host-layer builtins. - handler-dispatch — the handler stack: two-pass lookup, self-masking strip-and-restore, deep frames on the dynamic context.
- capability-enforcement — the meet-fold chokepoint as a module boundary (
capability::check_*(&Context, …)); in-process gate for what ral dispatches, an OS sandbox for what a spawned child does (net is OS-only; per-command on macOS/Linux, projection-keyed on Windows); the grant body evaluates locally. - pipeline-execution — value folds vs one process group; the resolve-time
StageLaunchfreeze, one value-edge judgment, the shared child-eval frame pair, then ≥ 2pgid anchor, Windows Job Objects and their spawn-boundary limits. - a-turn-end-to-end — one top-level turn for a ral REPL line and an exarch tool call, through the shared framed door (
run_turndispatching onProgram::Source/Program::Hook→TurnReport). - output-capture-and-detachment — capture drains each child’s pipe to EOF, so a process that never closes it stalls the foreground to the wall and is killed with its tree;
spawnmoves it to a root-parented worker with a 16 MiB-bounded buffer under an idle-observation lease (1 h unobserved, 24 h backstop;servicebirths the durable class). A long-running server is the canonical case. - binding-leases — exarch expires idle top-level scratch names: the unlocked single-writer ledger on
LocalState, the committed-turn clock, static use-harvest over the typed IR, the prune verb whose signature pairs notices with the post-prune checkpoint, the pin walk that refuses closure captures — and the worked capture scenario (a hot closure keeps a pruned name’s value alive and working; command-position use renews, value-position does not). - cancellation — stopping in-flight work: one cooperative cause-bearing
CancelScopetree (Interrupt < Explicit < Deadline < Terminate < RootAbort) that the platform handlers translate signals into via signal-safe slots (SIGINT → foregroundInterrupt, SIGTERM/SIGHUP → rootTerminateat exit 143), backed by an escalation ladder whose third delivery_exits;process::checkpolls only the scope tree; per-host gestures — REPL Ctrl-C relays + foreground-cancels, REPL Ctrl-\root-aborts, exarch TUI active-turn Ctrl-C/Esc drives a per-turn token. - provider-fault-recovery — genai errors at the LLM boundary: the structural
Faultwalk (Status/Transport/Terminal) that reads retryability from typed variants, never theDisplaystring; the 429 / 5xx / 4xx split intoProviderError; the oneretry_with_backoffdriver with a patient rate-limit tier and explicitRetry-After; the streaming commit-don’t-double-render rule; the idle timeout that turns a silent socket into a retryable fault with a bounded budget.
invariants/ — hard rules
- fixed-arity — every invocable has fixed arity; no variadic or optional arguments.
- single-binary — ral ships as one executable; no sibling helpers.
- ir-pure-cbpv — the IR carries no surface sugar; conveniences unfold at elaboration.
- optionality-via-variants — no Option/null; optionality is open variants.
- turn-ends-ready — a turn returns the session
ReadyForUserhowever it ends; the driver never strands a prompt mid-protocol. - schemes-leave-closed — a
Schemeleaves its minting unifier only if closed; quantification is nominal-by-listing, so a residual id aliases a foreign unifier’s variables.
decisions/ — why it changed
Newest last; the page carries the full reasoning.
-
superseded — typed-state-flow-wrappers — typed wrappers for shell-state flow; the boundaries closed by env-is-dynamic-only and a
current_diraudit, so the wrappers were not built. -
active — hot-path-cancellation — cooperative cancellation in hot loops.
-
active — completion-escape-refactor —
EvalSignalretired forSettled+ Escape/BodyResult. -
fixed — escape-propagation-bugs — try-swallows-exit and grant tail-call bypass.
-
active — repl-builtins-stay-in-repl — REPL builtins live in the REPL layer, above core.
-
proposed — repl-architecture — REPL direction: stream console now, hybrid workbench later.
-
active — background-tool-calls — scoped in-turn agent batching is active:
dispatchstages a whole tool-call batch under onethread::scopeand joins before the parent turn continues; true turn-outliving backgroundable tools remain future work. -
fixed — redirect-drop-on-handler-dispatch — redirects install on the handler arm.
-
active — ast-stays-flat — the surface AST enum stays flat.
-
active — handlers-deep-self-masking — handlers are deep and self-masking.
-
active — infer-case-stays-whole —
infer_caseis left as one function. -
superseded — env-overrides-scope-overlap —
env_overrides/ scope overlap. -
open — linux-exec-confinement — path-scoped exec is unenforced on Linux (no landlock).
-
active — env-is-dynamic-only — the environment is dynamic state, read through
$env. -
active — modes-equality-constrained-shared — pipeline modes are equality-constrained, in one shared definition.
-
superseded — reduced-authority-witness — a reduced-authority witness made the capability chokepoint a type.
-
active — xdg-resolver-consolidation — one XDG base-directory resolver; exhaustive transport walks.
-
active — exec-authority-partitioned — exec gets a partitioned
ExecMap; the two capability folds stay separate. -
active — capability-stage-collapse — collapse the syntactic/resolved stage split into one always-frozen
Capabilities; freeze at decode, the xdg guard a per-profile invariant. -
active — witness-collapse — the reduced-authority witness collapses to free
capability::check_*(&Context, …)functions; a module boundary, not a typestate. -
proposed — stateful-handlers — a handler frame threads a state value across interceptions; a fold, no continuations.
-
rejected — related-borrowables-rejected — duplicate-label lint,
--effectslisting, record restriction, effect rows, capability boxing. -
active — session-scheme-continuity — turn schemes persist into the next turn’s check, living on the runtime binding (1/4 of the one-mode-engine series).
-
superseded — handler-alias-mode-preservation — handlers and aliases preserve a head’s pipe signature (2/4).
-
active — ir-pipespec-annotation — the checker writes ground mode wires into the IR; the evaluator reads, never infers (3/4, landed).
-
active — unconditional-mode-pass — the inference pass always runs; the runtime mode engine (
ty.rs/classify.rs) deleted;--no-typecheckand its fragment/verdict apparatus retired — value-type errors fatal on every path (4/4, window closed). -
active — unify-one-sided-obligations — the co-inductive unifier memoizes one-sided var-vs-structural-key obligations, so a recursive type anchored at a ty-var unifies with the same type anchored at a comp-var instead of overflowing the stack.
-
active — alias-head-defines-its-modes — a fresh alias/handler head defines its own modes (unknown
head_pipe_specyields a freshF[μ, ν]); preservation binds only a known head;?chains union their arms’ modes; the byte discipline enforces at the connection, not the definition (supersedes handler-alias-mode-preservation). -
active — cacheless-module-loader — module loads carry no cross-session cache (freshness over a memoisation a shell never needs); the cycle/depth guards are retained and load-bearing;
usebecomes a scope-projecting wrapper over the sharedevaluate_source. -
active — esc-non-escalating-interrupt — exarch’s Esc drives ral’s non-escalating
process::interrupt(the termination counter to exactly 1, never afetch_add), so the first press returns to the prompt and repeated presses can’t reach the third-signal_exit; only a genuine external signal still escalates. -
active — one-debug-path — one debug-tracing primitive,
dbg_trace!, gated ondebug_assertionsalone with no environment flag; thetry-caught-error echo shares the gate, and consumers of a debug child’s stderr must drain it concurrently since tracing is unconditional. -
active — witness-hash-h-prefix — the exarch edit witness is
hplus six hex so a bare witness lexes asString, notVal::Int; an all-digit digest would otherwise fail its ownequaland trap the agent in an infinite edit loop (string-coercingedit-hashcan’t recover a leading-zero digest; type-directed literals left as an open language question). -
proposed — raw-delimiter-near-miss — a
#\'near-miss errors in value position ([L0004]) and back-points the [L0001] it strands in line-leading position; the grammar is unchanged (#\'is still not a raw-string opener). Rests on confirming “do not change the grammar” permits a hard error. -
active — pure-pipe-equation —
x | f = f !{x}unconditionally at every value edge; Step streams are ordinary variants eliminated explicitly by the prelude combinators, the runtime stream recognizer and the checker’s probe are deleted (one β-rule, two recognizers gone), which fixes theUnitsoundness hole and the`done 5swallow by absence. -
active — child-eval-unification — one re-exec’d-child eval protocol (
run_child_eval+ChildKind) replaces the sandbox/pipeline pair; one wire shape in and out, the two-snapshots-must-agree hazard gone. -
active — evaluator-runtime-split — the command/pipeline/transport machinery moves to
core/src/runtime/;evaluator/is the ~2.6k CBPV machine and the seam a module boundary, not just a claim. -
active — host-embedding-api —
BakedPrelude+boot_shelldeduplicate hosting a Shell; the postcard bake moves into core, the schema-evolution hazard collapses from three files to one. -
active — value-edge-locality — every value-edge judgment is taken where its facts live: resolve owns dispatch (a bundled head with a value edge routes HelperEval), the allocator owns the edge definition, the child owns forcing, the protocol owns its env; the pgid anchor is a multi-stage device (none at n = 1).
-
active — authenticated-confinement-marker — the OS-confinement marker
RAL_SANDBOX_ACTIVEis trusted only when it carries a per-re-exec capability token the parent shipped in the IPC request (not the bare inheritable env var); a forged marker no longer suppressesnet/fs confinement, and bundled coreutils reach the fs floor by running in the confined child (deep-review S1/S2/S8, A8). -
active — per-root-turn-cancel — one root-turn
Tokenis shared down parent and sub-agent turns; minting is the reset, and exarch session-shell boot owns stale-interrupt discard plus cancel-handler re-chaining. -
superseded — terminal-foreground-ownership — the
tcsetpgrphandoff is gated on owning the terminal’s foreground (startup_foreground, atcgetpgrp == getpgrpprobe), not on being an interactive REPL, so terminal-launched scripts foreground interactive children (claude,fzf);ForegroundGuardmasks SIGTTOU during the parent-local restore, and parking on stop stays REPL-only. Superseded by terminal-lease, which reifiesstartup_foregroundas a held lease. -
proposed — provider-config-ral-script — exarch declares almost nothing: a famous provider whose conventional key is in the env auto-populates and its models are fetched live (genai
all_model_names, lazy + cached + manual fallback); a hand-written XDGconfig.ralis only for unusual providers (endpoint + protocolcompletions/responses/anthropic→AdapterKind, no-authority eval); the active model + tuning are picker-written runtime state at$XDG_STATE_HOME/exarch/<project-slug>/state.json(per-project, beside that project’s session logs via the sharedbootstrap::project_dir; outside cwd so the agent can’t reach it — no deny-list), loaded on startup./modelis a searchable strip picker, a secondary picker sets tuning. Reasoning is never stripped (genai handles it per adapter; DeepSeek/Kimi require the echo). DRY: provider knowledge fromProviderKind::info, models from genai, protocols fromAdapterKind. Slices: auto-discovery+picker → tuning → unusual-provider config → OAuth. -
proposed — structural-bug-prevention — a wide review’s serious bugs collapse into nine recurring shapes (check/use path mismatch, cancellation that never cancels, a privileged fd/handle leaking into a confined child, an invertible range, a record read as an ordered key-stream, continuation by token-suffix heuristic, error classification by
Display-scraping, adebug_assert-only guard, a never-populated source identity). Each gets a missing type that makes the bad state unconstructable (ResolvedPath, a parent-linkedCancelScope, an RAIIIpcEndpoint+Tokenedresponse, a non-invertibleSpan, a typed record-decode, anIncompletenesssignal, typed provider-error variants, PWD/OLDPWD excluded from settable env, a non-optional source id) with a clippy backstop only at the one sanctioned call site — type-first, lint-as-backstop, the disciplineclippy.tomlalready applies to paths. Staged behind the immediate point-fixes. -
active — no-core-repr-leak-into-exarch — the host reads core’s capability state through accessors (
ExecPolicy::admit_label/is_denied), never by destructuring its representation;Subcommandsbecame aBTreeSet(idempotence by construction) and exarch’s prompt stopped matchingExecPolicy. The data-shape corollary of the host-embedding seam. -
active — handle-settle — a finished handle has one settle
{stdout, stderr, outcome: <ok: α | err>};poll → <pending | settled>reports it as data (total, never raises), whileawait/raceunwrap it to{value, stdout, stderr}and re-raiseerr(the dead always-0statusfield dropped). Buffers drain once into a cachedCompletedHandle; a panicked worker’sDisconnectedsettles aserr(fixingpoll-forever /race-spin);is-donetotal. Outcome-as-variant, the orthogonal done/ok split, over siblingready/failedarms. -
active — tool-boundary-steering — queued prompts drain after the current assistant tool-call batch, before the next provider request. Every requested tool id receives a
ToolResultbefore the user message is appended; same-batchagentcalls can overlap, and queued input steers the next assistant step rather than skipping already-issued sibling calls. -
proposed — force-eliminates-blocks — the surface
!should eliminate a value-producing thunk (a block,U(F α)), not a function-thunk;!$bodythen typesbodyas a nullary block, so a function-bodied argument fails at the call site instead of returning unrun. The naive runtime arm is unsound —step_forceis shared with the elaborator’sApp(Force(Variable), args)call head — so the two force sites must first be told apart. -
active — unify-turn-evaluation — lift one top-level turn into
ral_coreaseval_turn(shell, src, frame): the frame carries anIoFrameregime sum (Inherit|Capture), the foregroundCancelScope,Capabilities, and lifecycle callbacks;TurnOutcomehas static and runtime arms (the runtime arm carriesresult,eval_status, andsingle_command). The root/foreground split fixes exarch’s collateral kill by rooting detached workers at the durable shell root while compiled turns run under a child foreground scope. Scope cancellation is cause-bearing (Interrupt,Explicit,Deadline,RootAbort), translated into the scope tree byral_coresignal-safe cancel slots published per turn: Ctrl-C/Esc interrupt the foreground, handle cancellation and deadlines reap workers decisively, and Ctrl-\aborts the root. The single timer/reaper service and the death-clock were since built in concurrency-detached-vs-structured (process::reaper; exarch’s per-call watchdog thread retired onto it). Still deferred: the worker registry/survivor warning, and unifying theralbatch path througheval_turn. -
active — concurrency-detached-vs-structured — with the root/foreground split,
spawnand&are root-detached handle workers,watchis root-detached but REPL-only because it needs a durable output sink, pipelines are foreground-bounded, andparremains the accepted prelude compromise: success joins every rootspawnhandle, while foreground early exit or first failedawaitcan orphan the unjoined tail until cancel/one-hour ceiling/root abort/session exit.awaitadoptsrace’s cancel-aware wait loop,cancel/raceloser cancellation use explicit worker teardown,forgetis deleted, and exarch arms a frame-owned one hour lifetime ceiling on a sharedprocess::reaperdeadline service (the timer/reaper the turn-evaluation ADR left unbuilt). Truly long-lived agent jobs remain future separate host-managed work rather than a per-spawn timeout knob. The death-clock’s creation-age policy and the no-introspection doctrine were since amended by leases-and-budgets (idle-observation lease over a universal worker registry); the handle model and everything else stand. -
complete — turn-local-state — split
Shellby lifetime:mobileremains the persistable computation state;turnis the installed top-level frame (Io,surface, typed foreground scope, full non-dblocation cursor);sessionholds the durable root,SourceDb, exit hints, and the host-installed builtin table;localshrinks to audit and REPL scratch.eval_turnbecomes oneTurnStateswap plus same-extent signal-slot publication; same-thread IO still keeps its inherit/return manifest; IPC mobiles do not carry builtin bodies, so receivers preserve their ownsession.builtins; visibility narrows around invariant-bearing state with opaque root/foreground handles and host accessors. -
active — bundled-tools-as-exec-images — bundled coreutils/diffutils/ripgrep heads become an
ExecImage: clean terminal calls may stay inline under a tiny uucore-global lock, while redirects, capture/audit, env/cwd mismatch, and byte pipeline stages spawn ral itself through a hidden bundled-tool sentinel. Value-edge bundled heads still routeHelperEval; the pipeline helper stops being a coreutils launcher. -
active — watch-repl-builtin — a builtin a host cannot run should be absent from it, not present-but-vetoed:
watchleftCORE_BUILTINSforcore::builtins::WATCH_BUILTIN, a one-entry slice the ral host installs through the sameregister_builtinsmechanism exarch uses for its agent tools — registered process-wide inregister_host_surfaceand installed in both the REPL and batch paths (the ral binary has a durable stdout sink in every mode); exarch installs only its agent tools. Core keeps the host-agnostic primitives (spawn/await/race/cancel/poll); the line-framed spawn machinery andscheme::watchstay private, only theBuiltinEntrymoves (the one asymmetry). Withwatchadmission gone from the frame,DetachedPolicycollapsed to the bare per-host lifetime ceilingdetached_ceiling: Option<Duration>, andWatchAdmission/the runtime gate are removed. exarch genuinely lackswatch— absent from its builtin table,help, and system prompt — so naming it is an ordinary unknown command (a shell defers to external-command exec → command not found at runtime), not the compile-time diagnostic the proposal first claimed. Revises only thewatch-admission mechanism of concurrency-detached-vs-structured; the detached-root model, death-clock, reaper, andforget’s deletion stand. -
superseded — long-running-work — long work is born durable via a distinct exarch-registered verb into a listable durable-job registry, never a promoted
spawn; the open Regime 1/Regime 2 question and the cancel-by-id/pinning machinery are resolved by leases-and-budgets: Regime 1 ships as the durable lease class of a universal worker registry (verbservice), Regime 2 stays deferred. -
proposed — scheduled-wakeups — a cron-like wakeup is a timer that injects a synthetic user turn into the prompt queue, re-engaging the agent loop without a human; it schedules the agent, not a worker (the line that separates it from long-running-work, where a born-durable worker yields a value — here the payload is a prompt the model acts on, no ral code runs). Ephemeral and per-session: schedules live on the
Session, die on session end and/clear, and aforkdoes not inherit them (host state, likeexpect_action). The when is a cron expression — model-fluent, and the right surface for the calendar schedules a headless resident agent needs (“weekdays at 09:00”); plusafter <dur>for the one-shot relative delay cron cannot express. It reuses what is already compiled:jiff(timezone/DST next-occurrence, a hard transitive dep via the bundleddate→uu_date→jiff-icu) evaluates the expression; the five-field grammar is parsed in-tree rather than pulling achrono-based cron crate (a second datetime tree); the reaper fires it, its one action generalised from cancel-a-scope toCancel | Runso the wakeup rides the existing daemon (recurrence stays host-side, entries one-shot); thenudgesynthetic-prompt path delivers it. Delivery at the turn boundary (not mid-turn steering, which is user redirection); one pending wakeup per schedule (cron overlap-skip, also the suspend catch-up bound); a listable registry with cancel-by-id (theScheduleIdpin against agent-binding-reaping was since dissolved by leases-and-budgets — the registry is the authority,schedulesre-lists after compaction). The one genuinely new mechanism is a delivery seam: decomposing “something happens to the loop later” into(trigger, effect, target), cron and a future async worker share an effect+target — post a message, wake the loop — soPromptQueue(aVecDeque<String>) generalises into a typed per-session inbox (source + drain-boundary tags, the inbound twin of the outboundKindstream) and the idle wait becomes a select over{input, inbox}; built once, for cron now and async delivery later. Two seams kept orthogonal by type: an event-trigger (a worker settling) is not a zero-delay timer entry, and a cancellation (control plane, read atcheck) is not anInboxMsg(data plane), per structural-bug-prevention; a shared channel is not a uniform drain policy. Self-scheduling gated behind aschedulegrant authority. Persistence and a durable cron are out of scope (future, paired with long-running-work’s registry). -
active — async-agent-tool —
agentis a launch-only, always-asynchronous orchestration edge: it forks a child, returns an opaque start receipt{id, title, status, log_dir}at once, and later delivers a typedAgentResultthrough the session inbox. Shipped shape diverges from the original bimodal proposal — there is nomode: "sync"dependency edge ormodefield at all (exarch/src/tools/agent.rs); every call is the orchestration edge, and a caller wanting an in-turn answer waits on the inbox delivery instead. The async path is an ephemeral, listable per-session registry (agents/agent_cancel) with request-local provider cancellation, explicit reaper ceiling, and/cleargeneration rejection, exactly as proposed. -
active — sandbox-external-children —
grantconfines external children under the effective sandbox instead of re-execing the grant body; bundled heads become exec images when process semantics are required, pipeline helpers stay, endpoint-shapednetleaves, and offline mode must fail closed where unsupported. -
active — tui-transcript-as-graphic — the scrollback is re-projected as an information graphic whose Bertin variables (shape, value, size, hue, grain) are encoded per-
Blockrather than buried in prose: the decorative❖rail becomes a 2-column marginal index (shape→kind, hue→agent, value→magnitude), the tab bar becomes a reorderable agents×steps matrix,rule_lineearns data-ink via actx%value-ramp and a phase Gantt ribbon, collapse becomes graded reduction. Phases 0–2 landed: the per-Blocksubstrate, the data-encoding rail (Move 1), andrule_line’s ramp+ribbon (Move 3); Phases 3–8 (size, grain, reduction, matrix, fidelity, projections) remain proposed. -
superseded — host-seam-turn-observer — drive both hosts’ turns through one core entry and make the structured surface
!Sendso the daemon-task hang becomes a compile error; superseded by run-turn-host-loop because!SendforcesShell: !Send, colliding with exarch’spump/Sessionmove. -
active — run-turn-host-loop — a turn is one synchronous, runtime-agnostic core entry
run_turn(src, TurnRequest) -> TurnReport;TurnRequestcarries policy (TurnIo, capabilities, limits, lifecycle, and a turn-localSurfaceSink). exarch owns the event loop and exits on an explicit completion fact, not event-channel disconnect (the invariant; as built this is a worker-setAtomicBoolpolled by a retainedpump/drivewith no tokio in the turn loop — the ADR’sselect!/oneshotsketch is the proposal shape, see its As-built note). The daemon hang dies because completion is a control-flow fact no detached worker can influence;ShellstaysSend, tokio stays out of core, same-thread children inherit the surface, detached workers get bounded once-only surface replay, and the bus remains presentation rather than liveness. Completes unify-turn-evaluation by making REPL, exarch, and batch request suppliers. -
active — run-turn-is-host-api — the public evaluation seam is one layer: hosts call
Shell::run_turn(src, TurnRequest) -> TurnReportand may nameTurnRequest,TurnIo,SurfaceSink/EventSink, lifecycle hooks,Captured,StaticDiagnostics, andTurnReport;TurnFrame,IoFrame, coreTurnOutcome, and publiceval_turncollapse.TurnIois host intent,Io/TurnStateare materialised resources, and capture/timeout classification live inrun_turnbeside the state that proves them. -
open — after-turn-api-simplifications — draft follow-on cleanup after the run-turn cutover: the common architectural error is turn-local facts leaking into long-lived or host-owned machinery (completion through presentation transport, surface as persistent cloned state, materialised frames as host API). The draft orders the next simplifications around enforcing that boundary: close old core exports first, move tests to
run_turn, shrink or fold exarch’s ral adapter, share the TUI/headless explicit-done driver, narrowShellhost accessors, and consider renaming exarch’s provider-levelTurnOutcome. Guardrails: keepEvent/Kind, keep bytes and surface separate, keepset_stdoutuntil live-printer setup has an explicit replacement, and keep tokio out ofral_core. -
accepted — surface-carries-documents —
surfacecarries a render document — an ordered stack of Bertin marks the kit composes in ral — that one generic interpreter binds to visual variables, replacing the closed tag set (`patch/`wrote/`task/`meter) exarch decodes into a closedKindenum with a bespoke renderer each (five Rust sites per surfaceable thing). The set of cards becomes open (compose marks, zero Rust per card), the set of marks stays closed and small (so the renderer is total and reflow/disclosure/aggregation/events.jsonsurvive). The discipline extends tui-transcript-as-graphic from chrome to content: the kit declares data and its level of measurement (quantitative → size/value/grain; nominal → hue/shape), exarch owns the binding, so magnitude can never land on hue. Five marks —text(qualitative, nominal-roled spans),measure(magnitude bar),fields(aligned matrix, subsumestask+provider_error),diff(the dense composite, subsumespatchwith aggregation + disclosure),raw(un-encoded bytes, the scoped “just print it” escape) — plus acardcontainer; composition is one rule at three scales (plane stacks marks,fieldsnests marks,textnests roles). Core untouched (already carries rawValue; detached replay free);TaskStatusand the four bespokelinebuilders retire;provider_errorfolds into the sharedfieldsrenderer. -
proposed — surface-reads-writes-execs — redirect reads/writes and external or bundled exec images surface at runtime doors as structural I/O events that exarch renders with the existing card marks. Bulk helper I/O (
grep-files,window-hash, and recommendededit-hash) moves below the ral line so plumbing does not leak onto the rail; events carry path/mode/argv/status/outcome, not file-size counts, and enforcement is a clippy-checked door set plus outcome-fused emit. -
active — handlers-and-aliases-are-lambdas — every per-name handler and alias must be a unary lambda
{ |args| … }and every catch-all a binary lambda{ |name args| … }; the calling convention is fixed by the surface position, not inferred from the value’s runtime shape, and arity is validated at install time (a bare block, a non-lambda, or a wrong-arity lambda is rejected).Nullaryis removed: a bare-block handler is no longer treated as a zero-argument handler. The rationale is structural — currying makes a binary lambda used as a unary handler return a partial-application closure rather than fault, a silent wrong result, so the bad state is made unconstructable at install rather than guarded at dispatch. -
fixed — terminal-lease — terminal-foreground authority is one session-owned
TerminalLeaseparked in core, with post-startupForegroundGuard::try_acquiredemanding&TerminalLease;TurnRequestseparates requested foreground policy (RequestedTerminalAccess) from byte input (TurnStdin), while internalTerminalAccess::ExplicitLoanis only a within-turn loan, so exarch tools areDenied + Empty, pipedral -ccan beDenied + Inherit, and_ed-tuiis an explicit terminal loan.JobControlwas narrowed to a process-groupLaunchRoleonce terminal authority moved to the lease. Landed in295fe5b. -
proposed — same-thread-body-shares-the-session — a same-thread thunk body — forcing a block or applying a lambda — must run in the caller’s
Shell, sharing the session store by identity, not in a freshShellwhoseSessionStateis default-constructed and then re-attached field by field. Today blocks evaluate in place (with_blockswaps only the mobile on the live shell) while lambdas build a child viawith_child→child_of→Shell::new(Default::default())+inherit_from— a hand-maintained allow-list with no totality check, whereShell::newactively re-mints the terminal lease from a blank predicate toNone, severing it from every function/alias/handler body (terminal-lease, fixed as a way-station). The semantics has no second store: CBPV elimination threads one ⟨M, ρ, σ⟩. The decision routes lambda elimination through the in-place mechanism blocks already use, parameterised by a small explicit fold-back set (last_statusalways,cwdfor a lambda); the owned-Shellconstruction (from_captured) stays only for genuinely separate runtimes — spawned workers, the cross-process helper, the REPL aside — which correctly default toDenied/no lease. Makes the forgotten-field severance unconstructable rather than guarded, per structural-bug-prevention; refines force-eliminates-blocks (the value-levelBlock/Lambdaelimination split is left intact). -
active — session-lifetime-event-bus — an async
agentis muted only because the event bus is per-turn (pumpmints thechannel()in itsthread::scope); a detached child that outlives the turn cannot hold the turn’s sender, the same denial watch-repl-builtin makes. Minting the(tx, rx)once per session lets a background child streamBorn/Token/Diedto a live tab through the existing id-routed draw path — pure presentation, nothing the model sees changes (its reply still returns only as the deferred inboxTurn::Agent). Safe because run-turn-host-loop already made completion a control-flow fact (done), not a bus state: the foreground turn must end ondoneeven while the channel is non-empty with concurrent producers, and the{input, inbox}idle select (scheduled-wakeups) gains the bus as a third source so background tabs advance at rest. Worker lifetime/registry/generation/reaper-ceiling untouched;/clearages out live tabs by generation; TUI-only (headless keeps muting). Answers async-agent-tool’s deferred “lift the bus” open question; the settle breadcrumb is already one path viaAgentOutcome::breadcrumb, so this is only about live streaming. -
proposed — surface-carries-control — the
surfacesink is the language→host typed-Valuechannel, not only presentation: beside the render classes (a kit`card, a coreioevent — both terminate in aCard) it now carries a control class,`spawn-started(carrying a liveValue::Handle), that exarch consumes to register the handle and arm an inbox-posting waiter — it renders nothing and cannot serialise toevents.json, so the host dispatches surfaced values by class. The fulcrum: once one surfaced event draws nothing, “surface is presentation” is the meaning of one decoder arm, not the channel. This letsspawnnotify instead of being polled: the control event is surfaced in-turn (no foreground resource outlives the turn) and the detached worker’s closure is unchanged — so “detachment holds only root/handle-owned resources” (concurrency-detached-vs-structured, watch-repl-builtin) holds by construction; the completion rides the existing inbox asSpawnResult→Turn::Spawn, generation-gated likeAgentResult. Rejects both a secondEventSinkand giving the worker the foregroundEmitter(the unsafe variant, where “only call.inbox()” is mere discipline). Extends surface-carries-documents and surface-reads-writes-execs (render vs control, one level up from operation vs appearance); refines the poll idiom and the poll-instructing timeout text of concurrency-detached-vs-structured; needs one shell-free handle settle whose two-observer cache handoff is the sole concurrency risk. Answers part of session-lifetime-event-bus’s background-surfacing open question (control class only; the render class stays foreclosed). -
accepted — surface-pins-state — a rendered value is an event or state:
surface’s render class gains a disposition, append (event → scrollback, logged) or pin (state → a keyed register slot, overwritten in place, unlogged).`pin [key, body]writes a`cardto a session-lived register the focused session draws as a reserved right-hand column (the model-authored dual of the matrix);`unpindrops it. Oneemitarm, one viewport field, no new concurrency invariant — it reusesvalue_to_cardand is emitted in-turn. Turnstasks.ralfrom the encode-don’t-stream doctrine’s counterexample into its first client. The concept page is pins. -
proposed — sync-surface-async-notify — counter-proposal to surface-carries-control: there are exactly two language→host channels, split by the one property a value’s tag can never carry — lifetime.
surfaceis the sync channel (in-turn, alive, holds the terminal-lease, renders now);notifyis its async sibling (post-turn, deferred, rendered at the next boundary as a fresh turn). Both carry a typedValueand both dispatch by class, so a new event is a new decoder arm on whichever channel’s lifetime fits — never a new channel.spawn’s completion is the first async event: the detached worker emits a structural`spawn-donethrough its session-livednotifyat its existing settle point (tx.send), exarch’sInboxNotifypushes it onto the inbox the asyncagentalready uses, and it lands next turn as[spawn 'build' finished]. This deletes the sibling’s machinery — no non-rendering control class, no exarch waiter thread, no shell-free core settle, no two-observer cache handoff (deliver-once becomes a boundary-timejoined-flag check; the worker emits, it is never observed). Safe by construction: the worker holdsnotify(session-owned, the same class the asyncagentworker already holds), never the foregroundEmitter, andnotifycannot splice into sealed scrollback because it only ever lands at a boundary — the late-card hazard is a property of the delivery regime, not of holding a channel. Honours the one-channel instinct at the right grain (don’t proliferate pipes; dispatch by class), correcting only its collapse of the lifetime axis. The async value is data, not a live handle, soevents.jsonneeds no special-casing. Refines concurrency-detached-vs-structured’s poll idiom (shared with the sibling); extends operation vs appearance (surface-reads-writes-execs) to sync vs async; the live-render question of session-lifetime-event-bus stays foreclosed (notifyis completion-only). -
proposed — host-seam-transport-parametric — the front-end↔engine seam (the framed
run_source_turndoor, run-turn-is-host-api) is made parametric in its transport: one source-dispatch/result/cancel/lease vocabulary runs as an in-process call sharing a kernel or across a process/VM boundary, with no construct in one mode and not the other — the local case is the identity transport, not a special case. Dispatch carries source text andTurnRequest; the REPL’srun_value_turnuse for prompt/plugin/startup thunks stays an in-process embedding helper, not a protocol arm or executable core-Valuewire form. A front-end owns a controlling terminal; the engine never owns one — it operates a terminal it is granted, a real kernel tty that is the login tty under the identity transport and a PTY under a remote one (a PTY is a tty, so the engine’s job-control code does not branch). Three corollaries: the terminal lease’s authority is conveyed across the seam by the terminal’s owner (replacing the localstartup_foreground→mint_at_startupself-mint — completing the witness discipline the lease began); POSIX signals exist only within a process and cross the seam as typedcancel/suspend/resizemessages (deleting commit6abb905’s signal-chaining at the seam, on a full-duplex transport so the sandbox-ipc-cancel out-of-band-cancel rule holds); the singlesurfacevocabulary crosses as live events plus deferred boundary batches (surface-delivers-itself); and the cut falls only around a whole turn, never inside an effect scope — the boundary sandbox-external-children blessed, not the grant-body IPC it killed. The engine is session-lived across turns, not process-per-turn: a disconnect cancels the in-flight turn and reaps its foreground child, then host lifecycle policy decides whether the engine dies with its front-end or remains available for re-attach.exarchstays only an agent, the shell host its peer, both driving one engine over one seam. Direction ADR from a design conversation; rests on landed pieces, nothing built. Records the lease state machine and the disconnect-mid-lease corner. -
active — shared-transport — the tokio runtime and genai clients belong to the fleet, not to each
Provider: anEngine(runtime,cache_key, atransports: Mutex<HashMap<TransportKey, Arc<Transport>>>keyed by credential+adapter) is built once (Engine::new) and hung off theFleetasArc<Engine>, whileProvidershrinks to a cheap per-agent selection (engine handle + id + model + tuning) that resolves its transport from the shared map instead of callingmake_runtime()on every/modelswitch. Concurrency is already proven — sibling peers already ran detached against their ownArc<Provider>, andruntime.block_oncan be invoked from many threads at once. The banner/ctx%chrome now reads the focused agent’s liveprovider().current()instead of a frozenSessionInfo, so a/modelswitch and a subsequent/clearcannot disagree;SessionInfokeeps only its static fields. Landed as proposed, including theBackend::Scriptedno-runtime test seam (realised in place of the sketchedEngine::Scripted). -
proposed — agent-binding-reaping — exarch leases scratch bindings per
Agentshell, not per fleet: ral/REPL bindings remain ordinary lexical state, while an optional ral-tool-call epoch lets core renew and prune top-level scratch at ready boundaries. Lease records live onShell::local, generation guards require one lease-aware top-level write door, accepted source turns harvest static read/write sets, running handles recursively pin any top-level value that reaches them, settled handles are scratch, and exarch must refreshAgent::durableimmediately after a committed prune so panic recovery cannot resurrect deleted names. leases-and-budgets answers its host-pins question: durable jobs need none — the worker registry retains the handle itself. -
superseded — long-session-resource-budgets — a days-long exarch run is bounded by heap, queues, shell values, and logs, not by model compaction; proposed per-accumulator budgets (bounded bus, windowed viewports, inbox quotas without silent loss, physical post-compaction reclamation,
/resources). Carried forward whole into leases-and-budgets, which adds the probe convention, the worker registry as accounting spine, and a per-agent admission cap. -
proposed — provider-heartbeats-and-retry-boundaries — exarch judges provider liveness by raw wire progress, not by semantic model output: Anthropic
pingevents become heartbeats, long-thinking models such asclaude-fable-5andclaude-sonnet-5may remain semantically quiet without tripping a decoded-event timeout, raw silence remains retryable, explicit wall-clock budgets stay separate, and exhausted provider failures surface as provider errors rather than synthetic model-visible nudges. Partial streamed work still commits and continues through truncation recovery. -
active — partial-poll-pending-output —
poll $hon a still-running handle now returns`pending{stdout, stderr}— the bytes written so far — instead of aUnitpayload, cloned non-destructively from the live buffers via a newpeek_buffer(the peer oftake_buffer). Chosen cumulative (a growing prefix), not a drain-on-poll delta: draining would reintroduce the peek-vs-drain split the settle decision closed and break theCompletedHandlecache, so the completiontake_bufferstays the sole place bytes leave a buffer. The cost is a deliberate reversal — pending polls are non-idempotent (monotone growth), so 260615’s “repeated observations are consistent” is re-scoped to settled observations only. Watched handles buffer nothing and report empty; the 16 MiB cap still bounds a chatty server. Gives exarch a headless, pull-based read of a running worker — the counterpart to REPL-only watch. -
fixed — windows-spawn-boundary — Windows pipeline launch gets a custom
CreateProcessWlayer: helper protocol handles cross by explicit handle list, not parent-global inheritable bits, and pipeline children enter their Job Object before user code can run, via a job-list attribute or suspended-create/assign/resume. This closes the report/value/gate handle leak race and the direct-external early-fork escape without making helper routing a safety crutch. -
active — subagent-memory-modes — sub-agent spawning splits into two model-memory contracts:
amnemonis tabula rasa, whilemnemonimports the parent’s model-visible context, reuses the parent’s provider selection for cache locality, and appends the tool call’spromptas the fresh final user prompt. Both keep the same shell snapshot, permission meet, detacheddrive, and inbox return path;AgentLogdrops any unanswered parent tool-call frame before importing context so the child never inherits a dangling protocol. -
active — spawn-fuel-ceiling — every
Agentcarriesfuel: u32; the trunk starts atSPAWN_FUEL = 3and eachforkspends one unit on the child. A newGate::Spawnsaxis withholdsamnemon/mnemon/commit/verify_commitmentoncefuelreaches zero, so a delegation chain terminates by tool absence rather than recursing forever — the same silent-gating shapereply/Schedulesalready use, not a dispatch-time refusal. Refines uniform-agent-nodes’s unbounded-depth claim: fuel is a pure function of tree position, so no agent is privileged by special-case code. -
superseded — protected-commitment-pins —
commitment:*is a protected pin prefix: ordinarysurfacewrites/unpins to it are rejected, and a live commitment pin rides the same one uniform pinned-state nudge every other pin kind does — budget-free, for every actionable agent regardless of role, additive with a returning agent’s separatereplyobligation, but quiet while a verifier child is still running.commitaccepts a key and a free-text description and launches anamnemonwriter, opening the pin only on a matching structured card with at least one criterion;verify_commitmentaccepts only the key and launches anamnemonverifier, clearing the pin only on a matching pass verdict — both launch-only and always-asynchronous likeamnemon/mnemon, settling on the host’s own thread;/clearstill clears them as part of the session reset. Superseded by names-and-schedule-labels, which deletes the feature whole. -
active — per-agent-eval-cancel — cancellation reaches the eval layer: only the signal-facing session publishes the process-global signal slots (
SessionState::publishes_signal_slots;fork_sessionclears it), restoring the slots’ single-thread LIFO discipline and closing a cross-session mistarget and a latent use-after-free under concurrent agent dispatch; and the registry cascade cancels each parented agent’s ownDurableRoot(Shell::cancel_handle, carried aseval_rooton the entry) alongside itsToken, so a cancelled agent’s in-flightraleval unwinds at the evaluator’s poll points (~100 ms) instead of grinding to itstimeout_secswall. The trunk registers no eval-root; its turn cancel rides the published foreground slot. -
active — session-ledger — the unifying frame over leases-and-budgets: everything alive between turns is a resident with four facets — identity in the ownership tree, a typed capability (
Handle, pgid job spec,AgentId, schedule id, top-level name), a lease, a probe — and the session keeps one ledger in five chapters (workers, agents, stopped jobs, schedules, bindings) whose management surfaces are folds written once: list, exit warn-then-sweep, cancellation cascade,/cleargeneration,/resourcesprobe. Lifetime moves on a graded partial order of residency (foreground → stopped → background → durable → survives-exit); leases push down, verbs move up; the REPL’s job control is the discovery traversal (Ctrl-Z/bg/fg/disown—bgis promotion,disownis Regime 2’s shipping interactive ancestor) while agent work declares its grade at birth — dissolving birth-vs-promote into two disciplines of one order. Splices job control at the listing layer only: REPLjobsfolds over stopped groups and detached handles (healing the&wart),fg/bgstay pgid-typed,awaitis the handle’sfg; deep fusion (aStoppedHandleState, terminal semantics inawait) is refused as two implementations behind one name. Accumulators (viewports, bus, inboxes) stay probe-only: no capability, no listing. Graduates to adesign/residencypage once accepted. -
active — enquiry-channel — the host seam gains its answered engine→host channel, Enquiry → Answer, dual of Dispatch → Report: a turn-local
EnquiryDeskonTurnRequest(direct trait object under the identity transport,Event::Enquiry/Frame::Answerframes under the wire), nested in the turn’s clock and cancel scope, correlated byEnquiryId, extended only by class (anFOValuevariant label + a decoder arm), never by new channels. Every seam payload unifies onFOValue— a serialisable first-order ral value, first-order by construction via an uninhabited extension slot,SerialValue = FOValue<Closure>kept for the in-kernel helper IPC — deleting the whole “ground” vocabulary (is_ground/from_ground/into_ground, six silent-drop arms) for stdTryFrom/From; the envelope stays typed frames behind theAttachversion handshake. Lands LoC-negative: a folded-simplifications list also collapses the duplicated drain loops, request assembly, sink family, and dispatch tracker, and deletes the incoherentRAL_WIREplumbing. Amends surface-carries-documents: surface carries an open set of classes — facts, state, presentation, and classes not yet invented — the render document being one;doneand the reap/prune/large-binding notices become structured, engine-pushed classes instead of bare cards and polled accessors, and pin registers storeFOValuestate with cards derived at the edge. Names the residual host→engine traffic (Probe → Readingboundary reads;Fork/Clearlifecycle frames) that today bypasses the seam viashell_mut()— theRAL_WIREincoherence. Lays the rail that made the tool migration possible; the migration itself landed in agent-tool-to-exarch-builtin (class vocabulary,ExarchDesk/HostServices, bench-gated per family), reachingralalone —replymigrated too, once the enquiry litmus this ADR states was corrected to cover the refusal arm, not only the value arm. -
active — signals-are-causes — closes core-audit #3 (signals can’t preempt a blocked standalone external; SIGTERM never cancels) by deleting the parallel delivery mechanism instead of patching it: the platform handlers translate each delivered signal into a
CancelCauseon the published cancel slots — SIGINT → foregroundInterrupt, SIGTERM/SIGHUP → rootTerminate(new cause, message “terminated”, exit 143, SIGTERM-first teardown) —process::checkpolls only the scope tree, andSIGNAL_COUNTis demoted to the escalation ladder (ESCALATION: third delivery_exits;interrupt()/is_interrupted()deleted).RunningChild::waitloses itspark_on_stopblocking bypass — parking is a stop classification, every external wait is one cancel-aware poll loop — so SIGTERM now preempts even an interactive foreground external; the REPL loop observes a sticky root cancel at the prompt boundary and exits with its code (also un-bricks Ctrl-\). Rejects the audit’s self-pipe sketch (the poll loop was never blocked) and boot-time root-slot publication (the slots’ LIFO discipline). -
superseded — session-scoped-appcontainer — the Windows OS sandbox as one AppContainer profile per shell session, imitating MXC Tier 3 (0e7c3dd) with permanent provenance breadcrumbs; its union-of-projections consequence was judged unsafe for within-session attenuation.
-
active — projection-keyed-appcontainer — the Windows OS sandbox mints one AppContainer SID per distinct fs projection (bind_spec identity; profile names pid+counter, never content hash), so a child’s kernel-checked authority is exactly its own declared projection — narrowed grants and subagent permissions hold at the OS layer; same-projection commands share one SID and its stamps, profile registrations are ledgered before the OS create, and ACEs revert at session teardown, not frame exit.
-
open — ace-free-fs-confinement — the ported DACL tier stamps an inheritable ACE per fs prefix and
SetNamedSecurityInfoWpropagates it to every existing descendant, so confining one command under a grant overcwd:is O(files under cwd) — 120 s+ on a repo with atarget/(48k files), symmetric on teardown. The cost is inherent to ACL-confining an existing tree; excluding subtrees is whack-a-mole. Records the exact findings and the three MXC tiers with on-host availability (T1 BaseContainer feature OFF; T2 BFS present but the tier MXC ships disabled; T3 DACL the pathology). Decision deferred; leading candidate is a tiered detector, BFS-preferred, spike-gated, with a reduced-authority fallback. -
proposed — vm-workspaces-cross-by-copy — an exarch VM receives a private ext4 workspace through a bounded, content-addressed bulk plane beside the ral control seam; no host directory or NIC enters the guest, turn-boundary deltas land first in immutable private history, and only explicit witnessed publication changes the granted tree, while ral’s in-process gate and per-spawn OS projection preserve narrower grants inside the VM.
-
active — agent-names-and-schedule-labels — the exarch agent surface collapses to one record-spec verb,
agent [prompt, name, type: `amnemon|`mnemon, grant]→[name: Str, log-dir: Str]: the closed record row makes a missing/misspelled field a static error naming it, while the opentype/grantvariant rows defer to runtime doors that enumerate the legal tags. An agent’s model-facing identity becomes its name (fleet-unique among live agents — the desk’s cheap didactic pre-check plus the race-freeAgentRegistry::registerNameTaken) and a schedule’s its label (scheduleanswers[label, next-s],unschedule <label>,sched-<n>reserved for defaults); numeric ids leave the model surface entirely (message/agent-cancelresolve by name, closing the silent cross-family id no-op anunschedule <agent-id>once was), while internalAgentIdplumbing stays.amnemon/mnemonsurvive only as thetypefield’s two memory modes. The protected-commitment feature is retired whole — thecommit/verify-commitmentbuiltins, thecommit-open/commit-verifydesk arms,CommitmentIntent/CommitmentSettle, thecommitment:*projection, andPinKind— leaving the pin register as the plain digest mirror plus the host-owned, write-protectedservicesledger pin. Records: record specs give static field diagnostics and an extensible surface (future per-child budgets ride as new fields); names survive context compaction and read in prose; the schedule receipt’snext-scatches a mis-meant cron at arm time; commitments did not earn their machinery. Supersedes protected-commitment-pins; amends subagent-memory-modes (verb → field) and agent-tool-to-exarch-builtin (the class inventory); leaves async-agent-tool, spawn-fuel-ceiling, and enquiry-channel standing. -
active — total-wait-status — Unix wait results use rustix’s transparent, total
WaitStatusthrough distinct pid/pgid and blocking/polling funnels; the doors ownEINTRretry andNOHANGoptionality,ECHILDremains an error,Pgidadmits only positive identifiers, and neither negative-pid encoding nor fallible enum decode reaches lifecycle code.
related/ — comparison to existing work
ral read against the literature; durable on the external work, keyed to ral’s
design pages via the against stamp.
- system-c — Brachthäuser et al. 2022: effects and capabilities reconciled; box = thunk, self-masking as capability-set subtraction, the type-based pole of grant.
- scoped-labels — Leijen 2005: the record calculus row-types implements, minus the restriction primitive — override is shadowing, never removal.
- handlers-of-algebraic-effects — Plotkin–Pretnar 2009: the founding handler calculus, on CBPV, with shell redirection as its own example; ral is its tail-resumptive fragment.
- rows-and-handlers — Hillerström–Lindley 2016: the effect typing ral declined — the same row machinery extended to every arrow; nearly ral’s runtime, the inverse of ral’s wild/handleable split.
- call-by-push-value — Levy 1999/2003: the substrate taken as surface design; ral grades
Fwith byte modes, adds the pipe as a combinator, drops computation products. - access-control-algebra — XACML, Saltzer–Schroeder, Bruns–Huth, Bonatti, Tschantz–Krishnamurthi, Al-Shaer–Hamed: grant’s restrict/extend-base as standard policy composition — deny-overrides (a deny is a floor, both ways) as fail-safe default; the four-valued bilattice is the deeper structure.
map/ — where things live
- core — ral-core overview hub: the compile-to-typed-IR pipeline, the single framed turn door (
Shell::run_turn, dispatching on theProgramsum) that is the only evaluation seam, thehost/driversplit, and the subsystems below.- syntax — lexer, parser, flat AST; a command’s pipe modes are the projection of its declared type; one shared depth cap bounds lexer and parser recursion.
- elaboration — surface AST → CBPV IR; the one sugar-aware phase.
- ir — the
Val/Compcall-by-push-value IR;Bindcarries the checker’s scheme,Pipeline/Bindcarry non-optional ground mode wires, andPipelinealso carries per-stage value types. - typecheck — HM with row types; the sole mode engine returns an annotated comp (schemes, ground wires, per-stage value types) and one
SessionSchemesseed; handler/alias arms are fixed-arity lambdas; the pipeline-mode lattice and equality-strict unify rule (mode.rs). - evaluator — the trampolined CBPV machine: crate-private verbs entered only through the framed turn door, trampoline, scope frames, matching, audit; a same-thread thunk body runs in the caller’s session.
- runtime — the command/pipeline machinery the machine dispatches into: bundled heads as
--ral-bundled-toolexec images (inline only on a clean terminal), external children spawned under the effective sandbox, grant bodies run locally, redirect/exec surfaced at runtime doors, plus the shared re-exec’d-child eval runner (child_eval.rs). - capabilities — the grant decision layer (free
capability::check_*(&Context, …)folds),path/grant resolution, and the OS process sandbox (macOS Seatbelt, Linux bwrap, Windows projection-keyed AppContainer with grant- and deny-ACEs); kernel-denial hints offer a path to grant only forfile-*denials. - io-process — byte sinks/sources, the lease-gated foreground handoff, the Cancel|Run reaper daemon, Stream labels.
- builtins — the
builtin_registry!macro andCORE_BUILTINS, plus bundled coreutils/diffutils/ripgrep heads dispatched in-process viauutils_invoke; registry/seeding here, runtime exec-image dispatch on runtime. - shell-state — runtime
Value, thesurfacesink, handler stack, and theShellstate split by lifetime intoMobile/TurnState/SessionState/LocalState; a scope entry isBinding { value, scheme }. - transport — serde mirror (
SerialValue = FOValue<Closure>,SerialBinding) and wire envelope (WireMobile) carrying a shell — values and schemes — across a re-exec; the codec also carriesWireChannelframes. - diagnostics —
Span/FileIdbyte-range positions rendered directly via ariadne; the layer also exposesbyte_to_charand the type-error label so an external surface can draw the same underline; ANSI gating; exit-code hints. - prelude — the embedded
prelude.ralstandard library.
- repl — the
ralbinary: argv dispatch into a turn through core’s framed door, driving one of three selectable frontends (minimal / readline / structural live-state projection) over the REPL session, plugins, and jobs.- startup —
main.rsprocess dispatch,cli.rsargv →Mode(--surfacefrontend, clap-derived argv terminator),batch.rsexecution through core’s framed turn door, the annotated-prelude bake, platform glue. - loop — the
Sessionstate machine over an ownedIdentityTransport; one turn is aProgram::Sourcedispatch throughtransport::dispatch_to_report, seeded from the live session; the selectable frontend, prompt/theme/rc. - frontend — the
Frontendtrait and its minimal / rustyline / structural editors, the shared fuzzy completion engine, and the in-editor plugin surface (ghost text, highlights, keybindings) both TUIs drive. - plugins — the plugin runtime: the hook model with source-mapped faults and a buffer-change circuit breaker, hooks registered in the shell’s hook table and framed as
Program::Hookturns, the_ed-*editor builtins, keybindings, and captured session commands, driving the in-editor plugin surface both frontends render. - jobs — process-group job control, fg/bg/disown,
Escape::Stopped;fgresume gated on a held terminal lease.
- startup —
- exarch — the exarch agent’s front door: startup (CLI dispatch, bootstrap, account selection, credential resolution + env scrub, sandbox early_init) and system-prompt assembly; the agent is a provider loop over one
raltool, each turn a grant-framed ral turn.- agent — the uniform node (was
session) and the thinFleet: the turn loop (round-trips, tool dispatch with tool-boundary steering, auto-compaction, nudge-retry, theMAX_STEPSceiling, sub-agent forks in two memory modes), the construction-fixedreturnsbit,HeldByChildren, thefuelspawn budget, the owned hot-swappable provider, dynamic focus, and the subtree cancel cascade. - provider — LLM transport and live model catalogs: famous/custom/ChatGPT-account identity, shared OAuth renewal, streaming liveness, retry, prompt caching, usage/pricing, structural error classification.
- shell-eval — one tool call as a transport-level
Sourceturn dispatched viadispatch_to_reportagainst theIdentityTransport, under a pushed grant frame, seeded from the live session, withDeniedterminal access; buffered output digested for the transcript, the surface host sink decoding card marks onto the bus. - io-surface — redirect reads/writes and exec images surface at runtime I/O doors as structural events, rendered as grouped cards; bulk helper I/O sunk below the ral line, enforced by a clippy-checked door set.
- policy — capability composition (base ∨ extend ⊓ restrict for the root,
parent ⊓ basefor a spawned child), the six bake-in profiles, and restrict-file self-denial; the boundary is ral’s grant. - tools —
ralalone is the tool; spawning, messaging, cancelling, scheduling, andreplyare builtins reached by writing ral inside it, answered by the per-callExarchDesk, withtools.rskeeping only theralentry and the shared fork-detach-register spawn spine. - builtins — the resident host atoms (
view-text,grep-files,edit-hash/edit-replace,explore-dir,fff, the skill readers,service-handle) reading below the redirect frame, theagent.ralkit over them, and the harness verbs the desk answers (the one record-specagentspawn verb, the label-keyed schedule family,reply), for search, adaptive-context line witnesses, witnessed editing, and sub-agent orchestration. - frontend — the agent/UI event bus, durable session log, the headless frontend, and the inline TUI: a two-voice transcript laid out as a graphic — human band vs agent field, the marginal rail, slash-command routing, and an in-flight reply as a growing magnitude.
- cards — the
surfacerender document: a closed set of six Bertin marks a kit composes in ral, decoded once and drawn through one generic interpreter; the kit names data and level of measurement, the host owns the visual binding.
- agent — the uniform node (was
- ral-sh — the POSIX-bridge login-shell dispatcher; execs
ralinteractively, forwards everything else to/bin/sh.