AGENTS: Cheap Routing - No job refused for ordinary words

The actual documents the agents read and work from, shown exactly as they are on disk — not a summary. See the progress view instead · All projects

Plan PLAN.proposed.txt

# PLAN — ROUTER EVALUATION — the cheap bench actually gets the work (2026-09-09 shape)

Owner: the Group E overseer. Written 2026-09-09 by the Opus senior-engineer session, on Nick's own routing instruction ("why dont you pull back to opus and sonnet where reasonable for build but focus the UI on fable", 2026-09-05) and the programme plan's §3b row 1, which names Opus as the writer of every lane plan. Added to the programme on Nick's words of 2026-09-09: "we need to evaluate whether the router needs additional work so add that to the plans list". This is the lane's first plan; there is no earlier one to supersede.

**🔴🔴 THIS IS THE ONLY PLANNING DOCUMENT FOR THIS LANE. Do not create a second plan, tracker, summary, or scratch state file — extend THIS file or its PROGRESS.txt companion. Any status view is GENERATED from this plan; if a view disagrees with the plan, the plan wins.**

**NORTH STAR:** every job Nick's agents hand to a cheap model actually gets done by a cheap model — nothing turned away for ordinary words, the cheap bench able to write its own checks through one sanctioned route, and what it costs counted and reported to him every morning rather than capped. His words: "there is ZERO limit to what we hand off to cheap models … we track how they are being used for data not to limit them" (2026-09-09).

**FINISH LINE:** each item passes its one check — (a) every hard-floor refusal on record since 2026-09-02 is either re-run and passing, or a proven floor hit with the matched kind named, in one evidence file, and every future refusal records the file and the matched kind so the next re-run is exact; (b) a cheap worker can write a test or guard file through exactly ONE sanctioned route, proven refused outside that route and allowed inside it, with the nightly runner leaving the route's file alone; (c) the morning report says what went cheap, what stayed and why, and states in words that the per-task ceiling is off; (d) a first cheap call stops coming back empty on the vendor response shape that produced 48 empty replies, proven red then green; (e) the routing code's own text says cheap models check another agent's work and Sonnet is the backup, with both router suites still green and no case count lower than today's; (f) every failure row on record carries a machine-readable kind, so quota exhaustion never reads as a refusal; (g) rule C of the shared-copy fence has a written answer — on or off today, what created and what removed its switch, and what every live lane in the shared copy does — handed to the Workshop lane. Written once, never raised mid-drive.

**Owner:** the Group E overseer · **Overseer:** ONE — Opus or Codex `gpt-6-astra`; never builds · **Design authority:** none — nothing here is rendered
**Rule: a step starts the moment its named inputs exist, whatever its number. A step closes on ONE independent check by a different model. Nothing waits on Nick to test.**

### STEP 0 — ARM THE LOOP, BEFORE ANYTHING ELSE
Set a 5-minute loop. Every time it fires, answer these four in order and CORRECT any failure before doing anything else:
1. **NORTH STAR** — is what I am doing this minute moving this plan's North Star? If not, drop it and take the highest-value unblocked step that does.
2. **FAN-OUT** — is every step whose START WHEN inputs exist running, up to the cap of 8? Below the cap with ready work: dispatch now. At the cap: queue, never launch.
3. **CHEAP** — is every build and every check on a cheap model by name? A refusal from the router is a failure to log (Nick, 2026-09-09), never a reason to promote the job to Sonnet or Opus; a cheap vendor failure goes to the named backup vendor.
4. **BLOCKED** — is anything "waiting"? Re-read its START WHEN line; if the artefact is there, start it; if it truly is not, one line to the overseer naming the ONE missing thing, and on to the next step.

## Already true (facts, not story)

- DECIDED, and never asked again: spend is counted and never capped; cheap models check another agent's work with Sonnet as the backup; the floor is four things and the refuser must prove the value — evidence: Nick's words of 2026-09-09 quoted in §1a rows 2, 3 and 4
- BUILT: the floor scanner treats a credential label followed by prose or a placeholder as prose, not a value — evidence: commit `2d223168b4fb0a834086a639fc95476c16d4a704`, 2026-09-09 16:09 -0500, "Floor scanner: a credential label followed by prose or a placeholder is not a value", the code at `projects/personal/skippy-app/lib/grunt-egress-scan.mjs` lines 2224-2235
- BUILT: the floor is four items in code and the refuser must name the hit — evidence: commit `48166a9a41b5b1d6a57fbcbaceed86458008410b`, 2026-09-09 11:16 -0500, and the frozen floor list at `projects/ops/lib/vendor-fence.mjs` line 272
- BUILT: the per-task ceiling is off unless an environment variable is set, and the ledger records every dollar regardless — evidence: `projects/ops/spend-tracker.mjs`, `perTaskCeilingUsd()` at line 90 and `canSpendForTask()` at line 221, with the comment at line 217 reading "SKIPPY_PER_TASK_CEILING_USD is explicitly set (Nick, 2026-09-09). The ledger still records every dollar because the tracking exists for DATA and not for limiting"
- BUILT: a daily cheap-routing report already exists and already reaches Nick — `projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs` (21 KB), scheduled at 07:17 by `projects/ops/skippy-jobs/runner.mjs` line 269. It is EXTENDED by this lane, never copied
- BUILT: the dispatch gate's test-authoring override category is already retired, so test authoring no longer needs a special reason — evidence: `projects/ops/skippy-jobs/lib/dispatch-contract.mjs` line 1090, "RETIRED 2026-09-09 (Nick: these models are smart enough to do their own initial QA)", and `OVERRIDE_CATEGORIES` at lines 1165-1167 now holding the floor category alone
- MEASURED: the routing check waves a `_test-`/`check-` file rather than refusing it — evidence: `projects/ops/skippy-jobs/lib/check-routing-missed.mjs` line 437 and the wave at line 947
- MEASURED: the one gate that actually stops a cheap worker writing a test or guard file is the cheap-lane path verdict — `_test-` and `test-` sit in the control-plane list at `projects/ops/lib/vendor-fence.mjs` lines 938 and 954, refused through `projects/ops/cheap-task.mjs` lines 1432-1435
- MEASURED: the refused payload is recorded nowhere. `projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` holds 52 hard-floor refusals, 36 of them dated 2026-09-02 or later, and each row carries only a timestamp, the vendors tried and the matched KIND inside a truncated sentence — kinds across all 52: secret label 31, account word beside digits 18, long account or card number 3. `projects/ops/skippy-jobs/lib/routing-decisions.log` holds 26 wall refusals dated 2026-09-02 or later that DO name a file, plus 428 rows of the `cd_then_relative_target` block class; `projects/ops/skippy-jobs/lib/cheap-build-failures.log` names a file per refusal too — evidence: the counts and field lists gathered 2026-09-09, recorded in `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/CHECK.txt`
- MEASURED: the largest non-quota cheap failure is an empty reply, 48 rows, thrown at `projects/ops/cheap-build.mjs` line 1341 when the reply text extractor comes back empty and the response keys are printed instead; the sibling vendor succeeded on each one
- MEASURED: the wall-verdict instrument this plan's STEP 1 leans on exists and is safe — `projects/ops/cheap-build.mjs` defines `--verdict-json` at line 204 and its block at lines 730-745 prints the classification as JSON and exits at line 744 before anything is sent, with no vendor call; it requires `--file` and `--do` alongside it, and was run once on a harmless file at this lane's check record, exit 0
- MEASURED: the shared-copy fence is wired as a hook at `.claude/settings.json` line 255, its rule C switch is absent right now, the fence log carries rows reading RULE-C BLOCKED (36 at the time of this lane's check record, and the log is live) and zero rows reading `ruleC=on`, the switch sits in a gitignored folder so it has no history, and the only record of its creation is a fence test run at 2026-09-09T22:09:00Z — evidence: `projects/ops/skippy-jobs/lib/worktree-fence.log`, and the header of `projects/ops/skippy-jobs/lib/check-worktree-fence.mjs` lines 29-44 stating rule C is default off on purpose
- BUILT: the two router suites are green today, counted by running them — `projects/ops/skippy-jobs/_test-work-type-gate.mjs` exits 0 at 72 checks and `projects/ops/skippy-jobs/_test-dispatch-override-categories.mjs` exits 0 at 52; both are named by `projects/ops/skippy-jobs/lib/ROUTER-SETUP.md` as the suites that must stay green after any edit here, and 72 and 52 are the floor no step may go below

## 0 · Gate Zero receipts (the plan may not exist without these)
- Failure Mode Registry loaded: 2026-09-09 — 229 table lines (`command grep -c '^|' ZION/skills/plan/references/failure-registry.md` → 229), which the plan checker counts as 193 parseable entries; the seven this lane is exposed to are named in §4
- Canonical specs loaded: the plan skill's 2026-09-09 shape (`ZION/skills/plan/SKILL.md` §1C §M §F §S §P §U §A §B), the router's own onboarding `projects/ops/skippy-jobs/lib/ROUTER-SETUP.md` (its two suites must stay green), and the floor as it exists in code, `projects/ops/lib/vendor-fence.mjs`
- Ownership check: this lane's folder held no plan before this file (its only sibling is the planner brief); every tool this plan touches already has exactly one owner and is EXTENDED in place — the morning report job, the floor scanner, the cheap-lane path verdict, the failover record and the spend ledger. No new job, no new log, no second report
- Expected inputs confirmed to exist: `projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` (526,585 B), `projects/ops/skippy-jobs/lib/routing-decisions.log` (1,697,142 B), `projects/ops/skippy-jobs/lib/worktree-fence.log` (3,202,593 B), `projects/ops/cheap-task.mjs` (158,852 B), `projects/ops/cheap-build.mjs`, `projects/ops/lib/vendor-fence.mjs` (93,137 B), `projects/personal/skippy-app/lib/grunt-egress-scan.mjs` (236,446 B), `projects/personal/skippy-app/lib/grunt-lane.mjs` (124,400 B), `projects/ops/spend-tracker.mjs` (17,295 B), `projects/ops/skippy-jobs/lib/check-routing-missed.mjs` (92,948 B), `projects/ops/skippy-jobs/lib/dispatch-contract.mjs` (105,242 B), `projects/ops/skippy-jobs/lib/check-worktree-fence.mjs` (14,688 B), `projects/ops/skippy-jobs/runner.mjs` (225,862 B), `projects/ops/walkaway/MODEL-MATRIX.md` (10,734 B), `projects/ops/sp-sec/PLAN.md` (40,801 B) — each checked on disk 2026-09-09
- PLAN AUTHOR: the Opus senior-engineer session of 2026-09-09 that wrote this lane plan from the programme plan and the lane's measured facts
- COLD READER: none — SINGLE-AUTHOR, UNREVIEWED — the Group E overseer's pickup read is the one cold read
- PROMPT-SPEC scan (P1–P7): P3 fired on "every refusal of the last week re-run against the fixed scanner" — the refused payload is stored nowhere, measured tonight, so STEP 1 defines the re-run as by-file where a row names a file and by matched-kind fixture where it does not, and closes the gap by recording the file and the kind on every future refusal. P1 fired on "the three gates deadlocking on that file class" — measured tonight, one of the three has its category retired and one only waves, so STEP 2 fences the single gate that actually refuses. P7 fired on "whether the router needs additional work" — read as the six items of the programme's §3d and §1b Group E row plus the two polish items named there, and nothing wider.

## 1 · Goal and definition of done
- **What we're building, one paragraph.** The router finished as the thing that hands work OUT rather than holds it back: no cheap job refused for ordinary words, one sanctioned route for a cheap worker to write its own test or guard file, a morning line telling Nick what went cheap and what stayed and why with the ceiling off, a first cheap call that stops coming back empty, honest failure kinds so a weekly quota never reads as a refusal, and a written answer on the shared-copy fence handed to the lane that owns it.
- **HOW IT'S USED:** every agent dispatch passes through it all day without anyone thinking about it, and Nick reads one line about it in his morning message. · HOW WE KNOW: Nick, 2026-09-09, asked for the evaluation and for tracking "for data not to limit them"; the morning report already reaches him at 07:17.
- **WHAT IT LOOKS LIKE:** no screen. One added paragraph in the morning report he already gets, and silence everywhere else — a job that is not refused produces nothing to look at. · HOW WE KNOW: `projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs` is the existing daily digest and its send path is unchanged.
- **WHERE IT LIVES:** the router's own files under `projects/ops` and `projects/personal/skippy-app/lib`, the failure and decision records beside them, and the morning report line that Nick opens on his phone. · HOW WE KNOW: the ownership check above; the runner's 07:17 schedule row.
- **WHAT IT MUST DO:** (1) re-run every recorded hard-floor refusal since 2026-09-02 and show each one passing now or proven as a real floor hit with its kind named; (2) record the file and the matched kind on every future refusal, never the value; (3) let a cheap worker write a test or guard file through exactly one sanctioned route, refuse it everywhere else, and keep the nightly runner off that route's file; (4) say in the morning report what went cheap, what stayed and why, with the per-task ceiling stated as off; (5) stop the empty first reply on the vendor response shape that produced 48 of them; (6) put a machine-readable kind on every failure row so quota, refusal, timeout, shape and parse are told apart; (7) make the routing code's own text say cheap models check another agent's work and Sonnet is the backup, with both suites still green and no case count lower than today's; (8) answer the shared-copy fence's rule C in writing and hand it to the Workshop lane.
- **NOT in scope:** the ANTI-SCOPE — (a) security and privacy work of any kind: nobody chases it, a finding costs one line in `projects/ops/sp-sec/PLAN.md` and the agent returns to its step in the same turn (Nick, 2026-09-09); (b) the wording of `projects/ops/MACHINE-RULES.md` itself — the rule file belongs to the Rule-file trim lane and is handed to it by name in §3c, while this lane owns the CODE text only; (c) switching rule C of the shared-copy fence on or off — the Workshop lane owns the shared copy and receives this lane's measured finding, because a hook only protects sessions that start after it lands and flipping it mid-flight refuses live lanes' ordinary work; (d) widening or narrowing the floor — it is four items, fixed by Nick on 2026-09-09, and this lane makes the refuser prove a hit rather than changing the list; (e) choosing which vendor gets which job — the model matrix decides that and every step names its vendor from it; (f) the weekly GLM quota itself — 239 of the recorded first failures are that quota, which is a purchase question and not a router defect, so this lane only makes it READ as quota.
- **Trip-over protocol:** a lane that finds something outside the fence writes one handover line to its named owner (a security- or privacy-shaped thing: one line in `projects/ops/sp-sec/PLAN.md`), then back to building — never investigates, never fixes.

## 1a · Critical variables — the confirmation sheet is GENERATED from this table

| # | The variable, in plain words | Value chosen | Alternatives rejected | Class | HOW WE KNOW | Cost if wrong | CONFIRMED |
|---|---|---|---|---|---|---|---|
| 1 | **SURFACE — which screen this lands on, and who opens it** | no screen: one added paragraph in the morning message Nick already reads at 07:17, plus silence on every dispatch that is no longer refused | a new dashboard; a new report; a Hub card per refusal | V1 | Nick asked for the tracking to be data he can see, not a gate, and he already receives this daily message | he keeps paying for expensive work without seeing that it happened, or gets a second daily report nobody reads | Nick, 2026-09-09, "we track how they are being used for data not to limit them" |
| 2 | Whether spend is ever gated | counted, never capped; the per-task ceiling stays off unless he sets it himself | a per-task ceiling on by default; a daily cap that refuses work | V1 | his instruction the same night, and the ceiling code already reads that way | a cheap job is refused for cost and the work climbs back onto the expensive bench | Nick, 2026-09-09, "there is ZERO limit to what we hand off to cheap models" |
| 3 | Who checks another agent's finished work | a cheap model on a different vendor from the builder; Sonnet only as the backup, or when the floor is hit | Anthropic-only checking; the builder checking itself | V1 | his answer the same night to the checking question | every step's check climbs back to Sonnet and the drive costs several times what it should | Nick, 2026-09-09: cheap models check other agents' work; Sonnet only as backup or when the floor is hit |
| 4 | What may keep a job off a cheap vendor | exactly four things — a login, a credential/token/key VALUE, a government ID, a card/bank/routing number — and the refuser must name the item and the matched value | a fifth category; a file-name or folder rule; "it feels sensitive" | V1 | his floor ruling, already in the router's code | ordinary briefs are refused again for ordinary words and the cheap bench goes unused | Nick, 2026-09-09, "It needs to prove that it is not a credential, social security number, bank account number, bank routing number, credit card number, token, key, logins … There's only a small list" |
| 5 | Where the guard-file deadlock actually sits | one place: the cheap-lane path verdict's control-plane list, which refuses a `_test-`/`test-` path; the routing check only waves such a file and the dispatch gate's test-authoring category is already retired | all three gates refusing it; a new tool for writing test files | V2 | opened all three files 2026-09-09 | a fix is built in two places that were never blocking, and the one that blocks stays shut | opened `projects/ops/lib/vendor-fence.mjs` (lines 938, 954), `projects/ops/skippy-jobs/lib/check-routing-missed.mjs` (lines 437, 947) and `projects/ops/skippy-jobs/lib/dispatch-contract.mjs` (line 1090), 2026-09-09, saw: one refusal, one wave, one retired category |

- V1 confirmation reads `<name>, <date>, "<their own words>"` — the date is required.
- V2 confirmation reads `opened <what>, <date>, saw: <what was actually there>`.

**Considered and ruled NOT critical:**
- `which cheap vendor builds which step` — the model matrix decides it; a wrong pick costs one failover, not a different product.
- `where the re-run evidence file lives` — the lane's own evidence folder; the ownership rule settles it.

## 1b · Subproject decomposition — could a piece of this ship on its own?

| Subproject | End goal (one sentence — what's TRUE when done) | Depends on (named artefact) | OWNER | PLAN | Confirmation-sheet status |
|---|---|---|---|---|---|
| Refusals | every recorded hard-floor refusal since 2026-09-02 passes now or is a proven floor hit, and every future refusal names its file and kind | none — start now | this lane | this file, STEP 1 | §1a signed |
| The test-file route | a cheap worker writes a test or guard file through one sanctioned route and nowhere else | none — start now | this lane | this file, STEP 2 | §1a signed |
| Spend in the open | the morning report says what went cheap, what stayed and why, ceiling stated off | none — start now | this lane | this file, STEP 3 | §1a signed |
| Honest failures | a first cheap call stops coming back empty, and every failure row carries a machine-readable kind | none — start now | this lane | this file, STEP 4 and STEP 7 | §1a signed |
| The checking rule in code | the routing code's own text matches Nick's 2026-09-09 answer, both suites green | none — start now | this lane (the rule-file sentence handed to the Rule-file trim lane) | this file, STEP 5 | §1a signed |
| The shared-copy fence answer | rule C answered in writing and handed to the lane that owns the shared copy | none — start now | this lane for the finding, the Workshop lane for the decision | this file, STEP 6 | §1a signed |

**Carve-out rule:** the wording of `projects/ops/MACHINE-RULES.md` is carved out to the Rule-file trim lane by §3c with its name; the decision to switch rule C on is carved out to the Workshop lane by §3c and §7 item 1; the weekly GLM quota purchase is carved out to Nick by §7 item 2.

## 2 · The complete UX map (this becomes the test manifest verbatim)

| Id | Screen / entry point | State (default·empty·error·loading) | Element / interaction | Expected behavior | Navigation from → to |
|---|---|---|---|---|---|
| U1 | A cheap job carrying ordinary words about a credential (a label followed by prose, or a placeholder) | default · refused | the dispatch | the job runs on the named cheap vendor; nothing is refused, and no row is written to the failover record | dispatch → done cheap |
| U2 | A cheap job carrying a real credential VALUE | floor hit | the dispatch | the job is refused, the refusal names the matched kind and the file, and the value itself appears nowhere | dispatch → refused, named |
| U3 | The refusal record, read a week later | populated · empty | one refusal row | the row names the file and the matched kind, so the same input can be re-run exactly; the value is absent | record → re-run |
| U4 | A cheap worker asked to write a test or guard file inside the sanctioned route | allowed | the write | the file is written, and the nightly runner leaves it alone | brief → file on disk |
| U5 | A cheap worker asked to write a test or guard file outside the sanctioned route | refused | the write | the write is refused in words naming the route it should have used | brief → refused with the route named |
| U6 | Nick's 07:17 morning message | populated · a zero day | the routing paragraph | it says how much went cheap, what stayed and the reason each thing stayed, and states that the per-task ceiling is off | phone → read |
| U7 | A first cheap call on the vendor response shape that returned no text | error today | the reply | the text is read out of the response and the job proceeds on the first vendor | dispatch → answered first time |
| U8 | The failure record, any row | populated | the kind field | quota, refusal, timeout, shape and parse are separate machine-readable kinds; a weekly quota row never reads as a refusal | record → report |
| U9 | The shared copy of the workspace, any live lane session | rule C off · rule C on | an ordinary write | the lane knows from a written answer what it is supposed to do in the shared copy, and the answer is in the Workshop lane's hands | session → written finding |
| U10 | Both router suites, after every edit in this lane | green · red | the run | both exit 0 with no failing case, and neither has fewer cases than today | edit → suites green |

## 2d · DESIGN FIDELITY GATE (plan skill §D — mandatory when the deliverable is looked at)

DESIGN FIDELITY GATE: N/A — nothing rendered. The only thing a person looks at is one paragraph of plain text inside a message he already receives, and its check is the wording in STEP 3's proof.

## 3 · Lanes and frozen contracts

| Lane | Scope (in / out) | Owner | Definition of done | Builder (cheap, named) | Backup builder | Checker (different model) | Backup checker |
|---|---|---|---|---|---|---|---|
| Refusals | the re-run of every recorded refusal, and the fields a refusal row carries / out: changing the floor list itself | this lane | U1, U2 and U3 pass with the evidence file readable | GLM 5.3 (zai) | DeepSeek | Qwen | Sonnet |
| The test-file route | one sanctioned route in the cheap-lane path verdict, and the nightly runner's manifest / out: the routing check's wave, the retired dispatch category | this lane | U4 and U5 pass, refused outside and allowed inside | DeepSeek | GLM 5.3 (zai) | Qwen | Sonnet |
| Spend in the open | the morning report's routing paragraph and the ceiling statement / out: any cap, any new job | this lane | U6 passes on a dry run | GLM 5.3 (zai) | Qwen | DeepSeek | Sonnet |
| Honest failures | the empty-reply fix and the failure-kind field / out: buying more GLM quota | this lane | U7 and U8 pass red then green | Qwen | DeepSeek | GLM 5.3 (zai) | Sonnet |
| The checking rule in code | the routing code's own category text and its suite cases / out: the rule file's wording | this lane | U10 passes with no case count lower than today's | DeepSeek | Qwen | GLM 5.3 (zai) | Sonnet |
| The fence answer | the written rule C finding and its handoff / out: switching rule C on | this lane | U9 passes as a written finding in the Workshop lane's plan | Qwen | GLM 5.3 (zai) | DeepSeek | Sonnet |

**Contracts between lanes (FROZEN at plan time — change = dated PLAN-CHANGES.md delta):** THE CHEAP LANE HAS EXACTLY FOUR TOOLS — list, read, search, write. It cannot run a command, git, a browser or a proof. So every step here names its executor by the KIND of work: the cheap builder WRITES the change; the `exerciser` agent or the overseer's own shell RUNS the proof into the lane's evidence folder; the cheap checker READS that evidence file and the diff and returns PASS or FAIL. A step that would mix the two is written as two steps · the floor list is frozen at four items and only the REFUSER's burden changes here · one report, one ledger, one failover record, one decision record — extended in place, never duplicated · a refusal row carries the file and the matched kind and NEVER the matched value · the two router suites named in `projects/ops/skippy-jobs/lib/ROUTER-SETUP.md` stay green after every edit, and a case count never goes down (a check made green by weakening it is a failure, not a pass) · the Workshop lane owns the shared copy and the rule C decision; the Rule-file trim lane owns the wording of `projects/ops/MACHINE-RULES.md`.

**Data floor, binding:** the only reasons a file stays inside are a login, a credential or token or key VALUE, a government ID, or a card, bank or routing number — and the refuser must prove the hit. Code that merely says token, secret, auth or password, a brief that describes reading a credential at run time, and a placeholder in angle brackets are NOT on that list (Nick, 2026-09-09); a wall refusing them is logged as a failure in PROGRESS.txt and the job goes to the named backup vendor, never to Sonnet or Opus. Every fixture in this lane that reproduces a floor hit is composed at RUN TIME from fake parts and never contains a real value.

## 3b · Execution map — FRONT first, POLISH last, one row per step

A task is DONE only when its review-ledger row is CLOSED by a reviewer that is not the builder.

**Step map (read this first) — the three FRONT rows are the three things Nick asked to see; the POLISH rows run after them, or the moment one bites:**

| Stage | # | TIER | Task (step name) | FOR NICK | Needs (named artefact, or `none — start now`) | EXECUTOR (cheap model) | EXECUTOR BACKUP | CHECKER (different model) | CHECKER BACKUP | DONE-PROOF (runnable command) |
|---|---|---|---|---|---|---|---|---|---|---|
| Refusals | 1 | FRONT | No cheap job is refused for ordinary words: every recorded hard-floor refusal since 2026-09-02 re-run against the current scanner, each one passing or proven a real floor hit with its kind named, and every future refusal recording its file and kind | your jobs stop being turned away for ordinary words, and next time we can prove it in a minute instead of a night | none — start now | GLM 5.3 (zai) | DeepSeek | Qwen | Sonnet | `node projects/ops/cheap-build.mjs --file <the row's file or the fixture> --do "wall verdict only, change nothing" --verdict-json` run once per row by the exerciser (it prints the class as JSON and exits without calling a vendor), then `command grep -c '"replay":"pass"' projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/evidence/replay` plus the real-floor-hit count equals the row count and no row reads `unexplained` |
| The test-file route | 2 | FRONT | One sanctioned route for a cheap worker to write a test or guard file — allowed inside it, refused outside it, and the nightly runner leaves the route's file alone | the cheap bench can finish a whole job including its own checks, so work stops climbing back to the expensive bench | none — start now | DeepSeek | GLM 5.3 (zai) | Qwen | Sonnet | `node projects/ops/cheap-task.mjs --test-file-route --selfcheck` prints `sanctioned route: allowed · unsanctioned path: refused · nightly runner: skipped` and exits 0 |
| Spend in the open | 3 | FRONT | The morning report says what went cheap, what stayed and why, and states that the per-task ceiling is off | your morning message tells you where the work actually went and what it cost, and nothing is ever refused for cost | none — start now | GLM 5.3 (zai) | Qwen | DeepSeek | Sonnet | `command grep -c "SKIPPY_DRY_RUN" projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs` returns at least 1 (0 today), then `SKIPPY_DRY_RUN=1 node projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs --routing-why` prints a cheap total, a stayed total, one reason line per thing that stayed, and `per-task ceiling: OFF`, and sends nothing |
| Honest failures | 4 | POLISH | The empty first reply fixed: the vendor response shape that returned no text 48 times is read correctly, proven red then green | nothing you notice; a cheap job stops silently failing its first try and burning a second vendor | none — start now | Qwen | DeepSeek | GLM 5.3 (zai) | Sonnet | `node projects/ops/cheap-build.mjs --selftest-reply-shapes` prints `reply shapes: N of N read · 0 empty` and exits 0, and the same command against the pre-fix file prints at least one `empty` |
| The checking rule in code | 5 | POLISH | The routing code's own text says a cheap model checks another agent's work and Sonnet is the backup, with both router suites green and no case count lower than today's | nothing you notice; the code stops disagreeing with what you decided about who checks work | none — start now | DeepSeek | Qwen | GLM 5.3 (zai) | Sonnet | `node projects/ops/skippy-jobs/_test-work-type-gate.mjs && node projects/ops/skippy-jobs/_test-dispatch-override-categories.mjs` both exit 0 with no `❌ FAIL` line and case counts no lower than the 72 and 52 counted at this lane's check record |
| The fence answer | 6 | POLISH | The shared-copy fence's rule C answered in writing — on or off today, what created and what removed its switch, what a live lane does — and handed to the Workshop lane | nothing you notice; the lanes working in one shared copy stop guessing what the rules are | none — start now | Qwen | GLM 5.3 (zai) | DeepSeek | Sonnet | `command grep -c "RULE-C BLOCKED" projects/ops/skippy-jobs/lib/worktree-fence.log` and `command grep -c "ruleC=on" projects/ops/skippy-jobs/lib/worktree-fence.log` both recorded verbatim in the finding, and `command grep -c "ROUTER STEP 6 closed" projects/ops/life-os/REGROUP-2026-09-08/plans/LANE-1-WORKSHOP/PLAN.proposed.txt` returns 1 |
| Honest failures | 7 | POLISH | Every failure row carries a machine-readable kind, so a weekly quota never reads as a refusal, and the timeout floor is reviewed against the sizes that actually timed out | nothing you notice; when something did not go cheap, the reason you are told is the real one | STEP 1 closed (the refusal row's fields) | GLM 5.3 (zai) | DeepSeek | Qwen | Sonnet | `command grep -c '"failure_kind":"quota"' projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` is at least 1 and `command grep -c '"failure_kind":"timeout"' projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` is at least 1 |
| Close-out | 8 | POLISH | Close-out: the FINISH LINE checked item by item, the postmortem written into this file, nothing left on the Mac | you get one line saying the router lane is done, and nothing else to read | STEP 1 to STEP 7 closed | DeepSeek | GLM 5.3 (zai) | Qwen | Sonnet | `python3 projects/ops/agents/check_plan.py --progress projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PLAN.proposed.txt` prints `PROGRESS: 9/9 steps have complete evidence` |

### §3c · CUT — overkill for the outcome, or another lane's by name; recorded once and not worked
- Building a fix into the routing check's `_test-`/`check-` wave and the dispatch gate's test-authoring category — measured 2026-09-09: the wave does not refuse anything and the category is already retired, so a fix there changes nothing. STEP 2 fences the one gate that refuses.
- A new tool for writing test files, a second refusal log, a second spend report, a quarantine folder for refused payloads — the ownership rule forbids all four; the existing tools are extended in place and the refusal ROW gains fields.
- The wording of `projects/ops/MACHINE-RULES.md` about who checks a worker's finished output — HANDED TO THE RULE-FILE TRIM LANE by name, with the sentence this lane's STEP 5 makes true in code, so the rule file and the code stop disagreeing. That lane files its own governed-document ticket; this lane never edits the rule file.
- Switching rule C of the shared-copy fence on — HANDED TO THE WORKSHOP LANE by name, with STEP 6's measured finding attached; it owns the shared copy and the drain of live sessions.
- The wording every other lane needs about the cheap lane's four tools — HANDED TO EVERY LANE by this file's §3 contracts paragraph, quotable as one sentence: the cheap lane can list, read, search and write files and nothing else, so the proof is run by the exerciser or the overseer and the cheap checker reads the evidence file.
- Buying more weekly GLM quota — §7 item 2, Nick's, with a default.

**Then one block per step, in this exact shape:**

### STEP 1 — No cheap job is refused for ordinary words
**FOR NICK:** your jobs stop being turned away for ordinary words like "read the password from the vault", and next time it happens we can prove it in a minute instead of a night. · **Tier:** FRONT
**Start when:** none — start now.
**Builder:** GLM 5.3 (zai) · **Builder backup:** DeepSeek · **Checker:** Qwen, a different session · **Checker backup:** Sonnet
**Files you may touch:** `projects/personal/skippy-app/lib/grunt-lane.mjs` (the refusal row it writes), `projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` (new fields on new rows only, never a rewrite of an old row), and the lane's evidence folder `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/evidence`. **Never** the floor list in `projects/ops/lib/vendor-fence.mjs` (frozen at four items), the scanner's matching rules in `projects/personal/skippy-app/lib/grunt-egress-scan.mjs` (STEP 4 and STEP 7 do not touch it either), the routing decision record's existing rows.

**Do exactly this:**
1. Build the replay list from what is actually on record, three sources, into one file in the lane's evidence folder: the 36 hard-floor rows dated 2026-09-02 or later in the failover record (each gives a timestamp, the vendors tried and the matched kind); the 26 wall refusals in the routing decision record dated 2026-09-02 or later (each names a FILE); and the refusal rows in `projects/ops/skippy-jobs/lib/cheap-build-failures.log` (each names a file too). Write one row per refusal with its source, its date, its matched kind if recorded, and its file if recorded.
2. Re-run each row that names a FILE by asking the wall about that file as it stands today, once per row, and record the verdict verbatim beside the row. Where the file is gone, record `file no longer present` and treat the row as explained.
3. For each row that names only a KIND, build one fixture per kind — secret label, account word beside digits, long account or card number — in the lane's evidence folder, composed AT RUN TIME from fake parts, never a real value, and covering both directions: the prose and placeholder forms that must now PASS (a label followed by ordinary words, an angle-bracket placeholder, a "read from the vault at run time" sentence) and a real-shaped value that must still be REFUSED. Ask the wall about each fixture and record both verdicts.
4. Mark every row `pass`, `real floor hit` with the kind named, or `unexplained`. A row is never marked from reasoning; only from a recorded verdict.
5. Add two fields to the refusal row the cheap lane writes from now on: the file it was asked to work on, and the matched kind. Never the matched value, never a span of it — the row must be safe to read by anyone.
6. Write the evidence file and the counts into the lane's evidence folder, and one dated line into PROGRESS.txt naming the counts.

**DEFINITION OF DONE:** every recorded hard-floor refusal since 2026-09-02 is marked `pass` or `real floor hit` with its kind named, zero rows read `unexplained`, the fixtures prove the scanner still refuses a real-shaped value, and a refusal written today carries its file and its matched kind and no value.
**PROOF:** the exerciser asks the wall about each row and each fixture with `node projects/ops/cheap-build.mjs --file <the row's file or the fixture> --do "wall verdict only, change nothing" --verdict-json` — that mode prints the classification as JSON and exits before anything is sent, proven on a harmless file at this lane's check record — writing every verdict into the lane's evidence folder; then `command grep -c '"replay":"pass"' projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/evidence/replay` plus the `real floor hit` count equals the row count, and `command grep -c unexplained` over the same folder returns 0 · **FAILS IF:** any row reads `unexplained`, a fixture with a real-shaped value is allowed through, a prose or placeholder fixture is still refused, or any evidence file contains something that could be a real credential value

**If the check fails:** the builder fixes and re-checks the named failure until it passes. If this step cannot close from this machine: one line to the overseer naming the ONE missing thing, then the next step whose inputs exist.
**Checker's job:** read the evidence file the exerciser wrote for you on a second, independent run into your own folder, and the diff of the two changed files. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** the moment this step closes, post one dated line into `projects/ops/life-os/REGROUP-2026-09-08/plans/FILES/PLAN.proposed.txt`: `STEP 1 closed <date> — the upload-helper brief that was refused three times passes the wall now; a refusal from here names the file and the kind.`

### STEP 2 — One sanctioned route for a cheap worker to write a test or guard file
**FOR NICK:** the cheap bench can finish a whole job including writing its own checks, so work stops climbing back onto the expensive bench for the last ten per cent. · **Tier:** FRONT
**Start when:** none — start now.
**Builder:** DeepSeek · **Builder backup:** GLM 5.3 (zai) · **Checker:** Qwen, a different session · **Checker backup:** Sonnet
**Files you may touch:** `projects/ops/lib/vendor-fence.mjs` (the control-plane path verdict, the one narrow exception), `projects/ops/cheap-task.mjs` (the `--test-file-route` mode and its `--selfcheck`), and the lane's evidence folder. **Never** `projects/ops/skippy-jobs/lib/check-routing-missed.mjs` (its wave refuses nothing — §3c), `projects/ops/skippy-jobs/lib/dispatch-contract.mjs` (its category is retired — §3c), the two router suites' existing cases (STEP 5 owns their text), any top-level `_test-*.mjs` file as a build target.

**Do exactly this:**
1. In the cheap-lane path verdict, add ONE narrow exception to the control-plane refusal: a `_test-` or `check-` prefixed file is allowed when, and only when, the request came through the `--test-file-route` mode AND the target sits inside a directory that mode names. Every other `_test-`/`check-` write stays refused, and the refusal sentence names the route it should have used.
2. In `cheap-task.mjs`, add the `--test-file-route` mode: it takes the target directory and the file, refuses a path outside the allowed directories in words, and records the write in a manifest file inside the lane's evidence folder so the nightly runner can tell a route-written file from a stray one.
3. Make the nightly runner skip a file listed in that manifest rather than running it unattended, keeping archive-over-delete: a stray cheap-written test file still moves to the snapshot folder, and a manifest-listed one is left where it is.
4. Add `--selfcheck` to the same mode: it writes one `_test-` named file through the route into the lane's evidence folder and confirms it lands; attempts the same write outside the route and confirms the refusal names the route; and confirms the nightly runner's own skip list contains the first file and not the second. Three checks, one line of output.
5. Run both router suites; neither may lose a case.

**DEFINITION OF DONE:** a cheap worker writes a `_test-` named file through the one sanctioned route and it lands; the same write outside that route is refused with the route named; the nightly runner leaves the route's file alone; and both router suites still exit 0 with no fewer cases than today.
**PROOF:** `node projects/ops/cheap-task.mjs --test-file-route --selfcheck` → `sanctioned route: allowed · unsanctioned path: refused · nightly runner: skipped` and exit 0 · **FAILS IF:** the unsanctioned write is allowed, the sanctioned write is refused, the refusal sentence does not name the route, the nightly runner would run the route's file, or either suite loses a case

**If the check fails:** the builder fixes and re-checks the named failure until it passes. If this step cannot close from this machine: one line to the overseer naming the ONE missing thing, then the next step whose inputs exist.
**Checker's job:** read the evidence file from the exerciser's own second run of the selfcheck, and the diff of the two changed files. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** the moment this step closes, post one dated line into `projects/ops/life-os/PLAN-LIFE-OS-2026-09-09.md`: `ROUTER STEP 2 closed <date> — a cheap worker may write a test or guard file through one named route; every lane may now assign its own check-writing cheap.`

### STEP 3 — The morning report says what went cheap, what stayed and why
**FOR NICK:** your morning message tells you where the work actually went and what it cost, and says plainly that nothing is being refused for cost. · **Tier:** FRONT
**Start when:** none — start now.
**Builder:** GLM 5.3 (zai) · **Builder backup:** Qwen · **Checker:** DeepSeek, a different session · **Checker backup:** Sonnet
**Files you may touch:** `projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs` (the dry-run guard, the new `--routing-why` mode and the paragraph it adds), and the lane's evidence folder. **Never** a second report job, `projects/ops/spend-tracker.mjs`'s decision logic (the ceiling stays exactly as it is), the report's send path or its schedule row in `projects/ops/skippy-jobs/runner.mjs`, the spend ledger's contents.

**Do exactly this:**
1. FIRST, before the mode is ever run: give the job a dry-run guard, because it carries none today and running it sends a real message to Nick. Under `SKIPPY_DRY_RUN=1` it composes and prints and sends nothing; a live send still needs `SKIPPY_TEST_ALLOW_LIVE=1`. Nothing else in this step happens until this is in place.
2. Add `--routing-why` to the existing morning report: it reads the spend ledger for the previous Cancun day and the routing decision record for the same day, and prints how much work went cheap, how much stayed, and ONE reason line per thing that stayed, using the reason already on record rather than a guess.
3. Print one line stating the per-task ceiling's state in words, read from the tracker's own function rather than from a copy of the rule: `per-task ceiling: OFF` when no ceiling is set.
4. Where a thing stayed with no reason on record, print it as `stayed with no reason recorded` and count it — that count is the lane's own defect list, not a silent zero.
5. Write the dry run's output into the lane's evidence folder.

**DEFINITION OF DONE:** the job carries a dry-run guard, and under it the report prints a cheap total, a stayed total, one reason line per thing that stayed, a count of things that stayed with no reason recorded, and `per-task ceiling: OFF` — sending nothing.
**PROOF:** two parts, because the second cannot safely run until the first exists — `command grep -c "SKIPPY_DRY_RUN" projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs` returns at least 1 (it returns 0 today, which is the red baseline), and then `SKIPPY_DRY_RUN=1 node projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs --routing-why` prints the five lines above and exits 0 · **FAILS IF:** the ceiling line is absent or reads ON, a reason is invented rather than read from the record, anything is sent, or the totals disagree with the ledger for that day

**If the check fails:** the builder fixes and re-checks the named failure until it passes. If this step cannot close from this machine: one line to the overseer naming the ONE missing thing, then the next step whose inputs exist.
**Checker's job:** read the evidence file from the exerciser's own second dry run, and the diff. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.

### STEP 4 — The empty first reply, fixed red then green
**FOR NICK:** nothing you notice directly; a cheap job stops silently failing on its first try and burning a second vendor to do the same work. · **Tier:** POLISH
**Start when:** none — start now.
**Builder:** Qwen · **Builder backup:** DeepSeek · **Checker:** GLM 5.3 (zai), a different session · **Checker backup:** Sonnet
**Files you may touch:** `projects/ops/cheap-build.mjs` (the reply-text extractor and its new `--selftest-reply-shapes` mode), and the lane's evidence folder. **Never** the spend recording beside it (`recordSpend` and `recordTaskSpend` stay exactly as they are), the `--verdict-json` block STEP 1 depends on, `projects/ops/lib/vendor-fence.mjs`, the vendor credentials, any other lane's file.

**Do exactly this:**
1. Capture the response shape that produced the empty reply — the keys named in the recorded failure sentence — as a fixture in the lane's evidence folder, built from a fake body and no real vendor call.
2. Add `--selftest-reply-shapes`: it feeds every known response shape, including that one, through the reply-text extractor and prints how many were read and how many came back empty.
3. Run it against the code as it stands and record the RED result — at least one empty — into the lane's evidence folder before any fix.
4. Extend the extractor to read the text out of that shape, changing nothing about the shapes it already reads.
5. Run the selftest again for the GREEN result, and run both router suites.

**DEFINITION OF DONE:** the selftest reads every known response shape with zero empty results, the recorded red run against the unfixed code shows at least one empty, and both router suites still exit 0.
**PROOF:** `node projects/ops/cheap-build.mjs --selftest-reply-shapes` → `reply shapes: N of N read · 0 empty` and exit 0, with the red run's own output kept in the lane's evidence folder · **FAILS IF:** no red run was recorded before the fix, a shape that used to be read comes back empty, or the selftest passes against the unfixed code

**If the check fails:** the builder fixes and re-checks the named failure until it passes. If this step cannot close from this machine: one line to the overseer naming the ONE missing thing, then the next step whose inputs exist.
**Checker's job:** read the red and green evidence files from the exerciser's own runs, and the diff. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.

### STEP 5 — The routing code's own text matches what Nick decided about checking
**FOR NICK:** nothing you notice; the code stops disagreeing with your own answer about who checks another agent's work. · **Tier:** POLISH
**Start when:** none — start now.
**Builder:** DeepSeek · **Builder backup:** Qwen · **Checker:** GLM 5.3 (zai), a different session · **Checker backup:** Sonnet
**Files you may touch:** `projects/ops/skippy-jobs/lib/check-routing-missed.mjs` (the control-plane and checking-layer category text only), `projects/ops/skippy-jobs/_test-work-type-gate.mjs` and `projects/ops/skippy-jobs/_test-dispatch-override-categories.mjs` (cases ADDED in the same step, never removed), and the lane's evidence folder. **Never** `projects/ops/MACHINE-RULES.md` (the Rule-file trim lane owns it — §3c), the category's matching behaviour where a test suite already pins it, the floor category.

**Do exactly this:**
1. Rewrite the category's own text so it states Nick's 2026-09-09 answer in plain words: checking on information or status is ordinary cheap work; checking another agent's finished work is ALSO cheap, on a different vendor from the builder; Sonnet is the backup checker, or the checker when the floor is hit; and the builder never checks itself.
2. Add cases to both suites, in the same step, that pin the new text's meaning: a cheap checker on a different vendor is accepted; the builder checking itself is refused; Sonnet as a backup checker is accepted; a claim that checking must stay on Anthropic is refused.
3. Run both suites and record their case counts before and after into the lane's evidence folder. A count that goes down is a failure, never a pass.
4. Write the one sentence the rule file needs into PROGRESS.txt for the Rule-file trim lane to carry, and post it as this step's handoff.

**DEFINITION OF DONE:** the category's text states the cheap-checks-cheap rule with Sonnet as backup, both suites exit 0 with no failing case, and neither suite has fewer cases than it had before this step.
**PROOF:** `node projects/ops/skippy-jobs/_test-work-type-gate.mjs && node projects/ops/skippy-jobs/_test-dispatch-override-categories.mjs` → both exit 0, no line containing `❌ FAIL`, and the case counts recorded in the evidence folder are no lower than the 72 and 52 counted at this lane's check record · **FAILS IF:** either suite reports a failing case, either case count drops, or a case was made to pass by removing what it asserted

**If the check fails:** the builder fixes and re-checks the named failure until it passes. If this step cannot close from this machine: one line to the overseer naming the ONE missing thing, then the next step whose inputs exist.
**Checker's job:** read the before-and-after case counts and both suite outputs from the exerciser's own run, and the diff. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** the moment this step closes, post one dated line into `projects/ops/life-os/REGROUP-2026-09-08/plans/AGENTS/PLAN.proposed.txt`: `ROUTER STEP 5 closed <date> — the routing code now says a cheap model on a different vendor checks another agent's work and Sonnet is the backup; the rule file's own sentence is yours to file.`

### STEP 6 — The shared-copy fence's rule C, answered in writing
**FOR NICK:** nothing you notice; the lanes all working inside one shared copy stop guessing what they are allowed to do there. · **Tier:** POLISH
**Start when:** none — start now.
**Builder:** Qwen · **Builder backup:** GLM 5.3 (zai) · **Checker:** DeepSeek, a different session · **Checker backup:** Sonnet
**Files you may touch:** the lane's evidence folder and `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PROGRESS.txt`, plus ONE appended dated line in `projects/ops/life-os/REGROUP-2026-09-08/plans/LANE-1-WORKSHOP/PLAN.proposed.txt`. **Never** `projects/ops/skippy-jobs/lib/check-worktree-fence.mjs`, the switch file, the hook wiring, or any product file — a fault found here is written down, not fixed.

**Do exactly this:**
1. Answer four questions from the file and the log only, each with a line number or the words `not recorded`: is rule C on right now; what created its switch; what removed it; and where the hook is wired.
2. Record what is already measured, and re-count rather than copying a number, because the log is live: the switch is absent; the fence log carries rows reading RULE-C BLOCKED (36 at this lane's check record) and zero rows reading `ruleC=on`; the switch sits in a gitignored folder so it has no history; the log's own entry at 2026-09-09T22:09:00Z shows a fence test run creating it, and nothing records its removal.
3. Record the wiring: it is `.claude/settings.json` line 255, in the WORKSPACE settings file. Searching the home-directory settings file instead returns nothing and is a false negative — say which file was searched, as the finding's own evidence line.
4. Write what a live lane in the shared copy is supposed to do while rule C is off, in two sentences, from the fence's own header rather than from memory.
5. Post one dated line into the Workshop lane's plan carrying the finding and the decision it owns.

**DEFINITION OF DONE:** a written finding in the lane's evidence folder answers all four questions with a line number or `not recorded` beside each, states what a live lane does today, and the Workshop lane's plan carries one dated line naming this step's closure.
**PROOF:** `command grep -c "RULE-C BLOCKED" projects/ops/skippy-jobs/lib/worktree-fence.log` and `command grep -c "ruleC=on" projects/ops/skippy-jobs/lib/worktree-fence.log` are both recorded verbatim in the finding, and `command grep -c "ROUTER STEP 6 closed" projects/ops/life-os/REGROUP-2026-09-08/plans/LANE-1-WORKSHOP/PLAN.proposed.txt` returns 1 — that phrase returns 0 today, which is why it is the check; the words "rule C" alone already appear in that plan and would pass without any handoff · **FAILS IF:** any of the four answers is written without a line number or the words `not recorded`, the finding states a cause the log does not carry, or the Workshop line is absent

**If the check fails:** the builder fixes and re-checks the named failure until it passes. If this step cannot close from this machine: one line to the overseer naming the ONE missing thing, then the next step whose inputs exist.
**Checker's job:** re-read the finding against the two counts from the exerciser's own run, and confirm every claim carries a line number or `not recorded`. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** the moment this step closes, post one dated line into `projects/ops/life-os/REGROUP-2026-09-08/plans/LANE-1-WORKSHOP/PLAN.proposed.txt`: `ROUTER STEP 6 closed <date> — rule C is off, its switch was created by a fence test run and its removal is not recorded; the decision to switch it on is yours, with the live-session drain named.`

### STEP 7 — Every failure row says which kind of failure it was
**FOR NICK:** nothing you notice; when something did not go to a cheap model, the reason you are told is the real one instead of a guess. · **Tier:** POLISH
**Start when:** STEP 1 closed — the refusal row's new fields exist in `projects/personal/skippy-app/lib/grunt-lane.mjs`.
**Builder:** GLM 5.3 (zai) · **Builder backup:** DeepSeek · **Checker:** Qwen, a different session · **Checker backup:** Sonnet
**Files you may touch:** `projects/personal/skippy-app/lib/grunt-lane.mjs` (the failure row's kind field and the timeout size), `projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs` (reading the kind instead of matching prose), and the lane's evidence folder. **Never** the failover record's existing rows, the vendor adapters' request bodies, the floor scanner.

**Do exactly this:**
1. Put one machine-readable kind on every failure row from now on: `quota`, `refused`, `timeout`, `shape`, `parse`, `other` — derived where the vendor's own answer says so, and `other` where it does not, never a guess dressed as a kind.
2. On a timeout row, record the payload size in bytes and the limit that was hit, so the timeout floor can be judged from data.
3. Make the morning report read the kind field rather than matching the prose of a reason, so a weekly quota shows as quota and never as a refusal.
4. Review the 120-second floor against the sizes now recorded, and write the decision and its number into PROGRESS.txt — raise it, leave it, or make it size-dependent. A change to the number is one line with the measured sizes beside it.
5. Force one row of each kind through with fake vendor answers, into the record, so every kind is proven writable.

**DEFINITION OF DONE:** every failure row written after this step carries one of the six kinds; a timeout row also carries the payload size and the limit; the morning report tells quota from refusal without reading prose; and the timeout floor decision is written with the sizes it rests on.
**PROOF:** `command grep -c '"failure_kind":"quota"' projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` is at least 1 and `command grep -c '"failure_kind":"timeout"' projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` is at least 1, both after the forced rows · **FAILS IF:** a row carries no kind, a quota row reads as a refusal in the report, a timeout row carries no size, or the floor decision is written with no measured sizes beside it

**If the check fails:** the builder fixes and re-checks the named failure until it passes. If this step cannot close from this machine: one line to the overseer naming the ONE missing thing, then the next step whose inputs exist.
**Checker's job:** read the two counts and the forced rows from the exerciser's own run, and the diff. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.

### STEP 8 — Close-out
**FOR NICK:** you get one line saying the router lane is done, and nothing else to read. · **Tier:** POLISH
**Start when:** STEP 1 to STEP 7 closed.
**Builder:** DeepSeek · **Builder backup:** GLM 5.3 (zai) · **Checker:** Qwen, a different session · **Checker backup:** Sonnet
**Files you may touch:** this file's POSTMORTEM and STEPS sections, `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PROGRESS.txt`, `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/STEPS.json`. **Never** a product file.

**Do exactly this:**
1. Check the FINISH LINE item by item against the closed steps' proofs; write the postmortem below.
2. Confirm nothing this lane made is left on the Mac outside the lane folder, and declare in PROGRESS.txt what remains and how big.
3. Move the lane's board card to done through the guarded updater, and bring the progress screen current on the same beat.

**DEFINITION OF DONE:** the FINISH LINE's seven items each point at a closed step's VERIFIED line, the postmortem is written, and PROGRESS.txt declares what is left on the Mac and its size.
**PROOF:** `python3 projects/ops/agents/check_plan.py --progress projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PLAN.proposed.txt` → `PROGRESS: 9/9 steps have complete evidence` · **FAILS IF:** any FINISH LINE item has no closed step behind it, or anything this lane made is still sitting on the Mac undeclared

**If the check fails:** the builder fixes and re-checks the named failure until it passes. If this step cannot close from this machine: one line to the overseer naming the ONE missing thing, then the next step whose inputs exist.
**Checker's job:** re-run the PROOF yourself against the evidence the exerciser wrote, once. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** the moment this step closes, post one dated line into `projects/ops/life-os/PLAN-LIFE-OS-2026-09-09.md`: `ROUTER lane closed <date> — every §3d Router item true.`

**Step-writing rules:** every step names the literal command and the literal expected output — "verify it works" is a defect · as many steps as the North Star needs, no more · red-first for any fix step · builds and per-step checks on the cheap tier by name; the overseer never builds; the plan is written and the FINISH LINE signed off on Anthropic or OpenAI.

## 4 · Regret Check (the registry failures this build is actually exposed to)

| Failure mode (registry entry) | The measure in THIS plan that prevents it | Where it lives (section / artifact / gate) |
|---|---|---|
| A capability was ruled impossible on the strength of a query that structurally could not see the answer — a confident negative that nothing downstream ever re-tests | STEP 1 states what the record CAN see before it concludes anything: the payload is stored nowhere, so a re-run is by file where a row names one and by matched-kind fixture where it does not, and every fixture is run in both directions | §0 PROMPT-SPEC scan; STEP 1 items 1-4; the `unexplained` marker that cannot be reasoned away |
| A metric whose limitation was already recorded got cited anyway, because the number it produced agreed with the conclusion | the refusal count is never used as the measure of "was it a real secret" — only a recorded verdict from the wall marks a row, and a row with no verdict reads `unexplained` and fails the step | STEP 1 DEFINITION OF DONE and FAILS IF |
| Every instrument reported a state that was not the system's state; the audit question is when each one last went red and whether it could | every fix step is red-first: STEP 4 records the empty result against the unfixed code before the fix, STEP 2's selfcheck proves the refusal as well as the allowance, and STEP 5 forbids a case count going down. Caught by this rule at plan time: STEP 6's first proof phrase already appeared in the receiving plan and would have passed with no handoff written, so it was replaced with a phrase that returns zero today | STEP 2 item 4; STEP 4 item 3; STEP 5 item 3; STEP 6 PROOF |
| A dispatch gate blocked the exact defensive pattern its own preceding line prescribed, and the gate itself stayed unpatched | STEP 2 fences the ONE gate measured to refuse and records in §3c that the other two do not, so the fix lands where the block is instead of where it was assumed to be | §1a row 5; §3c first entry; STEP 2 file fence |
| An acknowledgement from the system under test was read as evidence of the outcome — `queued`, `ok:true`, `deployed` | every proof here reads a DESTINATION: the wall's own verdict per row, the file on disk after a route write, the record's own kind field, the report's own printed lines — never a tool's report that it did the thing | §3b DONE-PROOF column; STEP 1, STEP 2 and STEP 7 proofs |
| A deliberate, gate-passing commit was pre-empted by an automatic snapshot that bundled it with unrelated files | this lane writes only its own plan folder and the router files fenced per step, commits with pathspecs and never a bare commit, and makes each step's edits and their commit in one shell invocation. Measured live while this plan was written: one new lane file was swept off disk minutes after being written, recorded in PROGRESS.txt, which is why a new file is committed the moment it exists | §5 write-contention; every step's file fence; PROGRESS.txt |
| Four builds in one night went to Sonnet or Fable because the cheap-lane walls refused files that hold nothing private | the floor is four items and the refuser proves it; every step names a cheap builder, a cheap backup and a cheap checker; a refusal is logged as a failure in PROGRESS.txt and the job goes to the named backup vendor | §3 data floor; STEP 0 item 3; §3b EXECUTOR columns |

## 5 · Topology and roles
- **OVERSEER-AUTHORITY:** none named — `projects/ops/OVERSEER-AUTHORITY.md`'s CURRENT HOLDER table reads "none named yet — grant dormant"; the Group E overseer's word binds this lane. **The four approval classes (money leaving · credential rotation · irreversible destruction · a message sent as Nick) and the floor (logins · credentials, tokens and keys · government IDs · card, bank and routing numbers) never move on the overseer's word.**
- Thread layout: one Group E overseer thread; builders and checkers as cheap dispatches from it; the `exerciser` agent runs every command, because the cheap lane cannot.
- Overseer: Opus or Codex `gpt-6-astra` · Workers: GLM 5.3 (zai), DeepSeek, Qwen by step; Sonnet only as a backup checker · Cap: 8 per session, ~40 machine-wide, counted before each wave
- State files location: `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PROGRESS.txt` (dated lines, newest last), `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/STEPS.json` (the step record the progress screen reads)
- **Board card id:** none yet — the lane posts to the AI build board's router card through the guarded updater; its slug is written here by the overseer at pickup
- **Artefact consumers:** STEPS.json → the progress screen; PROGRESS.txt → the morning report; STEP 1's handoff → the FILES lane; STEP 2's handoff → the programme plan, and through it every lane; STEP 5's handoff → the AGENTS lane and the Rule-file trim work inside it; STEP 6's handoff → the WORKSHOP lane; §7 → Nick, once.
- **Write-contention (parallel lanes in a shared checkout):** this lane writes its own plan folder, and per step only the router files its fence names; two steps never share a file except `grunt-lane.mjs` (STEP 1 then STEP 7, in that order, which is why STEP 7 names STEP 1 as its input), `cheap-build.mjs` (STEP 4 alone, and never its `--verdict-json` block) and the morning report job (STEP 3 then STEP 7). Scoped commits with pathspecs, never a bare commit; edits and their commit in one shell invocation; a new file committed the moment it exists, because an untracked one can be swept away by another session.

**Per-stage topology — counts DECLARED at plan time (machine-gated: a number in every row):**

| Stage | Overseer | Sub-overseers | Workers |
|---|---|---|---|
| Refusals | 1 | 0 | 2 |
| The test-file route | 1 | 0 | 2 |
| Spend in the open | 1 | 0 | 2 |
| Honest failures | 1 | 0 | 2 |
| The checking rule in code | 1 | 0 | 2 |
| The fence answer | 1 | 0 | 2 |
| Close-out | 1 | 0 | 2 |

**The walk-away contract — a stranger resumes the drive from files alone:**
- **STATE FILE:** `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PROGRESS.txt`
- **HEARTBEAT ROW:** `router-lane-2026-09-09` in `projects/personal/skippy-app/ala-state/work-threads.json`
- **MORNING-REPORT LINE:** "Router — FRONT <n> of 3 · polish <m> of 5" in `projects/ops/walkaway/REPORT.md`

## 6 · Evals — what "working" means, decided now

| Capability | Check (exact command or procedure) | Pass looks like |
|---|---|---|
| no cheap job is refused for ordinary words | the exerciser asks the wall about every recorded refusal row and every fixture, `node projects/ops/cheap-build.mjs --file <the row's file or the fixture> --do "wall verdict only, change nothing" --verdict-json` | every row marked pass or real floor hit with its kind; zero unexplained; a real-shaped value still refused |
| a refusal can be re-run exactly next time | `command grep -c '"matched_kind"' projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` after a forced refusal | at least one row carrying the file and the matched kind, and no value |
| a cheap worker can write a test or guard file | `node projects/ops/cheap-task.mjs --test-file-route --selfcheck` | allowed inside the route, refused outside it with the route named, nightly runner skipped |
| the morning report says what went cheap and why | `SKIPPY_DRY_RUN=1 node projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs --routing-why` | cheap total, stayed total, a reason per stayed thing, and `per-task ceiling: OFF`, sending nothing |
| a first cheap call is not empty | `node projects/ops/cheap-build.mjs --selftest-reply-shapes` | every shape read, zero empty, with the pre-fix red run kept |
| the code agrees with Nick about who checks | `node projects/ops/skippy-jobs/_test-work-type-gate.mjs && node projects/ops/skippy-jobs/_test-dispatch-override-categories.mjs` | both exit 0, no failing case, no case count below the 72 and 52 counted at this lane's check record |
| a failure says which kind it was | `command grep -c '"failure_kind":"quota"' projects/ops/skippy-jobs/lib/cheap-vendor-failover.log` | at least one row of each forced kind, and a timeout row carrying its size |
| the shared-copy fence has a written answer | `command grep -c "ROUTER STEP 6 closed" projects/ops/life-os/REGROUP-2026-09-08/plans/LANE-1-WORKSHOP/PLAN.proposed.txt` | returns 1, and four answers each carrying a line number or `not recorded` |

## 7 · THE ONE DECISION LIST FOR NICK — everything genuinely his, asked once

Nothing in this lane is one of your four approvals — no money leaves, no credential is rotated, nothing is destroyed, and no message goes out as you. Two things are yours to overrule if you want, and each names the default that applies if you say nothing, so no step waits.

1. **The shared copy of the workspace: lock it to deploy-and-pull for every session, or leave it open?** Default: leave it as it is today, open, and the Workshop lane holds the switch with this lane's measured finding attached — because the guard only protects sessions that start after it lands, so turning it on mid-evening refuses work already in flight. Say "lock the shared copy" to change it.
2. **The weekly quota on the GLM vendor.** 239 of the recorded first failures are that vendor's weekly limit being used up, not a router defect; every one of them fell through to a working vendor. Default: nothing is bought and the report simply tells you when the week's quota is gone. Say "buy more" and it becomes a purchase to price.

Not asked, because you already answered: spend is counted and never capped and the per-task ceiling stays off (2026-09-09); cheap models check other agents' work with Sonnet as the backup (2026-09-09); the floor is four things and the refuser must prove the value (2026-09-09); nobody chases security or privacy — one line in the security file and back to work (2026-09-09); there is no limit to what is handed to cheap models (2026-09-09).

## If you get stuck (all steps)

Before writing "blocked": (1) re-read the step's START WHEN line — most "stuck" is a misread gate, (2) try a concrete workaround, (3) write one line to the overseer naming the ONE missing artefact. Then keep working every other step whose inputs exist. Never idle on a blocker; never end a turn waiting on a background result. When the documentation gate refuses a write, run `node projects/ops/skippy-jobs/lib/request-ticket.mjs <path> --reason "..."` at once — it posts an Approve button to Nick's Slack within a minute; never record "needs Nick's keystroke" without filing it.

## Your loop

Every pass: every step whose START WHEN inputs exist and which is not yet CLOSED is running, up to the cap → the cheap builder writes the change and hands the proof to the exerciser → the exerciser runs it into the lane's evidence folder → ONE cheap checker on a different vendor reads that evidence and the diff and returns PASS or FAIL → PASS closes it, FAIL loops the builder on the named failure → the three FRONT steps first, then the five POLISH steps → repeat until the FINISH LINE is proven.

## SUMMARY — a few plain-English lines, read by the status generator

The cheap bench is where nearly all the work is supposed to happen, and this lane makes sure it actually gets there. Three things Nick asked for come first: nothing turned away for ordinary words, a cheap worker able to write its own checks through one named route, and a morning line telling him what went cheap, what stayed and why with no cap on spending. Five quieter things follow: a first cheap call that stops coming back empty, honest failure reasons so a used-up weekly quota never reads as a refusal, the code's own wording matching his answer about who checks work, a written answer about the shared copy handed to the lane that owns it, and the close-out. The refused text itself was never recorded anywhere, so the re-run is done by file where the record names one and by a fake-value fixture where it does not — and from now on every refusal names its file and its kind so the next check takes a minute.

## STEPS

1. No cheap job is refused for ordinary words — 25%
   DEFINITION OF DONE: every recorded hard-floor refusal since 2026-09-02 is marked pass or real floor hit with its kind named, zero unexplained, and a refusal written today carries its file and matched kind and no value
   PROOF: `node projects/ops/cheap-build.mjs --file <the row's file or the fixture> --do "wall verdict only, change nothing" --verdict-json` per row, then the pass and floor-hit counts equal the row count
2. One sanctioned route for a cheap worker to write a test or guard file — 20%
   DEFINITION OF DONE: allowed inside the one route, refused outside it with the route named, the nightly runner leaves the route's file alone, both router suites green
   PROOF: `node projects/ops/cheap-task.mjs --test-file-route --selfcheck`
3. The morning report says what went cheap, what stayed and why — 30%
   DEFINITION OF DONE: the job carries a dry-run guard and under it prints a cheap total, a stayed total, a reason per stayed thing, a count of stayed-with-no-reason, and per-task ceiling OFF, sending nothing
   PROOF: `command grep -c "SKIPPY_DRY_RUN" projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs` then `SKIPPY_DRY_RUN=1 node projects/ops/skippy-jobs/jobs/cheap-routing-morning-report.mjs --routing-why`
4. The empty first reply, fixed red then green — 10%
   DEFINITION OF DONE: every known response shape is read with zero empty, and the pre-fix red run is on record
   PROOF: `node projects/ops/cheap-build.mjs --selftest-reply-shapes`
5. The routing code's own text matches what Nick decided about checking — 40%
   DEFINITION OF DONE: the category text states cheap-checks-cheap with Sonnet as backup, both suites exit 0, no case count lower than the 72 and 52 counted at this lane's check record
   PROOF: `node projects/ops/skippy-jobs/_test-work-type-gate.mjs && node projects/ops/skippy-jobs/_test-dispatch-override-categories.mjs`
6. The shared-copy fence's rule C, answered in writing — 50%
   DEFINITION OF DONE: four answers each with a line number or "not recorded", what a live lane does today, and one dated line in the Workshop lane's plan
   PROOF: `command grep -c "ROUTER STEP 6 closed" projects/ops/life-os/REGROUP-2026-09-08/plans/LANE-1-WORKSHOP/PLAN.proposed.txt`
7. Every failure row says which kind of failure it was — 0%
   DEFINITION OF DONE: every new failure row carries one of six kinds, a timeout row carries its size and limit, the report tells quota from refusal without reading prose
   PROOF: `command grep -c '"failure_kind":"quota"' projects/ops/skippy-jobs/lib/cheap-vendor-failover.log`
8. Close-out — 0%
   DEFINITION OF DONE: the FINISH LINE's seven items each point at a closed step, the postmortem is written, what is left on the Mac is declared with its size
   PROOF: `python3 projects/ops/agents/check_plan.py --progress projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PLAN.proposed.txt`

## NEXT

Everything found after the FINISH LINE passes goes here as one line, and is not worked. Empty at plan time.

## POSTMORTEM

Written by STEP 8. Empty at plan time; the lane is not closed until it is filled.

Current state PROGRESS.txt

ROUTER EVALUATION LANE — PROGRESS. Current state only, newest line last. The plan is PLAN.proposed.txt in this folder; this file is where a fresh agent picks up.

2026-09-09 — Lane opened and planned. PLAN.proposed.txt written in the plan skill's 2026-09-09 shape by the Opus senior-engineer session: three FRONT steps (no cheap job refused for ordinary words · one sanctioned route for a cheap worker to write a test or guard file · the morning report saying what went cheap, what stayed and why with the ceiling off) and five POLISH steps (the empty first reply · the routing code's own checking text · the shared-copy fence's rule C answered in writing · a machine-readable failure kind on every failure row · close-out). Nothing is built yet; every percent in the plan's STEPS section is honest and no step carries a VERIFIED line. The plan's own checker results, the plan's sha256 and the executed baseline of every proof are in CHECK.txt beside this file.

2026-09-09 — Facts measured tonight that the steps rest on, recorded once. (1) The refused payload is stored nowhere: the failover record carries 52 hard-floor refusals, 36 dated 2026-09-02 or later, each naming only a matched kind inside a truncated sentence; the routing decision record carries 26 wall refusals since that date that DO name a file, and cheap-build-failures.log names a file per refusal too. So STEP 1 re-runs by file where a file is named and by a fake-value fixture where only a kind is named, and adds the file and the kind to every future refusal row. (2) Only ONE of the three gates named in the planner brief actually refuses a cheap worker's test-file write — the control-plane path list in vendor-fence.mjs (lines 938, 954) reached through cheap-task.mjs (lines 1432-1435). The routing check merely waves such a file (check-routing-missed.mjs lines 437, 947) and the dispatch gate's test-authoring category is already retired (dispatch-contract.mjs line 1090, dated 2026-09-09). STEP 2 fences the one that refuses; the other two are in the plan's CUT list with the measurement beside them. (3) The nightly host to extend is cheap-routing-morning-report.mjs, already scheduled at 07:17 (runner.mjs line 269); no job under jobs/ reads either the failover record or the routing decision record today, and that job carries NO dry-run guard, so STEP 3 adds one before the mode is ever run. (4) The wall-verdict instrument is real and safe: cheap-build.mjs --file <path> --do "..." --verdict-json prints the classification as JSON and exits at line 744 with no vendor call; run once tonight on a harmless file, exit 0. (5) Both router suites are green at 72 and 52 checks — those two numbers, not the older pair in the onboarding file, are the floor no step may go below. (6) The shared-copy fence is wired at .claude/settings.json line 255 in the WORKSPACE settings file; searching the home-directory settings file returns nothing and is a false negative.

2026-09-09 — INCIDENT, and it is a hazard for whoever picks this lane up: this file was written once, confirmed present by a directory listing, and was GONE from disk minutes later while STEPS.json and HANDOFF-PROMPT-FOR-BUILDER.txt written in the same few minutes survived. Nothing in this session removed it. That is the known concurrent-session sweep of untracked files in the shared checkout. WHAT TO DO ABOUT IT: commit a new lane file with a pathspec the moment it is written, in the same shell invocation, rather than leaving it untracked while you work; and re-check that a file you wrote is still there before you rely on it. The plan's own checker refused the draft twice for naming files that had been swept, which is how this was caught at all.

2026-09-09 — Nothing left on the Mac by the planning pass except this lane folder's own five files: PLAN.proposed.txt, PROGRESS.txt, STEPS.json, CHECK.txt, HANDOFF-PROMPT-FOR-BUILDER.txt, beside the planner brief that was already here. No worktree, no copy of the workspace, and the session scratchpad holds only the draft.

2026-09-09T23:29:23Z - Planner: cold check returned PASS on all five files after the autostash restore; STEP 8 proof reworded to the line the tool prints; two findings carried to the overseer in CHECK.txt (per-step evidence lines; the override-categories suite is red since 18:13 from another lane s gate commit). Committed to main by pathspec.

2026-09-10T01:14:09Z - HANDED OFF: Nick approved the plan for hand-off ("done what next", 2026-09-09, read as: the two section-7 defaults stand — shared copy stays open with the Workshop lane holding the switch; no extra GLM quota bought — and the plan goes to its overseer). The overseer prompt is HANDOFF-PROMPT-FOR-BUILDER.txt in this folder.