mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
Four lines in docs/as-built/arteries.md's regression log, in its existing form: date, issue, artery, and which layer missed it. Nothing else in the file changes. - F1 (0.7.0 candidate), launch: a fresh Codex kernel stopped at Codex's hook review. Caught at the cut by native first-user and daily runs; missed by the stub layer. Fixed in #1824. - B6 (0.7.1 candidate), launch and resume: an upgraded Codex seat stopped at "Hooks need review: 6". Caught at the cut by a native upgrade run; missed by source tests, stub scenarios and the installed-upgrade check. Fixed in #1948. - Pane-only attention (0.7.1 candidate): a Codex seat at a prompt only its pane showed read as working. Caught at the cut by real work; missed by fixtures that all carried hook signals. Fixed in #1946. - #1955, delivery: quoted approval text in a working Claude seat's output read as a waiting prompt. Present since 0.7.0, found during the 0.7.1 cut by real work, and fixed for 0.7.2. The log exists so each line becomes a scenario; where one is owed, the line names it. Docs only: +4/-0.
119 lines
15 KiB
Markdown
119 lines
15 KiB
Markdown
---
|
||
kind: as-built
|
||
title: Arteries — where a small change has a large effect
|
||
status: active
|
||
applies-when: |
|
||
You are about to change, or review a change to, an area that many other paths depend on:
|
||
message delivery, launch and resume, the queue, rig identity, skill projection, native
|
||
process observation, migrations, or restore. Read this to see what depends on the area and
|
||
what broke there before, then go and read the code.
|
||
siblings: [README.md, test-layers.md]
|
||
prerequisite-reads: [../../ARCHITECTURE.md]
|
||
last-verified-against-source: e8f0ab340db773392ec8be75b072d1c0f3068a50
|
||
last-updated: 2026-10-08
|
||
---
|
||
|
||
# Arteries: where a small change has a large effect
|
||
|
||
## Read this first: this map is not the territory
|
||
|
||
- **This page is a zoomed-out view, not a description of OpenRig.** It names a few areas where
|
||
small changes caused large regressions before. OpenRig is much larger than this page. Code,
|
||
behaviour and risk that are not listed here exist, and some of it is riskier than anything below.
|
||
- **It is incomplete by construction.** A pull request is not safe because its files are missing
|
||
from this list.
|
||
- **It may be stale.** It was checked against commit `e8f0ab34`. Files and dependencies move. If
|
||
this page and the code disagree, the code is right; fix the page.
|
||
- **Use it to change altitude, then go to the ground:** read the code, run the commands, check the
|
||
behaviour. When you are lost, go and look rather than leaning on this page.
|
||
|
||
## The arteries
|
||
|
||
Paths are relative to `packages/`. The key files are entry points, not the full extent of each
|
||
area.
|
||
|
||
| Artery | Key files | What depends on it | Past regressions and lessons |
|
||
|---|---|---|---|
|
||
| **Message delivery to seats** | `daemon/src/domain/session-transport.ts`, `daemon/src/domain/seat-delivery-guard.ts`, `daemon/src/adapters/tmux.ts` | Every `rig send`, queue wake, nudge, watchdog wake and handoff | #150 (the fix for #142) stopped typing into a bare shell. It then refused managed Codex seats (fixed by #171 before release) and every Claude seat launched with an explicit permission mode, which shipped in 0.6.2 as #197 (fixed by #220). The delivery guard's rule that a name must resolve to exactly one target, added in #109 (0.6.0), was involved in #141 (fixed by #151), the delivery half of #174 (fixed by #181) and #188 (fixed by #189). Since #310/#327, a Claude seat whose runtime can't be observed still receives ordinary messages with a warning; only a plain idle shell or the wrong recipient is refused. |
|
||
| **Launch, relaunch, resume** | `daemon/src/adapters/claude-code-adapter.ts`, `daemon/src/domain/claude-managed-launch.ts`, `daemon/src/adapters/codex-runtime-adapter.ts`, `daemon/src/domain/codex-daemon-support.ts`, `daemon/src/adapters/pi-runtime-adapter.ts`, `daemon/src/domain/seat-launch-environment.ts`, `daemon/src/domain/native-resume-probe.ts`, `daemon/src/domain/startup-orchestrator.ts` | Every seat start, restore and permission mode, plus the non-interruptive, `permission_policy`, effort, kernel operational-authority and team permission-default settings that each launch and resume command carries | Always setting `CLAUDE_CONFIG_DIR` gave explicit-mode Claude seats a blank config (#154). #225 fixed which config home is selected, and #565 writes the onboarding and trust bootstrap into that home. Codex seats launch with `--no-daemon` only when `codex --help` shows support (#69, #77); when support is unknown, OpenRig refuses to launch, so a failing or slow `codex --help` blocks the seat. The readiness window defaults to 30 seconds and can time out under load (#182); since #643 it is configurable as `runtime.readiness_timeout_seconds` (env `OPENRIG_RUNTIME_READINESS_TIMEOUT_SECONDS`, 1–600). A git worktree's `.git` file broke the Codex sandbox (#121; see #126). #618 moved non-managed Claude, Codex and Pi launches behind the seat launch environment, and #628, #638 and #666 followed within days. Pi launch and resume carry `--thinking` from the seat's resolved effort (OMP does not). |
|
||
| **Queue claim, pickup, wakes** | `daemon/src/domain/queue-repository.ts`, `daemon/src/domain/queue-pickup.ts`, `daemon/src/domain/queue-wake-ladder.ts`, `daemon/src/routes/require-sender-identity.ts`, `cli/src/client.ts` | All durable work between seats | After a slow `/healthz`, the CLI host-qualified a local sender and claims failed (#131, fixed by #135). Done-and-closed items read as stalled forever (#164, fixed by #176). |
|
||
| **Rig identity: same names, archived and deleted rigs** | `daemon/src/domain/rig-repository.ts`, `daemon/src/domain/rig-lifecycle-service.ts`, `daemon/src/domain/rig-teardown.ts`, `daemon/src/domain/seat-handover-service.ts`, `daemon/src/domain/running-name-guard.ts` | Seat resolution, `rig remove`, restore, importing a rig from YAML | Archived rigs still matched seat references, and `rig remove` killed another rig's live seat with the same name (#174, fixed by #181). Replacing stopped same-name rig generations on YAML import followed in #196. |
|
||
| **Projection of skills and files** | `daemon/src/domain/rigspec-instantiator.ts`, `daemon/src/domain/projection-planner.ts`, and each runtime adapter's `project()` (Claude Code, Codex, Pi, OMP, stub) | What every agent is given at start | Codex seats skipped skills that a Claude sibling had already projected (#159, fixed by #162). File-shaped entries failed with ENOTDIR (fixed by #185). |
|
||
| **Native process observation** | `daemon/src/domain/native-process-lineage.ts` (parses `ps -Ao pid,ppid,pgid,tpgid,ucomm,lstart,command`) | Liveness, identity verdicts, delivery checks | Processes whose command name contains a space were dropped (#134, fixed by #82). Non-English locales broke the date parsing (fixed by #239, which runs `ps` with `LC_ALL=C`). #220 also changed this file to recognise managed Claude seats behind launch wrappers. Claude identity proof (wrappers, launcher shims, conversation rotation, effort, version-named binaries, launch `--settings`) changed ten more times after 0.6.3. |
|
||
| **Schema and migrations** | `daemon/src/db/migrations/`, `daemon/src/db/all-migrations.ts`, `daemon/src/db/migrate.ts` | Every install, and every upgrade of a long-lived install | Parallel pull requests pick the same next number: #112 adds `086_rig_kernel_variant` while `main` already has `086_classification_fields_and_attempts`, and #155 and #195 landed as 090 and 091. `migrate.ts` applies by migration name in name order, so a duplicate number is not skipped, but its order relative to the other migration changes; 093 is unused at `e8f0ab34`. A long-lived install upgrading across several releases applies many migrations in one start, against real data. |
|
||
| **Restore and recovery** | `daemon/src/domain/restore-orchestrator.ts`, `daemon/src/adapters/{claude,codex,pi,omp}-resume.ts`, `daemon/src/domain/reconciler.ts`, `daemon/src/domain/restore-check-service.ts`, `daemon/src/domain/startup-orchestrator.ts`, `daemon/src/adapters/tmux.ts` | Reboots, power loss, daemon restarts | Detached seats kept stale tmux pane ids after a reboot (#141, fixed by #151 in `tmux.ts`). `restore-check` read queue files and Claude hooks from a fixed shared-docs path (#130); #139 made it use the configured root, and #328 replaced the queue-file check with a probe of the daemon's queue store and checks the selected Claude hooks. After a fresh relaunch, a stale Codex restore attention flag never clears (#116). Full-restore recovery of OMP seats is open (#41). |
|
||
|
||
## How reviews use this page
|
||
|
||
- **Find where the effect lands.** A reviewer notes which artery a pull request touches, if any,
|
||
and what depends on it.
|
||
- **At an artery, review the effect, not only the diff.** Whatever the diff size, the reviewer
|
||
states the downstream effects and checks behaviour with a scratch reproduction or a test.
|
||
- **Away from the arteries,** small pull requests keep a normal review. Scrutiny goes where the
|
||
effect is.
|
||
|
||
## The scenario rule
|
||
|
||
**A pull request that touches an artery adds or extends a stub-agent scenario, or says in its
|
||
description why it cannot.** Reviewers check for one or the other. Scenarios live in
|
||
`packages/test-system/scenarios/`; [ARCHITECTURE.md](../../ARCHITECTURE.md#a-scenario) says how to
|
||
write and run one, and [test-layers.md](test-layers.md) says what each test layer can and cannot
|
||
catch.
|
||
|
||
The goal is that every artery has at least one scenario whose seeded regression fails, so a small
|
||
change to an artery meets a real end-to-end check before it merges, not only a reviewer's reading.
|
||
|
||
### Scenario coverage today
|
||
|
||
At commit `e8f0ab34`. For why the stub cannot reach some of these yet, see "What a stub can and
|
||
cannot prove" in [test-layers.md](test-layers.md).
|
||
|
||
- **Queue durability across a daemon restart: covered.** CI runs
|
||
`queue-baton-survives-restart.yaml` healthy, then with a seeded `baton-drop` fault that must
|
||
fail, then healthy again (#243). It runs `rig queue claim` and checks the destination after the
|
||
restart, but pickup, wakes and claim-identity edge cases (such as the #131 sender case) have no
|
||
scenario yet.
|
||
- **Delivery (the #197 class): not covered.** The stub runtime has no input behaviour, so it cannot
|
||
exercise delivery to a managed wrapper; `send-verify-means-rendered.yaml` is not admitted to CI
|
||
for that reason (`packages/test-system/ci/README.md`).
|
||
- **Addressing and rendered delivery: partial.** CI runs the transcript and capture stub scenarios
|
||
in passing form, and the capture scenario catches a seat resolved by member-name prefix.
|
||
`send-renders-in-addressed-pane.yaml` and `down-stops-every-seat.yaml` run with the local runner
|
||
only. `stub-cli-journeys.integration.test.ts` checks that a verified send lands in the addressed
|
||
pane and not its sibling. None of these proves a seat consumed a message.
|
||
- **Restore (the #141 class): not covered.** The scenario runner does not bind the `restore` verb
|
||
yet; it fails with `UnboundActionError`.
|
||
- **Rig identity (the #174 class): not covered.** No library scenario sets up a same-name archived
|
||
or removed rig.
|
||
- **Projection (the #159 class): not coverable with `runtime: stub`,** which projects skills into its
|
||
own `.openrig/stub/skills/` directory. Use adapter-level unit tests.
|
||
- **Native process observation: unit tests only** (for example
|
||
`packages/daemon/test/codex-process-observation.test.ts`, `native-ps-locale.test.ts` and
|
||
`send-runtime-not-running.test.ts`).
|
||
- **Migrations: unit guards only** (`migration-fixture-parity.test.ts`,
|
||
`startup-migrations-mirror.test.ts`). Every scenario starts from an empty database, so upgrading
|
||
an existing database is not covered.
|
||
- **Launch:** every scenario's `up` goes through the real `rig up`, instantiation and startup
|
||
orchestration with stub seats. The stub adapter types its runner directly; Claude Code, Codex
|
||
and Pi seats launch through the seat launch environment (`seat-launch-environment.ts`), which
|
||
no scenario reaches. Real Codex launch and handover run only in two opt-in daemon tests
|
||
(`OPENRIG_E2E_REAL_CODEX=1`, which needs tmux, `codex` and a Codex login). The CI containers
|
||
have no provider CLIs or credentials.
|
||
|
||
## Regression log
|
||
|
||
When a regression reaches a release, add one line: date, issue, artery, and which layer missed it
|
||
(the layers are named in [test-layers.md](test-layers.md)). Each line should end up as a scenario.
|
||
|
||
- 2026-09-30, #197 (0.6.2), delivery: review saw a small diff in isolation; CI had no managed
|
||
explicit-mode Claude seat; the project's own day-to-day seats use the default launch path.
|
||
- 2026-09-29/30, #141, #174, #188 (after 0.6.0), delivery and rig identity: reviews of #109 did not
|
||
see the downstream effect of the exactly-one-target rule.
|
||
|
||
- 2026-10-19, #1483, managed compaction: a helper-only command-composition check did not bind the submitted command to the precompact effect. `managed-compaction-effect.test.ts` now drives the real enforcer command into a version-bound stub input model and checks the shipped precompact marker; its 851-character pasted-command control lacks that effect despite a successful submit receipt. `rig compact` rejects stub runtimes, so this is an adapter/helper check, not an installed CLI or native compaction proof. The model uses the recorded Claude Code 2.1.282 distinction; other versions and actual context compaction remain unverified here.
|
||
- 2026-10-19, #484, refocus orientation: script tests supplied basis strings without composing parked typed queue state through `queue whoami` and the hook. `refocus-parked-baton.yaml` plus the active-baton contrast now assert the shipped hook's own emitted context; installed-scenario runs healthy/seeded/healthy; its scratch trace-child fault moves the parked row from held work into the work candidates as current work, and the hook's emitted context must then fail with that row's slice shown as `WORK FROM QUEUE EVIDENCE`. This proves the composed diagnostic effect, not native hook scheduling or an agent reading and acting on it. With the work packet's bounds and per-runtime default, the parked scenario runs with the packet on and asserts the row as held work. `refocus-parked-baton-packet-off.yaml` keeps the labelled fallback as the packet-off control.
|
||
- 2026-10-23, F1 (0.7.0 candidate `2fec7382`), launch (a fresh Codex kernel): a fresh Codex kernel stopped at Codex's hook review. Caught at the cut, before publication, by native first-user and daily runs. Missed by the stub layer, which has no real Codex binary. Fixed in #1824.
|
||
- 2026-10-24, B6 (0.7.1 candidate `7badb557`), launch and resume (Codex hook trust): after an upgrade from 0.7.0, a Codex seat stopped at "Hooks need review: 6", because Codex 0.164 asks to approve a plugin's whole content again. Caught at the cut, before publication, by a native upgrade run with a real Codex. Missed by source tests and stub scenarios (a stub can't see the real Codex binary) and by the installed-upgrade check (no real Codex). Fixed in #1948. Scenario owed: a cheap real-agent Codex upgrade with approval renewal.
|
||
- 2026-10-24, pane-only attention (#1876 on the 0.7.1 candidate), native observation: a Codex seat waiting at a prompt that only its pane showed read as working, with no attention signal. Caught at the cut, before publication, by real work on a test machine. Missed by fixtures that all carried hook signals. Fixed in #1946. Scenario owed: a stub pane showing a selector with no hook event must read as needing attention.
|
||
- 2026-10-25, #1955, delivery (send readiness): approval-prompt text that a working Claude seat quoted in its own output read as a waiting prompt, so sends and queue wakes to that seat were refused. This one was present since 0.7.0, so it did reach a release; it was found during the 0.7.1 cut by real work on a test machine. Missed by classifier fixtures that had no working pane quoting prompt text. Fixed in #1955 for 0.7.2.
|