mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 22:58:42 -10:00
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:
1 parent
eeca3e02a7
commit
b6d37bfadc
19 files changed
+497
-494
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user