Skip to content

docs: the front door, the runner and the budgets read as built — every page says a plain message routes itself, no pause exists, turns are a guard, and the plan's graph matches its text - #1031

Merged
justinhelmer merged 1 commit into
mainfrom
docs/front-door-staleness-sweep
Sep 14, 2026
Merged

Conversation

@justinhelmer

Copy link
Copy Markdown
Contributor

Tonight's decisions landed on main and left the docs describing an earlier design: a pause before a routed coding run, a compound unit still to build, turns as a budget, a ship flag that no longer exists, and tutorials that tell people they must type agent:coding. This PR sweeps the markdown so every page reads as built; no code changes.

What & why

Six themes, each a decision already on main:

  1. The front door is live and there is no pause. The request router (#1022; record 0026 slice two; plan U30) routes a plain message to general, research, review, explore or coding on the fast model, and a routed coding dispatches at once; ship is routable: false because it holds the merge grant, and the conductor is reached only through the compound form. #1028 fixed R46 and U30's text; this PR fixes what it left: R48 still said a compound with a write part "pauses once, at the parent", KTD15 still decided how the pause was expressed, U30's Files and Patterns still listed the route_pending notes and the inbox the pause read, U31 still said a write route "waits for go", the Phase H receipt and the Definition of Done still required "no write preset dispatched from a route without go". The plan's names for the record also catch up with what U30 built: run_meta.agentSource and a route event, not chosenBy, routedReason and a run_note routed.
  2. U32 is merged (#1030), ahead of U31. The unit gets a Status line, its Goal drops the pause rule, its Files describe the built shape (parts: [{ text, preset }], compound_rejected: <why>, routeCompoundFixtures.ts with twenty compounds and five decoys), its test scenarios name the tests that exist, and its Verification states the built bar (detection on at least 90 percent, no decoy split). The phase table's H row says U30 and U32 are built and U31 open; U30 gets a Status line too.
  3. Turns are a runaway guard, not a budget (#1020). The two pages that still said otherwise, the code map's src/runner.ts row ("Turn budgets on the agent def") and the GitHub-tools spec's general item ("with 8 turns"), now say the wall clock is the budget and the turn cap is derived from it. The owner's per-run dollar cap is not documented as shipped; it is one row in the plan's follow-ups ledger, marked planned and built outside this plan.
  4. U26 waits on the front door being built, not on U31's calendar week. The plan's graph already had U32 --> U26 and nothing from U31, but KD5, the Summary and the Phase G row said "U26 to U29 wait behind U30 to U32", which reads as U31 too. KD5, the Summary, the Sequencing paragraph, the Phase G row, U26's Dependencies, U31's new Status line and the ledger row now say explicitly that U31 is a calendar wait (the default flip after a week of production routes from 2026-09-14 06:16Z) and blocks nothing in Phase G; U25 is marked live in 1.208.0.
  5. One ship implementation (#1011). Record 0032 (proposed, so amendable) still counted the in-process round loop's two children among runAgent's call sites and described ship's children "under ship.coordinator: true" with the round loop's retirement as future work; its today table and Rollout now say the round loop is deleted, ship.coordinator is no longer a key, and runAgent has three call sites. The agents explanation's ship row says the plan runner runs each round as a child run in a thread of its own.
  6. A plain message routes itself; the directive forces a preset. The get-started tutorial, the first-request tutorial and the Slack commands reference told people to type agent:review or agent:coding to pick an agent. They now say the directive forces one and that, where the router is on (routing.auto: on for the production deployment since 1.211.0 through infrastructure#86, and the default for every install once U31 lands), a plain message picks its own agent and the card says why. The README's seam sentence, the request-flow explanation and the config-layers explanation gain the routing stage in one sentence each; the request-flow table gains the conductor.

Tour

1. The tutorial no longer tells a person to pick an agent by hand

The get-started table's agents row said "Say agent:review or agent:coding in a message to pick one". It now says the directive forces one, and that with the router on a plain message picks its own agent and the card says why, with the link to the row that turns it on. The first-request tutorial and the Slack commands reference say the same thing in their own words.

Look for: the wording stays conditional on routing.auto, because a fresh install has it off until U31 flips the default.

| **Three agents** | `general` answers and reads links. `review` reviews a pull request. `coding` makes a change and opens the PR. Say `agent:review` or `agent:coding` in a message to force one; the default is `general`, and with the router on (`routing.auto`, [Turn features on and off](../how-to/turn-features-on-and-off.md)) a plain message picks its own agent and the card says why. |

2. R48 and the plan's pause vocabulary

R47 already recorded the owner's call; R48 still said a compound with a write part "pauses once, at the parent". It now says a compound with a write part dispatches at once under R47's rule and carries the built part shape ({ text, preset }). KTD15 is amended the way R47 was: it records that the pause was decided, built and removed, and what survives (no card control, Stop and Kill as the one write control). The Phase H receipt and the Definition of Done drop "without go".

Look for: the remaining route_pending, route_confirmed and reply go to start mentions in the plan are all inside sentences that say the pause was built and removed (R47, KTD15, U30 Approach step 4); none describes a pause that exists.

- R47. A route to `coding` dispatches at once like any other preset — the owner's call, 2026-09-14 ("creating PRs aren't really destructive"): this requirement first asked for a pause (the card's `reply go to start`, no repository, memory read, executor or token until the thread answered `go`), and U30 built, reviewed and removed it, because a wrong route to `coding` costs a pull request, which is cheap to undo, and the card's `routed: <reason>` is the affordance for catching it; record 0026's "harmless when wrong" line is the risk accepted for `coding`, not met. One preset is never routed, structurally: `ship` holds the merge grant (the plan runner merges on an approving review with the guards green), so a wrong route into it would be code landing on main — its def is `routable: false`, it is absent from the table the model is shown and refused by the parse when the model names it, and a request that wants it types `agent:ship`. `general`, `research`, `review`, `explore` and `coding` route; `conductor` (it starts other runs) and `ship` do not.
- R48. The router may answer that a request has several independent parts — `{ preset: "conductor", parts: [{ text, preset }, …], reason }`, accepted only when `conductor` and every part's preset are in the offered table and the parts are within `spawn.maxChildren` — and the run becomes a `conductor` run whose request names one child per part with the preset the router chose for it; each child is a `dispatch()` run through `spawnChild()` as the requester under every gate, its text beginning `agent:<preset>` so a child never routes; a compound with a write part dispatches at once, under R47's rule (no pause: the owner's call of 2026-09-14); a deployment that restricts `conductor` never receives a compound answer, because the router is offered only the presets the requester may run.

3. U32 reads as built

The unit gets a Status line naming what landed and that its live receipt on the board item closes it; the Goal drops "under the same pause rule for write presets". Below it (not anchored) the Files, Approach, Test scenarios and Verification describe the built shape: the fixture file's real name and counts, compound_rejected instead of a fallback, no agent:review reply that collapses a compound (never built), detection at 90 percent and zero decoys split instead of "at most 1 of the 10 singles".

### U32. Compound requests route to the conductor
- **Status**: built, merged 2026-09-14, ahead of U31: the compound form is a second shape of the router's one call, a compound resolves `conductor` through the same gates as any route, each part becomes one child through `spawnChild()` with its own directive, the card lists the parts from its first paint, and the replay scores the checked-in set (twenty compounds, five decoys) and machine-checks record 0026's read-only-to-write clause as a row of its own. The live receipt on the board item closes the unit.
- **Goal**: A message with several independent parts becomes one conductor run that spawns one child per part, each on the preset the router chose for it, with no pause for a write part (R47) — and the replay scores the split.

4. KD5 says which front-door units U26 waits on

The decision that reordered the pi series behind the front door now names the units: U26 to U29 wait behind U30 and U32 being built, and U31 is a calendar unit that blocks nothing in Phase G. The Summary, the Sequencing paragraph, the Phase G row, U26's Dependencies and the ledger row say the same, so the text agrees with the graph's U32 --> U26.

- KD5. **The front door before the pi series.** (Governs R45 to R49; reorders Phase G.) The owner's call, 2026-09-14: "people really need that feature for it to be useful to them so it might make sense over pi as a priority." Record 0026's slice two — the router — is the next work after the proxy (U25) lands; U26 to U29 wait behind the front door being built, U30 and U32 (both merged 2026-09-14). U31 is not in that wait: it is a calendar unit (the default flip after a week of production routes from 2026-09-14 06:16Z, `help` in plain language, the override on the card) and it blocks nothing in Phase G, so U26 may start once U25 and U32 are receipted. Chosen over finishing the pi series first because the series is invisible to a person who mentions the bot and the front door is what that person meets; the series loses nothing but calendar, since U25 touches no routing code and U30 touches no harness code.

5. Record 0032's today table knows the round loop is gone

The record is proposed, so its today facts may be amended. The native-loop row counts three runAgent call sites and says the round loop's two children were call sites until the plan runner became ship's one implementation; the ship row says every agent:ship request hands to the runner and ship.coordinator is no longer a key. The Rollout's steps 2 and 4 (not anchored) drop the round-loop retirement as future work.

Look for: the three call sites and their line numbers match git grep -n "runAgent(" on main (runLoop.ts:463, descriptionTurn.ts:164, reviewRound.ts:488).

| The native loop is 3,471 lines in nine files (`src/runner.ts` 937; `src/providers/` 694; `src/execution/executor.ts`, `resident.ts`, `cloudflareSandbox.ts` 1,132; `src/tools/workspace.ts` 708) of 85,214 non-test `.ts` lines under `src/`. Every preset reaches it through `runAgent`, from three call sites: the run stage, the description turn (the coding definition clipped to 8 turns / 5 min) and the head-move re-review; the in-process ship round loop's two children were call sites until the plan runner became ship's one implementation | `src/core/dispatch/runLoop.ts:463`, `src/core/descriptionTurn.ts:40-41,164`, `src/core/reviewRound.ts:488` |
| The resident runs an agent command as one `/exec`: privilege-dropped to the thread's OS user (`worker2` to `worker17`; `worker1` builds), 5 min by default, 20 min at most, output collected at the end; no route holds a stream open across calls. A resident Worker deploy swaps the isolate (the container survives), invalidates the SDK's process handle (`runtime-replaced`) and re-mints the DO's incarnation id; the collect phase is never retried. Nothing kills a detached process: the sweep removes an idle worktree and recycles its user without a kill, and `kill -9 -1` runs only on a forced detach with an op in flight | `deploy/cloudflare-resident/worker.ts:414-439,464,546-560,1521-1532,1680-1725,5087-5160,5453-5507`, `src/execution/residentDetach.ts:30-38`, `docs/reference/specs/resident-repos.md:30`, `src/execution/bashTimeout.ts:7,15` |
| The bot alone holds the model key and calls the provider itself (the review abridge's `meat` binary runs on the bot host with the same key); there is no proxy, no run-scoped credential, no per-run meter beyond the `model.turn` span's token attrs; `maxTokens` is a per-call output cap, `maxTurns` is the runaway guard derived from the wall clock (six turns a minute over `maxMinutes`) and ends a run pacing like a loop with a tool-less write-up that says so; the costs page's LLM line is the Anthropic Admin cost report by workspace | `src/runner.ts:251-313,612-620,738-750`, `src/core/meatProcess.ts:85`, `src/core/costs.ts:21-22` |
| The policy table has no tool-level row; what bounds a coding run's push is the repo-scoped token attach writes into the worktree, and the authorization spec carries the tool-level gate as a `[gap]`; the `agent` actor kind and `onBehalfOf` exist and effective grants intersect | `src/core/authz/policy.ts:83-88`, `src/core/authz/types.ts:12,47`, `src/execution/residentCredentials.ts:8`, `deploy/cloudflare-resident/worker.ts:4461`, `docs/reference/specs/authorization.md` item 1 and the roadmap row "Agent actors gate tool-level actions" |
| The pi investigation: 34 sourced facts on the RPC protocol, extensions, key resolution, sessions and security; an event map with seven kinds that have no home on the run stream; the driver that ran the measurement, on the operator's machine in a warm checkout, never in a container; pi is MIT, its start was inside the dry run's 2 s wall | the spike receipt and the comparison comment on the tracker; `src/load/piRpc.ts:67` (`PI_EVENT_HOME`), `piProcess.ts`, `piExtension.ts`, `piTasks.ts`, `piPolicyPreview.ts`, `scripts/load.ts` |
| The execution images measured 3.28 GB / 924 MB (sandbox) and 2.65 GB / 718 MB (resident), unpacked / compressed, in the pull request that grew them (merged 2026-09-12); both copy in Node 24.21.0, above pi's `>= 22.19.0` floor; both containers are 4 vCPU / 12 GiB / 20 GB, the resident hosts up to 16 thread users under a 1,536 MB heap cap each. The bot redeploys on every release: three on 2026-09-12 alone (v1.204.0 to v1.206.0) | the validation table of the pull request that grew the images, `deploy/cloudflare-sandbox/Dockerfile:13`, `deploy/cloudflare-resident/Dockerfile:12,43`, both `wrangler.template.jsonc`, `gh release list` |
| Ship has one implementation, the plan runner: every `agent:ship` request hands to it and its children are `dispatch()` runs; the in-process round loop is deleted and `ship.coordinator` is no longer a key; the plan's decision that pi runs a child and its harness-seam unit word the adoption as a `harness: native \| pi` preset property | `src/core/coordinator/contract.ts`, `src/core/dispatch/ship.ts`, `docs/reference/specs/agent-ship.md` item 16, the program plan (the decision that pi runs a child, the harness-seam unit, the round-loop retirement unit) |

6. The last two pages that called turns a budget

The GitHub-tools spec's general item said the assistant runs "with 8 turns"; it now says it runs on the 5-minute budget with the turn guard derived from it (the registry gives general loopBudget(5), so its cap is 30). The code map's src/runner.ts row (not anchored) says the wall clock is the budget and the turn cap a runaway guard, pointing at run-loop item 1.

5. **`general` becomes the assistant** ([agent-general.md](agent-general.md)): toolset `assistant` = the GitHub tools + `web_fetch` + `update_status` — no shell, no file writes, no `web_search`, no verdict/PR submission — with the same 5-minute budget (a repo read is repos → tree → file; an issue action one or two calls) and the turn guard derived from it. Its prompt names the tools, tells it to resolve a loosely-named repo with `github_repos` (or the thread) rather than asking, to read a repo before answering about it, to report exactly what a tool did and never claim an unperformed action, and to redirect code changes / PR reviews / web research to `agent:coding` / `agent:review` / `agent:research`. It still declares no `resources` — the tools are REST in the bot process, so a general ask never provisions a workspace (agent-general item 4).

7. Remaining changes

  • README.md — the dispatcher sentence gains "routing" between config layers and authorization.
  • docs/explanation/how-a-request-flows.md — the core sentence gains routing; the seam table's Agent row gains conductor; one paragraph after the sequence diagram says what happens without a directive.
  • docs/explanation/agents-and-toolsets.md — the ship row describes the plan runner's child rounds; one paragraph after the table says the directive forces a preset and a plain message routes itself where the router is on.
  • docs/explanation/config-layers.md — one sentence: the router picks the preset when no layer above the defaults names one.
  • docs/reference/code-map.md — the src/runner.ts row: wall clock is the budget, the turn cap a runaway guard.
  • docs/reference/slack-commands.md — the agent:<name> directive row: forces an agent; without it a plain message picks its own where the router is on.
  • docs/tutorials/first-request-in-slack.mdagent:coding forces the agent.
  • docs/plans/2026-09-10-001-feat-orchestration-program-plan.md — beyond the anchored steps: R46 and R49 use run_meta.agentSource and the route event; U30 gains a Status line and its Files, Approach, Patterns and test scenarios drop the pause and the old record names; U31 gains a Status line and drops "waits for go" and the paused card; the Phase G and H rows carry unit states; Appendix B gains the cost-budget row.
  • docs/decisions/0032-pi-is-the-harness-the-native-loop-retires.md — the Rollout's steps 2 and 4.

Decisions

  • Frozen records untouched. 0026 and 0029 are accepted and every implemented plan (the agent-ship pipeline plan among them) still describes the round loop as history; decisions:check holds their bodies unchanged against origin/main. Only the proposed plan and record 0032 were amended.
  • The help text is code and is U31's. src/core/commands/help.ts still leads with the directive list; rewriting it is U31's unit and no doc pretends otherwise.
  • The dollar cap is not pre-documented. One ledger row in the plan says a per-run cost budget is planned and built outside this plan; no spec, how-to or reference page describes it.
  • Routing stays conditional in user-facing pages. routing.auto is off in a fresh install until U31, so the tutorials say "with the router on" rather than "a plain message always routes"; the production deployment has it on since 1.211.0.
  • History in the plan stays. Sentences that say the pause was built and removed (R47, KTD15, U30 Approach step 4) keep their old vocabulary because they describe what was removed; the git grep counts below name them.
  • AGENTS.md untouched. It is 22 bytes under its budget and its agent:review and agent:coding mentions describe the house process (a review is requested by directive), not a rule for users.

Validation

Gates run in the worktree at the pushed head; the log for the whole gate is verify.log in the session scratchpad (1045 lines, last line verify exit=0).

Gate Result
npm run decisions:check ok — 45 record(s) carry a valid status, every superseded_by resolves, accepted bodies unchanged against origin/main
npm run docs:gen then npm run docs:check docs:gen ok — 0 of 10 file(s) changed; docs:check ok — 10 file(s) match
npm run specs:check ok — 44 spec(s), 2783 proof reference(s) checked, every Code/Tests path exists
npm run specs:coverage -- --changed origin/main...HEAD --test-guard every changed source path has a covering spec; test-guard ok — 0 test file(s) changed
npm run hygiene:check ok — 0 file(s), 0 hit(s) still listed (no new #NNN in a spec or record)
npx prettier --check on the 11 files All matched files use Prettier code style!
npm run verify exit 0 (check:site ok — 8 required page(s) present)
npm run check:pr-title -- "<this title>" check:pr-title ok

Stale phrases, git grep -c -F over README.md AGENTS.md CONTRIBUTING.md docs on origin/main and at this head:

Phrase main head Where a hit remains
pauses once 1 0
without \go`` 2 0
waits for \go`` 1 0
reply go to start 3 2 R47 and U30 Approach step 4: "built and removed"
route_pending 3 2 KTD15 and U30 Approach step 4: history of the removal
route_confirmed 2 1 U30 Approach step 4: history of the removal
route_unconfirmed 3 1 U30 Approach step 4: history of the removal
chosenBy: router 4 0
profile.chosenBy 2 1 R46: "first written as"
run_note routed 2 1 U30 Status: "the plan's first words"
routedReason 3 2 R46 and U30 Status: the old names, named as old
parts: [{ preset, prompt } 2 0
ten single requests 1 0
routeCompound.fixtures 1 0
U26 to U29 wait behind U30 to U32 2 0
Turn budgets 1 0
with 8 turns 1 0
Ship's children under \ship.coordinator: true`` 1 0
round-loop retirement unit deletes 2 0
pick one; the default 1 0
picks the agent; \in acme/api`` 1 0
run this agent for this message only 1 0

Human-gated: none; a docs-only change with no visual surface.

🤖 Generated with Claude Code

…y page says a plain message routes itself, no pause exists, turns are a guard, and the plan's graph matches its text

The program plan, record 0032 and the user-facing pages catch up with the
decisions on main: the request router is live with no pause for write presets,
U32 is built ahead of U31 and U31 blocks nothing in Phase G, turns are a runaway
guard rather than a budget, ship has one implementation, and a plain message
routes itself where the router is on while a directive forces a preset.

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 staleness sweep; every updated claim I spot-checked matches the code as built (round loop gone, agentSource/route event, routable flag, turn cap as derived guard).

Verdict: approve — docs-only staleness sweep, and the updated claims match the code.

Reviewed head 6ad059b (#1031, 11 files, +51/−41 — full diff and digest covered). No findings.

What I verified against the source, not just the prose:

  • 0032 decision record — "three call sites" for runAgent is accurate: src/core/dispatch/runLoop.ts:463, src/core/descriptionTurn.ts, src/core/reviewRound.ts:488; the ship round loop's children no longer call it (the only runAgent mention in src/core/dispatch/ship.ts is a comment noting they are dispatch() runs). ship.coordinator is indeed no longer a key — src/config/validate.ts:344 refuses it by name.
  • Plan R46/U30 rewording (chosenBy/routedReason/run_note routedrun_meta.agentSource + route event) matches what was built: agentSource is on src/core/dispatch/provision.ts:277-278 and threaded to the record/ledger meta.
  • R47/KTD15 pause removal and routable: falsesrc/agents/registry.ts carries routable?: false on ship (line 505) and conductor (line 555), exactly as the plan now says.
  • github-tools.md item 5 and code-map.md — the "8 turns" → "turn guard derived from the budget" edit matches runawayTurnCap(maxMinutes) / loopBudget() in the registry.
  • Agent seam row gaining conductor in how-a-request-flows.md matches the registry.
  • specs:coverage reports no spec-covered source paths changed (spec edits here are the sweep itself, updated in-diff — not contradictions); test-guard ok, no tests touched.

Nothing blocking, nothing to flag.

@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
justinhelmer merged commit bdf2504 into main Sep 14, 2026
30 checks passed
@justinhelmer
justinhelmer deleted the docs/front-door-staleness-sweep branch September 14, 2026 07:01
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