Skip to content

docs(agents): the program plan absorbs record 0032 — five units retire the native loop behind pi, one preset at a time - #1005

Merged
justinhelmer merged 1 commit into
mainfrom
docs/plan-absorbs-0032-series
Sep 14, 2026
Merged

justinhelmer merged 1 commit into
mainfrom
docs/plan-absorbs-0032-series

Conversation

@justinhelmer

@justinhelmer justinhelmer commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Record 0032 decided that pi is the one agent harness and that the hand-written turn loop retires in five gated steps, each landing a pi piece and deleting its native counterpart. This PR puts those five steps into the orchestration program plan as units on the board, so the retirement is scheduled, ordered and receipted like every other unit of the program. Docs only.

What & why

Record 0032 is the adoption record the plan's harness track (U10) was waiting for. It decided more than the plan's U11 anticipated: no harness: native | pi preset property, one harness for every preset, and a replacement series (proxy → coding → review and explore → ship, where nothing moves → general, research and conductor) that never leaves two loops serving one preset. The plan is proposed, so its body may be amended, and this PR amends it the way PR #957 added Phase F: five new units U25 to U29 in a new Phase G, six requirements R39 to R44 stating what the record decided, the phase table, the dependency graph, the open questions, the verification contract, the definition of done and the follow-ups ledger updated to match. Every unit's Files come from the record's "today at sha" table, its Approach from the record's hard-part sections, its Test scenarios from the record's validation criteria and its Verification from the record's rollout gates — no new design.

U11 (#833) is superseded by the series and closed pointing here; U9 and U10 stay as they are. Phase C's accept line now points at record 0032. Parent issue: #821; the harness exploration issue the plan retired: #765.

Board items (Phase G, project 4, Status Todo): #1006 U25, #1007 U26, #1008 U27, #1009 U28, #1010 U29 — listed with their dependencies in a comment on the parent, the way the Phase F items were.

Tour

1. The six requirements the series builds to

R39 is the series invariant (one pi piece in, one native counterpart out, per pull request, gated by the preset's live receipt; no harness field; a one-way door after step five). R40 is the detached process and the re-attach on the resident and sandbox. R41 is the gate as a hook that asks the bot per tool call. R42 is the record under pi (the homeless events' dispositions, compaction as a superset, one model.turn span per proxied call). R43 is the presets without a workspace on pi in the bot container, with the deletions of step five. R44 is the generated tool schemas and the version pin. Look for: every clause traces to a sentence in record 0032's difficulty sections, invariants or rollout; nothing here is new design.

**The harness: pi replaces the native loop** (record 0032)
- R39. The native loop retires in a replacement series of five steps in the record's order (the proxy; coding; review and explore; ship, where nothing moves; general, research and conductor), each one pull request that lands one pi piece and deletes its native counterpart in the same change, gated by that preset's live receipt on the tracker before the next step starts, so at no release do two loops serve the same preset and no week is without a fallback; each step's pull request carries a source-scan test that its deleted symbols and `runAgent` callers are gone; no preset carries a `harness` field, because there is one harness; after the fifth step no native path exists and reversal is a fork of the last native release, a one-way door by the owner's choice.
- R40. On the resident and the sandbox, a run's pi is one detached process per thread key (`setsid -f pi --mode rpc --no-extensions -e <the image's extension>`, stdin a FIFO, stdout a log under the thread user's home), started, fed and killed through `POST /harness/{attach,send,stop}` on both Workers and identified by pid and start time from `/proc/<pid>/stat` on every operation; `attach` is idempotent per thread key and streams the log from the offset the caller names; the bridge writes four facts (pid, start time, the log offset consumed, the last mirrored session entry id) on the run's ledger row under its generation's fence, and `send` is accepted only from the generation that attached last; every operation stamps the thread's activity and every path that ends a run or releases a worktree (the run's release, a hard stop, `/detach`, the sweep) calls `stop` first, so no thread user returns to the pool with a live pi; a bot death costs a run one failed model call and one extra `prompt` and never a tool's effects; a container roll writes the mirrored entries back as a session file and pi restarts with `--session <path>`, losing only the in-flight turn, whose last `bash` settles as "restarted, re-check effects" by the ledger's existing rule. The process's environment carries the run bearer, the proxy URL and the exec path's defaults, never a model key, a Slack token or a GitHub App credential; the worktree's repo-scoped token is the only credential a `bash` command can reach, as today.
- R41. Every tool call a pi run makes is decided in the bot from the policy table, as the agent actor on behalf of the requester (effective grants the intersection), through the extension's `tool_call` hook posting `/harness/authorize` with the run bearer, the tool name and its input before execution; the table gains its first tool-level rows (the push target as `authorize(agent, "repo:use", repo)`, as the authorization spec's gap row words it; the shell and write tools as rows on the run resource); a refusal is a `run_note tool_refused` whose reason the model reads; a hook that cannot reach the bot within 90 s blocks the tool; the hook carries no copy of the rules and caches nothing, so a grant lost mid-run is refused on the next call.
- R42. Every documented pi event has a home on the run stream or a decided disposition, held by a unit test: two new `run_note` kinds, `compacted` (`tokensBefore`, `tokensAfter`, the summary entry's id, the retry count folded in from `summarization_retry_*`) and `harness_error` (an `extension_error`; a dialog request answered `cancelled`); `tool_execution_update` folded into the result recorded at the end, capped at `TOOL_OUTPUT_CAP`; `bash_execution_update` and `auto_retry_*` made impossible by the bridge; an event kind a pi bump adds is recorded as `harness_error` naming the kind. Compaction is allowed: the transcript mirrors every session entry verbatim, compaction entries included, so it is a superset of the model's context and the model's view at any turn is derivable from it. A pi run's record replays on the run page in the shape of a native run and is complete without pi's session file; every proxied model call is one `model.turn` span carrying the runner's token attrs, so the run page, the friction analyzer and the costs page keep one vocabulary, and pi's own `usage.cost` is never what a page shows.
- R43. `general`, `research` and `conductor` (machine `none`) run pi as a child process of the bot container with `--no-builtin-tools`, the extension supplying `web_fetch`, `web_search`, the GitHub reads and issue writes, the conductor's five run tools and any MCP-bridged tool as relays to bot routes over loopback with the run bearer, executed in the bot with the bot's credentials and the policy table; the conductor's spawn relay calls `dispatch()` exactly as its in-process tool does today, so the dispatcher stays the only place a run starts; no attach route, the proxy on loopback so the meter stays one, the bridge the same code; a bot death takes pi with it, today's behaviour, and the ledger's resume applies unchanged. In the same step `src/runner.ts`, `src/providers/` and `src/tools/workspace.ts` are deleted, `@earendil-works/pi-ai` is the library behind reflection's one model call (OpenRouter comes with it), `run-loop.md` retires with its code, and the bot image grows by pi's 434 MB under the `imagePins` check.
- R44. The tool definitions exist once (record 0008): the extension registers `submit_pr_description`, `submit_handoff`, `submit_dispositions`, `submit_verdict`, `update_status`, `diff_digest` and the relay tools with plain JSON Schema rendered from the definitions by a `gen` step, and a `check` step fails `verify` when the rendering drifts; at the fifth step the native rendering is deleted and the definitions have one reader. pi is pinned at `@earendil-works/pi-coding-agent` 0.85.1 in the sandbox, resident and bot images under the `imagePins` check; the driver's dry run against the scripted provider runs on every bump, and two consecutive breaking bumps freeze the pin.

2. R20's open parenthetical, closed by the record

R20 left the proxy's wire shape to U10 ("OpenAI-compatible; whether it also speaks the Anthropic shape is U10's call"). Record 0032 decided both shapes; the parenthetical now says so, and the same question leaves the Open questions table (step 11).

- R20. If adopted: the model credential reaches pi through a per-run proxy in the bot (Anthropic-shaped `/v1/messages` and OpenAI-shaped `/v1/chat/completions`, as record 0032 decided) with a run-scoped bearer that expires at the run's budget plus a margin, is revoked when the run's ending is registered, pins the preset's model, enforces the run's token budget and is never logged; the sandbox and resident planes hold no provider, Slack or GitHub credential beyond the executor's repo-scoped token, so the extension's GitHub, Slack and `submit_*` tools are relays to the bot over the same run-scoped channel; pi's events are bridged into run events, spans and the transcript ledger so the run record is complete without pi's session file; the policy gate is a pi extension installed read-only in the image outside every worktree with project-local extension discovery disabled; pi is pinned in the sandbox-base and resident images.

3. Phase G in the table, and Phase C's accept line

Phase C now closes on record 0032 (Justin's acceptance flips it and opens Phase G) and no longer lists U11. Phase G carries the record's five gates in order as its receipt, and the dependency chain: U26 on U25, U27 on U26, U28 on U27 and U15, U29 on U27. Look for: the first pull request waits on the record's accepted flip — R19 makes the record the gate for any harness code.

| C. Harness | U8 to U10; U11 superseded by Phase G | U1 | the adoption record, [record 0032](../decisions/0032-pi-is-the-harness-the-native-loop-retires.md), with the spike's table; Justin's acceptance flips it and opens Phase G |
| D. Ship coordinator | U12 to U15 | U20 to U24; the distribution series for U14; U16 and U17 for U13 | a bot kill under a ship pipeline resumes the child and the parent proceeds; a bot deploy landing on a `spawn` step is retried through; two dependent plan units land through the runner; the round loop is deleted |
| E. Self-driving edges (record 0031) | U16 to U19 | nothing (U17 on U16); run ahead of D and of U6 and U14 | a coding child of this program ran from a rendered contract and its handoff reached its board issue without a person; the delivery table shows the trunk pass's week; a deliberate test-weakening pull request drew the guard's finding |
| F. Ship prerequisites (record 0026 slice one; spawn/await Phase 1 and 2) | U20 to U24 | nothing (the baseline is cut; units land on `main`); U21 on U20; U22 on U21; U23 on U21 and U22; U24 on U23 | `agent:explore` against a large onboarded monorepo lands in a cold sandbox with a read token and reports a claim table (the comparison run); a channel bounded to `read` refuses `agent:coding` by name; a parent spawns children as the requesting user into threads of their own, steers one and collects every write-up; a child for a requester without the grant reaches no executor |
| G. The harness: pi replaces the native loop (record 0032) | U25 to U29 | record 0032 `accepted` (U10's flip, Justin's); U26 on U25; U27 on U26; U28 on U27 and U15; U29 on U27 | the record's five gates in order, each on the tracker before the next step starts: the spike's driver through the proxy with equal tokens and the overhead measured; the detached re-attach proof, then five coding tasks on the switchboard resident with a bot kill and a resident roll mid-run, records complete, pull requests opened; a review on a real pull request and a 30-minute explore run on a cold sandbox; a plan run end to end on pi children; `ask` on general and research in production and a conductor run with two children — after which `src/runner.ts`, `src/providers/` and `src/tools/workspace.ts` are gone |

4. The graph gains the series' edges

U10 now leads into U25 (the U11 node stays, relabelled superseded), the chain runs U25 → U26 → U27 → U29, and U28 hangs off U27 and U15.

U10 -. if adopted .-> U11[U11 harness seam: superseded by 0032]
U10 --> U25[U25 the proxy, the run bearer, the meter]
U25 --> U26[U26 pi in the images, extension, bridge, attach route, coding moves]
U26 --> U27[U27 review and explore move]
U27 --> U28[U28 ship: nothing moves, a plan on pi children]
U15 --> U28
U27 --> U29[U29 general, research, conductor move, the native loop is deleted]

5. U25 — the proxy, the run bearer and the meter

Step one of the series: two routes on the bot (/v1/messages, /v1/chat/completions) reached through the shim like /ingress; a bearer minted per run, expiring at budget plus 5 minutes, revoked by RunEnding, never logged; the preset's model and the profile's max_tokens pinned on the wire, maxTurns refused as turn_budget_exhausted; one model.turn span per call with the runner's four token attrs. Nothing is deleted and no preset moves; the receipt is the spike's driver through the proxy with the overhead measured. Look for: the new module path src/core/harness/proxy.ts is the one thing here the record does not fix (see Decisions).

### U25. The proxy, the run bearer and the meter
- **Goal**: pi believes it holds a model key while holding a token the bot minted for this run alone, every proxied call is metered in the runner's vocabulary, and no behaviour changes for any preset: the first step of record 0032's series deletes nothing and moves nothing, so its receipt measures the proxy alone.
- **Requirements**: R20, R39, R42 (the `model.turn` span)
- **Dependencies**: record 0032 `accepted` (U10's flip, Justin's); U21 (done: the effective profile whose model, `maxTokens` and `maxTurns` the proxy pins).
- **Files**: new `src/core/harness/proxy.ts` (+ `.test.ts`): the two routes `/v1/messages` (Anthropic-shaped) and `/v1/chat/completions` (OpenAI-shaped), the bearer's validation, the model and `max_tokens` pins, the `maxTurns` refusal, the streamed forward and the `model.turn` span per call, mounted by `src/channels/http.ts` beside `/ingress` (outside the Access application; the shim Worker `deploy/cloudflare/worker.ts` forwards every path it does not answer itself, so it does not change); the run bearer, minted by the run stage (`src/core/dispatch/run.ts`), bound to the run id, revoked by `RunEnding` (`src/core/runEnding.ts`); `src/runner.ts` (read, not changed: the `model.turn` span and its `inputTokens`, `outputTokens`, `cacheReadTokens` and `cacheWriteTokens` attrs the proxy reproduces); `src/core/costs.ts` (the LLM line reads the proxy's meter beside the Anthropic Admin cost report by workspace); the spike's driver, `scripts/load.ts` with `src/load/piProcess.ts` and `src/load/piRpc.ts`, re-run through the proxy for the receipt; `docs/reference/specs/http-ingress.md` (the proxy's rows; the module's tests bound), `costs.md`, `tracing.md`.
- **Approach**:
1. The bearer: minted per run, bound to the run id, expiring at the run's budget plus 5 minutes, revoked when `RunEnding` registers the ending; never logged, never on a command line, never in a file inside the checkout. It reaches a container only in the pi process's environment (U26), so any command the model runs can spend through the proxy, bounded by the same `maxTurns` and `max_tokens` the model is bounded by and revoked with the run.
2. The routes: `/v1/messages`, so effort, adaptive thinking and prompt caching pass through untouched, and `/v1/chat/completions` for the compatible providers; a request carries the bearer as the provider key; reached through the shim the way `/ingress` is.
3. Per call: validate the bearer (this run, unexpired, unrevoked); ignore the request's `model` and send the preset's; pin `max_tokens` to the profile's `maxTokens`; refuse a call past the profile's `maxTurns`, recorded as `turn_budget_exhausted` (the bridge, which counts `turn_end`, has already steered the write-up at the last turn with tools blocked, so the native loop's tool-less write-up stays); stream the provider's response back; close one `model.turn` span with the token attrs read from the streamed response. pi's own `usage.cost` is a catalog estimate; the proxy's meter is what the run page and the costs page show.
4. The receipt: the spike's five tasks re-run through the proxy with a minted bearer; equal tokens to the direct-key arm; the proxy's span median under 2 s per call (51 calls against a 447 s gain, the record's third "what would change our mind" row).
- **Patterns to follow**: `src/core/coordinator/instancesRoute.ts` (a bot route a Worker calls over a bearer, the precedent for the policy table answering over HTTP), `src/channels/http.ts` `/ingress` (a route outside the Access application), `src/runner.ts` (the span's attrs), `src/providers/anthropic.ts` and `openaiCompat.ts` (the two wire shapes the proxy forwards, until U29 deletes them).
- **Test scenarios**:
- The proxy refuses a bearer not minted for the run, an expired one (budget plus 5 minutes passed), a revoked one (after `RunEnding`), and a call past `maxTurns` (recorded `turn_budget_exhausted`); each refusal is one response and no provider call.
- A valid bearer forwards with the preset's model and the profile's `max_tokens` whatever the request named; the response streams back; one `model.turn` span per call carries the runner's four token attrs.
- The bearer appears in no log line, no span attr and no record.
- Both shapes: an Anthropic-shaped request passes `output_config.effort`, adaptive thinking and cache control through untouched; an OpenAI-shaped request reaches the compatible provider with the same pins.
- `src/core/costs.test.ts`: a run's proxied spend appears on the LLM line beside the Admin report.
- The source-scan test the series carries, at its baseline: no `runAgent` caller changed and nothing native was deleted at this step.
- **Verification**: `npm test` green; `http-ingress.md`, `costs.md` and `tracing.md` rows bound; the gate, human-gated on the tracker: the five spike tasks through the proxy with a minted bearer, equal tokens, the proxy's span median under 2 s per call. No preset moves at this step.

6. U26 — pi in both images, the extension, the bridge, the attach route; coding moves

The largest step. Files: the two Dockerfiles under imagePins, the extension package with gen + check schemas, the bridge, the POST /harness/{attach,send,stop} route on both Workers with stop wired into release, detach and the sweep, the two run_note kinds and tool_refused, the first tool-level policy rows, and the deletions (the run stage's coding path, TOOLSETS.full, the native write_file and submit_* tools, the coding exec path). Approach steps 1 to 8 are the record's trace: the proof before the first pull request, the wrapper and FIFO, the bridge's event map and steers, the re-attach by pid and start time, the hook, the terminal tools, the three open questions closing here, the deletion.

### U26. pi in both execution images, the extension, the bridge and the attach route; the coding preset moves and its native path is deleted
- **Goal**: A coding run is a detached pi process in the thread's worktree, driven by the bot over the attach route, gated per tool call by the policy table, recorded completely on the run stream, and it survives a bot death at the cost of one failed model call and never a tool's effects; the native coding path is deleted in the same pull request.
- **Requirements**: R20, R21, R39, R40, R41, R42, R44
- **Dependencies**: U25 receipted; the detached re-attach proof (Approach step 1) before this unit's first pull request.
- **Files**: `deploy/cloudflare-sandbox/Dockerfile` and `deploy/cloudflare-resident/Dockerfile` (`@earendil-works/pi-coding-agent` 0.85.1 installed root-owned; both Node 24.21.0 images already clear pi's `>= 22.19.0` floor; about 434 MB each), `src/deploy/imagePins.ts` (+ `.test.ts`: the pin; the read-only extension path); new `packages/pi-tools/` (the source of the image's root-owned `/opt/switchboard/pi-tools/index.ts`, outside every worktree: the terminal tools `submit_pr_description`, `submit_handoff`, `submit_dispositions` and `update_status`, the facts-as-you-go tool for a child, the `tool_call` hook, `before_agent_start` setting the composed system prompt, the provider headers; its schema file rendered by a `gen` step from the tool definitions with a `check` under `verify`); new `src/execution/piBridge.ts` (+ `.test.ts`: the bot-side client — `prompt`, `steer`, `abort`, `get_entries`, `set_thinking_level`; the event map, seeded from `src/load/piRpc.ts`'s `PI_EVENT_HOME`; the transcript mirror; the wrap-up steer, the turn cap, the soft and hard stops and the wall clock; the four harness facts), with `src/load/piProcess.ts` and `src/load/piExtension.ts` as its seeds; the attach route: `deploy/cloudflare-resident/worker.ts` (`POST /harness/{attach,send,stop}` beside `/exec`, `/attach` and `/detach`; the wrapper that opens the FIFO and execs `setsid -f pi --mode rpc --no-extensions -e …`; `models.json` written from the profile under `PI_CODING_AGENT_DIR`; pid and start time from `/proc/<pid>/stat`; the attached generation on the DO's row; `stop` wired into the run's release, `/detach`, the sweep and the forced detach; every operation stamping `lastAttachAt`) and `deploy/cloudflare-sandbox/worker.ts` (the same three operations); `src/execution/resident.ts`, `src/execution/residentDetach.ts` and `src/execution/cloudflareSandbox.ts` (the client of the route; `stop` before `/detach`); `src/core/dispatch/runLoop.ts` (the coding branch calls the harness entry instead of `runAgent`), `src/core/descriptionTurn.ts` (a `prompt` on the run's own session instead of a clipped `runAgent`), `src/core/ship/codingChild.ts` (calls the harness entry the run stage calls, until U15 deletes the round loop); `src/core/runEvents.ts` (`run_note` kinds `compacted` and `harness_error`; `tool_refused`; results capped at `TOOL_OUTPUT_CAP`), `src/core/runLedger/types.ts` and `writeThrough.ts` (`harness: { pid, startTime, logOffset, lastEntryId }` on the row under the fence), `src/core/runLedger/resume.ts` (the container-roll write-back and `--session`); the gate: `src/core/authz/policy.ts` (the first tool-level rows: the push target as `authorize(agent, "repo:use", repo)`; the shell and write tools as rows on the run resource), a `POST /harness/authorize` route beside the proxy in `src/core/harness/`; `src/effort.ts` (the five tiers onto pi's seven thinking levels, one table); deleted in the same change: the run stage's native coding path, `TOOLSETS.full` in `src/tools/workspace.ts` and `ship`'s vestigial `toolset: full` in `src/agents/registry.ts`, the native `write_file`, `submit_pr_description`, `submit_handoff` and `submit_dispositions`, the coding executor exec path in `src/execution/executor.ts`; specs `agent-coding.md` items 3 and 7, `execution.md` item 10, `authorization.md` item 1 and its gap row "Agent actors gate tool-level actions", `run-visibility.md`, `tracing.md`, `resident-repos.md` (the exec model and the sweep), `http-ingress.md` (`/harness/authorize`), `costs.md`.
- **Approach**:
1. The gate's proof first, before the first pull request: on a staging resident, the wrapper with a JSONL echo loop in pi's place, started through `/exec`; read its log and write its FIFO 25 minutes later; repeat across a resident Worker deploy and a bot restart; read the process's RSS and start time. If it fails within the resident's exec model, the fallback is record 0032's named one (pi in the bot container with remote operations through the executor) by a superseding record, not by this unit.
2. The process: `attach` is idempotent per thread key; the wrapper from the image opens the FIFO `/home/<user>/.pi/rpc.in` for writing on a spare descriptor and execs `setsid -f pi --mode rpc --no-extensions -e /opt/switchboard/pi-tools/index.ts` with stdin the FIFO and stdout appended to `.pi/rpc.log`, so the FIFO never runs out of writers while pi lives; `PI_CODING_AGENT_DIR` holds a `models.json` the DO wrote from the profile (one provider, `api: anthropic-messages`, `baseUrl` the proxy, `apiKey: "$SWITCHBOARD_RUN_BEARER"`); the environment is the exec path's plus the bearer and the proxy URL, no model key, Slack token or GitHub App key; the DO records pid and start time and checks them on every operation, records which generation attached last and refuses `send` from any other; `attach` streams the log from the caller's offset (a file read every 500 ms, heartbeat-framed like `/exec`, bounded and re-opened from the offset); `send` writes one line by a seconds-long command as the thread's user, and a collect phase lost to an isolate swap is recovered by finding pi's `response` with the command's `id` in the log before resending; `stop` kills the verified pid as the thread's user, and every ending path calls it first.
3. The bridge: sends `prompt` with the first turn the dispatcher composes today (the extension's `before_agent_start` sets the system prompt); disables auto-retry at start; sets the thinking level from the effort tier; maps every event (the `PI_EVENT_HOME` table extended: `tool_execution_start` and `tool_execution_end` become `tool_call` and `tool_result` under a `tool.<name>` span; `compaction_*` and `summarization_retry_*` become `compacted`; `extension_error` and a dialog request answered `cancelled` become `harness_error`; `tool_execution_update` folds into the capped result; an unknown kind is `harness_error` naming it); mirrors every session entry verbatim, compaction entries included; supplies the wrap-up steer three minutes before the deadline, the turn cap as a steer to write up with tools blocked, the soft stop as a steer then an abort after a grace, the hard stop and the wall clock as `abort`; writes the four harness facts under its generation's fence.
4. The re-attach: a new generation reclaims the row, reads the facts, calls `attach` with its generation and the offset; the DO matches the pid's start time and streams from the offset; the bridge sends `get_entries` since the last mirrored id, mirrors what it missed (a tool result, an errored assistant entry) and sends `prompt` ("continue"), which runs at once on an idle session. The container-roll variant: the mirrored entries, stored as pi wrote them, are concatenated into a session file on the new container and pi restarts with `--session <path>`; the in-flight turn is lost and its last `bash` settles as "restarted, re-check effects" by the rule the ledger already has.
5. The gate: the `tool_call` hook posts `/harness/authorize` with the bearer, the tool name and its input before execution; the bot resolves the agent actor on behalf of the requester and evaluates the table (the first tool-level rows land here); a refusal is a `run_note tool_refused` the model reads; the hook retries against the shim for up to 90 s (three lease lengths) then blocks with "authorization unavailable"; it caches nothing.
6. The terminal tools: the extension writes `submit_pr_description` as a `custom` session entry; the bridge validates it with `parsePrDescription` and records `pr_description`; on `agent_settled` the run finishes through `RunEnding`, the post-step observes the workspace over the bot's own `/exec` as today, opens the pull request and revokes the bearer, and the release calls `stop` before `/detach`. The description turn becomes a `prompt` on the same session.
7. The record's three open questions close inside this unit: the effort-to-thinking table is one table in the pull request, checked against the proxy's pass-through of `output_config.effort`; how a repository's skills reach pi and whether pi's `AGENTS.md` reading duplicates the injected contract are answered by the first live coding run on a repository with skills and its transcript.
8. Delete in the same change: the run stage's native coding path, `TOOLSETS.full` and ship's `toolset: full`, the native `write_file` and the three `submit_*` tools, the coding executor exec path; the series' source-scan test holds the deletion.
- **Patterns to follow**: `src/execution/resident.ts` (attach idempotency per thread key; `runtime-replaced` handling), `src/core/runLedger/writeThrough.ts` (writes under the generation's fence), `src/load/piRpc.ts` (the event map as a table with a test), `scripts/docs-gen.ts` (the `gen` + `check` shape), the run page's SSE reader (a bounded stream re-opened from an offset), `/exec`'s heartbeat framing, `src/core/coordinator/instancesRoute.ts` (a bearer-authenticated bot route).

The test scenarios are the record's validation table rows for step 2, one bullet each (the PI_EVENT_HOME block, the route, the bridge's re-attach, the run-page replay, the hook, the image test, the gen + check pair, the ledger unchanged, the effort map, the source scan); Verification is the record's gate: the re-attach proof plus five coding tasks with a bot kill and a resident roll mid-run.

- **Test scenarios**:
- `src/load/piRpc.test.ts`, the `PI_EVENT_HOME` describe block: every documented pi event has a home or a disposition, extended to `compacted`, `harness_error` and `tool_refused`; an unknown kind is recorded as `harness_error` naming it; no event is dropped without a note.
- The route's tests (plain-Node, beside the resident's pure modules): `attach` twice for one thread key starts one process; a pid whose start time differs from the recorded one never matches; a `send` from a generation other than the attached one is refused; `stop` is called on the run's release, a hard stop, `/detach` and the sweep, so no user returns to the pool with a live pi; a `send` whose collect phase was lost finds pi's `response` by `id` before resending; every operation stamps the thread's activity.
- `src/execution/piBridge.test.ts`: the re-attach over a recorded stream (`get_entries` since the last id, the missed entries mirrored, `prompt` continue, no command re-issued); the container-roll write-back is a concatenation of the mirrored entries; the wrap-up steer, the turn cap with tools blocked, the soft stop's grace and the hard stop's `abort` fire at the runner's timings; auto-retry is off at start.
- Integration over a recorded stream: a pi run's record replays on the run page in the shape of a native run and is complete without the session file; results are capped at `TOOL_OUTPUT_CAP`.
- The hook (the extension's tests): a tool the table denies is refused with `tool_refused` and its reason; the bot unreachable past 90 s blocks the tool; `src/core/authz/policy.test.ts` rows for the push target (`repo:use`) and the shell and write tools; `src/core/authz/authorize.test.ts::effectiveGrants` unchanged.
- The image test beside `src/deploy/imagePins.test.ts`: pi pinned at 0.85.1 in both Dockerfiles; the extension path root-owned and not writable by a thread user; a `.pi/extensions/` directory in the checkout does not load; the pi environment carries no model key, Slack token or GitHub App credential.
- The `gen` + `check` pair: the extension's schema file equals the rendering of the tool definitions; a drifted file fails `verify`.
- `src/core/runLedger/writeThrough.test.ts`: the lease, fence and reclaim are unchanged by the harness facts.
- `src/effort.test.ts`: the five tiers map onto seven thinking levels, one table.
- The source-scan test: `TOOLSETS.full`, ship's `toolset: full`, the native `write_file` and three `submit_*` tools, the coding exec path and the run stage's coding `runAgent` call are gone.
- **Verification**: `npm test`, `npm run verify -w deploy/cloudflare-resident` and `npm run verify -w deploy/cloudflare-sandbox` green; `agent-coding.md`, `execution.md`, `authorization.md` (the gap row closed), `run-visibility.md`, `tracing.md` and `http-ingress.md` rows bound; the gate, human-gated on the tracker: the detached re-attach proof (Approach step 1, with RSS, start time and the hook's round trip as numbers) and five coding tasks on the switchboard resident with a bot kill and a resident roll mid-run, records complete, pull requests opened, cost and wall time at or better than the spike's pi arm (the five-task table).

7. U27 — review and explore move

The head-move re-review and the round loop's review child become a prompt on the run's own session; explore exercises the sandbox Worker's attach route on a cold sandbox; deleted: the native bash, read_file, diff_digest, submit_verdict, the readonly and explore toolsets, the sandbox executor's tool transport and the /exec executor path for agent commands. Look for: the bot's own post-step /exec commands (workspace observation, the reading diff, the verification guard) and meat stay — the record's boundaries.

### U27. Review and explore move; the `/exec` executor path for agent commands is deleted
- **Goal**: A review or explore run is a pi process like a coding run, under the read identity, the head-move re-review and the round loop's review child ride the same session, and the native read-only tools, their toolsets and the sandbox executor's tool transport are deleted in the same pull request.
- **Requirements**: R39, R40, R41, R42
- **Dependencies**: U26 receipted.
- **Files**: `src/core/dispatch/runLoop.ts` (the review and explore branches call the harness entry), `src/core/reviewRound.ts` (the head-move re-review becomes a `prompt` on the run's own session), `src/core/ship/reviewChild.ts` (calls the harness entry, until U15 deletes the round loop); `deploy/cloudflare-sandbox/worker.ts` (the attach route from U26, first exercised for a `repo-cold` explore run on a cold sandbox); `packages/pi-tools/` (`submit_verdict` and `diff_digest` from the same generated schemas); `src/core/meatProcess.ts` (stays: the review abridge runs on the bot host with the bot's key); `src/execution/residentReadonly.ts` (the read-only worktree, unchanged); deleted in the same change: the native `bash`, `read_file`, `diff_digest` and `submit_verdict`, `TOOLSETS.readonly` and `TOOLSETS.explore` in `src/tools/workspace.ts`, the sandbox executor's tool transport in `src/execution/cloudflareSandbox.ts` and the `/exec` executor path for agent commands in `src/execution/executor.ts` (the bot's own post-step commands over `/exec` — workspace observation, the reading diff, the verification guard — are the bot's and stay); specs `agent-review.md`, `agent-explore.md`, `execution.md`, `agent-ship.md` item 16.
- **Approach**:
1. The two presets move onto the harness entry U26 built, identity `read`; the review child's first turn is the one it composes today (the contract block after REVIEW TARGET); the head-move re-review is a `prompt` on the same session instead of a second `runAgent`, so a head move mid-review starts no second process.
2. Explore on a cold sandbox: the sandbox Worker's attach route, one process per thread, the same FIFO, log, pid and start time; the 120-minute budget as the bearer's expiry plus 5 minutes; a job past the 20-minute command cap is pi's `bash`'s concern under the same `setsid -f` hint.
3. Delete the read-only natives and their two toolsets, the sandbox executor's tool transport and the `/exec` executor path for agent commands; the bot's post-step `/exec` commands stay; the series' source-scan test extends to this step's symbols.
4. `meat` stays on the bot host: the abridge is the bot's, not the agent's.
- **Patterns to follow**: U26's harness entry and route client; `src/core/reviewRound.ts` as it stands (the re-review's composition).
- **Test scenarios**:
- `src/core/dispatcher.test.ts` (factory stubbed): `agent:review` and `agent:explore` reach the harness entry with identity `read`; the resident is never asked for a write token.
- `src/core/reviewRound.test.ts`: a head move mid-review sends one `prompt` on the same session and starts no second process; the round loop's review child calls the harness entry.
- The sandbox route's tests mirror the resident's: `attach` once per thread key, pid and start time checked, `stop` on release and the sweep.
- The source-scan test: `bash`, `read_file`, `diff_digest`, `submit_verdict`, `TOOLSETS.readonly`, `TOOLSETS.explore`, the sandbox tool transport and the executor's agent-command path are gone; `runAgent` has one caller left, the run stage's branch for the presets without a workspace.
- **Verification**: `npm test` and both Worker verifies green; `agent-review.md`, `agent-explore.md` and `execution.md` rows bound; the gate, human-gated on the tracker: a review on a real pull request and a 30-minute explore run on a cold sandbox, both records complete on the run page.

8. U28 — ship: nothing moves

A receipt-only unit in the U7 shape: no files in the tree; ship's children are dispatch() runs already on pi, and the receipt is a plan of two dependent units running end to end on pi children with a bot kill during the coding child. Look for: Dependencies name U27 as well as U15 — the record's step four says the coordinator's children are "presets already on pi", which means review too.

### U28. Ship: nothing moves — a plan runs end to end on pi children
- **Goal**: The coordinator's children are `dispatch()` runs and nothing else, and a plan runs end to end on children that are pi runs without ship changing; this unit is that receipt.
- **Requirements**: R21, R39
- **Dependencies**: U15 (the round loop deleted, so ship has one implementation whose children are `dispatch()` runs); U27, which carries U26 (the coordinator's children, coding and review, are presets already on pi).
- **Files**: none in the tree; `src/core/coordinator/contract.ts`, `src/core/dispatch/spawn.ts` and `src/core/dispatch/ship.ts` are read, not changed; the `[agent]` row in `docs/reference/specs/agent-ship.md` item 16 points at the receipt.
- **Approach**: ship's children are `dispatch()` runs (R11, R36) and `dispatch()` provisions a pi run for coding and review since U26 and U27, so the step lands nothing; pi applies per child and never as ship's orchestrator (pi's sub-agents spawn ungated child processes, the shape record 0029 forbids, and the extension in the image registers none). Run one plan of two dependent units through the runner on staging, killing the bot once during the coding child, and read the records: the child re-attaches, the parent proceeds on `run finished`, both children's records replay complete, the merge is the runner's under `plan:merge`.
- **Patterns to follow**: U13's live receipt (a plan run under `ship.coordinator: true` carrying two units through review to a merge).
- **Test scenarios**: Test expectation: none -- a human-gated live receipt; the invariant that no sub-agent extension loads is U26's image test (`--no-extensions`; `-e` loads ours alone).
- **Verification**: the receipt on the tracker: a plan of two dependent units runs end to end on pi children with the bot killed once during the coding child; `agent-ship.md` item 16's `[agent]` row points at it.

9. U29 — general, research and conductor; the native loop is deleted

pi as a child process of the bot container with --no-builtin-tools; our web, GitHub, run and skill tools as relays to bot routes over loopback with the run bearer; the conductor's spawn relay still calls dispatch(); reflection's one call moves to @earendil-works/pi-ai; deleted: src/runner.ts, src/providers/, src/tools/workspace.ts; run-loop.md retires. The gate is ask on general and research in production and a conductor run with two children.

### U29. General, research and conductor move; the native loop, both provider adapters and the native tool table are deleted
- **Goal**: Every preset runs on pi; the tree keeps the tool definitions, the record vocabulary and the policy table and loses the native loop, both provider adapters and the native tool table; `@earendil-works/pi-ai` is the provider layer behind reflection's one call.
- **Requirements**: R21, R39, R42, R43, R44
- **Dependencies**: U27 receipted.
- **Files**: `src/core/dispatch/runLoop.ts` (the last `runAgent` caller: `general`, `research` and `conductor` call the bot-container harness entry); `Dockerfile` (the bot image: pi 0.85.1 root-owned, about 434 MB; `src/deploy/imagePins.ts` covers it); `packages/pi-tools/` (the relay tools: `web_fetch`, `web_search`, the GitHub reads and issue writes, the conductor's `spawn_run`, `await_runs`, `send_to_run`, `list_runs` and `get_run_status`, the skill tools, any MCP-bridged tool — each a post to a bot route over loopback with the run bearer); the bot routes the relays call (beside `/harness/authorize` in `src/core/harness/`), executing `src/tools/web.ts`, `src/tools/github.ts`, `src/tools/runs.ts` and `src/tools/skills.ts` in the bot as the requesting user under the policy table; `src/execution/piBridge.ts` (the same code; no attach route: pi is a child process of the bot); `src/core/memory/reflection.ts` (`deps.provider.complete` moves to pi-ai as a library in the bot process); `config/config.example.yaml` (the `providers:` entry re-documented on pi-ai, OpenRouter with it); `src/agents/registry.ts` (the presets' names, prompts and budgets hold; the `toolset` values left — `web`, `assistant`, `conductor`, `none` — index a table this unit deletes, and the extension exposes each preset's tools by the same names); `src/load/` (the driver's dry run against `scriptedProvider.ts`, the check that runs on every pin bump); deleted in the same change: `src/runner.ts`, `src/providers/` (`anthropic.ts`, `openaiCompat.ts`, `registry.ts`, `types.ts`), `src/tools/workspace.ts` (the native rendering of the tool definitions; the schema `gen` reads the definitions in their modules from here on); `docs/reference/specs/run-loop.md` retires with its row in the specs `README.md`; `agent-general.md`, `web-tools.md`, `agent-conductor.md`, `memory.md`, `costs.md`, `execution.md`; `docs/explanation/agents-and-toolsets.md`.
- **Approach**:
1. pi as a child process of the bot container, `--no-builtin-tools`, `--no-extensions -e` the same extension package, `models.json` pointing at the proxy on loopback so the meter stays one; the bridge is the same code; no attach route; a bot death takes pi with it, today's behaviour, and the ledger's resume applies unchanged.
2. The relays: each extension tool posts to a bot route over loopback with the run bearer; the bot executes it with its own credentials under the policy table, the same decision the hook asks for; the conductor's `spawn_run` relay calls `dispatch()` through `spawnChild()` exactly as its in-process tool does today, so the dispatcher stays the only place a run starts; MCP-bridged tools take the same shape, since pi has no MCP client.
3. Reflection's one `provider.complete` moves to pi-ai as a library in the bot process; OpenRouter comes with it and the example config documents it there.
4. Delete `src/runner.ts`, `src/providers/` and `src/tools/workspace.ts`; the schema `gen` reads the definitions in their modules and the native rendering has no reader; `run-loop.md` retires and its rows about budgets, wrap-up and the forced write-up move to the spec of the code that now enforces them; the series' source-scan test asserts no `runAgent`, no provider adapter and no native tool table.
5. The bot image grows by pi's 434 MB under the `imagePins` check.
- **Patterns to follow**: U26's bridge and extension; `src/tools/runs.ts` as it stands (the conductor's tools under the requester's predicate); `src/core/harness/proxy.ts` (a bearer-authenticated route).
- **Test scenarios**:
- `src/core/dispatcher.test.ts` (factory stubbed): `general`, `research` and `conductor` reach the bot-container harness entry and no attach route is called.
- The relays: a tool executes in the bot as the requesting user under the table; a denied one is `tool_refused`; `spawn_run` through the relay reaches `spawnChild()` and every gate (U23's child scenarios pass unchanged); `await_runs` and `send_to_run` through the relay behave as U24's scenarios say.
- `src/core/runLedger/resume.test.ts`: a bot death with pi as a child resumes by the existing rules.
- `src/core/memory/reflection.test.ts`: the reflection call through pi-ai returns the same shape.
- The load harness: the driver's dry run against the scripted provider settles.
- The image test: the bot's `Dockerfile` pins pi 0.85.1.
- The source-scan test: `src/runner.ts`, `src/providers/` and `src/tools/workspace.ts` are absent, no `runAgent` symbol remains, `run-loop.md` is gone from the specs index; `specs:check` resolves every proof reference the retired spec carried or the references are gone with it.
- **Verification**: `npm run verify` green; `agent-general.md`, `web-tools.md`, `agent-conductor.md` and `memory.md` rows bound; the gate, human-gated on the tracker: `ask` on general and research in production and a conductor run with two children, records complete; after this step no native path exists and reversal is a fork of the last native release.

10. U11 superseded

One Status line in the plan's convention (U0, U6, U13 and U14 carry the same key): superseded by record 0032, the seam's substance is U26 and U27, not built as written. The unit's text below it is untouched.

### U11. If adopted: the harness seam
- **Status**: superseded by record 0032 (Phase G, U25 to U29): there is one harness and no `harness` field on a preset; the seam's substance is steps 2 and 3 of the series (U26, U27) and its board item is closed pointing there. Not built as written below.

11. Open questions: one resolved, three carried

The proxy-shape question resolves to "both, by record 0032, built by U25" (the #957 amendment resolved a row the same way). The record's own three open questions (repository skills, AGENTS.md versus the injected contract, the effort-to-thinking map) enter the table pointing at U26 and its gate.

| Does the credential proxy also speak the Anthropic shape? Each accepted shape is another parser on a route reachable from untrusted containers | maintainer | decided by record 0032: both — `/v1/messages` (Anthropic-shaped, so effort, adaptive thinking and prompt caching pass through untouched) and `/v1/chat/completions` (OpenAI-shaped, for the compatible providers); U25 builds them | U25 |
| Record 0032's three open questions: how a repository's skills reach pi; whether pi's own `AGENTS.md` reading duplicates the contract the child is handed, and which wins; the map from the five effort tiers onto pi's seven thinking levels | maintainer | U26: the first live coding run on a repository with skills and its transcript answer the first two; one table in U26's pull request, checked against the proxy's pass-through of `output_config.effort`, answers the third | U26's gate |

12. Verification contract, definition of done, follow-ups ledger

Three new gate rows (the sandbox Worker's verify, the gen + check pair, the no-two-loops source scan) and U25 to U29 on the existing rows; the harness bullet of the definition of done names the record and the end state; the ledger's harness property row flips to superseded and three rows enter for what the record defers or hands elsewhere.

| Bot project tests | `npm test` (root; `deploy:gen` runs first) | U3, U4, U11, U12, U13, U14, U15, U20 to U24, U25 to U29 | all shards green |
| Resident Worker | `npm run verify -w deploy/cloudflare-resident` | U3 to U6, U26, U27 | green |
| Sandbox Worker | `npm run verify -w deploy/cloudflare-sandbox` | U26, U27 | green |
| Extension schemas match the tool definitions | the `gen` + `check` pair U26 adds under `verify` | U26, U27, U29 | green |
| No two loops serve one preset | the source-scan test each Phase G pull request extends (`npm test`) | U25 to U29 | the step's deleted symbols and `runAgent` callers are gone |

- The harness track has its adoption record, [record 0032](../decisions/0032-pi-is-the-harness-the-native-loop-retires.md), accepted with the spike's table behind it, and the replacement series has landed in the record's order with each step's live receipt on the tracker: `src/runner.ts`, `src/providers/` and `src/tools/workspace.ts` are gone, `run-loop.md` has retired, no preset carries a `harness` field, and no execution container ever held a model key.

| The `harness` property on presets | harness exploration Phase 3 | Superseded by record 0032: one harness, no field; the seam's substance is U26 and U27. |
| The fallback if the detached design fails its proof: pi in the bot container with remote operations through the executor for coding | record 0032, the detached process | Deferred; trigger: U26's re-attach proof fails on the resident within its exec model; taken up by a superseding record at the cost of the record's criterion (5), never by a unit here. |
| Children reporting facts as they go | record 0032, boundaries (the owner's same-day follow-ups) | Absorbed into U26 as one more generated extension tool. |
| The conductor's cap and hand-off to the plan runner; cost roll-up per plan and unit; unit threads on a configurable channel | record 0032, boundaries | Out of scope for Phase G: record 0032 assigns them to other units; none touches the harness. |

13. Remaining changes

  • docs/plans/2026-09-10-001-feat-orchestration-program-plan.md L17–L18 — Goal Capsule: the objective names the retirement; record 0032 joins the authorities for the harness.
  • L22, L23 — execution profile lists the specs Phase G changes; tail ownership says Phase G's first PR waits on the record's flip.
  • L31 — the product contract summary states the adoption as decided.
  • L112, L120 — scope: the series is in scope; the record's named fallback replaces the "native loop stays for general, review, research, explore" bullet, which 0032 contradicts.
  • L128, L145 — KD2 and KTD8 gain one sentence each on the series and the superseded harness property.
  • L241 — "Eight phases".
  • L870 — Appendix C lists record 0032.

Decisions

  • U28 waits on U27, not only U26 and U15. The record's step four reads "the coordinator's children are presets already on pi" — ship's children are coding and review, so review must have moved. The task brief said U26; the record's text wins.
  • U29 waits on U27, not U28. The record names a gate for steps 1, 2, 3 and 5 and none for step 4 ("nothing moves"), so step five's precondition is step three's receipt. U28 is a receipt-only unit that can be posted whenever U15 has landed.
  • Six new requirements rather than R20 alone. R20 covers the proxy and the credential boundary and is reused by U25 and U26. The series invariant, the detached process, the per-call gate, the record's dispositions, the no-workspace presets and the generated schemas had no requirement; R20's one open parenthetical is amended because the record answered it (the plan is proposed; docs(ship): the orchestration program absorbs record 0026 slice one and spawn/await as five units, and the plan runner holds plan:merge for plan branches #957 amended R31 the same way).
  • No board links in the plan body. U20 to U24 are not linked from the plan either; their board issues are linked from a comment on the parent issue. This PR follows that: the five issues are listed above and in a comment on Orchestration program (record 0029): residents on Workflows, the ship coordinator, the harness track #821.
  • Three module paths the record does not fixsrc/core/harness/proxy.ts, src/execution/piBridge.ts (the record's word for the bot-side client), packages/pi-tools/ (U11's name for the extension source) — are named because a unit names its files; the record fixes routes and rules, not paths.
  • U11's text stays. decisions:check allows a proposed plan to change freely, but the plan's convention for a superseded unit is a Status line above unchanged text (U0, U6, U13, U14).

Validation

Criterion Receipt
npm run decisions:check green decisions:check ok — 45 record(s) carry a valid status, every superseded_by resolves, accepted bodies unchanged against origin/main
npm run docs:check green docs:check ok — 10 file(s) match the command registry, the decision records and the diagram sources
npm run specs:check green specs:check ok — 43 spec(s), 2697 proof reference(s) checked, every Code/Tests path exists
npm run hygiene:check green (plans are exempt; checked anyway) public-hygiene ok — 0 file(s), 0 hit(s) still listed, 130 line(s) allowed
npm run format:check green All matched files use Prettier code style!
npm run check:pr-title on this title check:pr-title ok — docs(agents): the program plan absorbs record 0032 — …
npm run verify green, exit 0 root vitest Test Files 368 passed (368), `Tests 6772 passed
No bare #NNN added to the plan body; no dates in the new units `grep -nE '(^
The diff removes exactly the 20 lines this amendment replaces (prettier reflowed nothing else) `git diff --
Human-gated: each unit's Files, Approach and Test scenarios trace to record 0032's tables and sections; no new design the reviewer reads the record beside U25–U29

🤖 Generated with Claude Code

…e the native loop behind pi, one preset at a time

Record 0032 decided pi is the one harness and the native turn loop retires in a replacement series of five steps, each landing a pi piece and deleting its native counterpart behind that preset's live receipt. The orchestration program plan now carries that series as Phase G, units U25 to U29, with requirements R39 to R44 stating what the record decided: the proxy and run bearer (U25), pi in both execution images with the extension, the bridge and the attach route while the coding preset moves (U26), review and explore moving with the /exec executor path for agent commands deleted (U27), ship unchanged as the receipt that a plan runs on pi children (U28), and general, research and conductor moving while the native loop, both provider adapters and the native tool table are deleted with pi-ai as the provider layer (U29). U11 is superseded by the series; Phase C's accept line points at record 0032; the graph, the open questions, the verification contract, the definition of done and the follow-ups ledger follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@coreplane-switchboard coreplane-switchboard Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM: Docs-only plan update absorbing record 0032's Phase G (U25–U29); internally consistent, links resolve — two cosmetic leftovers around superseded U11.

  • [nit] F1 docs/plans/2026-09-10-001-feat-orchestration-program-plan.md:183 — Stale mermaid edge U22 --> U11 after U11 is superseded
  • [nit] F2 docs/plans/2026-09-10-001-feat-orchestration-program-plan.md:788 — Verification-contract row still lists superseded U11

Verdict: approve — clean, coherent docs-only plan update; the two findings are cosmetic leftovers around the superseded U11 (a stale mermaid edge and a verification-table mention) and don't block.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-approved: coreplane-switchboard[bot] reviewed this PR and posted an LGTM verdict (see its review). This repository opted in through its REVIEW_BOT_LOGIN and REVIEW_BOT_ID variables.

@justinhelmer

Copy link
Copy Markdown
Contributor Author

Review round 1 (LGTM, two nits) — both skipped under the current severity level (minor), no repush:

  • F1 (nit, stale U22 --> U11 mermaid edge): kept on purpose. The U11 node stays in the graph, relabelled superseded by 0032, the way the plan keeps U0, U6, U13 and U14 as superseded/deferred nodes rather than deleting history; its incoming edge records what the seam once waited on.
  • F2 (nit, U11 in the verification contract's npm test row): pre-existing text; the row lists every unit the gate ever applied to, U11 included, and the unit's Status line says it is not built as written.

Both are cosmetic and pre-existing; the amendment's own lines carry no U11 dependency. Resolved as skipped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant