docs(as-built): align selected maps to one verified source snapshot (#570)

Re-verifies and restamps 19 as-built pages against one source commit (25412287): moved anchors, counts and named-Enter mentions updated, and portable grep in the README inventory commands. arteries.md, test-layers.md and the five web UI pages keep their own verification stamps. Docs only.
This commit is contained in:
Mike Schwarz authored and GitHub committed 2026-10-02 18:30:13 -07:00
1 parent eeca3e02a7
commit b6d37bfadc
19 files changed
+497 -494

No files matched your search

+8 -8
View File
@@ -10,14 +10,14 @@ applies-when: |
its own source-verification stamp.
siblings: [codemap.md, arteries.md, test-layers.md, cli-reference.md, frontmatter-schema.md]
prerequisite-reads: []
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# OpenRig as-built docs
These pages describe the code and point to its owning files. This index was checked against
main commit `712a61fbcc5ffe5a7c786fea12132567083ab507`. A linked module's
main commit `254122872cf477511514979a4300b695d77cd1f7`. A linked module's
`last-verified-against-source` is its own verification boundary; updating this index does not
revalidate that module or identify a running daemon or published package.
@@ -93,11 +93,11 @@ At the named commit there are **26 Markdown files**: **14** under `architecture/
these counts from the repository root:
```bash
source_commit=712a61fbcc5ffe5a7c786fea12132567083ab507
git ls-tree -r --name-only "$source_commit" -- docs/as-built | rg '\.md$' | wc -l
git ls-tree -r --name-only "$source_commit" -- docs/as-built/architecture | rg '\.md$' | wc -l
git ls-tree -r --name-only "$source_commit" -- docs/as-built/ui | rg '\.md$' | wc -l
git ls-tree -r --name-only "$source_commit" -- docs/as-built | rg '^docs/as-built/[^/]+\.md$' | wc -l
source_commit=254122872cf477511514979a4300b695d77cd1f7
git ls-tree -r --name-only "$source_commit" -- docs/as-built | grep -E '\.md$' | wc -l
git ls-tree -r --name-only "$source_commit" -- docs/as-built/architecture | grep -E '\.md$' | wc -l
git ls-tree -r --name-only "$source_commit" -- docs/as-built/ui | grep -E '\.md$' | wc -l
git ls-tree -r --name-only "$source_commit" -- docs/as-built | grep -E '^docs/as-built/[^/]+\.md$' | wc -l
```
Read a module's source stamp and prerequisite pointers before relying on it. Source code at the
+3 -3
View File
@@ -9,14 +9,14 @@ applies-when: |
overview or the current as-built module index linked here.
siblings: [README.md, codemap.md, arteries.md, test-layers.md, ui.md]
prerequisite-reads: []
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Architecture module navigation
This compatibility page points to the modular documentation. Its links were checked against main
`712a61fbcc5ffe5a7c786fea12132567083ab507`; the linked pages retain their own source stamps.
`254122872cf477511514979a4300b695d77cd1f7`; the linked pages retain their own source stamps.
- [Repository architecture](../../ARCHITECTURE.md): package boundaries, request flow, derived
source counts and contributor recipes.
@@ -12,8 +12,8 @@ applies-when: |
layer).
siblings: [daemon-core.md, agent-spec-and-startup.md, lifecycle-snapshot-restore.md]
prerequisite-reads: [../README.md, daemon-core.md]
last-verified-against-source: 264fade9
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Adapters and Runtimes
@@ -24,58 +24,58 @@ methods. A separate resume-honesty layer answers the question "did this harness
*actually* resume, or did it silently fresh-launch?" truthfully rather than
optimistically.
> Verified against source at main `264fade9`. Each count below sits beside the
> Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. Each count below sits beside the
> command that produces it; run the command from the repository root to refresh
> it.
## 1. The five-method RuntimeAdapter contract
`RuntimeAdapter` is `packages/daemon/src/domain/runtime-adapter.ts:131`
`RuntimeAdapter` is `packages/daemon/src/domain/runtime-adapter.ts:132`
(`interface RuntimeAdapter`). Every adapter declares a `readonly runtime`
string (`runtime-adapter.ts:134`) and implements five required methods
(`runtime-adapter.ts:140–163`; **5** =
string (`runtime-adapter.ts:135`) and implements five required methods
(`runtime-adapter.ts:141–164`; **5** =
`sed -n '/^export interface RuntimeAdapter/,/^}/p' packages/daemon/src/domain/runtime-adapter.ts | grep -c -E '^ [a-zA-Z]+\('`):
| Method | Signature (`runtime-adapter.ts`) | Responsibility |
|---|---|---|
| `listInstalled` | `(binding)` `:141` | List currently installed/projected resources for a node. |
| `project` | `(plan, binding)` `:144` | Project resources from a `ProjectionPlan` to the runtime's target locations. |
| `deliverStartup` | `(files, binding)` `:147` | Deliver resolved startup files to the runtime. |
| `launchHarness` | `(binding, opts)` `:157` | Launch the harness inside the bound tmux session; return a resume token. |
| `checkReady` | `(binding)` `:163` | Probe whether the harness is responsive and ready. |
| `listInstalled` | `(binding)` `:142` | List currently installed/projected resources for a node. |
| `project` | `(plan, binding)` `:145` | Project resources from a `ProjectionPlan` to the runtime's target locations. |
| `deliverStartup` | `(files, binding)` `:148` | Deliver resolved startup files to the runtime. |
| `launchHarness` | `(binding, opts)` `:158` | Launch the harness inside the bound tmux session; return a resume token. |
| `checkReady` | `(binding)` `:164` | Probe whether the harness is responsive and ready. |
The interface also has two optional members: `claudeManagedLaunch` (`:133`)
and `skillTargetPath?()` (`:138`, implemented by `OmpRuntimeAdapter`,
The interface also has two optional members: `claudeManagedLaunch` (`:134`)
and `skillTargetPath?()` (`:139`, implemented by `OmpRuntimeAdapter`,
`omp-runtime-adapter.ts:12`).
Startup *action* execution (`slash_command` / `send_text`) is explicitly **not**
part of this contract — the contract docstring (`runtime-adapter.ts:125–130`)
part of this contract — the contract docstring (`runtime-adapter.ts:126–131`)
states actions belong to the `StartupOrchestrator` *after* `checkReady()`. The
orchestrator delivery split is in `agent-spec-and-startup.md`.
### `launchHarness` opts and the fork seam
`launchHarness` opts is `{ name: string; resumeToken?: string; forkSource?:
ForkSource }` (`runtime-adapter.ts:159`).
ForkSource }` (`runtime-adapter.ts:160`).
Per the contract docstring (`runtime-adapter.ts:149–156`) `resumeToken` and
Per the contract docstring (`runtime-adapter.ts:150–157`) `resumeToken` and
`forkSource` are mutually exclusive — if both are provided the adapter **must
refuse** with a clear error, not guess; `forkSource` triggers a fork and the
captured token is the NEW post-fork token, never the parent. `ForkSource` is
`runtime-adapter.ts:120` (`kind: "native_id" | "artifact_path" | "name" |
"last"`, `:121`; v1 MVP accepts `native_id` only — other shapes rejected at
schema validation, docstring `:110–119`).
`runtime-adapter.ts:121` (`kind: "native_id" | "artifact_path" | "name" |
"last"`, `:122`; v1 MVP accepts `native_id` only — other shapes rejected at
schema validation, docstring `:111–120`).
### `HarnessLaunchResult` is a discriminated union with an honest failure arm
`HarnessLaunchResult` is a **discriminated union** (`runtime-adapter.ts:85–90`):
`HarnessLaunchResult` is a **discriminated union** (`runtime-adapter.ts:86–91`):
`| { ok: true; resumeToken?; resumeType?; appliedLaunch? }`
`| { ok: false; error: string; recovery?: HarnessLaunchRecovery; evidence? }`.
The failure arm carries a typed `recovery` hint
(`HarnessLaunchRecovery = "retry_fresh" | "attention_required"`, `:83`) and
(`HarnessLaunchRecovery = "retry_fresh" | "attention_required"`, `:84`) and
optional `evidence` (last-N pane lines, flowed through to
`RestoreNodeResult.attentionEvidence` for `attention_required` outcomes,
`:87–90`). This is the honest-failure shape, not a smoothed optional `error`.
`:88–91`). This is the honest-failure shape, not a smoothed optional `error`.
## 2. The runtime adapters
@@ -87,55 +87,57 @@ optional `evidence` (last-N pane lines, flowed through to
Hono, `git grep -l 'from "hono' -- packages/daemon/src/adapters | wc -l`).
`OmpRuntimeAdapter` (Oh My Pi) extends `PiRuntimeAdapter`, so the daemon wires
**6** runtime keys — `claude-code`, `codex`, `pi`, `omp`, `stub`, `terminal`
(`startup.ts:941`;
`sed -n '941p' packages/daemon/src/startup.ts | grep -o -E '"[a-z-]+": ' | wc -l`).
(`startup.ts:945`;
`grep 'adapters: {' packages/daemon/src/startup.ts | grep -o -E '"[a-z-]+": ' | wc -l`).
### ClaudeCodeAdapter (`claude-code-adapter.ts:53`)
- `readonly runtime = "claude-code"` (`:54`).
- **Projects** to `.claude/` targets: guidance managed blocks →
`<cwd>/CLAUDE.md` by default (`:187`, `:478`; default at
`<cwd>/CLAUDE.md` by default (`:187`, `:483`; default at
`managed-blocks.ts:16`, and the rig may select `CLAUDE.local.md` instead,
`managed-blocks.ts:14`); `skill_install` →
`<cwd>/.claude/skills/<name>/` (`:193`, `:559`); subagents → `.claude/agents`,
`<cwd>/.claude/skills/<name>/` (`:193`, `:564`); subagents → `.claude/agents`,
plugins → `.claude/plugins/<id>`, runtime resources →
`.claude/extensions/<id>` (`:561–563`); settings fragments merged into
`.claude/settings.local.json` (`:571`) and MCP fragments into `<cwd>/.mcp.json`
(`:574`).
- **Launches** (`:309–310`): fresh = `claude <permissionMode> --session-id
`.claude/extensions/<id>` (`:566–568`); settings fragments merged into
`.claude/settings.local.json` (`:576`) and MCP fragments into `<cwd>/.mcp.json`
(`:579`).
- **Launches** (`:311–312`): fresh = `claude <permissionMode> --session-id
<generatedId> --name <name>`; resume = `claude <permissionMode> --resume
<token> --name <name>`; fork = `claude <permissionMode> --resume <parentId>
--fork-session --name <seat>` (`:281`). Each command may carry a
--fork-session --name <seat>` (`:283`). Each command may carry a
`CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1` env prefix (`yolo-mode.ts:98`) and a
`--model <model>` argument (`:263`). When `binding.permissionMode` is set, the
command is built by `claudeManagedLaunch` instead (`:243–249`, `:307`).
- **Readiness** (`checkReady`, `:336`): verifies tmux session alive (`:341`),
captures 40 pane lines + pane command (`:346–347`), and assesses them with
`assessNativeResumeProbe` (§3) through `assessManagedProbe` (`:366`); ready
only when probe `status === "resumed"` (`:350`). The resume-launch
verification loop `verifyResumeLaunch` (`:397`) retries up to **16 attempts**
(`:399`), failing loudly with `recovery: "retry_fresh"` on
`no_conversation_found` (`:406–411`) and with `recovery:
`--model <model>` argument (`:263`) and `--effort <effort>` (`:265`).
When `binding.permissionMode` is set, the command is built by `claudeManagedLaunch` instead (`:243–249`, `:309`).
- **Readiness** (`checkReady`, `:338`): verifies tmux session alive (`:343`),
captures 40 pane lines + pane command (`:348–349`), and assesses them with
`assessNativeResumeProbe` (§3) through `assessManagedProbe` (`:368`); ready
only when probe `status === "resumed"` (`:352`). The resume-launch
verification loop `verifyResumeLaunch` (`:402`) retries up to **16 attempts**
(`:404`), failing loudly with `recovery: "retry_fresh"` on
`no_conversation_found` (`:411–416`) and with `recovery:
"attention_required"` plus pane evidence on an `attention_required` probe
(`:421–427`) — the adapter never relaunches on its own.
(`:426–432`) — the adapter never relaunches on its own.
### CodexRuntimeAdapter (`codex-runtime-adapter.ts:61`)
- `readonly runtime = "codex"` (`:62`).
- **Projects** to `.agents/` targets: `guidance_merge` → `<cwd>/AGENTS.md`
(`:290`, `:549`); `skill_install` → `<cwd>/.agents/skills/<name>/` (`:240`,
`:296`); skills resolve under `.agents/skills/<id>` (`:628`).
- **Launches/resumes**: fresh = `codex<posture> -C <cwd> …` (`:413`), then
capture a fresh thread id (`:429`); resume is built by
`buildCodexResumeCore` (`:412`) as `codex<posture> resume
[<queueStateDirArg>] <token>` (`native-resume-probe.ts:76`); fork = `codex<posture> fork<queueStateDirArg>
<parentId>` (`:384`). `<posture>` is `codexPostureArg(...)`
(`:290`, `:551`); `skill_install` → `<cwd>/.agents/skills/<name>/` (`:240`,
`:296`); skills resolve under `.agents/skills/<id>` (`:630`).
- **Launches/resumes**: fresh = `codex<posture> -C <cwd> …` (`:415`), then
capture a fresh thread id (`:431`); resume is built by
`buildCodexResumeCore` (`:414`) as `codex<posture> resume
[<queueStateDirArg>] <token>` (`native-resume-probe.ts:81`); fork = `codex<posture> fork<queueStateDirArg>
<parentId>` (`:386`). `<posture>` is `codexPostureArg(...)`
(`yolo-mode.ts:59`): the `-p <profile>` argument, `-s workspace-write`, or
`-s danger-full-access`. Each command may also carry `--no-daemon` (`:364`)
and `-m <model>` (`:337`). With a thread id it returns `{ ok: true,
resumeToken: threadId, resumeType: "codex_id" }` (`:399`, `:426`, `:432`);
`-s danger-full-access`. Each command may also carry `--no-daemon` (`:366`)
and `-m <model>` (`:337`), plus `-c 'model_reasoning_effort="<effort>"'`
when effort is selected (`codex-runtime-adapter.ts:339`). With a thread id it
returns `{ ok: true,
resumeToken: threadId, resumeType: "codex_id" }` (`:401`, `:428`, `:434`);
a fresh launch whose thread id is not captured returns `{ ok: true }` without
a token (`:435`).
a token (`:437`).
- Refuses `resumeToken` + `forkSource` together with a clear error
(`:331–332`) — honors the mutual-exclusivity contract.
@@ -149,7 +151,7 @@ Hono, `git grep -l 'from "hono' -- packages/daemon/src/adapters | wc -l`).
servers, log tails, build watchers. A terminal node cannot fork:
`launchHarness` refuses a `forkSource` with a clear error (`:38–42`), as the
runtime-adapter docstring requires of adapters without fork
(`runtime-adapter.ts:117–118`).
(`runtime-adapter.ts:118–119`).
### Pi, Oh My Pi and stub adapters
@@ -163,9 +165,9 @@ Pi and stub launch an OpenRig runner inside the seat's tmux pane (header
comments `pi-runtime-adapter.ts:1–10`, `stub-runtime-adapter.ts:1–8`); their
projection, launch and readiness behaviour is not detailed here.
`createDaemon` constructs the adapters (`startup.ts:751`, `:752`, `:755`,
`:756`, `:761`) and creates the terminal adapter inline in the runtime adapter
map (`startup.ts:941`); see `daemon-core.md` §4 "Startup sequence".
`createDaemon` constructs the adapters (`startup.ts:755`, `:756`, `:759`,
`:760`, `:765`) and creates the terminal adapter inline in the runtime adapter
map (`startup.ts:945`); see `daemon-core.md` §4 "Startup sequence".
## 3. Resume honesty
@@ -175,43 +177,43 @@ assessment:
### `native-resume-probe.ts`
`assessNativeResumeProbe(input)` (`native-resume-probe.ts:79`) reads pane
`assessNativeResumeProbe(input)` (`native-resume-probe.ts:84`) reads pane
command + pane content and returns one of four honest statuses
(`NativeResumeProbeStatus`, `:7`):
- `resumed` — runtime-specific indicators confirm a resumed session.
- `failed` — terminal failure (e.g. Claude printed "No conversation found" →
code `no_conversation_found`, `:87–90`).
code `no_conversation_found`, `:92–95`).
- `inconclusive` — we don't know yet (e.g. Claude trust gate, code
`trust_gate`, `:101–104`).
`trust_gate`, `:106–109`).
- `attention_required` — alive and recoverable but **needs operator action**
(e.g. Claude resume-selection prompt, code `claude_resume_selection_prompt`,
`:94–97`). This is the proxy for "an operator must choose the conversation";
`:99–102`). This is the proxy for "an operator must choose the conversation";
it is *distinct* from `inconclusive` and `failed` (comment `:4–6`).
`buildNativeResumeCommand` (`:30`) builds the resume command per runtime:
claude → `claude --resume <token> [--name <name>]` (`:39`); codex →
`buildCodexResumeCore` (`:42`), i.e. `codex<posture> resume <token>`
(`:76`); other runtimes → `null` (`:44`).
`buildNativeResumeCommand` (`:32`) builds the resume command per runtime:
claude → `claude --resume <token> [--name <name>]` (`:41`); codex →
`buildCodexResumeCore` (`:44`), i.e. `codex<posture> resume <token>`
(`:81`); other runtimes → `null` (`:46`).
At the adapter level (Architecture Rule 15, §4), the adapter's
`verifyResumeLaunch` returns `ok:false` with a `retry_fresh` recovery hint and
never relaunches itself. Acting on that hint is the caller's choice:
`StartupOrchestrator` retries once fresh on `retry_fresh` unless the caller
passes `allowFreshFallback: false` (`startup-orchestrator.ts:259–267`), and the
passes `allowFreshFallback: false` (`startup-orchestrator.ts:260–268`), and the
restore orchestrator passes `false` when a pod-aware node requested resume
(`restore-orchestrator.ts:1397`).
(`restore-orchestrator.ts:1399`).
### `resume-metadata-refresher.ts`
`ResumeMetadataRefresher` (`resume-metadata-refresher.ts:49`). Post-launch
resume-token capture: `refresh(sessions, opts?)` (`:107`) skips `codex`
sessions that already have a `resumeToken` (`:123`, `:139`) and otherwise
captures the thread id (`:142–145`). For `claude-code` sessions with a token
`ResumeMetadataRefresher` (`resume-metadata-refresher.ts:52`). Post-launch
resume-token capture: `refresh(sessions, opts?)` (`:112`) skips `codex`
sessions that already have a `resumeToken` (`:128`, `:144`) and otherwise
captures the thread id (`:147–150`). For `claude-code` sessions with a token
it runs a `probeClaudeResume` returning `"resumable" | "not_resumable" |
"inconclusive"` (`:36`, `:186`) — a real launch of the resume command in a
throwaway probe tmux session (`:243–246`), not a metadata guess. In
`fillNullOnly` mode (`:116`) it skips that probe (`:171`).
"inconclusive"` (`:37`, `:194`) — a real launch of the resume command in a
throwaway probe tmux session (`:251–254`), not a metadata guess. In
`fillNullOnly` mode (`:121`) it skips that probe (`:177`).
### `codex-thread-id.ts`
@@ -234,21 +236,21 @@ module.
- **Rule 1** — §2: **0** files in `adapters/` import Hono.
- **Rule 5** — restore picks a node's adapter by that node's own `runtime`
(`restore-orchestrator.ts:1252`).
(`restore-orchestrator.ts:1253`).
- **Rule 13** — the readiness loop is `StartupOrchestrator.waitForReady`
(`startup-orchestrator.ts:473`; 1 s doubling to a 16 s cap, `:479–480`,
`:498`; default timeout 30 s, `:476`), which calls each adapter's
(`startup-orchestrator.ts:488`; 1 s doubling to a 16 s cap, `:494–495`,
`:510`; default timeout 30 s, `:491`), which calls each adapter's
`checkReady` (§2).
- **Rule 14** — the probe's `attention_required` (§3) reaches the restore
result through `HarnessLaunchResult.recovery` and `evidence` (§1), which
become `RestoreNodeResult.status` (`types.ts:457`, `:475`) and
`attentionEvidence` (`types.ts:478`). A startup's `continuityOutcome` is a
separate field that also allows `forked` (`startup-orchestrator.ts:68`).
become `RestoreNodeResult.status` (`types.ts:458`, `:476`) and
`attentionEvidence` (`types.ts:479`). A startup's `continuityOutcome` is a
separate field that also allows `forked` (`startup-orchestrator.ts:69`).
- **Rule 15** — adapters return `ok:false` and never relaunch (§2, §3);
restore passes `allowFreshFallback: false` for pod-aware resume
(`restore-orchestrator.ts:1397`), and a resume that concludes failed rolls
back to zero sessions as `awaiting-decision` (`restore-orchestrator.ts:1229`,
`:1415`).
(`restore-orchestrator.ts:1399`), and a resume that concludes failed rolls
back to zero sessions as `awaiting-decision` (`restore-orchestrator.ts:1230`,
`:1417`).
## See also
@@ -11,8 +11,8 @@ applies-when: |
resolve and preserve identity.
siblings: [daemon-core.md, adapters-and-runtimes.md, lifecycle-snapshot-restore.md, packaging-bootstrap-bundles.md]
prerequisite-reads: [../README.md, daemon-core.md]
last-verified-against-source: 264fade9
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Agent/Rig Spec, Resolution, Startup, Identity
@@ -22,7 +22,7 @@ and identity-addressable topology. The spec-and-startup contract: parse →
resolve → project → deliver pre-launch files → persist replay context → launch
→ wait → deliver post-launch files.
> Verified against source at main `264fade9`. `domain/…` and `routes/…` paths
> Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. `domain/…` and `routes/…` paths
> are under `packages/daemon/src/`; a bare file name such as
> `rigspec-schema.ts:115` is in `packages/daemon/src/domain/`. Each count sits
> beside the command that produces it; run the command from the repository
@@ -30,10 +30,10 @@ resolve → project → deliver pre-launch files → persist replay context →
## 1. Canonical spec and topology types
- **AgentSpec** (`types.ts:935`) — parsed from `agent.yaml`
- **AgentSpec** (`types.ts:936`) — parsed from `agent.yaml`
(`agent-resolver.ts:150`). Owns imports, defaults, startup, resources, and
profiles. Canonical parse/normalize/validate is `domain/agent-manifest.ts`.
- **RigSpec** (`types.ts:1163`) — canonical pod-aware rig topology. Uses
- **RigSpec** (`types.ts:1167`) — canonical pod-aware rig topology. Uses
`version: "0.2"` and `pods[]`; owns cross-pod `edges[]`, rig-level startup
overlays, and `cultureFile`, plus the optional `summary`, `permissionPolicy`,
`managedBlocks`, `docs`, `services` and `workspace` fields.
@@ -42,16 +42,16 @@ resolve → project → deliver pre-launch files → persist replay context →
code constant: `RigSpecSchema.validate` requires only a non-empty string
(`rigspec-schema.ts:115`) and `RigSpecSchema.normalize` carries the authored
value through (`rigspec-schema.ts:243`). `"0.2"` is the canonical authored
value, and the pod-aware exporter writes it (`rigspec-exporter.ts:221`).
value, and the pod-aware exporter writes it (`rigspec-exporter.ts:223`).
- **RigServicesSpec** (`types.ts:1210`) — optional `services` block on a
- **RigServicesSpec** (`types.ts:1214`) — optional `services` block on a
pod-aware RigSpec. Shipped kind is Compose-backed env management
(`kind: "compose"`) with `composeFile`, `projectName?`, `profiles?`,
`downPolicy?`, `waitFor?`, `surfaces?`, `checkpoints?`.
- **RigSpecPod** (`types.ts:1119`) — pod-local bounded context with
- **RigSpecPod** (`types.ts:1123`) — pod-local bounded context with
`members[]`, pod-local `edges[]`, pod startup, optional continuity policy.
- **RigSpecPodMember** (`types.ts:1064`) — member-level runtime/startup
surface: `agentRef`, `profile`, `runtime`, `model?`, `cwd`, `restorePolicy?`,
- **RigSpecPodMember** (`types.ts:1067`) — member-level runtime/startup
surface: `agentRef`, `profile`, `runtime`, `model?`, `effort?`, `cwd`, `restorePolicy?`,
member startup overlays (`startup?`), plus `label?`, `codexConfigProfile?`,
`role?`, `permissionPolicy?`, `compactionStrategy?`, `mechanic?`,
`sessionSource?` and `starterRef?`.
@@ -66,23 +66,23 @@ Restore/snapshot types are detailed in `lifecycle-snapshot-restore.md`; the
spec/projection types live here.
- **ResolvedNodeConfig** (`profile-resolver.ts:31`) — output of profile
resolution. Carries effective runtime/model/cwd, narrowed restore policy,
resolution. Carries effective runtime/model/effort/cwd, narrowed restore policy,
selected resources, layered startup block, resolved spec identity.
- **ProjectionPlan** (`projection-planner.ts:41`) — runtime projection plan for
a node: runtime, cwd, projection entries, startup block, diagnostics,
conflict/no-op classifications.
- **RuntimeAdapter** (`runtime-adapter.ts:131`) — the five-method contract
- **RuntimeAdapter** (`runtime-adapter.ts:132`) — the five-method contract
(adapter detail in `adapters-and-runtimes.md`): `listInstalled(binding)`
(`runtime-adapter.ts:141`), `project(plan, binding)` (`:144`),
`deliverStartup(files, binding)` (`:147`), `launchHarness(binding, opts)`
(`:157`), `checkReady(binding)` (`:163`). The interface also declares one
optional method, `skillTargetPath?(...)` (`:138`). Required methods: **5**
(`runtime-adapter.ts:142`), `project(plan, binding)` (`:145`),
`deliverStartup(files, binding)` (`:148`), `launchHarness(binding, opts)`
(`:158`), `checkReady(binding)` (`:164`). The interface also declares one
optional method, `skillTargetPath?(...)` (`:139`). Required methods: **5**
(`sed -n '/^export interface RuntimeAdapter /,/^}/p' packages/daemon/src/domain/runtime-adapter.ts | grep -c -E '^ [a-zA-Z]+\('`).
- **HarnessLaunchResult** (`runtime-adapter.ts:85`) — returned by
- **HarnessLaunchResult** (`runtime-adapter.ts:86`) — returned by
`launchHarness`: either `{ ok: true, resumeToken?, resumeType?,
appliedLaunch? }` or `{ ok: false, error, recovery?, evidence? }`, where
`recovery` is `"retry_fresh"` or `"attention_required"` (`:83`).
- **StartupOrchestrator** (`startup-orchestrator.ts:106`) — drives the full
`recovery` is `"retry_fresh"` or `"attention_required"` (`:84`).
- **StartupOrchestrator** (`startup-orchestrator.ts:107`) — drives the full
startup sequence (§4 below).
## 3. Parsing, validation, resolution pipeline
@@ -114,30 +114,30 @@ them imports Hono: **0**
## 4. Startup orchestration (the spec-startup contract)
`StartupOrchestrator.startNode` (`startup-orchestrator.ts:128`) drives, in
source order: mark pending (`:155`) → project resources (`:166`) → deliver
pre-launch files (`:206`) → persist startup context (`:221`) → launch harness,
recording any resume token (`:236`) → wait for ready (`:321`) → deliver
interactive files (`:402`) → execute `after_files` then `after_ready` actions
(`:425`, `:431`) → mark ready (`:451`). The class doc comment
(`startup-orchestrator.ts:89`–`100`) still lists persistence as step 9, after
`StartupOrchestrator.startNode` (`startup-orchestrator.ts:129`) drives, in
source order: mark pending (`:156`) → project resources (`:167`) → deliver
pre-launch files (`:207`) → persist startup context (`:222`) → launch harness,
recording any resume token (`:237`) → wait for ready (`:322`) → deliver
interactive files (`:403`) → execute `after_files` then `after_ready` actions
(`:426`, `:432`) → mark ready (`:466`). The class doc comment
(`startup-orchestrator.ts:90`–`101`) still lists persistence as step 9, after
the actions; the code persists the startup context before the harness launch.
**Pre-launch vs interactive delivery split** — the load-bearing seam:
- Pre-launch (filesystem, before harness boot): `guidance_merge`,
`skill_install` (`startup-orchestrator.ts:92` — "Deliver pre-launch files
(guidance_merge, skill_install → filesystem)"; delivered at `:206`).
`skill_install` (`startup-orchestrator.ts:93` — "Deliver pre-launch files
(guidance_merge, skill_install → filesystem)"; delivered at `:207`).
- Post-launch (TUI, after harness is ready): `send_text`
(`startup-orchestrator.ts:96`; partition by concrete hint at `:181`,
`send_text` files held for post-launch at `:199`, delivered after readiness
at `:402`).
(`startup-orchestrator.ts:97`; partition by concrete hint at `:182`,
`send_text` files held for post-launch at `:200`, delivered after readiness
at `:403`).
The orchestrator persists replay context for future restores (consumed by
`lifecycle-snapshot-restore.md`): the projection entries, resolved startup
files and startup actions go to `node_startup_context`
(`startup-orchestrator.ts:224`), and a resume token returned by the launch is
written to the session row (`updateResumeToken`, `:253`).
(`startup-orchestrator.ts:225`), and a resume token returned by the launch is
written to the session row (`updateResumeToken`, `:254`).
**Startup layering is additive and ordered** (`resolveStartup`,
`startup-resolver.ts:15`–`22`): (1) agent base, (2) profile, (3) rig culture
@@ -152,38 +152,38 @@ actions (`:62`); action types are `slash_command`, `send_text` and
`startup_proof` (`:7`); non-idempotent actions must not apply on restore
(`:97`). Retrying a failed startup is handled as a restore, which is why the
orchestrator skips non-idempotent actions on restore
(`startup-orchestrator.ts:544`, "retry-as-restore safety"). The exception is
`PodRigInstantiator.retryFirstStart` (`rigspec-instantiator.ts:1145`), which
(`startup-orchestrator.ts:556`, "retry-as-restore safety"). The exception is
`PodRigInstantiator.retryFirstStart` (`rigspec-instantiator.ts:1146`), which
re-runs a first start that failed at projection, before any harness launch.
**Remote import constraints**: `agent_ref` and AgentSpec imports accept
`local:<relative path>` and `path:<absolute path>` only
(`rigspec-schema.ts:41`, `agent-manifest.ts:40`); a terminal member uses the
`builtin:terminal` sentinel (`rigspec-schema.ts:514`). Remote `agent_ref`
`builtin:terminal` sentinel (`rigspec-schema.ts:519`). Remote `agent_ref`
sources remain unsupported: RigSpec validation rejects them
(`rigspec-schema.ts:545`), `rigPreflight` runs that validation
(`rigspec-preflight.ts:248`), and the resolver refuses a remote import again
(`rigspec-schema.ts:550`), `rigPreflight` runs that validation
(`rigspec-preflight.ts:249`), and the resolver refuses a remote import again
(`agent-resolver.ts:203`).
## 5. Instantiation, preflight, export
- `runtime-adapter.ts` — adapter contract + bridge types.
- `rigspec-preflight.ts` — dual-stack legacy preflight (`RigSpecPreflight`,
`rigspec-preflight.ts:24`) plus rebooted `rigPreflight(...)` (`:240`).
`rigspec-preflight.ts:25`) plus rebooted `rigPreflight(...)` (`:241`).
- `rigspec-instantiator.ts` — dual-stack `RigInstantiator`
(`rigspec-instantiator.ts:33`) plus `PodRigInstantiator` (`:473`).
(`rigspec-instantiator.ts:33`) plus `PodRigInstantiator` (`:474`).
- `rigspec-exporter.ts` — dual-format live rig export to YAML/JSON.
- `pod-repository.ts` — pod CRUD plus live continuity-state CRUD.
`routes/rigspec.ts` is the dual-format seam; a parsed spec with a `pods` array
takes the pod-aware path (`routes/rigspec.ts:81`): validate
takes the pod-aware path (`routes/rigspec.ts:82`): validate
(pod-aware → `RigSpecSchema.validate`; legacy → `LegacyRigSpecSchema.validate`),
preflight (`rigPreflight({ rigSpecYaml, rigRoot, … })` vs
`RigSpecPreflight.check(spec)`), import
(`podInstantiator.instantiate(yaml, rigRoot, …)` vs
`RigInstantiator.instantiate(spec)`), export (pod-aware exports canonical
`version: "0.2"` RigSpec; legacy exports flat-node `schemaVersion: 1`,
`rigspec-exporter.ts:104`).
`rigspec-exporter.ts:105`).
## 6. Identity: whoami, materialize, bind, adopt
@@ -204,7 +204,7 @@ session-name fallback.
The tmux metadata keys use the `@rigged_` prefix:
`ClaimService.setRiggedMetadata` writes `@rigged_node_id` /
`@rigged_session_name` / `@rigged_rig_id` / `@rigged_rig_name` /
`@rigged_logical_id` (`claim-service.ts:176`–`180`), called from `bind`,
`@rigged_logical_id` (`claim-service.ts:180`–`184`), called from `bind`,
`reconcileSession` and `createAndBindToPod`;
`RigLifecycleService.unclaimSession` clears the same five keys
(`rig-lifecycle-service.ts:209`–`213`); and `rig whoami` reads
@@ -213,8 +213,8 @@ session-name fallback.
different prefix, `rig_*` (`packages/cli/src/mcp-server.ts`).
**Materialize / bind / adopt**: `POST /api/rigs/import/materialize`
(`routes/rigspec.ts:172`) creates a pod-aware topology without launching
sessions and refuses a legacy spec (`routes/rigspec.ts:185`);
(`routes/rigspec.ts:173`) creates a pod-aware topology without launching
sessions and refuses a legacy spec (`routes/rigspec.ts:186`);
`POST /api/discovery/:id/bind` (`routes/discovery.ts:77`) attaches a
discovered live session to an existing logical node (`logicalId`), or creates
a member in a pod (`podNamespace` + `memberName`) and binds it;
@@ -225,7 +225,7 @@ immediately). On the CLI, `rig bind` calls the bind route
topology and then calls the bind route for each session
(`packages/cli/src/commands/adopt.ts:163`, `:212`). Authored pod namespace is
preserved through adoption so logical ids stay `${podNamespace}.${memberName}`
(`claim-service.ts:657`). Adopted-session parity is tmux-metadata parity, not
(`claim-service.ts:661`). Adopted-session parity is tmux-metadata parity, not
fake env-var parity.
## See also
@@ -11,8 +11,8 @@ applies-when: |
compatibility limits that still describe the shipped system.
siblings: [daemon-core.md, coordination-primitive.md]
prerequisite-reads: [../README.md, daemon-core.md]
last-verified-against-source: 264fade9
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Architecture Invariants, Event System, Compatibility Notes
@@ -21,7 +21,7 @@ This module collects the cross-cutting invariants that do not belong to any
single subsystem: the architecture rules the codebase holds itself to, the
event-system shape, and the intentional compatibility limits.
> Verified against source at main `264fade9`. Each count below sits beside the
> Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. Each count below sits beside the
> command that produces it; run the command from the repository root to refresh
> it.
@@ -56,24 +56,24 @@ system-level invariants.
10. Restore replay uses classification-free projection intent, not stale
startup-time `no_op` / conflict classifications.
11. Startup status is explicit session state: `pending`, `ready`,
`attention_required`, `failed` (`types.ts:99`).
`attention_required`, `failed` (`types.ts:100`).
12. Session recency depends on monotonic ULIDs: `session-registry.ts` uses
`monotonicFactory()`. Restore does not pick the newest session by max
ULID; it resolves the active occupant recorded in the snapshot
(`resolveActiveSnapshotSession`, `active-occupant.ts:74`, called at
`restore-orchestrator.ts:1170`).
`restore-orchestrator.ts:1171`).
13. Readiness checking is a retry loop with exponential backoff and
configurable timeout, using adapter-specific probes (Claude TUI
indicator, Codex ready message, terminal immediate).
14. Restore outcome states are a fixed set (`RestoreNodeResult.status`,
`types.ts:475`): `resumed`, `rebuilt`, `fresh-primed`,
`types.ts:476`): `resumed`, `rebuilt`, `fresh-primed`,
`awaiting-decision`, `attention_required`, `failed`, plus
`operator_recovered` (set only by later reconciliation) and `fresh`
(retained only for the legacy continuity-restoring skip path). `rebuilt` =
new process assembled from artifacts.
15. Restore honesty: a failed resume stops loudly as `awaiting-decision`, with
the blank session rolled back and no session running
(`restore-orchestrator.ts:1229`). No automatic fresh fallback. Fresh launch
(`restore-orchestrator.ts:1230`). No automatic fresh fallback. Fresh launch
is explicit follow-up only (`rig up --fresh <logicalId>`,
`packages/cli/src/commands/up.ts:85`).
16. Post-command handoff required on `up`, `down`, `restore`,
@@ -115,15 +115,15 @@ system-level invariants.
The reboot supports `local:...` and `path:/abs/...` agent refs. Remote
`agent_ref` sources remain unsupported and fail in preflight (schema
validation, `rigspec-schema.ts:545`; restated in compat note 1).
validation, `rigspec-schema.ts:550`; restated in compat note 1).
## 2. Event system
The daemon's event surface is the single `RigEvent` discriminated union.
`RigEvent` is declared at `packages/daemon/src/domain/types.ts:105`
(`export type RigEvent =`) and runs through `types.ts:313`. It has **99 union
members** declaring **100** `type` literals: one member (`types.ts:106`) carries
`RigEvent` is declared at `packages/daemon/src/domain/types.ts:106`
(`export type RigEvent =`) and runs through `types.ts:314`. It has **99 union
members** declaring **100** `type` literals: one member (`types.ts:107`) carries
both `proof.judged` and `proof.sources_changed`.
- Members:
@@ -136,47 +136,47 @@ both `proof.judged` and `proof.sources_changed`.
`seat.model_divergence` is built in `startup.ts`). **5** are declared but never
constructed: `session.status_changed`, `continuity.sync`, `continuity.degraded`,
`qitem.closure_overdue` and `mission_control.view_refreshed` (the last two are
still named in SSE filters, `routes/queue.ts:967` and
`routes/mission-control.ts:289`). List them with:
still named in SSE filters, `routes/queue.ts:968` and
`routes/mission-control.ts:292`). List them with:
`for t in $(sed -n '/^export type RigEvent =/,/^export type PersistedEvent/p' packages/daemon/src/domain/types.ts | grep -oE '"[a-z_]+(\.[a-z_]+)+"' | tr -d '"'); do git grep -q -F "type: \"$t\"" -- packages/daemon/src ':!packages/daemon/src/domain/types.ts' || echo "$t"; done`
### Per-prefix event families
Each count below is the number of `type` literals per prefix in the union body
(`types.ts:105`–`313`):
(`types.ts:106`–`314`):
`sed -n '/^export type RigEvent =/,/^export type PersistedEvent/p' packages/daemon/src/domain/types.ts | grep -oE '"[a-z_]+(\.[a-z_]+)+"' | tr -d '"' | cut -d. -f1 | sort | uniq -c | sort -rn`.
| Prefix | Types | Sample / role |
|---|---|---|
| `node.*` | 15 | `node.added` (`types.ts:123`) … `node.startup_proof_rejected` (`:226`) — lifecycle/startup |
| `node.*` | 15 | `node.added` (`types.ts:124`) … `node.startup_proof_rejected` (`:227`) — lifecycle/startup |
| `workflow.*` | 8 | workflow runtime (detail in `workflow-runtime.md`) |
| `session.*` | 8 | session discovery / status / detach / vanish / stop / clean / resume-token audit |
| `rig.*` | 7 | `rig.created` / `rig.deleted` / `rig.imported` / `rig.stopped` / `rig.archived` / `rig.unarchived` / `rig.expanded` (`:238`) |
| `watchdog.*` | 5 | `watchdog.evaluation_fired` (`:277`) … `watchdog.job_stopped` (`:281`) |
| `seat.*` | 5 | `seat.model_divergence` (`:112`) … `seat.handover_completed` (`:197`) — model divergence, fresh launch, attention clear, handover |
| `queue.*` | 5 | queue lifecycle (`:246`–`:249`, `:259`; detail in `coordination-primitive.md`) |
| `rig.*` | 7 | `rig.created` / `rig.deleted` / `rig.imported` / `rig.stopped` / `rig.archived` / `rig.unarchived` / `rig.expanded` (`:239`) |
| `watchdog.*` | 5 | `watchdog.evaluation_fired` (`:278`) … `watchdog.job_stopped` (`:282`) |
| `seat.*` | 5 | `seat.model_divergence` (`:113`) … `seat.handover_completed` (`:198`) — model divergence, fresh launch, attention clear, handover |
| `queue.*` | 5 | queue lifecycle (`:247`–`:250`, `:260`; detail in `coordination-primitive.md`) |
| `package.*` | 5 | legacy package/install engine events |
| `mission_control.*` | 5 | audit/notification (`:307`–`:313`; detail in `mission-control.md`) |
| `mission_control.*` | 5 | audit/notification (`:308`–`:314`; detail in `mission-control.md`) |
| `bootstrap.*` | 5 | legacy bootstrap-run events |
| `restore.*` | 4 | restore start/complete/subset-complete/reconcile (detail in `lifecycle-snapshot-restore.md`) |
| `classifier.*` | 4 | classifier-lease lifecycle |
| `qitem.*` | 2 | `qitem.fallback_routed` (`:250`), `qitem.closure_overdue` (`:251`) |
| `proof.*` | 2 | `proof.judged`, `proof.sources_changed` — one union member (`:106`) |
| `pod.*` | 2 | `pod.created` (`:212`), `pod.deleted` (`:213`) |
| `inbox.*` | 2 | `inbox.absorbed` (`:252`), `inbox.denied` (`:253`) |
| `continuity.*` | 2 | `continuity.sync` (`:227`), `continuity.degraded` (`:228`) |
| `agent.*` | 2 | `agent.activity` (`:148`), `agent.session_identity` (`:155`) |
| singletons | 12 | one type each: `workflow_spec.*`, `view.*`, `transport.*` (`:152`), `topology.*` (`:135`), `stream.*` (`:242`), `snapshot.*`, `project.*`, `kernel.*`, `event.*` (`:107`), `chat.*` (`:236`), `bundle.*`, `binding.*` |
| `qitem.*` | 2 | `qitem.fallback_routed` (`:251`), `qitem.closure_overdue` (`:252`) |
| `proof.*` | 2 | `proof.judged`, `proof.sources_changed` — one union member (`:107`) |
| `pod.*` | 2 | `pod.created` (`:213`), `pod.deleted` (`:214`) |
| `inbox.*` | 2 | `inbox.absorbed` (`:253`), `inbox.denied` (`:254`) |
| `continuity.*` | 2 | `continuity.sync` (`:228`), `continuity.degraded` (`:229`) |
| `agent.*` | 2 | `agent.activity` (`:149`), `agent.session_identity` (`:156`) |
| singletons | 12 | one type each: `workflow_spec.*`, `view.*`, `transport.*` (`:153`), `topology.*` (`:136`), `stream.*` (`:243`), `snapshot.*`, `project.*`, `kernel.*`, `event.*` (`:108`), `chat.*` (`:237`), `bundle.*`, `binding.*` |
Family counts sum to 100 type literals (18 multi-type families totalling 88 +
12 singletons).
### Emission and delivery
Events are emitted via `eventBus.emit({ type: ... })` (`event-bus.ts:56`) or,
Events are emitted via `eventBus.emit({ type: ... })` (`event-bus.ts:57`) or,
inside a caller-managed transaction, `eventBus.persistWithinTransaction(...)`
(`event-bus.ts:69`) with subscribers notified after commit — across domain
(`event-bus.ts:70`) with subscribers notified after commit — across domain
services (`stream-store.ts`, `workflow-runtime.ts`, `restore-orchestrator.ts`,
`node-launcher.ts`, etc.) and route handlers. The event log is append-only and
SQLite-backed.
@@ -190,7 +190,7 @@ SSE delivery surfaces include the following. The daemon has **11**
`app.route("/api/events", eventsRoute)`).
- `GET /api/stream/watch` — new stream items (`routes/stream.ts:194`).
- `GET /api/queue/watch` — queue/inbox coordination events
(`routes/queue.ts:985`).
(`routes/queue.ts:986`).
- The chat SSE stream `GET /api/rigs/:rigId/chat/watch` delivers
`chat.message` for one rig (`routes/chat.ts:68`, mounted at `server.ts:783`;
rig-scoped; see compat note 6).
@@ -7,13 +7,13 @@ domains: [engineering-advisor, operating-advisor]
applies-when: |
Tracing allowlisted file access, conflict-checked writes, progress indexing,
steering composition, or the daemon health-summary endpoints.
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Content Surfaces — Files, Progress, Steering, and Health
Source snapshot: `712a61fbcc5ffe5a7c786fea12132567083ab507`. This describes the source at that commit;
Source snapshot: `254122872cf477511514979a4300b695d77cd1f7`. This describes the source at that commit;
it does not establish the version or behavior of a running daemon.
The daemon exposes filesystem content through the files, progress, and
@@ -10,8 +10,8 @@ applies-when: |
transactional handoff guarantee, or where queue closure is enforced.
siblings: [workflow-runtime.md, mission-control.md, daemon-core.md]
prerequisite-reads: [../README.md, daemon-core.md]
last-verified-against-source: e6a391b6
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Coordination Primitive — Stream/Queue/Inbox/Outbox
@@ -21,10 +21,10 @@ daemon exposes through `/api/stream` and `/api/queue`. It replaces the POC
filesystem `rigx queue` / `rigx stream` path for daemon-backed work; the POC
filesystem path remains untouched, and the daemon-backed `rig queue` /
`rig stream` commands operate only through the daemon HTTP API, so they write
only to SQLite (`packages/cli/src/commands/queue.ts:18`,
only to SQLite (`packages/cli/src/commands/queue.ts:19`,
`packages/cli/src/commands/stream.ts:11`).
> Verified against source at main `e6a391b6`. Each count below sits beside the
> Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. Each count below sits beside the
> command that produces it; run the command from the repository root to refresh
> it.
@@ -67,9 +67,9 @@ Five host-scoped tables back the primitive, one per migration `023`–`027` in
`pending | sending | delivered | failed | indeterminate | retained |
retired`.
These five are migrations `023`–`027` of the daemon's **91**
These five are migrations `023`–`027` of the daemon's **92**
(`git ls-files packages/daemon/src/db/migrations | wc -l`), applied in the
order of `ALL_MIGRATIONS` (`packages/daemon/src/db/all-migrations.ts:100`).
order of `ALL_MIGRATIONS` (`packages/daemon/src/db/all-migrations.ts:101`).
Later migrations add columns to these tables; `daemon-core.md` covers the
migration set.
@@ -85,26 +85,26 @@ Routes import these; services are Hono-free:
and inclusive time-window filters, soft archive. `direction=latest` applies
every filter before taking the newest bounded page, then returns that page
chronologically.
- **`queue-repository.ts`** — L3 queue: create (`:1383`), claim/unclaim
(`:2142`, `:2224`), update (general state mutator with hot-potato
strict-rejection on `done`, `:2287`), transactional handoff (close source as
`handed-off` plus create new owned qitem in a single transaction, `:1611`;
`handoffAndComplete`, `:1788`, closes the source as `done` instead),
pod-fallback rerouting (`routeToFallback`, `:3536`), overdue lookup
(`findOverdue`, `:3315`), nudge-result tracking (`recordNudgeAttempt`,
`:3521`). Nothing in the daemon writes the `last_heartbeat` column
- **`queue-repository.ts`** — L3 queue: create (`:1391`), claim/unclaim
(`:2165`, `:2247`), update (general state mutator with hot-potato
strict-rejection on `done`, `:2310`), transactional handoff (close source as
`handed-off` plus create new owned qitem in a single transaction, `:1634`;
`handoffAndComplete`, `:1811`, closes the source as `done` instead),
pod-fallback rerouting (`routeToFallback`, `:3580`), overdue lookup
(`findOverdue`, `:3333`), nudge-result tracking (`recordNudgeAttempt`,
`:3565`). Nothing in the daemon writes the `last_heartbeat` column
(`queue-pickup.ts:16`). Cross-rig validation hook exposed as `validateRig`
constructor option (`:703`).
constructor option (`:711`).
- **`queue-transition-log.ts`** — append-only state-transition log; used by
`queue-repository.ts`, exposed as the read-only property
`QueueRepository.transitionLog` (`queue-repository.ts:652`).
`QueueRepository.transitionLog` (`queue-repository.ts:660`).
- **`hot-potato-enforcer.ts`** — pure validator for the load-bearing API
contract (see §3).
- **`inbox-handler.ts`** — mailbox handler: drop (idempotent on `inbox_id`,
`:99`), absorb (promotes a pending entry to a `queue_item`, idempotent,
`:154`), deny (records reason, `:214`). The handler has no auth hook: the
inbox routes take the sender or receiver from the `X-OpenRig-Session` header
through `requireSenderIdentity` (`routes/queue.ts:1025`), never from the body
through `requireSenderIdentity` (`routes/queue.ts:1026`), never from the body
(`inbox-handler.ts:71`).
- **`outbox-handler.ts`** — sender-side outbox: idempotent record (`:130`),
delivery-state marks (`markDelivered` `:239`, `markFailed` `:256`,
@@ -115,11 +115,11 @@ Routes import these; services are Hono-free:
The standing detector in `queue-stuck-sweep.ts` creates findings through the
queue repository, with `evidenceRef: rig queue show <source-qitem-id>`
(`queue-stuck-sweep.ts:519`) pointing to the underlying durable work row. This
(`queue-stuck-sweep.ts:523`) pointing to the underlying durable work row. This
satisfies the existing human-route evidence contract without changing
destination resolution: an admitted finding can still be unroutable. Repeated
detections refresh the existing finding; when the source condition resolves,
the sweep closes its own finding (`:545`).
the sweep closes its own finding (`:549`).
## 3. The hot-potato closure contract (where queue closure is enforced)
@@ -145,17 +145,17 @@ canceled, no-follow-on, escalation, superseded}`. The reasons
- `superseded` — the row was replaced by cancel-and-replace
(`closure_target` = the successor qitem). The update path records it on
`state=canceled` and refuses it there without a `closure_target`
(`queue-repository.ts:2543`).
(`queue-repository.ts:2566`).
Tier→SLA mapping for `closure_required_at` also lives in
`hot-potato-enforcer.ts` (`TIER_SLA_SECONDS`, `:114`). This validator is
invoked by `QueueRepository.update()` (and `updateWithinTransaction()`), both
through the call at `queue-repository.ts:2461`, so closure is enforced at the
through the call at `queue-repository.ts:2484`, so closure is enforced at the
daemon transaction boundary — the workflow runtime *projects* on closure but
does not otherwise gate it (see `workflow-runtime.md`). The one
workflow-aware check is in the queue: `update()` refuses a terminal close of a
live workflow frontier packet from a non-workflow verb
(`workflow_frontier_packet`, `queue-repository.ts:2487`), through a predicate
(`workflow_frontier_packet`, `queue-repository.ts:2510`), through a predicate
startup injects.
## 3b. Cross-host queue routing
@@ -166,10 +166,10 @@ Three queue write routes are host-aware — `POST /create`,
a write body may carry an out-of-band `hostId` envelope. The destination
session stays `member@rig`; the 3-part `agent@rig@host` form is CLI input
sugar that `resolveQueueHostDestination`
(`packages/cli/src/commands/queue.ts:363`) splits into the destination and
(`packages/cli/src/commands/queue.ts:364`) splits into the destination and
`hostId` before the request leaves the CLI. The mechanism follows the
forward-then-strip shape of mission-control's remote action
(`routes/mission-control.ts:350`): one shared route-layer helper
(`routes/mission-control.ts:353`): one shared route-layer helper
(`forwardQueueWrite`, `routes/queue.ts:195`) resolves the host registry
daemon-side (bearers never reach the caller), rejects ssh-declared hosts
(`unsupported-transport` — the daemon→daemon path is http-only), and forwards
@@ -188,7 +188,7 @@ message-passing closure (never 2PC).**
tmux (the forwarded body includes the `nudge` flag) — the sending daemon
never reaches across a host boundary. Before forwarding, the forwarding
daemon stamps its own host id, when it has one, onto the source session
(`stampSelfHostSuffix`, `queue-repository.ts:534`; called at
(`stampSelfHostSuffix`, `queue-repository.ts:542`; called at
`routes/queue.ts:357` and `:463`), so the target row records the sender as
`member@rig@<forwarding host>`.
- **Idempotency.** On create, the forwarding daemon mints the `qitemId` before
@@ -199,18 +199,18 @@ message-passing closure (never 2PC).**
conflict the origin returns the stored row when destination and source
match (idempotent absorb) and a structured `qitem_id_reuse` error (409) when
they differ (`QueueRepository.create()` catch path,
`queue-repository.ts:1417`, with `isQitemPrimaryKeyConflict`, `:447`).
`queue-repository.ts:1440`, with `isQitemPrimaryKeyConflict`, `:455`).
- **Cross-host handoff choreography.** The local atomic close+create cannot
span two DBs, so the route-layer choreography (`crossHostHandoff`,
`routes/queue.ts:297`) runs: successor-create on the target host FIRST (via
the one forward helper, `:376`), local source-close SECOND
(`QueueRepository.closeCrossHostHandoffSource`, `queue-repository.ts:1977`)
(`QueueRepository.closeCrossHostHandoffSource`, `queue-repository.ts:2000`)
— never the reverse. A crash between the two leaves a live duplicate that
the idempotent re-drive converges; the reverse order would leave a closed
source pointing at a successor that does not exist (a dropped potato — the
one forbidden outcome). The successor id is DERIVED, not minted:
`deriveCrossHostSuccessorId(source, destination, host)`
(`queue-repository.ts:475`) → `qitem-xh-<sha256[:16]>` — a pure stateless
(`queue-repository.ts:483`) → `qitem-xh-<sha256[:16]>` — a pure stateless
function, so a re-drive re-derives the same id across daemon restarts and
absorbs on the target PK. *(Residual, inherent to at-least-once delivery
without 2PC: a re-drive naming a DIFFERENT destination derives a different
@@ -223,11 +223,11 @@ message-passing closure (never 2PC).**
source already closed with the older `member@rig@<host>` target keeps it,
and a re-drive matches against it (`:331`). `closure_target` is OPAQUE
audit metadata, never parsed for routing; the standing stuck sweep reads it
only to check successor custody (`queue-stuck-sweep.ts:464`). The
only to check successor custody (`queue-stuck-sweep.ts:468`). The
host-qualified key appears only in the `closure_target` column on
`queue_items` and its verbatim mirror on `queue_transitions`: the minted
cross-host close note names the 2-part `toSession` only
(`queue-repository.ts:2033`), and `handed_off_to` and the
(`queue-repository.ts:2056`), and `handed_off_to` and the
`queue.handed_off` event stay 2-part.
Re-drive semantics: already-terminal + MATCHING `closure_target` = absorb;
MISMATCH = structured `cross_host_close_conflict` (409), checked in the route
@@ -245,17 +245,17 @@ message-passing closure (never 2PC).**
## 4. Coordination events
The `RigEvent` union (`packages/daemon/src/domain/types.ts:105`) has **99**
The `RigEvent` union (`packages/daemon/src/domain/types.ts:106`) has **99**
members
(`sed -n '/^export type RigEvent =/,/^export type PersistedEvent/p' packages/daemon/src/domain/types.ts | grep -c 'type: "'`);
this module covers only the coordination families below.
Coordination events emitted by these services: `stream.emitted`
(`StreamStore.emit`, declared at `types.ts:242`); `queue.created` /
(`StreamStore.emit`, declared at `types.ts:243`); `queue.created` /
`queue.handed_off` / `queue.claimed` / `queue.unclaimed` / `queue.updated` /
`qitem.fallback_routed` (QueueRepository); `inbox.absorbed` (`types.ts:252`) /
`qitem.fallback_routed` (QueueRepository); `inbox.absorbed` (`types.ts:253`) /
`inbox.denied` (InboxHandler). `qitem.closure_overdue` is declared
(`types.ts:251`) and the queue watch filter lets it through, but nothing emits
(`types.ts:252`) and the queue watch filter lets it through, but nothing emits
it: `types.ts` is the only file that contains its `type:` literal (**1** file:
`git grep -l -F 'type: "qitem.closure_overdue"' -- packages/daemon/src | wc -l`).
The `stream|queue|inbox|qitem` families declare **10** event types (`stream` 1
@@ -266,9 +266,9 @@ Two SSE surfaces stream coordination events (**2**:
`cat packages/daemon/src/routes/stream.ts packages/daemon/src/routes/queue.ts | grep -c 'return streamSSE('`):
`/api/stream/watch` (aliased `/api/stream/sse`, `routes/stream.ts:194`–`195`)
for new stream items, and `/api/queue/watch` (aliased `/api/queue/sse`,
`routes/queue.ts:985`–`986`) for queue/inbox events — every coordination type
`routes/queue.ts:986`–`987`) for queue/inbox events — every coordination type
except `stream.emitted` and `queue.updated` (filter at
`routes/queue.ts:962`–`969`). The event log remains append-only and
`routes/queue.ts:963`–`970`). The event log remains append-only and
SQLite-backed. `rig stream watch` is a thin, single-connection consumer of
`/api/stream/sse` (`packages/cli/src/commands/stream.ts:191`); it does not add
a daemon route or reconnect policy.
@@ -282,8 +282,8 @@ a daemon route or reconnect policy.
(`:207`).
- `/api/queue` (`server.ts:785`) — `POST /create` (`routes/queue.ts:400`),
`POST /:qitemId/claim` (`:499`), `POST /:qitemId/unclaim` (`:515`),
`POST /:qitemId/update` (`:553`), plus handoff (`:603`, `:679`), list
(`:833`) and watch (`:985`) surfaces.
`POST /:qitemId/update` (`:554`), plus handoff (`:604`, `:680`), list
(`:834`) and watch (`:986`) surfaces.
## See also
+41 -41
View File
@@ -9,8 +9,8 @@ applies-when: |
graph, the SQLite schema/migration set, or the route-mount surface.
siblings: [coordination-primitive.md, agent-spec-and-startup.md, lifecycle-snapshot-restore.md]
prerequisite-reads: [../README.md]
last-verified-against-source: 48f6cce7
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Daemon Core — Wiring, DB, Migrations, Startup
@@ -20,7 +20,7 @@ OpenRig is a local control plane for multi-agent coding topologies. The daemon
(`@openrig/cli`), the terminal UI (`@openrig/tui`), the web UI (`@openrig/ui`)
and the MCP server all sit on top of.
> Verified against source at main `48f6cce7`. Each count below sits beside the
> Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. Each count below sits beside the
> command that produces it; run the command from the repository root to refresh
> it.
@@ -29,7 +29,7 @@ and the MCP server all sit on top of.
For what OpenRig is and how its packages fit together, read `ARCHITECTURE.md` at
the repository root. This module covers the daemon's own wiring.
### Source footprint at `48f6cce7`
### Source footprint at `254122872cf477511514979a4300b695d77cd1f7`
The footprint counts use non-test TypeScript files under each package's `src/`
(tests live in separate `packages/*/test/` directories):
@@ -37,12 +37,12 @@ The footprint counts use non-test TypeScript files under each package's `src/`
| Metric | Count | Directory or command |
|---|---|---|
| All packages | **1151** | `packages/*/src` |
| Daemon | **623** (**408** under `domain/`, **24** under `adapters/`) | `packages/daemon/src`, `…/src/domain`, `…/src/adapters` |
| CLI | **161** | `packages/cli/src` |
| All packages | **1156** | `packages/*/src` |
| Daemon | **627** (**409** under `domain/`, **26** under `adapters/`) | `packages/daemon/src`, `…/src/domain`, `…/src/adapters` |
| CLI | **162** | `packages/cli/src` |
| Web UI | **304** | `packages/ui/src` |
| TUI | **63** | `packages/tui/src` |
| Database migrations | **91** (`001_core_schema.ts` … `091_human_questions.ts`) | `git ls-files packages/daemon/src/db/migrations \| wc -l` |
| Database migrations | **92** (`001_core_schema.ts` … `092_node_effort.ts`) | `git ls-files packages/daemon/src/db/migrations \| wc -l` |
| Files in `routes/` | **67**, of which **65** create a Hono router | `git ls-files packages/daemon/src/routes \| wc -l`; `git grep -l 'new Hono' -- packages/daemon/src/routes \| wc -l` |
| `app.route(...)` mounts in `server.ts` | **69** | `grep -c 'app.route(' packages/daemon/src/server.ts` |
| Top-level `rig` commands | **85** | `grep -c 'program.addCommand(' packages/cli/src/index.ts` |
@@ -51,7 +51,7 @@ The footprint counts use non-test TypeScript files under each package's `src/`
`OmpRuntimeAdapter` (Oh My Pi) extends `PiRuntimeAdapter`, so the daemon wires
six runtime keys — `claude-code`, `codex`, `pi`, `omp`, `stub`, `terminal`
(`startup.ts:941`).
(`startup.ts:945`).
### The stack
@@ -73,9 +73,9 @@ Hono daemon routes (69 app.route() mounts + direct handlers for /healthz,
+-- coordination routes (stream / queue / workflow / mission control)
|
v
Framework-free domain services (408 files under packages/daemon/src/domain)
Framework-free domain services (409 files under packages/daemon/src/domain)
|
+-- SQLite state (91 migrations)
+-- SQLite state (92 migrations)
+-- tmux / cmux / resume adapters
+-- runtime adapters (Claude Code / Codex / Pi / Oh My Pi / Stub / Terminal)
+-- rig environment services (compose adapter, service readiness, orchestrator)
@@ -88,10 +88,10 @@ handoff → inspect/attach → work → repeat`.
## 2. Database schema
The migrations live in `packages/daemon/src/db/migrations/` (91 files,
`001_core_schema.ts` … `091_human_questions.ts`). `ALL_MIGRATIONS`
(`packages/daemon/src/db/all-migrations.ts:100`) lists all 91, and `createDaemon`
applies them with `migrate(db, ALL_MIGRATIONS)` (`startup.ts:281`). `migrate.ts`
The migrations live in `packages/daemon/src/db/migrations/` (92 files,
`001_core_schema.ts` … `092_node_effort.ts`). `ALL_MIGRATIONS`
(`packages/daemon/src/db/all-migrations.ts:101`) lists all 92, and `createDaemon`
applies them with `migrate(db, ALL_MIGRATIONS)` (`startup.ts:283`). `migrate.ts`
sorts them by name (`:29`) and records each applied name in `schema_migrations`
(`:15`).
@@ -137,7 +137,7 @@ in `003_events.ts`, `snapshots` (serialized rig state) in `004_snapshots.ts`, an
- `040_workflow_specs_diagnostic.ts` — `ALTER TABLE workflow_specs ADD COLUMN`
for parser/validator diagnostics (no new table).
Migrations `041`–`091` continue in the same directory; list them with
Migrations `041`–`092` continue in the same directory; list them with
`git ls-files packages/daemon/src/db/migrations`.
The package, bootstrap and discovery tables remain: `packages`
@@ -169,32 +169,32 @@ also enabled (`server.ts:757`).
## 4. Startup sequence (`createDaemon`)
`createDaemon(opts?)` is `packages/daemon/src/startup.ts:272` (async, returns
`createDaemon(opts?)` is `packages/daemon/src/startup.ts:274` (async, returns
`DaemonResult`). It returns `{ app, db, deps, contextMonitor, eventLoopMonitor,
injectWebSocket }` (`startup.ts:2448`). In source order, it:
injectWebSocket }` (`startup.ts:2450`). In source order, it:
1. Opens SQLite and applies all 91 migrations (`migrate(db, ALL_MIGRATIONS)`,
`startup.ts:281`).
2. Constructs the coordination stores early: `StreamStore` (`:313`),
`QueueRepository` (`:411`) and `OutboxHandler` (`:426`).
3. Constructs `TranscriptStore` (`:538`) and the rig environment services
`ComposeServicesAdapter` and `ServiceOrchestrator` (`:617`–`618`).
4. Constructs `StartupOrchestrator` (`:749`) and the runtime adapters:
`ClaudeCodeAdapter` (`:751`), `CodexRuntimeAdapter` (`:752`),
`PiRuntimeAdapter` (`:755`), `OmpRuntimeAdapter` (`:756`) and
`StubRuntimeAdapter` (`:761`); the terminal adapter is created inline in the
runtime adapter map (`:941`).
5. Constructs `PodRigInstantiator` (`:937`), `PodBundleSourceResolver` (`:966`)
and `BootstrapOrchestrator` (`:968`).
6. Constructs `ContextUsageStore` (`:1042`), `ResumeMetadataRefresher`
(`:1070`), `SpecReviewService` (`:1094`) and `WhoamiService` (`:1097`).
7. Constructs `SessionTransport` (`:1202`), `ChatRepository` (`:1236`),
`InboxHandler` (`:1240`), `AskService` (`:1289`) and `SpecLibraryService`
(`:1327`). `InboxHandler` receives the same queue repository instance
1. Opens SQLite and applies all 92 migrations (`migrate(db, ALL_MIGRATIONS)`,
`startup.ts:283`).
2. Constructs the coordination stores early: `StreamStore` (`:315`),
`QueueRepository` (`:413`) and `OutboxHandler` (`:428`).
3. Constructs `TranscriptStore` (`:540`) and the rig environment services
`ComposeServicesAdapter` and `ServiceOrchestrator` (`:621`–`622`).
4. Constructs `StartupOrchestrator` (`:753`) and the runtime adapters:
`ClaudeCodeAdapter` (`:755`), `CodexRuntimeAdapter` (`:756`),
`PiRuntimeAdapter` (`:759`), `OmpRuntimeAdapter` (`:760`) and
`StubRuntimeAdapter` (`:765`); the terminal adapter is created inline in the
runtime adapter map (`:945`).
5. Constructs `PodRigInstantiator` (`:941`), `PodBundleSourceResolver` (`:970`)
and `BootstrapOrchestrator` (`:972`).
6. Constructs `ContextUsageStore` (`:1046`), `ResumeMetadataRefresher`
(`:1077`), `SpecReviewService` (`:1102`) and `WhoamiService` (`:1105`).
7. Constructs `SessionTransport` (`:1210`), `ChatRepository` (`:1244`),
`InboxHandler` (`:1248`), `AskService` (`:1297`) and `SpecLibraryService`
(`:1335`). `InboxHandler` receives the same queue repository instance
(`queueRepoInstance`) that serves `/api/queue`, so absorbed inbox items and the
queue route write to one store.
8. Constructs `ContextMonitor` (`:2351`).
9. Builds `AppDeps` and calls `createAppWithWebSocket(deps)` (`startup.ts:2446`)
8. Constructs `ContextMonitor` (`:2353`).
9. Builds `AppDeps` and calls `createAppWithWebSocket(deps)` (`startup.ts:2448`)
to mount the full route tree.
Node inventory is a set of functions (`getNodeInventory` and friends in
@@ -206,9 +206,9 @@ The daemon entrypoint `packages/daemon/src/index.ts:298` calls
## 5. Test files
A static count of tracked test files at `48f6cce7` (no pass counts are claimed
here; CI runs the suites in `.github/workflows/tests.yml`): daemon **797**,
CLI **214**, web UI **197**, TUI **92**
A static count of tracked test files at `254122872cf477511514979a4300b695d77cd1f7` (no pass counts are claimed
here; CI runs the suites in `.github/workflows/tests.yml`): daemon **816**,
CLI **216**, web UI **198**, TUI **96**
(`git ls-files 'packages/<package>/**/*.test.ts' 'packages/<package>/**/*.test.tsx' | wc -l`).
## See also
@@ -11,8 +11,8 @@ applies-when: |
and the CLI restore-packet command work.
siblings: [daemon-core.md, agent-spec-and-startup.md, transport-and-transcripts.md]
prerequisite-reads: [../README.md, agent-spec-and-startup.md]
last-verified-against-source: e6a391b6
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Lifecycle — Snapshot, Restore, Continuity
@@ -22,7 +22,7 @@ The durable-state half of the core product loop:
captures serialized rig state; restore replays it honestly (no silent
fresh-fallback); restore-check is a separate read-only readiness probe.
> Verified against source at main `e6a391b6`. Each count below sits beside the
> Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. Each count below sits beside the
> command that produces it; run the command from the repository root to refresh
> it.
@@ -31,19 +31,19 @@ fresh-fallback); restore-check is a separate read-only readiness probe.
All in `packages/daemon/src/domain/types.ts`. The spec/projection types live in
`agent-spec-and-startup.md`; the snapshot/restore types live here.
- **NodeRestoreOutcome** (`types.ts:500`) — the per-node restore outcome
- **NodeRestoreOutcome** (`types.ts:501`) — the per-node restore outcome
projected into `rig ps`. **9** values
(`grep '^export type NodeRestoreOutcome' packages/daemon/src/domain/types.ts | grep -o '"[^"]*"' | wc -l`):
`resumed`, `rebuilt`, `fresh`, `fresh-primed`, `awaiting-decision`,
`failed`, `attention_required`, `operator_recovered`, `n-a`. The restore
result itself, `RestoreNodeResult.status` (`types.ts:475`), has the same
result itself, `RestoreNodeResult.status` (`types.ts:476`), has the same
values without `n-a`: **8**
(`grep -E '^ status: "resumed"' packages/daemon/src/domain/types.ts | grep -o '"[^"]*"' | wc -l`).
Where `restore-orchestrator.ts` sets each one:
- `resumed` — `baseStatus = "resumed"` after a legacy resume (`:1212`); the
status is returned only by `finishJoinedResume` (`:1497`, `:1541`) after
status is returned only by `finishJoinedResume` (`:1499`, `:1561`) after
it has rebound and verified the pane, otherwise that becomes
`attention_required` (`:1518`).
`attention_required` (`:1533`).
- `rebuilt` — a checkpoint file was written (`:1272`).
- `fresh-primed` — the default for a deliberate non-resume launch
(`:1194`).
@@ -51,27 +51,27 @@ All in `packages/daemon/src/domain/types.ts`. The spec/projection types live in
is already `restoring` (`:992`).
- `awaiting-decision` — the seat could not resume and nothing is running:
before launch (`:1027`), or after launch once the blank session is rolled
back (`:1208`, `:1230`, `:1243`, `:1259`, `:1416`).
back (`:1208`, `:1230`, `:1243`, `:1259`, `:1417`).
- `attention_required` — a live session waiting on a runtime prompt
(`:1221`, `:1441`) or a harness that never started (`:1307`).
(`:1221`, `:1443`) or a harness that never started (`:1307`).
- `failed` — occupant ambiguity (`:979`), launch failure (`:1118`), a
checkpoint that cannot be delivered (`:1268`, `:1274`), a missing
required startup file (`:1343`), or a startup error.
- `operator_recovered` — only from later reconciliation,
`reconcileNodeRuntimeTruth` (`:1738`, event at `:1881`).
- **RestoreRigResult** (`types.ts:444`) — the rig-level rollup, **4** values
`reconcileNodeRuntimeTruth` (`:1759`, event at `:1902`).
- **RestoreRigResult** (`types.ts:445`) — the rig-level rollup, **4** values
(`grep '^export type RestoreRigResult' packages/daemon/src/domain/types.ts | grep -o '"[^"]*"' | wc -l`):
`fully_restored`, `partially_restored`, `failed`, `not_attempted`
(`rollupRestoreRigResult`, `restore-orchestrator.ts:82`).
- **SnapshotData** (`types.ts:351`) — the serialized snapshot payload. It has
- **SnapshotData** (`types.ts:352`) — the serialized snapshot payload. It has
**7** optional fields, kept optional so older snapshots still load
(`sed -n '/^export interface SnapshotData/,/^}/p' packages/daemon/src/domain/types.ts | grep -c '?:'`):
`activeSessionIdByNode?`, `activeOccupantsByNode?`, `topologyRoster?`,
`pods?`, `continuityStates?`, `nodeStartupContext?`, `envReceipt?`.
- **NodeStartupSnapshot** (`types.ts:344`) — persisted restore replay input:
- **NodeStartupSnapshot** (`types.ts:345`) — persisted restore replay input:
classification-free projection entries, resolved startup files, startup
actions, runtime.
- **PersistedProjectionEntry** (`types.ts:332`) — the classification-free
- **PersistedProjectionEntry** (`types.ts:333`) — the classification-free
restore replay seam: persists only entry identity + source metadata, NOT
stale `classification`, `conflicts`, or `noOps` (Architecture Rule 10).
Restore rebuilds each entry as `safe_projection` with empty `conflicts` and
@@ -85,9 +85,9 @@ All under `packages/daemon/src/domain/`:
- `snapshot-capture.ts` — captures pods, continuity state, startup replay
context, the latest env receipt, the active-occupant maps and the intended
topology roster. `captureSnapshot` (`:60`) derives the occupant maps
(`:73-79`), resolves the roster (`:81-90`), then reads `pods` (`:96`),
(`:73–79`), resolves the roster (`:81–90`), then reads `pods` (`:96`),
`continuity_state` (`:100`) and `node_startup_context` (`:106`) rows per rig
and the env receipt (`:116-123`).
and the env receipt (`:116–123`).
- `snapshot-repository.ts` — snapshot CRUD and restore-source selection:
`findLatestRestoreUsable` (`:72`) and `selectRestoreUsable` (`:94`), which
accept only snapshots that pass `isRestoreUsableSnapshotData` (`:283`).
@@ -109,9 +109,9 @@ Restore behavior, each point checked in `restore-orchestrator.ts`:
`restore-orchestrator.ts:974` and `:1171`). An ambiguous occupant fails the
node loudly (`activeOccupantAmbiguityError`, `active-occupant.ts:97`).
- Consults live `continuity_state`; preserves state when a node is already
`restoring` (`:985-998` — `SELECT status FROM continuity_state …` at
`restoring` (`:985–998` — `SELECT status FROM continuity_state …` at
`:987`; if `status === "restoring"` the node is skipped with a warning and
reported as `fresh`, `:990-992`; `degraded` only adds a warning).
reported as `fresh`, `:990–992`; `degraded` only adds a warning).
- Replays restore-safe startup using persisted startup context;
prefilters missing optional artifacts into warnings; **hard-fails a node if
a required startup file is missing** (`:1343` — status `"failed"`, error
@@ -123,39 +123,39 @@ Restore behavior, each point checked in `restore-orchestrator.ts`:
native session replays nothing (`replayContained`, `:1288`).
- Writes a **transcript boundary marker before re-launch**
(`writeBoundaryMarker`, `:1100`, called before `launchNode`).
- Refuses to restore over live sessions (`:258-261` — `rig_not_stopped`: "Rig …
- Refuses to restore over live sessions (`:258–261` — `rig_not_stopped`: "Rig …
has live sessions. Stop the rig with 'rig down' before restoring"). A tmux
probe that fails also blocks it (`classifyRunningSessions`, `:812`).
- Checks that the harness actually resumed. The resume adapters judge the
pane with `assessNativeResumeProbe` (`domain/native-resume-probe.ts:81`). A
pane with `assessNativeResumeProbe` (`domain/native-resume-probe.ts:84`). A
pod-aware resume counts as resumed only when startup reports `resumed`
continuity or the launched session row carries the snapshot's resume type
and token (`launchedSessionMatchesSnapshotResume`,
`restore-orchestrator.ts:1544`); a reported `fresh` continuity without that
proof rolls back to `awaiting-decision` (`:1404-1418`).
`restore-orchestrator.ts:1564`); a reported `fresh` continuity without that
proof rolls back to `awaiting-decision` (`:1405–1419`).
**Restore-honesty rules** (texts in `architecture-rules-and-event-system.md`;
cited here by number with the code that carries them):
- Rule 7 — `RESTORE_POLICY_LEVEL`
(`packages/daemon/src/domain/profile-resolver.ts:102`) orders the three
policies, and `resolveRestorePolicy` (`profile-resolver.ts:479`) rejects a
(`packages/daemon/src/domain/profile-resolver.ts:103`) orders the three
policies, and `resolveRestorePolicy` (`profile-resolver.ts:488`) rejects a
profile or member value that broadens it.
- Rule 14 — the outcome set is `RestoreNodeResult.status` (`types.ts:475`,
- Rule 14 — the outcome set is `RestoreNodeResult.status` (`types.ts:476`,
§1); `rebuilt` is set only when a checkpoint is written
(`restore-orchestrator.ts:1272`).
- Rule 15 — in `restore-orchestrator.ts`, a `resume_if_possible` seat with a
session but no token stops as `awaiting-decision` before launch unless
`--fresh` names it (`:1020-1029`); a failed resume kills the blank session
`--fresh` names it (`:1020–1029`); a failed resume kills the blank session
(`rollbackToZeroSession`, `:1140`) and returns `awaiting-decision`
(`:1230`); pod-aware resume passes
`allowFreshFallback: !(isPodAware && resumeRequested)` (`:1398`). Fresh
`allowFreshFallback: !(isPodAware && resumeRequested)` (`:1399`). Fresh
launch is explicit: `rig up --existing <rig> --fresh <seat...>`
(`packages/cli/src/commands/up.ts:85`).
- Rule 16 — `rig down` prints the snapshot id and a restore command
(`packages/cli/src/commands/down.ts:236-246`); `rig up` on an existing rig
(`packages/cli/src/commands/down.ts:236–246`); `rig up` on an existing rig
prints per-node statuses and the attach command
(`packages/cli/src/commands/up.ts:540-560`).
(`packages/cli/src/commands/up.ts:540–560`).
(The full rule list lives in `architecture-rules-and-event-system.md`.)
@@ -163,13 +163,13 @@ cited here by number with the code that carries them):
- `rig down <rig>` (name or id, `packages/cli/src/commands/down.ts:104`)
auto-captures an `auto-pre-down` snapshot before teardown when the rig has
live sessions (`packages/daemon/src/domain/rig-teardown.ts:111`; an
already-stopped rig returns at `:89-104` without one).
live sessions (`packages/daemon/src/domain/rig-teardown.ts:116`; an
already-stopped rig returns at `:89–109` without one).
- `rig up <rig-name>`: a source with no `/` and no
`.yaml`/`.yml`/`.rigbundle`/`.rigtopology` extension is a rig name
(`packages/daemon/src/domain/up-command-router.ts:49-50`). The CLI first
(`packages/daemon/src/domain/up-command-router.ts:49–50`). The CLI first
checks the spec library unless `--existing` is given, and refuses a name
that matches both (`packages/cli/src/commands/up.ts:287-302`). The daemon
that matches both (`packages/cli/src/commands/up.ts:287–302`). The daemon
finds the rig by name (`packages/daemon/src/routes/up.ts:222`) and
restores from `selectRestoreUsable` (`routes/up.ts:105`), which prefers the
newest `auto-pre-down` or `auto-periodic` snapshot, then the newest other
@@ -177,9 +177,9 @@ cited here by number with the code that carries them):
- If no usable snapshot exists, or the chosen one names an older occupant
(`snapshotMatchesCurrentOccupants`, `routes/up.ts:109`), `rig up` captures
an `auto-rehydrate` snapshot of current DB state when that state is
eligible (`routes/up.ts:136-145`). Otherwise it errors with code
eligible (`routes/up.ts:136–145`). Otherwise it errors with code
`no_snapshot` and guidance ("… current DB state is insufficient for
rehydrate. Start fresh with: rig up <spec-path>", `routes/up.ts:117-122`).
rehydrate. Start fresh with: rig up <spec-path>", `routes/up.ts:117–122`).
- Post-command handoff: `down` output includes the snapshot ID and
`To restore: rig up <name>` (or `rig restore <snapshotId> --rig <rigId>`
when the name is not unique); `up` output includes node statuses and an
@@ -190,71 +190,71 @@ cited here by number with the code that carries them):
### 5.1 `rig restore-check` — readiness probe
`GET /api/restore-check?rig=<name>&noQueue=true&noHooks=true&compact=1&ready=1`
(`routes/restore-check.ts:205-206`). The route calls
(`routes/restore-check.ts:205–206`). The route calls
`createRestoreCheckService` (`:149`), which assembles a framework-free
`RestoreCheckDeps` (`:153-201`) over existing daemon projections —
`listRigs` from `rigRepo` (`:172-175`), `getNodeInventory` (joining
`node_id` by `logical_id`, `:24-29`, `:176-182`), `getStartupContext` (reads
`RestoreCheckDeps` (`:153–201`) over existing daemon projections —
`listRigs` from `rigRepo` (`:172–175`), `getNodeInventory` (joining
`node_id` by `logical_id`, `:24–29`, `:176–182`), `getStartupContext` (reads
`node_startup_context`, parses `projection_entries_json` /
`resolved_files_json` / `startup_actions_json`, `:47-141`), `hasSnapshot`
/ `getLatestSnapshot` from `snapshotRepo` (`:186-192`), `probeQueueStore`
(`:155-167`), `getClaudeActivityHookEvents` (`:168-171`), and
`resolved_files_json` / `startup_actions_json`, `:47–141`), `hasSnapshot`
/ `getLatestSnapshot` from `snapshotRepo` (`:186–192`), `probeQueueStore`
(`:155–167`), `getClaudeActivityHookEvents` (`:168–171`), and
`probeDaemonHealth` (self-evident: "We're inside the daemon — if this route
is responding, daemon is healthy", `:193-196`). It then runs
`service.check(...)` (`:216-217`).
is responding, daemon is healthy", `:193–196`). It then runs
`service.check(...)` (`:216–217`).
`RestoreCheckService.check()` (`restore-check-service.ts:243`) layers:
1. **Host checks** — `checkDaemonReachable` (probe-throw → `verdict:
unknown`, NOT `not_restorable`, `:256-263`), `checkStateDirWritable`
unknown`, NOT `not_restorable`, `:256–263`), `checkStateDirWritable`
(`:265`), `checkHostInfraDeclaration` (`:266`, impl `:432`).
2. **Rig enumeration** — `listRigs()` throw → `buildUnknown` (`:271-278`);
`--rig` filter; unknown rig → red `rig.<name>.exists` (`:280-287`).
2. **Rig enumeration** — `listRigs()` throw → `buildUnknown` (`:271–278`);
`--rig` filter; unknown rig → red `rig.<name>.exists` (`:280–287`).
3. **Per-rig checks** — `checkSnapshot` (`:294`, impl `:701`),
`checkSpecPresent` (`:299`, impl `:1064`).
4. **Per-seat checks** — `checkSeatReadiness` (`:315`), `checkStartupContext`
(`:329`, impl `:789`; `unknownChecks` → `buildUnknown`, `:330-335`),
(`:329`, impl `:789`; `unknownChecks` → `buildUnknown`, `:330–335`),
`checkTranscript` (`:351`), `checkResumePath` (`:355`), and unless opted
out: the daemon SQLite `queue_items` availability probe `checkQueueStore`
(`:360`, gated by `--no-queue`; impl `:910`) and `checkHooks` (`:365`,
gated by `--no-hooks`; impl `:933`). Queue continuity is represented by
the shared daemon store; an empty queue is valid
(`routes/restore-check.ts:159-160`). Claude hook readiness follows the
(`routes/restore-check.ts:159–160`). Claude hook readiness follows the
persisted `claude_activity_hooks` runtime-resource selection
(`restore-check-service.ts:957`) and the adapter's activity-relay
projection in the seat CWD (`restore-check-service.ts:974-975`). A seat
projection in the seat CWD (`restore-check-service.ts:974–975`). A seat
that deliberately omits that resource is not applicable. In compact mode
without `ready=1`, a seat whose readiness check is green gets only the
startup-context check (`restore-check-service.ts:327`, `:347-349`); that
check still counts toward the verdict (`:337-340`).
startup-context check (`restore-check-service.ts:327`, `:347–349`); that
check still counts toward the verdict (`:337–340`).
5. **Verdict** — `buildResult` (`:1106`) aggregates: any red →
`not_restorable`; any yellow → `restorable_with_caveats`; else
`restorable` (`:1121-1128`); probe-uninspectable → `unknown`
`restorable` (`:1121–1128`); probe-uninspectable → `unknown`
(`buildUnknown`, `:1137`). Plus a `RecoveryPlan` (`buildRecovery`,
`:1241`) and a `RepairStep[]` packet (`buildRepairPacket`, `:1475`;
`null` when fully restorable, `:1476`).
The result shape is `RestoreCheckResult` (`restore-check-service.ts:144-156`):
The result shape is `RestoreCheckResult` (`restore-check-service.ts:144–156`):
`verdict`, `readiness`, `continuity`, `rigs[]`, `hostInfra`, `recovery`,
`counts {red,yellow,green}`, `classCounts`, `checks[]`, `repairPacket`. With
`compact=1` the route returns a reduced body (`routes/restore-check.ts:219-245`).
`compact=1` the route returns a reduced body (`routes/restore-check.ts:219–245`).
**Honest-error design:** a daemon-probe *exception* produces
`verdict: unknown` (uninspectable state), distinct from a daemon
definitely-down state which is `red` / `not_restorable`
(`restore-check-service.ts:253-263`, `checkDaemonReachable` `:393-413`). The
(`restore-check-service.ts:253–263`, `checkDaemonReachable` `:393–413`). The
route's catch-all returns the same `unknown`-shaped body with HTTP 500 + a
`probe.error` red check (`routes/restore-check.ts:249-297`).
`probe.error` red check (`routes/restore-check.ts:249–297`).
`CheckEntry.remediationSafe` defaults to `false` (conservative —
unclassified remediations are NOT auto-execution-safe,
`restore-check-service.ts:19-24`, applied at `:1490`).
`restore-check-service.ts:19–24`, applied at `:1490`).
CLI surface (`../cli-reference.md` `### rig restore-check`; options in
`packages/cli/src/commands/restore-check.ts:222-227`):
`packages/cli/src/commands/restore-check.ts:222–227`):
`rig restore-check [--rig <name>] [--full] [--ready] [--no-queue] [--no-hooks] [--json]`.
Output is compact unless `--full` (`:313`). Exit codes: `0` restorable (or
with caveats), `1` not restorable (red), `2` unknown / probe error
(`:214-217`).
(`:214–217`).
### 5.2 `rig restore-packet` — cross-runtime restore packet
@@ -7,13 +7,13 @@ domains: [engineering-advisor, operating-advisor]
applies-when: |
Tracing slice, mission, rig, or fleet review data back to scope documents,
proof artifacts, judgments, queue state, or frozen exports.
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Living Notes — Composed Review and Frozen Exports
Source snapshot: `712a61fbcc5ffe5a7c786fea12132567083ab507`. This describes the source at that commit;
Source snapshot: `254122872cf477511514979a4300b695d77cd1f7`. This describes the source at that commit;
it does not establish the version or behavior of a running daemon.
Living Notes builds review payloads from existing documents and recorded
+48 -47
View File
@@ -11,8 +11,8 @@ applies-when: |
coordination primitive's sources.
siblings: [coordination-primitive.md, workflow-runtime.md, ../ui/project-and-for-you.md]
prerequisite-reads: [../README.md, coordination-primitive.md]
last-verified-against-source: e6a391b6
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Mission Control — Queue Observability + Verb Contract
@@ -25,8 +25,8 @@ seven views, eight verbs, a recent-ships cap of 10
table. In the web UI the old `/mission-control` page now redirects to
`/for-you`, and the For You feed carries the verb actions (§6).
> Verified against source at main `e6a391b6`. A bare file name such as
> `mission-control-read-layer.ts:32` is in
> Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. A bare file name such as
> `mission-control-read-layer.ts:31` is in
> `packages/daemon/src/domain/mission-control/`; `domain/…`, `routes/…`,
> `middleware/…`, `db/…`, `index.ts`, `server.ts` and `startup.ts` are under
> `packages/daemon/src/`. Each count sits beside the command that produces
@@ -35,17 +35,17 @@ table. In the web UI the old `/mission-control` page now redirects to
## 1. The seven views
`MISSION_CONTROL_VIEWS` is the canonical view list
(`mission-control-read-layer.ts:32-40`): `my-queue`, `human-gate`, `fleet`,
(`mission-control-read-layer.ts:31–39`): `my-queue`, `human-gate`, `fleet`,
`active-work`, `recent-ships`, `recently-active`, `recent-observations`.
Views: **7** — `sed -n '/^export const MISSION_CONTROL_VIEWS = \[/,/\] as const;/p' packages/daemon/src/domain/mission-control/mission-control-read-layer.ts | grep -c '^ "'`
All seven return rows in the 9-field phone-friendly content model,
`CompactStatusRow` (`mission-control-read-layer.ts:61`): rig/mission name,
`CompactStatusRow` (`mission-control-read-layer.ts:60`): rig/mission name,
current phase, active|idle|attention|blocked|degraded, next-action,
pending-human-decision, read-cost, last-update timestamp,
confidence/freshness, evidence link. The type's doc comment
(`mission-control-read-layer.ts:44-60`) makes the model non-negotiable across
(`mission-control-read-layer.ts:43–59`) makes the model non-negotiable across
all seven views: UI may render compact, JSON preserves all nine. Four further
optional fields carry the queue item's id and context.
@@ -58,21 +58,21 @@ each view to its source:
`queue_items` via `QueueRepository`.
- `fleet` calls `MissionControlFleetCliCapability.rollupFleet()`
(`mission-control-read-layer.ts:197`;
`mission-control-fleet-cli-capability.ts:185`), which walks the rig
registry (`:186`), summarises each rig's `queue_items` (`:204`), and runs a
per-rig CLI capability probe (`makeLocalCliCapabilityProbe`, `:143`, wired
at `startup.ts:1545`).
`mission-control-fleet-cli-capability.ts:109`), which walks the rig
registry (`:110`), summarises each rig's `queue_items` (`:135`), and runs a
per-rig CLI capability probe (`makeLocalCliCapabilityProbe`, `:77`, wired
at `startup.ts:1547`).
- `recently-active` delegates to the view projector's built-in
`ViewProjector.show("recently-active")` (`mission-control-read-layer.ts:245`).
- `recent-observations` reads `stream_items` via `StreamStore`
(`mission-control-read-layer.ts:264`).
`ViewProjector.show("recently-active")` (`mission-control-read-layer.ts:246`).
- `recent-observations` reads the latest 50 `stream_items` via `StreamStore`
with `direction: "latest"` (`mission-control-read-layer.ts:265–268`).
There is no filesystem fallback. When no `StreamStore` is wired,
`recent-observations` returns no rows with
`sourceFallback: "stream-store-not-wired"`
(`mission-control-read-layer.ts:257-261`), and `fleet` always reports
(`mission-control-read-layer.ts:258–262`), and `fleet` always reports
`sourceFallback: "daemon-internal-projection"`
(`mission-control-fleet-cli-capability.ts:224`).
(`mission-control-fleet-cli-capability.ts:156`).
> Scope note: this module describes the daemon's verb vocabulary. It does
> NOT enumerate which verbs the For You cards offer; that surface is
@@ -80,7 +80,7 @@ There is no filesystem fallback. When no `StreamStore` is wired,
## 2. The eight verbs (the write contract)
`MISSION_CONTROL_VERBS` (`mission-control-action-log.ts:14-26`) is the verb
`MISSION_CONTROL_VERBS` (`mission-control-action-log.ts:14–26`) is the verb
list. The verbs execute through `MissionControlWriteContract.act`
(`mission-control-write-contract.ts:131`); the closure mapping is
`verbToClosure` (`:509`).
@@ -96,17 +96,17 @@ Verbs: **8** — `sed -n '/^export const MISSION_CONTROL_VERBS = \[/,/\] as cons
| `hold` | `state="blocked"`, `closure_reason="blocked_on"` |
| `drop` | `state="done"`, `closure_reason="canceled"` |
| `handoff` | same closure and new destination item as `route`; the 4-step shape (see below) |
| `resolve` | only for a `blocked` item parked on a human seat: moves it to `in-progress` with the required decision text as the transition note; no closure and no new item (`:369`, `:386`, `:410-411`) |
| `resolve` | only for a `blocked` item parked on a human seat: moves it to `in-progress` with the required decision text as the transition note; no closure and no new item (`:369`, `:386`, `:410–411`) |
Each verb is one atomic daemon transaction: the queue mutation via
`QueueRepository.updateWithinTransaction()` (`domain/queue-repository.ts:2314`,
`QueueRepository.updateWithinTransaction()` (`domain/queue-repository.ts:2337`,
which keeps the hot-potato closure validation — see
`coordination-primitive.md` §3), an audit row in `mission_control_actions`
(`mission-control-write-contract.ts:207`), and a persisted
`mission_control.action_executed` event (`:225`), all in one
`db.transaction`. For every verb except `annotate`, that transaction is
opened by `EventBus.withNotifyEnvelope` (`:157`; `domain/event-bus.ts:111`,
`:125`); `annotate` opens `db.transaction` itself
opened by `EventBus.withNotifyEnvelope` (`:157`; `domain/event-bus.ts:112`,
`:126`); `annotate` opens `db.transaction` itself
(`mission-control-write-contract.ts:308`).
The 4-step `handoff` shape (source-update + destination-create + best-effort
@@ -115,8 +115,8 @@ created in the same transaction (`createWithinTransaction`, `:173`), along
with the successor's wake intent (`stageWakeIntent`, `:195`, checked by
`assertTerminalClosureHasIntent`, `:242`). The wake itself runs after commit
(`deliverWakeForSuccessor`, `:264`). It is on unless the caller passes
`notify: false` (`domain/queue-repository.ts:1166`), and a notify failure
does NOT roll back durable mutations (`mission-control-write-contract.ts:272-275`).
`notify: false` (`domain/queue-repository.ts:1174`), and a notify failure
does NOT roll back durable mutations (`mission-control-write-contract.ts:272–275`).
A failed destination create rolls back the source closure, the audit row and
the new item together
(`packages/daemon/test/mission-control-write-contract.test.ts:220`).
@@ -129,9 +129,10 @@ API surface: `record()` is the only writer `MissionControlActionLog` exposes
(`mission-control-action-log.ts:113`). It records every operator action
through Mission Control with before/after queue item snapshots for forensic
reconstruction. Columns include `action_verb` (TEXT, app-layer enum
enforcement, `:60`) and `acted_at` (TEXT NOT NULL ISO timestamp, `:63`);
enforcement, `037_mission_control_actions.ts:60`) and `acted_at` (TEXT NOT
NULL ISO timestamp, `037_mission_control_actions.ts:63`);
indexes `(acted_at DESC, action_verb)`, `(qitem_id, acted_at DESC)`,
`(actor_session, acted_at DESC)` (`037_mission_control_actions.ts:72-77`).
`(actor_session, acted_at DESC)` (`037_mission_control_actions.ts:72–77`).
One later migration adds a nullable `identity_provenance` column
(`db/migrations/065_identity_provenance.ts:18`). The audit browse (§4) reads
only this table.
@@ -147,31 +148,31 @@ only this table.
nor Tailscale (a hostname is resolved first), and no bearer token is set
(`assertBindAuthInvariant`, `auth-bearer-token.ts:240`, called at
`index.ts:288`). Bearer enforced on the write routes:
`app.post("/action", requireAuth)` (`routes/mission-control.ts:305`) and
`app.post("/notifications/test", requireAuth)` (`:306`). Reads are not
`app.post("/action", requireAuth)` (`routes/mission-control.ts:308`) and
`app.post("/notifications/test", requireAuth)` (`:309`). Reads are not
gated. It is one static token; no OAuth/SSO/per-user model
(`auth-bearer-token.ts:11-14`).
(`auth-bearer-token.ts:11–14`).
- **Notification dispatcher** — two adapters
(`notification-adapter-ntfy.ts`, `notification-adapter-webhook.ts`) plus
`notification-dispatcher.ts`. `OPENRIG_NOTIFICATIONS_MECHANISM` selects
`ntfy`, `webhook` or `none`, and the default is `none`
(`startup.ts:1590`). A dispatcher starts only when
`OPENRIG_NOTIFICATIONS_TARGET` is also set (`:1591`, `:1598`) and the
target URL passes validation (`:1609`). The ntfy adapter posts to a topic
(`startup.ts:1592`). A dispatcher starts only when
`OPENRIG_NOTIFICATIONS_TARGET` is also set (`:1593`, `:1600`) and the
target URL passes validation (`:1611`). The ntfy adapter posts to a topic
URL that the ntfy phone app subscribes to
(`notification-adapter-ntfy.ts:1-6`).
(`notification-adapter-ntfy.ts:1–6`).
Adapters: **2** — `git grep -l 'implements NotificationAdapter' -- packages/daemon/src | wc -l`
- **Read-only audit-history browse** — `MissionControlAuditBrowse.query`
(`audit-browse.ts:67`) over `mission_control_actions`, exposed at
`GET /api/mission-control/audit` (`routes/mission-control.ts:439`) with
(`audit-browse.ts:78`) over `mission_control_actions`, exposed at
`GET /api/mission-control/audit` (`routes/mission-control.ts:442`) with
filters and `(limit, before_id)` pagination cursored on SQLite `rowid`
(`audit-browse.ts:121`).
(`audit-browse.ts:134`).
## 5. Mission Control events
The `RigEvent` union starts at `domain/types.ts:105`; its five
`mission_control.*` members are at `domain/types.ts:307-313`.
The `RigEvent` union starts at `domain/types.ts:106`; its five
`mission_control.*` members are at `domain/types.ts:308–314`.
`RigEvent` members: **99** — `sed -n '/^export type RigEvent =/,/^export type PersistedEvent/p' packages/daemon/src/domain/types.ts | grep -c 'type: "'`
@@ -180,7 +181,7 @@ The `RigEvent` union starts at `domain/types.ts:105`; its five
- `mission_control.action_executed` — written by the write contract
(`mission-control-write-contract.ts:225`).
- `mission_control.cli_drift_detected` — emitted by the fleet capability
probe (`mission-control-fleet-cli-capability.ts:239`).
probe (`mission-control-fleet-cli-capability.ts:171`).
- `mission_control.view_refreshed` — declared, and forwarded by the SSE
route, but nothing in `packages/daemon/src` constructs it.
- `mission_control.notification_sent` / `mission_control.notification_failed`
@@ -190,19 +191,19 @@ The `RigEvent` union starts at `domain/types.ts:105`; its five
## 6. Route surface
`missionControlRoutes({ bearerToken })` is mounted at `/api/mission-control`
(`server.ts:791-794`). Routes (`routes/mission-control.ts`): `GET /views`
(`:256`), `GET /cli-capabilities` (`:261`), `GET /destinations` (`:273`),
`GET /sse` and its alias `GET /watch` (`:301-302`; they forward
(`server.ts:791–794`). Routes (`routes/mission-control.ts`): `GET /views`
(`:259`), `GET /cli-capabilities` (`:264`), `GET /destinations` (`:276`),
`GET /sse` and its alias `GET /watch` (`:304–305`; they forward
`action_executed`, `cli_drift_detected` and `view_refreshed` events,
`:286-289`), `POST /action` (`:312`, auth-gated), `GET /audit` (`:439`),
`POST /notifications/test` (`:472`, auth-gated), and `GET /views/:view-name`
(`:500`).
`:289–292`), `POST /action` (`:315`, auth-gated), `GET /audit` (`:442`),
`POST /notifications/test` (`:482`, auth-gated), and `GET /views/:view-name`
(`:510`).
In the web UI (`packages/ui/src/routes.tsx`), `/mission-control` is a
redirect to `/for-you` (`:495-500`). `/for-you` renders the For You `Feed`
(`:130-134`), whose cards call `POST /action` through `VerbActions`
redirect to `/for-you` (`:495–500`). `/for-you` renders the For You `Feed`
(`:130–134`), whose cards call `POST /action` through `VerbActions`
(`packages/ui/src/components/for-you/FeedCard.tsx:13`), and `/search` renders
`AuditHistoryView` over `GET /audit` (`routes.tsx:304-308`).
`AuditHistoryView` over `GET /audit` (`routes.tsx:304–308`).
`MissionControlSurface`
(`packages/ui/src/components/mission-control/MissionControlSurface.tsx:9`)
still exists and renders the same `Feed`, but nothing imports it. UI detail
@@ -9,14 +9,14 @@ applies-when: |
source routing, bootstrap plan/apply, or the retained package install engine.
siblings: [agent-spec-and-startup.md, plugin-agent-image-context-pack.md]
prerequisite-reads: [../README.md, agent-spec-and-startup.md]
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Packaging, bootstrap and bundles
This module describes source at main commit
`712a61fbcc5ffe5a7c786fea12132567083ab507`. Source paths below are repository-relative.
`254122872cf477511514979a4300b695d77cd1f7`. Source paths below are repository-relative.
An npm CLI artifact and a `.rigbundle` have different builders and consumers; neither
source verification nor archive integrity establishes that a daemon has adopted an artifact.
@@ -9,14 +9,14 @@ applies-when: |
agent-image capture/fork/protection, or Claude guided-compaction behavior.
siblings: [packaging-bootstrap-bundles.md, agent-spec-and-startup.md]
prerequisite-reads: [../README.md, agent-spec-and-startup.md]
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Content libraries and compaction
This module describes source at main commit
`712a61fbcc5ffe5a7c786fea12132567083ab507`. Source paths below are repository-relative.
`254122872cf477511514979a4300b695d77cd1f7`. Source paths below are repository-relative.
Context packs, plugins and agent images have filesystem-backed content and daemon-side
discovery. Their consumers can read database identity, mutate files, deliver messages or
launch sessions; the whole layer is not a read-only catalog.
@@ -11,8 +11,8 @@ applies-when: |
vs tmux-metadata-key naming distinction.
siblings: [daemon-core.md, lifecycle-snapshot-restore.md]
prerequisite-reads: [../README.md, daemon-core.md]
last-verified-against-source: 264fade9
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Transport, Transcripts, Chat, Ask
@@ -22,7 +22,7 @@ capture/broadcast wrap tmux with honest errors; transcripts are bounded
`tmux capture-pane` snapshots written to files; chat is daemon-backed SQLite;
the daemon's `rig ask` service gathers evidence and never calls an LLM.
> Verified against source at main `264fade9`. Each count below sits beside the
> Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. Each count below sits beside the
> command that produces it; run the command from the repository root to refresh
> it.
@@ -44,7 +44,7 @@ source: **0** hits
**Axis 2 — tmux metadata keys are `@rigged_*`, and are correct as-is.** The
tmux metadata keys written at claim/bind time are a separate thing from MCP
tool names and were not renamed. `setRiggedMetadata` in
`packages/daemon/src/domain/claim-service.ts:176`–`180` writes exactly **5**
`packages/daemon/src/domain/claim-service.ts:180`–`184` writes exactly **5**
keys (`grep -c '@rigged_' packages/daemon/src/domain/claim-service.ts`):
`@rigged_node_id`, `@rigged_session_name`, `@rigged_rig_id`,
`@rigged_rig_name`, `@rigged_logical_id`. Do not blanket-sed `rigged` → `rig`.
@@ -98,8 +98,8 @@ Routes: `packages/daemon/src/routes/{transport,transcripts,ask,chat,whoami}.ts`
`unknown` one proceeds with an advisory `warning` (`:1321`). `--force` has
no effect on this path.
3. Two-step tmux send: a unique file/buffer pasted with `paste-buffer -d -r -p`
at every payload size (`packages/daemon/src/adapters/tmux.ts:587`) →
~200ms delay (`session-transport.ts:1378`) → separate `C-m` (`:1385`).
at every payload size (`packages/daemon/src/adapters/tmux.ts:604`) →
~200ms delay (`session-transport.ts:1378`) → separate named `Enter` (`:1385`).
Bracketed paste preserves multiline input in supporting TUIs; the payload
never enters a shell argument. A successful paste proves transport
execution, not runtime consumption.
@@ -180,8 +180,8 @@ remote daemon's ordinary local routes; the cross-host logic is in the CLI.
creation, before the harness boots
(`packages/daemon/src/domain/node-launcher.ts:181`).
2. Each rotation tick runs `tmux capture-pane -p -S -<lines>`
(`adapters/tmux.ts:994`) and atomically overwrites (temp file + rename,
`transcript-rotation.ts:143`)
(`adapters/tmux.ts:1020`) and atomically overwrites (temp file + rename,
`transcript-rotation.ts:146`)
`~/.openrig/transcripts/{rig-name}/{session-name}.log`. Defaults: 1000
trailing lines every 2 s (`transcript-rotation.ts:27`–`28`), tunable with
`OPENRIG_TRANSCRIPTS_LINES` and `OPENRIG_TRANSCRIPTS_POLL_INTERVAL_SECONDS`.
@@ -193,9 +193,9 @@ remote daemon's ordinary local routes; the cross-host logic is in the CLI.
4. `rig transcript <session> --tail N / --grep "pattern"` provides
agent-facing access.
5. On restore: a `--- SESSION BOUNDARY: … ---` marker is written before
re-launch (`restore-orchestrator.ts:1099`); each rotation tick keeps every
re-launch (`restore-orchestrator.ts:1100`); each rotation tick keeps every
boundary line as a header above the fresh capture
(`transcript-rotation.ts:119`). (Restore-side detail in
(`transcript-rotation.ts:122`). (Restore-side detail in
`lifecycle-snapshot-restore.md`.)
6. `rig ask` gathers rig summary plus transcript excerpts, chat excerpts,
insufficiency state, and guidance.
@@ -7,13 +7,13 @@ domains: [engineering-advisor, operating-advisor]
applies-when: |
Following workflow validation, instantiation, projection, routing, failure
recovery, lifecycle graph revisions, or the corresponding CLI/API.
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Workflow Runtime — Specs, Packets, and Lifecycle Graphs
Source snapshot: `712a61fbcc5ffe5a7c786fea12132567083ab507`. This describes the source at that commit;
Source snapshot: `254122872cf477511514979a4300b695d77cd1f7`. This describes the source at that commit;
it does not establish the version or behavior of a running daemon.
A workflow binds a specification to durable queue packets. The daemon
+103 -103
View File
@@ -12,8 +12,8 @@ applies-when: |
Project UI.
siblings: [content-surfaces.md, daemon-core.md, ../ui/project-and-for-you.md]
prerequisite-reads: [../README.md, daemon-core.md]
last-verified-against-source: e6a391b6
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# Workspace Primitive — RigSpec.workspace, Migrations 038/039, Missions/Projects/Slices
@@ -26,7 +26,7 @@ through `whoami` / node-inventory and gate per-item repo scope. Alongside it, a
the Project UI's mission / slice surfaces.
> Paths are relative to `packages/daemon/src/` unless prefixed `packages/` or
> `docs/`. Verified against source at main `e6a391b6`. Each count sits beside
> `docs/`. Verified against source at main `254122872cf477511514979a4300b695d77cd1f7`. Each count sits beside
> the command that produces it; run the commands from the repository root.
> The files / markdown / progress / steering surfaces are in the sibling
> `content-surfaces.md`.
@@ -60,13 +60,13 @@ Workspace initialization no longer seeds a getting-started mission (§3).
`RigSpec.workspace` is an **optional** typed block. Rigs without it stay valid;
`whoami` / node-inventory return a null workspace block in that case.
> Source: `domain/types.ts:1154-1161` (`WorkspaceSpec`), `:1144-1150`
> (`WorkspaceRepoSpec`), `:1177` (`RigSpec.workspace?`).
> Source: `domain/types.ts:1158–1165` (`WorkspaceSpec`), `:1148–1154`
> (`WorkspaceRepoSpec`), `:1181` (`RigSpec.workspace?`).
| Field | Shape | Notes |
|---|---|---|
| `workspaceRoot` | string | Verbatim from spec |
| `repos[]` | `{ name, path, kind }[]` | `path` resolved to absolute at parse time (`domain/rigspec-schema.ts:350-352`); authors may declare it relative to `workspaceRoot` in YAML |
| `repos[]` | `{ name, path, kind }[]` | `path` resolved to absolute at parse time (`domain/rigspec-schema.ts:350–352`); authors may declare it relative to `workspaceRoot` in YAML |
| `defaultRepo?` | string | active repo when no env override / cwd match |
| `knowledgeRoot?` | string | treated as `kind=knowledge` when surfaced |
@@ -74,7 +74,7 @@ Workspace initialization no longer seeds a getting-started mission (§3).
(`grep '^export const WORKSPACE_KINDS' packages/daemon/src/domain/types.ts | grep -o '"[a-z]*"' | wc -l`):
`user`, `project`, `knowledge`, `lab`, `delivery`.
> Source: `domain/types.ts:1140-1141` (`WORKSPACE_KINDS` / `WorkspaceKind`).
> Source: `domain/types.ts:1144–1145` (`WORKSPACE_KINDS` / `WorkspaceKind`).
### 1.1 Persistence — migration 038 + RigRepository
@@ -82,9 +82,9 @@ Migration **038** adds `workspace_json TEXT` to the `rigs` table. It holds the
typed `RigSpec.workspace` block as JSON when a rig declares one; NULL for rigs
without a workspace block.
> Source: `db/migrations/038_workspace_primitive.ts:16-21`
> Source: `db/migrations/038_workspace_primitive.ts:16–21`
> (`ALTER TABLE rigs ADD COLUMN workspace_json TEXT` at `:19`); doc comment
> `:3-15`.
> `:3–15`.
`RigRepository.setRigWorkspace(rigId, workspace)` persists it (UPDATE
`workspace_json` + `updated_at`); `getRigWorkspace(rigId)` reads it back,
@@ -93,9 +93,9 @@ are **defensive no-ops** when the column is absent (`hasRigColumn` probe) —
older test fixtures that bypass the canonical migration list don't have the
column; the setter's contract is "best-effort persistence".
> Source: `domain/rig-repository.ts:173-178` (setter; column probe `:174`),
> `:181-191` (getter; probe `:182`; parse failure → null `:188-190`);
> `hasRigColumn` `:323`.
> Source: `domain/rig-repository.ts:174–179` (setter; column probe `:175`),
> `:182–192` (getter; probe `:183`; parse failure → null `:189–191`);
> `hasRigColumn` `:324`.
The block is written only when declared. **3** call sites write it
(`git grep -n 'setRigWorkspace(' -- packages/daemon/src | grep -v 'rig-repository.ts' | wc -l`):
@@ -105,10 +105,10 @@ workspace-only import route `POST /api/rigs/import/workspace`
(`rig import --workspace-only --target-rig <rigId>`), which validates and
normalizes the block and writes it only when it differs from the stored one.
> Source: `domain/rigspec-instantiator.ts:617` (`materializeValidatedSpec`),
> `:653-655`; `:1258` (`instantiateOnce`), `:1345-1347`;
> `routes/rigspec.ts:131-169` (validation `:154`; compare-and-write
> `:164-166`), mounted `server.ts:735`; CLI
> Source: `domain/rigspec-instantiator.ts:618` (`materializeValidatedSpec`),
> `:654–656`; `:1259` (`instantiateOnce`), `:1346–1348`;
> `routes/rigspec.ts:132–170` (validation `:155`; compare-and-write
> `:165–167`), mounted `server.ts:735`; CLI
> `packages/cli/src/commands/import.ts:82`.
### 1.2 Runtime resolution — workspace-resolver
@@ -117,13 +117,13 @@ normalizes the block and writes it only when it differs from the stored one.
`whoami-service`) returns the `WhoamiWorkspaceBlock` or `null` when no spec.
`activeRepo` resolution: a non-blank `envOverride` wins **verbatim** — even an
unknown repo name is honored, because operators set `OPENRIG_TARGET_REPO`
consciously (the comment at `:35-36`); otherwise `defaultRepo` is used **only
consciously (the comment at `workspace-resolver.ts:35–36`); otherwise `defaultRepo` is used **only
if it names a declared repo**. `knowledgeKind` is `"knowledge"` when
`knowledgeRoot` is declared, else `null`.
> Source: `domain/workspace/workspace-resolver.ts:26-52` (resolver; null
> without a spec `:32`; env override `:39-40`; declared `defaultRepo`
> `:41-42`; `knowledgeKind` `:50`).
> Source: `domain/workspace/workspace-resolver.ts:26–52` (resolver; null
> without a spec `:32`; env override `:39–40`; declared `defaultRepo`
> `:41–42`; `knowledgeKind` `:50`).
`whoami-service` reads the persisted spec and resolves with the query's
`targetRepoOverride`, falling back to the daemon's own
@@ -131,10 +131,10 @@ if it names a declared repo**. `knowledgeKind` is `"knowledge"` when
`OPENRIG_TARGET_REPO` as `?targetRepo=`, which the route passes on as
`targetRepoOverride`.
> Source: `domain/whoami-service.ts:333-338` (`getRigWorkspace` +
> Source: `domain/whoami-service.ts:333–338` (`getRigWorkspace` +
> `resolveWorkspaceContext`, env fallback `:337`); `whoami` returns
> `workspace` in its payload `:350`; `routes/whoami.ts:29`;
> `packages/cli/src/commands/whoami.ts:261-262`.
> `packages/cli/src/commands/whoami.ts:261–262`.
`resolveNodeWorkspace({ spec, cwd })` (consumed by `node-inventory`) derives a
per-node `NodeWorkspaceInfo` by picking the **longest declared repo path that
@@ -145,12 +145,12 @@ Containment uses a `path.relative` boundary check (not a string `startsWith`)
so `/foo/bar` does not match `/foo/bar-other`. Node-inventory reads
`workspace_json` with its own queries rather than through `RigRepository`.
> Source: `domain/workspace/workspace-resolver.ts:57-95` (longest match
> `:67-73`; knowledge fallback `:77-79`; default-repo fallback `:82-88`),
> `isInside` `:97-103`; `domain/node-inventory.ts:626`
> (`workspace: resolveNodeWorkspace(...)`), readers `:711-720`
> (`readRigWorkspaceJson`) and `:637-647` (`readAllRigWorkspaceJson`,
> batched); `NodeWorkspaceInfo` `domain/types.ts:791-795`.
> Source: `domain/workspace/workspace-resolver.ts:57–95` (longest match
> `:67–73`; knowledge fallback `:77–79`; default-repo fallback `:82–88`),
> `isInside` `:97–103`; `domain/node-inventory.ts:626`
> (`workspace: resolveNodeWorkspace(...)`), readers `:711–720`
> (`readRigWorkspaceJson`) and `:637–647` (`readAllRigWorkspaceJson`,
> batched); `NodeWorkspaceInfo` `domain/types.ts:792–796`.
### 1.3 Per-item repo scope — migration 039 + queue validation
@@ -161,10 +161,10 @@ is NULL when the item is unambiguous against the rig's `default_repo` or no
workspace is declared, and Mission Control views surface the field for
cross-rig handoff clarity.
> Source: `db/migrations/039_queue_target_repo.ts:16-22`
> Source: `db/migrations/039_queue_target_repo.ts:16–22`
> (`ALTER TABLE queue_items ADD COLUMN target_repo TEXT` at `:19`;
> `CREATE INDEX … idx_queue_items_target_repo` at `:20`); doc comment
> `:3-15`; CLI flag `packages/cli/src/commands/queue.ts:430`.
> `CREATE INDEX … idx_queue_items_target_repo` at `039_queue_target_repo.ts:20`); doc comment
> `:3–15`; CLI flag `packages/cli/src/commands/queue.ts:431`.
`validateTargetRepo` checks an explicit `targetRepo` against the **source
rig's** `RigSpec.workspace.repos[]`: it parses the source session with
@@ -181,10 +181,10 @@ because the source rig's workspace lives on the source host. A handoff that
inherits the source row's `targetRepo` without an override is not
re-validated.
> Source: `routes/queue.ts:94-121` (fail-open guards `:100`, `:104`, `:107`,
> Source: `routes/queue.ts:94–121` (fail-open guards `:100`, `:104`, `:107`,
> `:110`; `getRigWorkspace` `:109`; `unknown_target_repo` + `knownRepos`
> `:112-118`); calls `:450`, `:632`, `:705` in handlers `:400`, `:603`,
> `:679`; ordering note `:442-448`; inherited value `:629-630`; mounted
> `:112–118`); calls `:450`, `:633`, `:706` in handlers `:400`, `:604`,
> `:680`; ordering note `:442–448`; inherited value `:630–631`; mounted
> `server.ts:785`.
## 2. Workspace HTTP route — frontmatter validator and doctor
@@ -203,8 +203,8 @@ endpoints (`grep -c -E 'app\.(get|post)\(' packages/daemon/src/routes/workspace.
and returns its report. 503 `settings_unavailable` without a settings
store; 500 `doctor_failed` when it throws.
> Source: `routes/workspace.ts:16` (no mutation), `:37-74` (validate;
> root-required `:46-48`; kind check `:50-58`; 500 `:72`), `:85-160`
> Source: `routes/workspace.ts:16` (no mutation), `:37–74` (validate;
> root-required `:46–48`; kind check `:50–58`; 500 `:72`), `:85–160`
> (doctor; 503 `:87`; `runWorkspaceDoctor` call `:145`; 500 `:158`);
> `domain/workspace/workspace-doctor.ts:643`; mounted `server.ts:786`.
@@ -222,10 +222,10 @@ given): `user`/`project` → `["doc"]`; `knowledge`/`lab`/`delivery` →
default; skips `node_modules` / `.git` / `.worktrees` / `dist` / `build`;
hard cap `maxFiles` default 10000.
> Source: `domain/workspace/frontmatter-validator.ts:12` (advisory), `:23-27`
> (gap kinds), `:53` (status enum), `:56-62` (per-kind required), `:81-83`
> (defaults), `:90-121` (walk; skip-dirs `:105-111`), `:160-172`
> (missing-frontmatter behavior), `:201-226` (required + status check), `:239`
> Source: `domain/workspace/frontmatter-validator.ts:12` (advisory), `:23–27`
> (gap kinds), `:53` (status enum), `:56–62` (per-kind required), `:81–83`
> (defaults), `:90–121` (walk; skip-dirs `:105–111`), `:160–172`
> (missing-frontmatter behavior), `:201–226` (required + status check), `:239`
> (opening delimiter).
CLI surface: `rig workspace validate [root]` and `rig workspace doctor`. The
@@ -268,20 +268,20 @@ runtime state, and the retired `artifacts/`, `evidence/`, `progress/`,
are not emitted.
> Source: `domain/workspace/default-workspace-scaffold.ts:7` (dirs),
> `:20-30` (`project.yaml`), `:32-36` (`workspace.yaml`), `:38-41`
> (`.gitignore`), `:43-54` (`workspaceScaffoldDirs`,
> `workspaceScaffoldFiles`), `:100-153` (`ensureDefaultWorkspace`; conflicts
> checked before writes `:117-135`; write-if-missing `:140`);
> `:20–30` (`project.yaml`), `:32–36` (`workspace.yaml`), `:38–41`
> (`.gitignore`), `:43–54` (`workspaceScaffoldDirs`,
> `workspaceScaffoldFiles`), `:100–153` (`ensureDefaultWorkspace`; conflicts
> checked before writes `:117–135`; write-if-missing `:140`);
> `domain/instance-initialization.ts:39` (`ensureOpenRigInstance`; default
> workspace root `:44`; dry-run plan `:83`; apply `:103`); default
> `workspace.root` `domain/user-settings/settings-store.ts:27-30`; daemon
> `index.ts:242`; CLI `packages/cli/src/daemon-lifecycle.ts:635`;
> `workspace.root` `domain/user-settings/settings-store.ts:27–30`; daemon
> `index.ts:242`; CLI `packages/cli/src/daemon-lifecycle.ts:658`;
> `packages/cli/src/commands/config-init-workspace.ts:10` (import), `:13`
> (re-export), `:28` (`--force`), `:56-67` (`runInitWorkspace`);
> `routes/config.ts:56-65`, mounted `server.ts:773`; `rig scope`
> (re-export), `:28` (`--force`), `:56–67` (`runInitWorkspace`);
> `routes/config.ts:56–65`, mounted `server.ts:773`; `rig scope`
> `packages/cli/src/commands/scope.ts:2071`; parity
> `packages/daemon/test/getting-started-narrative-parity.test.ts:15-25`,
> retired entries `:43-57`.
> `packages/daemon/test/getting-started-narrative-parity.test.ts:15–25`,
> retired entries `:43–57`.
## 4. File-backed missions / slices tree
@@ -301,13 +301,13 @@ directories. Listing, detail and mission-status caches are time-bounded (60 s
default); `invalidate()` drops all three plus the queue-membership index.
`isReady()` is true when any configured slice root exists on disk.
> Source: `domain/slices/slice-indexer.ts:2-15` (contract + no new state),
> `:238` (TTL), `:297-299` (`isReady`), `:331-336` (`invalidate`), `:476-521`
> (`readSliceLocations`; nested-vs-flat `:495-517`), `:61-88` (`SliceRecord`:
> Source: `domain/slices/slice-indexer.ts:2–15` (contract + no new state),
> `:238` (TTL), `:297–299` (`isReady`), `:331–336` (`invalidate`), `:476–521`
> (`readSliceLocations`; nested-vs-flat `:495–517`), `:61–88` (`SliceRecord`:
> `missionId` `:65`, `slicePath` `:67`, `qitemIds` `:77`, `proofPacket`
> `:81`), `:90-111` (`SliceListEntry`, which carries `qitemCount` /
> `hasProofPacket` instead); startup `startup.ts:1666`, `:1673-1678`,
> `:1691-1696`; default `domain/user-settings/settings-store.ts:479`.
> `:81`), `:90–111` (`SliceListEntry`, which carries `qitemCount` /
> `hasProofPacket` instead); startup `startup.ts:1668`, `:1675–1680`,
> `:1693–1698`; default `domain/user-settings/settings-store.ts:479`.
### 4.2 SliceDetailProjector — 0.3.0
@@ -325,12 +325,12 @@ workflow runtime is constructed the projector silently degrades to v0
behavior (`workflowBinding`, `specGraph`, `phaseDefinitions` and
`currentStep` all `null`).
> Source: `domain/slices/slice-detail-projector.ts:1-20` (six-tab contract +
> v1 enrichment), `:16-20` (v0 phase taxonomy removed), `:64`
> (`StoryEvent.phase`), `:177-203` (`SliceDetailPayload`), `:205-213`
> Source: `domain/slices/slice-detail-projector.ts:1–20` (six-tab contract +
> v1 enrichment), `:16–20` (v0 phase taxonomy removed), `:64`
> (`StoryEvent.phase`), `:177–203` (`SliceDetailPayload`), `:205–213`
> (optional `workflowSpecCache`); startup degrade path
> `startup.ts:1697-1708` (comment `:1697-1703`;
> `workflowSpecCache: workflowRuntime?.specCache` `:1707`).
> `startup.ts:1699–1710` (comment `:1699–1705`;
> `workflowSpecCache: workflowRuntime?.specCache` `:1709`).
### 4.3 Slices routes — 0.3.0
@@ -358,12 +358,12 @@ discipline:** literal `/` / `/refresh` / `/:name/proof-asset/*` /
`/:name/doc/*` are registered BEFORE the dynamic `/:name` so they are not
shadowed.
> Source: `routes/slices.ts:33-198` (handlers; route-order comments
> `:36-37`, `:118`, `:126-127`, `:175`; 503s `:40-46`; filters `:31`,
> `:47-53`; refresh `:54-57`; `boundToWorkflow` `:67-93`; readiness +
> `missions` sidecar `:99-110`; `/refresh` `:119-124`; proof asset
> `:131-154`, cache header `:235`, ranges `:237-266`; doc `:158-173`;
> project scope `:180-190`); mounted `server.ts:804`.
> Source: `routes/slices.ts:33–198` (handlers; route-order comments
> `:36–37`, `:118`, `:126–127`, `:175`; 503s `:40–46`; filters `:31`,
> `:47–53`; refresh `:54–57`; `boundToWorkflow` `:67–93`; readiness +
> `missions` sidecar `:99–110`; `/refresh` `:119–124`; proof asset
> `:131–154`, cache header `:235`, ranges `:237–266`; doc `:158–173`;
> project scope `:180–190`); mounted `server.ts:804`.
### 4.4 Missions route — 0.3.1
@@ -396,15 +396,15 @@ next `/api/slices` read sees the new status. The daemon is the audit-trail surfa
optimistic localStorage mirror. 404 `mission_not_found` when no slices match;
500 `mission_complete_write_failed` on a write error.
> Source: `routes/missions.ts:45-88` (GET; 503s `:46-64`; 404 `:68-70`;
> readiness + status `:77-78`; response `:79-87`), `:95-125` (complete; 500
> `:110-117`; cache drop `:123`), `:132-163` (`writeMissionStatusComplete`;
> manifest `:133-141`; node file `:145-162`), `:169-171`
> (`computeMissionPath` up two levels), `:179-186` (`readMissionStatus`),
> `:192-198` (`readMissionWorkflowSpec`), `:226-236`
> Source: `routes/missions.ts:45–88` (GET; 503s `:46–64`; 404 `:68–70`;
> readiness + status `:77–78`; response `:79–87`), `:95–125` (complete; 500
> `:110–117`; cache drop `:123`), `:132–163` (`writeMissionStatusComplete`;
> manifest `:133–141`; node file `:145–162`), `:169–171`
> (`computeMissionPath` up two levels), `:179–186` (`readMissionStatus`),
> `:192–198` (`readMissionWorkflowSpec`), `:226–236`
> (`computeMissionTopology`); node-file precedence
> `domain/scope/node-file.ts:17`; readiness
> `domain/proof/judgments.ts:288-295`; UI mirror
> `domain/proof/judgments.ts:288–295`; UI mirror
> `packages/ui/src/hooks/useCompletedMissions.ts:15`; mounted `server.ts:815`;
> §0: `routes/missions.ts` absent at `v0.3.0` ⇒ **0.3.1**.
@@ -430,12 +430,12 @@ overlap is a documented seam). Endpoints:
**Route-order discipline:** every literal path is registered BEFORE the bare
`/:projectId` catchall, which comes last.
> Source: `routes/projects.ts:27-373` (handlers; route-order notes `:25`,
> `:238`, `:307`, `:320`, `:337-338`, `:364`); worker sources `:98-109`;
> shadow `:111-125`; lease / reclaim / project `:135-235`
> (`evaluateDeadnessFirst` `:140-142`); attempts and eligible `:242-304`;
> lease and list `:308-334`; SSE `:339-362`; `/:projectId` `:365-370`;
> error-code → HTTP mapping `:58-95`; CLI
> Source: `routes/projects.ts:27–373` (handlers; route-order notes `:25`,
> `:238`, `:307`, `:320`, `:337–338`, `:364`); worker sources `:98–109`;
> shadow `:111–125`; lease / reclaim / project `:135–235`
> (`evaluateDeadnessFirst` `:140–142`); attempts and eligible `:242–304`;
> lease and list `:308–334`; SSE `:339–362`; `/:projectId` `:365–370`;
> error-code → HTTP mapping `:58–95`; CLI
> `packages/cli/src/commands/project.ts:46`; mounted `server.ts:787`.
## 5. UI route reality — source-verified at routes.tsx
@@ -447,22 +447,22 @@ mission / project / slice destinations as they exist:
| Path | routes.tsx reality | routes.tsx:NN |
|---|---|---|
| `/project` (workspace) | **REAL route** — `WorkspaceScopePage` | `:136-140` |
| `/project/mission/$missionId` | **REAL route** — `MissionScopePage` | `:142-146` |
| `/project/slice/$sliceId` | **REAL route** — `SliceScopePage` | `:148-152` |
| `/files` | **REAL route** — `FilesWorkspace` (see content-surfaces.md) | `:248-252` |
| `/mission-control` | **`<Navigate to="/for-you" />`** redirect-stub (the Mission Control *system* lives in the daemon at `/api/mission-control`, not as a UI destination) | `:496-500` |
| `/slices` | **`<Navigate to="/project" />`** redirect-stub | `:503-507` |
| `/slices/$name` | **`<Navigate to="/project/slice/$sliceId" />`** redirect-stub | `:509-516` |
| `/progress` | **`<Navigate to="/project" />`** redirect-stub (folds into Project tabs) | `:519-523` |
| `/steering` | **`<Navigate to="/project" />`** redirect-stub (folds into the Project workspace overview tab) | `:526-530` |
| `/project` (workspace) | **REAL route** — `WorkspaceScopePage` | `:136–140` |
| `/project/mission/$missionId` | **REAL route** — `MissionScopePage` | `:142–146` |
| `/project/slice/$sliceId` | **REAL route** — `SliceScopePage` | `:148–152` |
| `/files` | **REAL route** — `FilesWorkspace` (see content-surfaces.md) | `:248–252` |
| `/mission-control` | **`<Navigate to="/for-you" />`** redirect-stub (the Mission Control *system* lives in the daemon at `/api/mission-control`, not as a UI destination) | `:496–500` |
| `/slices` | **`<Navigate to="/project" />`** redirect-stub | `:503–507` |
| `/slices/$name` | **`<Navigate to="/project/slice/$sliceId" />`** redirect-stub | `:509–516` |
| `/progress` | **`<Navigate to="/project" />`** redirect-stub (folds into Project tabs) | `:519–523` |
| `/steering` | **`<Navigate to="/project" />`** redirect-stub (folds into the Project workspace overview tab) | `:526–530` |
| `/missions` (top-level) | **NO route** — missions are reached only via `/project/mission/$missionId` | absent: **0** (`grep -c 'path: "/missions"' packages/ui/src/routes.tsx`) |
| `/markdown` | **NO route** — markdown is a component in file/drawer surfaces, not a destination (see content-surfaces.md) | absent: **0** (`grep -c 'path: "/markdown"' packages/ui/src/routes.tsx`) |
> Source: `packages/ui/src/routes.tsx:136-152` (project routes), `:248-252`
> (`/files`), `:484-530` (redirect block, which also redirects `/context` to
> `/topology`), `:539-597` (route tree); `/api/mission-control` mounted
> `packages/daemon/src/server.ts:791-794`.
> Source: `packages/ui/src/routes.tsx:136–152` (project routes), `:248–252`
> (`/files`), `:484–530` (redirect block, which also redirects `/context` to
> `/topology`), `:539–597` (route tree); `/api/mission-control` mounted
> `packages/daemon/src/server.ts:791–794`.
Net: the daemon `/api/missions` + `/api/slices` + `/api/projects` routes are
real. The **operator-facing UI** consumes the missions and slices routes
@@ -479,18 +479,18 @@ through the `/project*` destinations; it does not call `/api/projects`
- **Optional + valid-without:** rigs without a `workspace` block stay valid;
whoami / node-inventory return null and queue target-repo validation fails
open (`workspace-resolver.ts:32`, `:62`; `rig-repository.ts:174`, `:182`;
open (`workspace-resolver.ts:32`, `:62`; `rig-repository.ts:175`, `:183`;
`routes/queue.ts:110`).
- **Defensive column access:** `RigRepository` probes for `workspace_json`
(`rig-repository.ts:174`, `:182`), node-inventory's own reads fall back to
null on error (`node-inventory.ts:645`, `:717-719`), and the queue
repository detects `target_repo` (`queue-repository.ts:744`), so partial
(`rig-repository.ts:175`, `:183`), node-inventory's own reads fall back to
null on error (`node-inventory.ts:645`, `:717–719`), and the queue
repository detects `target_repo` (`queue-repository.ts:752`), so partial
test fixtures don't crash. Migrations 038/039 ship separately so a fixture
can apply only the half it needs (`038_workspace_primitive.ts:12-14`).
can apply only the half it needs (`038_workspace_primitive.ts:12–14`).
- **Read-only projection, no new state for the tree:** SliceIndexer /
SliceDetailProjector add NO migration / event type; only the workspace
declaration (038) + per-item scope (039) touch SQLite
(`slice-indexer.ts:14-15`).
(`slice-indexer.ts:14–15`).
- **Advisory, never deletes:** the frontmatter validator never modifies files
(`frontmatter-validator.ts:12`), and the workspace scaffold never deletes or
overwrites operator content (`default-workspace-scaffold.ts:140`;
@@ -499,12 +499,12 @@ through the `/project*` destinations; it does not call `/api/projects`
## Open items
- `resolveWorkspaceContext` honors an `envOverride` **verbatim even when it
names no declared repo** (`workspace-resolver.ts:39-40`), whereas the queue
names no declared repo** (`workspace-resolver.ts:39–40`), whereas the queue
route **rejects** an unknown `target_repo` against the same `repos[]`
(`routes/queue.ts:112-118`). These two surfaces apply opposite policies to
(`routes/queue.ts:112–118`). These two surfaces apply opposite policies to
an unknown repo name (whoami trusts the operator; queue validates). The
resolver's comment at `:35-36` states the verbatim behavior is intended, but
the comment line before it (`:34`) and `whoami-service.ts:331-332` describe
resolver's comment at `workspace-resolver.ts:35–36` states the verbatim behavior is intended, but
the comment line before it (`workspace-resolver.ts:34`) and `whoami-service.ts:331–332` describe
a repo-name check that the code does not perform. Stated as-is; the
divergence is not reconciled in source.
- The `/api/projects` *classifier* and the `/project*` *workspace UI*
+3 -3
View File
@@ -9,13 +9,13 @@ applies-when: |
the source that implements a command. Use command help for invocation details.
siblings: [README.md, codemap.md]
prerequisite-reads: [../reference/help.md]
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# CLI Reference — Registered Commands and Options
Verified against source commit `712a61fbcc5ffe5a7c786fea12132567083ab507`.
Verified against source commit `254122872cf477511514979a4300b695d77cd1f7`.
The inventory below comes from the actual Commander tree returned by
[`createProgram()`](../../packages/cli/src/index.ts), not a grep of command
strings or an installed CLI from a different commit.
+3 -3
View File
@@ -9,14 +9,14 @@ applies-when: |
Follow the module and source entry points below, then read the implementation.
siblings: [README.md, arteries.md, test-layers.md, cli-reference.md]
prerequisite-reads: [README.md]
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# OpenRig codemap
The paths and named entry points below were checked at main
`712a61fbcc5ffe5a7c786fea12132567083ab507`. This is a navigation map, not a replacement for
`254122872cf477511514979a4300b695d77cd1f7`. This is a navigation map, not a replacement for
reading the implementation. Linked documents retain their individual verification stamps.
[README.md](README.md) lists the complete document tree and commands to derive its inventory.
+5 -5
View File
@@ -6,13 +6,13 @@ topics: [knowledge-and-context, frontmatter]
domains: [engineering-advisor, operating-advisor]
applies-when: |
Writing or refreshing a source-grounded document under docs/as-built/.
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
---
# As-Built Frontmatter
Source snapshot: `712a61fbcc5ffe5a7c786fea12132567083ab507`. This describes the source at that commit;
Source snapshot: `254122872cf477511514979a4300b695d77cd1f7`. This describes the source at that commit;
it does not establish the version or behavior of a running daemon.
Frontmatter makes an as-built page discoverable and records the source it
@@ -44,8 +44,8 @@ that an installed package or a running system uses that source. Keep runtime
observations separately qualified.
```yaml
last-verified-against-source: 712a61fbcc5ffe5a7c786fea12132567083ab507
last-updated: 2026-10-02
last-verified-against-source: 254122872cf477511514979a4300b695d77cd1f7
last-updated: 2026-10-03
```
Check that the commit resolves with `git cat-file -t <commit>` in a checkout