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.md — SCHOOL: Lesson Factory - a school week built right the first time
**🔴🔴 THIS IS THE ONLY PLANNING DOCUMENT FOR THIS PROJECT. Do not create a second plan, tracker, summary, or scratch state file — extend THIS file or its STATE.md companion. Any status view is GENERATED from this plan; if a view disagrees with the plan, the plan wins. The postmortem this plan is built from is `POSTMORTEM.md` beside it; the School lane's own plan (`projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/GO-BIG/PLAN.md`) keeps running and governs the lessons themselves — this plan governs the FACTORY that builds them.**
**NORTH STAR:** Nick, 2026-09-13 01:40Z: "a system that allows us to avoid rework and get it dialed in from the start line". Finished = a school week for Noah and Willow comes off one line, in one fixed order, with a gate between every stage, and nothing is built twice: no lesson written before the tools and journeys it needs, no sentence live unread, no picture graded before it is drawn, no bug found after the children could meet it. Nick's come-back for the week is a link to every lesson page and one line saying what the week cost.
**FINISH LINE:** (a) the factory tool runs a whole school day's stages in the fixed order and refuses any stage whose input artefact is missing — proven red-first by asking for a stage out of order; (b) every lesson has an address that opens straight into it as that child, driven in a real browser for both children; (c) the come-back for one real day is one paragraph, a link per lesson and a cost line, and passes the workspace's closing-message lint; (d) the first school day built after the tool lands — both children, both lessons — comes through the factory with zero second-write passes, one creative-director round, at most one re-walk per lesson, within 3 hours of wall time and 4 M helper tokens, and the ledger shows it; (e) the six environment fixes each prove red then green (a stage refused out of order, a second builder refused on a locked file, a missing artefact re-dispatched once, a progress entry committed by the tool and found in git, a scheduled job under Documents refused, the voice engine's proof-of-run red on a silent engine). Anything found after these pass goes on the NEXT list.
**Owner:** the cheap lane (GLM 5.3 · DeepSeek · Qwen) for every mechanical edit, Opus plumbing only under a recorded cheap failure · **Overseer:** Fable (the superintendent's own session, or the Fable session Nick names) — designs the stage contracts, unsticks, routes, signs off once, never builds · **Design authority:** Sienna, one grade per day of lessons, after the walks
> **STEP 0 — ARM THE LOOP, BEFORE ANYTHING ELSE.** Set a 5-minute loop. Every time it fires, answer
> these five 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.
> 2. **FAN-OUT** — declare the whole actual roster, dispatch useful ready work, and shed your own unnecessary processes. Coordinate through peers or the launching dispatcher; no numeric cap or load-wait rule applies.
> 3. **CHEAP** — are cheap models doing the building AND the per-step checking? If anything on
> Anthropic or OpenAI is building or checking a step, move it down now (§M).
> 4. **STUCK** — for anything I have called blocked: name the input that does not exist yet, or the
> three concrete things I tried. If I cannot, it is not blocked — drive through it now.
> 5. **NEXT** — did something just finish? Then the next step whose inputs exist starts THIS minute.
> A finished step is never a place to stop, a report is never a reason to wait, and Nick being
> away or asleep is the reason to keep going, not to pause.
> Then keep building. The loop never stops until the FINISH LINE is proven.
**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.**
## Already true (facts, not story)
- DECIDED: the punch list of 2026-09-12 is the standard for every lesson built from now on; instructions sit beside the controls; Willow's tablet is 1280×800 landscape; the next weeks are science, codes parked to 5 October — evidence: `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/evidence/punchlist-nick-2026-09-12.txt`; `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/ROUND-2-PLAN-2026-09-12.txt` (D1–D4)
- DECIDED: the reading bar — every child-facing string CLEAR · CONCISE · INSTRUCTIVE · FUN AND AT LEVEL, Willow's sentences under 12 words on average and never over 20, Noah's under 16 and never over 28, a heading a complete phrase — and no week publishes without a closed proof-read record newer than its bundle — evidence: `projects/personal/learning-app/standard/LESSON-STANDARD.md` (standing content rules, first bullet); `projects/personal/learning-app/weekly-plan-drafts/tools/deploy-live.sh` (fifth gate)
- BUILT: the stage checks the factory orders — the topic judge, the journey lint, the spec-breaker's cold read, the lesson gate, the kid firewall, speech lint, break spacing, the strict evidence check, the walk-verdict tool, the publish script with five gates and the cache-bust stamp — evidence: `projects/personal/learning-app/weekly-plan-drafts/tools/journey-lint.mjs`, `projects/personal/learning-app/weekly-plan-drafts/tools/lesson-evidence.mjs`, `projects/personal/learning-app/weekly-plan-drafts/tools/walk-verdicts.mjs`, `projects/personal/learning-app/standard/check-lessons.mjs`, `projects/personal/learning-app/test-kid-firewall.mjs`, `projects/personal/learning-app/weekly-plan-drafts/tools/stamp-version.mjs`
- BUILT: the plant kit, the two code kits, the kit-loader table, 117 step frames and the tile art keyed by tile text — evidence: `projects/personal/learning-app/skippy-school-site/worlds/plantkit.js`, `projects/personal/learning-app/skippy-school-site/worlds/tileart-week12.js`, `projects/personal/learning-app/skippy-school-site/worlds/toolhook.js`
- BUILT: an isolated test browser the project already drives the live site with, separate from Nick's Chrome and from the shared browser pane — evidence: `projects/personal/learning-app/standard/test-chrome.mjs`, `projects/personal/learning-app/standard/shot.mjs`, `projects/personal/learning-app/weekly-plan-drafts/tools/strongroom-journey.mjs`
- BUILT: the app opens a child's week or day from the address bar today (`?kid=<k>&go=week|today|meet`), but has no address for one lesson — evidence: `command grep -n "params.get('go')" projects/personal/learning-app/skippy-school-site/index.html` → one line, three values, none of them a lesson
- MEASURED (the superintendent, 2026-09-13): Monday and Tuesday of the plants week — eight lessons — cost 8.1 h of the superintendent's session, 3.7 M tokens of its own output plus 507 M tokens of context re-read, and fourteen helpers at 3.7 M tokens / 396 agent-minutes; per piece a lesson pair 180–200 k tokens / 11–13 min, a child-read of eight lessons 234 k / 11 min, a walk 300–500 k / 30–60 min, a creative-director round 180–300 k — evidence: `POSTMORTEM.md` §D beside this file
- MEASURED: the twenty rework rows and their classes — seven sequence faults, seven missing rules, seven missing tools, six environment faults — evidence: `POSTMORTEM.md` §B and §C
## 0 · Gate Zero receipts (the plan may not exist without these)
- Failure Mode Registry loaded: 2026-09-13, 229 table rows read in `.claude/skills/plan/references/failure-registry.md`; the eleven entries this build is exposed to are named in §4
- Canonical specs loaded: `.claude/skills/plan/SKILL.md` (§N §L §1C §M §P §U §A §B) · `projects/personal/learning-app/standard/LESSON-STANDARD.md` · `projects/ops/agents/CODE-STANDARD.md` · `projects/ops/MACHINE-RULES.md` (Rules 12, 16, 17, 20, 22, 24, 31, 35, 36, 41, cited by number)
- Ownership check: the project-status registry (`projects/ops/artifacts/project-status/registry.json`) carries no row for a lesson factory, a build pipeline or a school runbook (searched 2026-09-13 for "factory", "pipeline", "runbook": 0 hits); the School lane's tools folder holds no orchestrator (`command grep -ril factory projects/personal/learning-app/weekly-plan-drafts/tools/` → 0); the School row `life-os-school` governs the lessons and keeps doing so — this row governs only the factory
- Expected inputs confirmed to exist: the School lane's running record and handoff (opened 2026-09-13) · the eleven stage tools named under "Already true" (each opened) · the walk records `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/evidence/walks/week12-mon-noah/walk.txt` and four siblings (opened) · the two creative-director grades `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/evidence/sienna-grade-plants-2026-09-12-r1.txt` and `-r2.txt` (opened; the round-3 file named in the brief does not exist on disk at 01:46Z — its round was still running) · the model matrix `projects/ops/walkaway/MODEL-MATRIX.md` (opened) · the closing-message lint `projects/ops/skippy-jobs/lib/check-closing-message.mjs` (on disk)
- PLAN AUTHOR: a Fable planning session dispatched by the superintendent at Nick's ask, 2026-09-13 01:40Z–02:10Z
- COLD READER: none — SINGLE-AUTHOR, UNREVIEWED (not a visual plan; the spec-breaker's cold read of this file is the first job of STEP 1's checker before it checks anything else, and its findings are fixed in place)
- PROMPT-SPEC scan (P1–P7): P1 "a stage" = one of the fourteen named stages in §1, each with one input artefact and one output artefact; P2 "rework" = any artefact produced a second time for the same lesson, counted by the ledger; P4 "a day of lessons" = both children's two lessons for one calendar day, four lessons; P5 "cost" = wall minutes measured by the tool plus tokens read from the worker's transcript usage where the harness records it, else the worker's own USAGE line marked "claimed"; P6 the target in §1 comes from the measured pieces in `POSTMORTEM.md` §D; nothing left to ask
## 1 · Goal and definition of done
- **What we're building, one paragraph.** A factory for one school week: a single tool (`weekly-plan-drafts/tools/factory.mjs`, CREATED BY STEP 1) that knows the fourteen stages in order — brief → topic judge → kit-and-frame inventory LOCKED → journeys → spec-breaker → lessons written against a writer checklist → the draft check → assembly and the mechanical gates → the child-read record → tile art keyed by tile text → walks as each child on the harness through the isolated test browser → the creative director's one grade → the publish with its five gates → the come-back — and refuses to run a stage whose input artefact is missing, writes one ledger row per piece (model, minutes, tokens, artefact), holds one builder per file, dispatches in waves of four with one re-dispatch for a missing artefact, commits every progress entry the moment it is written, and renders the come-back as links to the lesson pages plus one cost line. Every stage check it orders already exists; the factory adds the ORDER, the LOCK, the LEDGER and the LINK.
- **HOW IT'S USED:** the superintendent (Fable) runs `factory.mjs` stage by stage for the next school day, one day ahead of the children; each stage dispatches the named worker with a brief the tool composes, waits for the artefact, records the row and unlocks the next stage; Nick reads the come-back and taps a lesson link. · HOW WE KNOW: Nick, 2026-09-13 01:40Z, "get that system refined while you finish the week"; the superintendent's own run order in PROGRESS 2026-09-12T18:20Z–20:59Z is the order the tool fixes.
- **WHAT IT LOOKS LIKE:** on Nick's side — one paragraph, then a line per lesson ("Noah · Wednesday · Lesson 1 — The leaf kitchen — <link>"), then "This day cost: 2 h 10 min wall · 2.4 M helper tokens · 0 pieces built twice"; on the machine's side — one command per stage, one refusal line when a stage is out of order ("STAGE journeys REFUSED — inventory not locked: weekly-plan-drafts/w13-build/INVENTORY.json missing"), one ledger line per piece. · HOW WE KNOW: Nick, 2026-09-13, "next time you can just bring up the links to the pages"; the closing-message shape in MACHINE-RULES (one paragraph, no header).
- **WHERE IT LIVES:** the tool in `projects/personal/learning-app/weekly-plan-drafts/tools/` (the School lane's tools folder; CREATED BY STEP 1); the per-week artefacts in that week's build folder (`weekly-plan-drafts/w<N>-build/`: INVENTORY.json, LEDGER.jsonl, the drafts, the proof-read record); the lesson addresses at https://skippytutor.pages.dev — opened by Nick from the come-back, and by Noah and Willow through the app as today. · HOW WE KNOW: every week's scripts already live in that tools folder and every week's drafts in its build folder (PROGRESS 2026-09-10T20:10Z: the folder is tracked for exactly these).
- **WHAT IT MUST DO:** (1) run the fourteen stages in the fixed order and refuse any stage whose named input artefact is missing; (2) lock the kit modes, frame keys and tile-art keys into INVENTORY.json before a journey is written, and fail any journey or draft that names a mode, frame or picture outside it; (3) run the writer checklist over the drafts before assembly — no emoji in tile, item or choice text; every heading a complete phrase; the week's canon facts present verbatim and their banned variants absent; "Mama or Dada", never one parent alone, in a reflect or share prompt; US spelling; the session string in words; sentence caps per child; every quiz restating its fact on its own page; (4) take the proof-read record on the assembled bundle before tile art, walks and publish, and re-open only the changed strings after any later text fix; (5) walk every lesson as each child on the harness through the isolated test browser before the publish, four walks at a time, re-walking only the parts a fix changed; (6) drive the shell's own Start → parts → Finish → comprehension check as each child in both worlds before any publish; (7) grade the look once, after tile art and walks, on a capture set that includes every after-tap state; (8) give every lesson an address that opens straight into it as that child; (9) render the come-back as one paragraph, a link per lesson and one cost line, and pass the closing-message lint; (10) write one ledger row per piece and report per lesson, per day and per week against the target; (11) hold one builder per file and refuse a second; (12) dispatch in waves of at most four, name each worker's artefact path before dispatch, and re-dispatch once when the artefact is missing at the window's end; (13) commit a progress entry in the same command that writes it and prove it landed; (14) refuse a scheduled job whose program or logs live under Documents, and read the voice engine's proof-of-run before a publish. · HOW WE KNOW: each is a row of §6 and maps to a rework row in `POSTMORTEM.md` §B.
- **NOT in scope:** writing any lesson, journey, kit, frame or tile (the School lane's plan does that; this plan builds the line they come down) · changing what any existing stage check measures (the lesson gate, the firewall, speech lint, the strict evidence check keep their rules; this plan only orders them, extends the strict check's capture and probe coverage, and adds the draft check) · the cheap-router tools themselves (`projects/ops/route-build.mjs`, `projects/ops/cheap-task.mjs` are the ROUTER lane's; STEP 11 posts the handoff line and builds the file lock on this side) · the Hub progress screen's generator (it reads this plan's STEPS block as today) · security and privacy work (one line to `projects/ops/sp-sec/PLAN.md` if seen) · a second tracker, ledger store or summary file anywhere (the ledger is per week, in the week's own build folder).
- **Trip-over protocol:** a builder that finds something outside the fence writes one handover line into the owning lane's record (`projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt` for a lesson fault; `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PROGRESS.txt` for a routing-tool fault; `projects/ops/sp-sec/PLAN.md` for anything security-shaped) and returns to its step — 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** | Nick opens each lesson from a link in the come-back message in his chat; the link lands on the live school site, in that child's world, on that lesson; the children keep opening lessons through the app as today | a parent dashboard page; a screenshot pack; a PDF | V1 | Nick's words | the come-back keeps describing pages he then has to go find | Nick, 2026-09-13, "next time you can just bring up the links to the pages" |
| 2 | The order of the stages, and that a gate sits between each | brief → topic judge → inventory locked → journeys → spec-breaker → lessons → draft check → assembly and mechanical gates → child-read record → tile art → walks → one grade → publish → come-back | the order the codes week ran (lessons first, tools after); walks after publish; grades before art | V1 | Nick's words, and the seven sequence faults in the postmortem | a week written twice again | Nick, 2026-09-13 01:40Z, "avoid rework and get it dialed in from the start line"; Nick, 2026-09-11, "you plan and then you execute the build" |
| 3 | Who designs, who plumbs, who checks | Fable designs the stage contracts and anything drawn; Opus writes lessons and plumbs only under a recorded cheap failure; the cheap lane makes every mechanical edit; Sonnet-tier readers and walkers verify | Codex writers; Opus building by default; Fable building | V1 | Nick's words | the wrong bench burns the week's quota, as on 2026-08-31 | Nick, 2026-09-11, "fable is supposed to be designing this"; Nick, 2026-09-10, "new opus lesson writer and cheap builders under fable manger and QA"; Nick, 2026-09-10, "i asked for opus writers by name but when it comes to building things make sure you go cheap" |
| 4 | The walker's instrument | the project's own isolated test browser (a separate Chrome for Testing the drivers already launch), never the shared browser pane | the shared browser pane; Nick's own Chrome | V2 | opened the walk records | a walk with no pictures and unreachable buttons, as on 2026-09-12 | opened `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/evidence/walks/week12-mon-noah/walk.txt` and `week12-monB-willow/walk.txt`, 2026-09-13, saw: the pane hidden for a whole walk and swapped between walkers; the two walks that drove the test browser produced pictures and measured lines |
| 5 | The cost target per school day | ≤ 3 h wall · ≤ 4 M helper tokens · 0 pieces built twice · 1 grade · ≤ 1 re-walk per lesson | the superintendent's hope of "a few hours"; no target | V2 | summed the measured pieces | a target nobody can miss or a target nobody can hit | opened the superintendent's measures of 2026-09-13 and summed them in `POSTMORTEM.md` §D, saw: 2.0–2.5 M tokens and 1.5–2 h for a day with no rework; the target adds headroom |
| 6 | The children's screen sizes | both 1280×800 landscape — Noah's 15-inch touch laptop, Willow's Galaxy Tab S9 in its keyboard case | Willow upright 800×1280 | V1 | Nick's words | every walk and probe measures the wrong screen, as before 2026-09-12 | Nick, 2026-09-12, "its never upright its on a landscape keyboard setup" |
| 7 | Where the tokens in the ledger come from | wall minutes measured by the tool; tokens read from the worker's transcript usage where the harness records it, else the worker's own USAGE line, marked "claimed" and never summed with measured numbers | trusting the hand-back's number; no token column | V2 | opened the token counter and the record | a cost line that is a guess wearing a number's clothes | opened `projects/shared-tooling/count-tokens.mjs`, 2026-09-13, saw: it counts a text's tokens with the model's own tokeniser, and the record's per-piece figures are the superintendent's reading of each worker's usage, not a tool's |
- 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:**
- `the shape of a ledger row` — JSON lines with stage, lesson, model, minutes, tokens, tokens_source, artefact, retries; any reader can sum it.
- `which week the factory's first day is` — the first school day not yet written when STEP 1 lands; the School lane's plan names the week.
## 1b · Subproject decomposition — could a piece of this ship on its own?
- **SINGLE SUBPROJECT:** `every step lands in one tool and one tools folder, the lesson link and the cost line are the factory's own outputs, and the first visible pieces (the link, the come-back) are FRONT steps that ship in days one and two rather than a second plan — a second plan file would only add a file for Nick to approve under Rule 31`
**Carve-out rule:** the routing tools' clean-tree scope stays with the ROUTER lane (`projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PLAN.proposed.txt`), handed a dated line by STEP 11; the lessons, kits, frames and journeys of any week stay with the School lane's plan.
## 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 | The live school site, `?kid=noah&go=lesson&week=week12&day=mon` | default | the address opens | Noah's world opens straight onto Monday Lesson 1 with its title and part count, no hub, no week screen in between | link → lesson |
| U2 | The same address for Willow, `?kid=willow&go=lesson&week=week12&day=monB` | default | the address opens | Willow's world opens onto Monday Lesson 2 | link → lesson |
| U3 | A lesson address whose week or day does not exist | error | the address opens | the child's own hub opens (the app's existing fallback), never a blank page or "undefined" | bad link → hub |
| U4 | Nick's chat, the come-back for a day | default | he reads it | one paragraph, then one line per lesson with the child, the day, the title and its link, then the cost line; no header, no path | chat → link → U1 |
| U5 | Nick's chat, the come-back for a day that is not finished | empty | he reads it | the paragraph says which stage the day is at and why; no link is shown for a lesson that is not live | chat |
| U6 | The factory tool, a stage asked for in order | default | `--stage <name>` | the stage's worker is dispatched, the artefact lands, one ledger row is written, the next stage is unlocked | terminal |
| U7 | The factory tool, a stage asked for out of order | error | `--stage <name>` | one refusal line naming the missing input artefact and the stage that makes it; nothing dispatched; exit 1 | terminal |
| U8 | The factory tool, a second builder asking for a locked file | error | `--lock <file>` | refused with the holder's name and the time the lock was taken; exit 1 | terminal |
| U9 | The factory tool, a wave whose worker never delivered | error | `--wave` window ends | the missing artefact is listed and re-dispatched once with the same brief; a second miss is a red row, never a silent gap | terminal |
| U10 | The factory tool, the ledger report | default | `--report` | per lesson, per day, per week: minutes, tokens (measured and claimed apart), pieces built twice, re-walks, grades — each against the target, red where over | terminal → U4 |
| U11 | The factory tool, the environment stage | error | `--stage env` | a scheduled job under Documents is named and refused; a silent voice engine is a red row | terminal |
| U12 | The progress record, an entry written by the tool | default | `--record` | the entry is appended, committed by pathspec and found by `git log -1` in the same command | terminal → the lane's record |
## 2d · DESIGN FIDELITY GATE (plan skill §D)
DESIGN FIDELITY GATE: N/A — nothing rendered by this plan. The lesson screens the factory produces are graded once per day of lessons by the creative director inside STEP 8, against her six gates, on a capture set that includes every after-tap state; a HOLD loops the builder and never summons a second grader.
## 3 · Lanes and frozen contracts
| Lane | Scope (in / out) | Owner | Definition of done | Builder (cheap, named) | Backup builder | Checker (different model) | Backup checker |
|---|---|---|---|---|---|---|---|
| A · THE LINE | the factory tool: stage order, gates, inventory lock, file lock, waves, ledger, come-back, record, env stage (STEPS 1, 3, 4, 9, 10, 11, 12, 13) / out: any lesson content | the superintendent (Fable) as overseer | every §6 row for the tool green; the first factory day within target | GLM 5.3 (zai) via cheap-task | DeepSeek | Sonnet | Qwen |
| B · THE LINK | the lesson address in the app's boot handler and its driver mode (STEP 2) / out: any other change to the app's main file | the superintendent | both children's addresses open the lesson in a real browser | DeepSeek via route-build | GLM 5.3 (zai) | Sonnet | Qwen |
| C · THE CHECKS | the draft check, the shell drive, the state captures, the walker's instrument (STEPS 5, 6, 7, 8) / out: weakening any existing gate | the superintendent | each check red on its fixture and green on the tree | Qwen via cheap-task | DeepSeek | Sonnet | GLM 5.3 (zai) |
**Contracts between lanes (FROZEN at plan time — change = dated PLAN-CHANGES.md delta):**
- **The stage list and its artefacts** (lane A writes it, lanes B and C read it): `brief` → BRIEF.txt · `judge` → the judge's file ending "NOTHING LEFT TO CUT" · `inventory` → INVENTORY.json · `journeys` → the eight journey files, lint PASS · `breaker` → the spec-breaker's file with every would-misbuild marked applied · `lessons` → the draft files · `draftcheck` → `DRAFT CHECK: 0 faults` · `assemble` → the bundle, manifests, lesson gate exit 0, firewall 130, speech clean, breaks 0, strict 0 on the harness · `read` → the proof-read record closed and newer than the bundle · `tileart` → the tile-art file with every tile key in INVENTORY.json drawn or listed as an answer · `walks` → one walk folder per lesson with all three verdicts HELD · `grade` → the creative director's file ending PASS · `publish` → deploy-live.sh exit 0 and strict 0 on production · `comeback` → the message text passing the closing-message lint.
- **The ledger row**: one JSON object per line — `{stage, week, day, kid, lesson, model, started, minutes, tokens, tokens_source: "measured"|"claimed", artefact, retries, rework: true|false}`; `rework` is true when the same stage writes the same lesson's artefact a second time.
- **The lesson address**: `?kid=<noah|willow>&go=lesson&week=<file id>&day=<mon|monB|tue|tueB|wed|wedB|thu|thuB|fri|friB>`; the file id, never the calendar number (ids never change; calendar numbers move — PROGRESS 2026-09-12T18:20Z).
- **The data floor**: nothing in any week's build folder or the ledger carries a login, a credential value, a government ID or a card number; the come-back carries links and a cost line and nothing else that is not already on the public site. **Buckets that share a goal message each other:** a dated line into the School lane's record when STEP 1 lands and when the first factory day closes; a dated line into the ROUTER lane's record from STEP 11.
## 3b · Execution map — the Step map, then one STEP block per row
> 🔴 The table is the INDEX; the `### STEP` blocks are the INSTRUCTIONS — 1:1. EXECUTOR comes by name from `projects/ops/walkaway/MODEL-MATRIX.md`; the CHECKER never shares a model (glm≡zai) or a session with its row's EXECUTOR; DONE-PROOF is a runnable command in backticks. Keep the next line verbatim — the gate looks for it:
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):**
| 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) |
|---|---|---|---|---|---|---|---|---|---|---|
| Framing | 1 | FRONT | The factory tool: the fixed order, a gate between stages, a ledger row per piece | a week is built in one order and the machine refuses to write a lesson before its tools and journeys exist | none — start now (Fable's one-page stage contract is the first hour of this step) | GLM 5.3 (zai) via cheap-task | DeepSeek | Sonnet | Qwen | `node projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs --selftest` (CREATED BY STEP 1) → `FACTORY: 14 stages · out-of-order refused 13 of 13 · in-order ran 14 of 14 · ledger rows 14 · PASS` |
| Elements | 2 | FRONT | The lesson address | he taps a link and is inside the lesson, as that child | none — start now | DeepSeek via route-build | GLM 5.3 (zai) | Sonnet | Qwen | `node projects/personal/learning-app/weekly-plan-drafts/tools/school-drive-url.mjs --lesson http://127.0.0.1:8788 week12 mon` (a new mode on the existing tool, CREATED BY STEP 2) → `LESSON LINK: noah week12 mon opened · willow week12 mon opened · bad link → hub · PASS` |
| Elements | 3 | FRONT | The come-back: one paragraph, a link per lesson, one cost line | every day's report is links to the pages and what it cost | `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` (CREATED BY STEP 1) and the address shape from STEP 2 | GLM 5.3 (zai) via cheap-task | Qwen | Sonnet | DeepSeek | `node projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs --stage comeback --week week12 --day mon --out /tmp/comeback.txt && node projects/ops/skippy-jobs/lib/check-closing-message.mjs /tmp/comeback.txt` → `PASS` and four `https://skippytutor.pages.dev/?kid=` lines |
| Elements | 4 | FRONT | The inventory lock: kit modes, frame keys and tile-art keys locked before a journey is written | no lesson promises a picture or a tool move that does not exist | STEP 1's tool | Qwen via cheap-task | DeepSeek | Sonnet | GLM 5.3 (zai) | `node projects/personal/learning-app/weekly-plan-drafts/tools/journey-lint.mjs --week=week12 --days=mon,monB,tue,tueB --inventory` (a new mode on the existing tool, CREATED BY STEP 4) → `INVENTORY: 8 journeys · 0 names outside the lock · PASS`, and on the fixture journey naming a made-up mode → `FAIL` |
| Elements | 5 | FRONT | The writer checklist as a machine check on the drafts, and the child-read record taken before tile art, walks and publish | no half-sentence, wrong picture or one-parent prompt reaches the children | STEP 1's tool | Qwen via cheap-task | GLM 5.3 (zai) | Sonnet | DeepSeek | `node projects/personal/learning-app/weekly-plan-drafts/tools/draft-check.mjs --selftest` (CREATED BY STEP 5) → `DRAFT CHECK: RED on 9 of 9 fault fixtures · GREEN on the clean fixture · PASS` |
| Tests | 6 | FRONT | Walks before publish, on the harness, through the isolated test browser, four at a time, re-walking only what changed | a bug is found before the children could meet it, not the morning after | STEP 1's tool | DeepSeek via cheap-task | Qwen | Sonnet | GLM 5.3 (zai) | `node projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs --stage walks --week week12 --day mon --base http://127.0.0.1:8788 --dry-run` → `WALKS: 4 briefs · instrument test-chrome · wave 4 · artefacts named 4 of 4 · PASS` and `command grep -c "Browser pane" .claude/agents/sup-kid-walker.md` → `0` |
| Tests | 7 | FRONT | The shell drive: Start → every part → Finish → the comprehension check, as each child, both worlds, before any publish; mouse drag on every part type whose words say drag | the Finish button with no size, the seeds that would not drag — caught by a machine before a publish | none — start now | Qwen via route-build | DeepSeek | Sonnet | GLM 5.3 (zai) | `node projects/personal/learning-app/weekly-plan-drafts/tools/hook-drive.mjs http://127.0.0.1:8788 --finish week12 mon` and `node projects/personal/learning-app/weekly-plan-drafts/tools/hook-drive-willow.mjs http://127.0.0.1:8788 --finish week12 mon` (a new mode on each existing tool, CREATED BY STEP 7) → `… Finish 127×44 · check opened · 3 of 3 answered · PASS` each |
| Tests | 8 | FRONT | The look graded once, at the end, on every state | the look is graded once, on everything, after the walks | STEP 1's tool and STEP 6's walks | Qwen via route-build | DeepSeek | Sonnet | GLM 5.3 (zai) | `node projects/personal/learning-app/weekly-plan-drafts/tools/lesson-evidence.mjs --states --weeks=week12 --days=mon --kids=both --base=http://127.0.0.1:8788` (a new mode on the existing tool, CREATED BY STEP 8) → `STATES: 4 lessons · N parts · after-tap captures N of N · PASS` |
| Output | 9 | FRONT | The ledger report and the cost line | he sees what a day of lessons cost, in one line, every time | STEP 1's tool and STEP 3's come-back | GLM 5.3 (zai) via cheap-task | Qwen | Sonnet | DeepSeek | `node projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs --report --week week12 --day mon` → one table with `minutes`, `tokens measured`, `tokens claimed`, `built twice`, `re-walks`, `grades` and a `vs target` column, exit 0 when within target |
| Polish | 10 | POLISH | A progress entry committed by the tool that writes it | nothing he notices — the record stops losing entries | STEP 1's tool | DeepSeek via cheap-task | GLM 5.3 (zai) | Sonnet | Qwen | `node projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs --record "<entry>" --into projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt --dry-run` → `RECORD: appended · committed <hash> · found by git log · PASS` (the dry run on a scratch copy of the record) |
| Polish | 11 | POLISH | One builder per file at a time, and the handoff to the ROUTER lane | nothing he notices — good edits stop being reverted | STEP 1's tool | GLM 5.3 (zai) via cheap-task | DeepSeek | Sonnet | Qwen | `node projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs --lock-selftest` → `LOCK: first holder granted · second refused naming the holder · release · re-grant · PASS`; `command grep -c "SCHOOL-FACTORY STEP 11" projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PROGRESS.txt` → `1` |
| Polish | 12 | POLISH | Waves of four and the retry rule | nothing he notices — a usage-limit death costs one re-dispatch, not a night | STEP 1's tool | Qwen via cheap-task | GLM 5.3 (zai) | Sonnet | DeepSeek | `node projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs --wave-selftest` → `WAVE: 9 jobs in waves of 4 · 1 missing artefact re-dispatched once · second miss red · PASS` |
| Polish | 13 | POLISH | The environment stage: no scheduled job under Documents; the voice engine's proof-of-run | nothing he notices — the voice cannot die unnoticed again | STEP 1's tool | DeepSeek via cheap-task | Qwen | Sonnet | GLM 5.3 (zai) | `node projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs --stage env` → `ENV: launchd jobs under Documents 0 · voice /say 200 engine openai · PASS`, and with a fixture plist under Documents → `FAIL` naming it |
### §3c · CUT (was in an earlier plan or brief and is overkill for the outcome; recorded once, not worked)
- A second creative-director round per day of lessons — one round after the walks, a HOLD loops the builder (plan skill §D).
- A per-lesson fidelity count against a locked design target — no locked target exists for lesson pages (School plan STEP 5's note); Sienna's six gates are the instrument.
- A new dashboard or page for the cost line — it is one line in the come-back and one command in the terminal.
### STEP 1 — The factory tool: the fixed order, a gate between stages, a ledger row per piece
**FOR NICK:** a week is built in one order and the machine refuses to write a lesson before its tools and journeys exist — the fault that had the codes week written twice and the plants Monday written twice. · **Tier:** FRONT
**Start when:** none — start now. The first hour is Fable's one-page stage contract (the fourteen stages, each with its input artefact, its worker, its output artefact and its refusal line), written into STATE.md's "Stage contract" section beside this plan; the builder starts from it.
**Builder:** GLM 5.3 (zai) via `projects/ops/cheap-task.mjs` · **Builder backup:** DeepSeek · **Checker:** Sonnet (a VERIFIER dispatch that first cold-reads this plan as the spec-breaker, then re-runs the proof) · **Checker backup:** Qwen. After two recorded cheap failures the overseer applies the file by hand under a recorded override and Opus plumbs the next revision (Nick, 2026-09-11: Opus does plumbing).
**Files you may touch:** `weekly-plan-drafts/tools/factory.mjs` (new, in the School lane's tools folder under the app repo) and `weekly-plan-drafts/tools/fixtures/factory/` (its fixtures). **Never** the app's public folder (`skippy-school-site/`), any lesson, journey or kit file (the School lane's), the publish script (STEP 13 reads it, nothing writes it), any other lane's record.
**Do exactly this:**
1. Read the stage contract in STATE.md and the run order in the School lane's record (PROGRESS 2026-09-12T18:20Z–20:59Z). The fourteen stages, in order, with their artefacts: brief · judge · inventory · journeys · breaker · lessons · draftcheck · assemble · read · tileart · walks · grade · publish · comeback (the exact artefacts are §3's frozen contract).
2. Write `factory.mjs` with: `--stage <name> --week <id> --day <d>`; before running a stage, resolve the previous stage's artefact path for that week and day and, if it is missing, print `STAGE <name> REFUSED — <previous stage> not done: <artefact path> missing` and exit 1; when the artefact exists, compose the worker's brief from the stage's template (the ROLE line, the MACHINE-RULES travel block, the fence, the definition of done, the proof) into the week's build folder, print it, and — with `--dispatch` — run the stage's existing check command (journey-lint, draft-check, the gates, lesson-evidence, walk-verdicts, deploy-live.sh) after the artefact lands.
3. Append one ledger row per stage run to `weekly-plan-drafts/w<N>-build/LEDGER.jsonl` in the frozen shape; `minutes` is measured by the tool from dispatch to artefact; `tokens` comes from the worker transcript's usage when the harness writes one beside the artefact, else from the worker's last `USAGE:` line with `tokens_source: "claimed"`; `rework` is true when the same stage has already written that lesson's artefact.
4. `--selftest`: build a scratch week folder under the machine's temporary area, ask for every stage out of order (thirteen refusals, each naming the right missing artefact), then plant each artefact in turn and run the fourteen stages in order with the check commands stubbed, then read the ledger back (fourteen rows, one rework row when a stage is run twice). Print the proof line.
5. Run the proof, read it, fix what it finds, then hand to the checker.
**DEFINITION OF DONE:** the tool refuses every one of the thirteen out-of-order stage requests by naming the missing artefact, runs the fourteen stages in order on a planted scratch week, and writes one ledger row per stage run.
**PROOF:** `node "$L/weekly-plan-drafts/tools/factory.mjs" --selftest` (with `L` the app folder) → `FACTORY: 14 stages · out-of-order refused 13 of 13 · in-order ran 14 of 14 · ledger rows 14 · PASS` · **FAILS IF:** any refusal count under 13, any stage running with its input missing, or fewer than fourteen ledger rows.
**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:** cold-read this PLAN.md as the spec-breaker first (what would you build wrong?), write the findings into STATE.md's Questions section, then re-run the PROOF yourself, 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/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt`: `SCHOOL-FACTORY STEP 1 closed <date> — the factory tool is on disk; the next school day not yet written runs through it stage by stage.`
### STEP 2 — The lesson address
**FOR NICK:** he taps a link and is inside the lesson, as that child — no hunting through the week screen. · **Tier:** FRONT
**Start when:** none — start now.
**Builder:** DeepSeek via `projects/ops/route-build.mjs` (two anchored edits in the app's boot handler) · **Builder backup:** GLM 5.3 (zai) · **Checker:** Sonnet · **Checker backup:** Qwen.
**Files you may touch:** the boot handler's `params.get('go')` branch in `skippy-school-site/index.html` (one anchored insertion: `go==='lesson'` reads `week` and `day`, calls the app's own `openWeek(kid)` then `openDay(day)` after the week is loaded, and falls to the kid's hub when the week or day is unknown), and `weekly-plan-drafts/tools/school-drive-url.mjs` (the new `--lesson` mode). **Never** any renderer, world, lesson or kit file; never a lesson id or calendar number in code (the address carries the file id).
**Do exactly this:**
1. Snapshot both files with their SHA-256 into the lane's evidence folder before the cheap job (the common brief's rule).
2. Anchored insertion in the boot handler beside `else if(go==='week') openWeek(kid);`: a `go==='lesson'` arm that stores the requested week the way the app already stores a chosen week, calls `openWeek(kid)`, waits for the week's bundle the way `openDay` requires (poll from node in the driver, never inside the page), then `openDay(day)`; unknown week or day → `openHub(kid)`.
3. `school-drive-url.mjs --lesson <base> <week> <day>`: for each child open `<base>/?kid=<k>&go=lesson&week=<week>&day=<day>` in the isolated test browser at 1280×800, wait for the lesson runner, read the title and part count, then open a bad address and confirm the hub; print the proof line.
4. Run the lesson gate and the kid firewall (both must stay green: exit 0, 130 passed) before handing over.
**DEFINITION OF DONE:** both children's addresses open straight into the named lesson in a real browser, a bad address lands on the child's hub, and every existing gate stays green.
**PROOF:** `node "$L/weekly-plan-drafts/tools/school-drive-url.mjs" --lesson http://127.0.0.1:8788 week12 mon` → `LESSON LINK: noah week12 mon opened · willow week12 mon opened · bad link → hub · PASS`; `node "$L/standard/check-lessons.mjs"; echo $?` → `0`; `node "$L/test-kid-firewall.mjs" | tail -1` → `130 passed, 0 failed` · **FAILS IF:** either child lands on the week screen or the hub for a good address, "undefined" appears on screen, or either gate turns red.
**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, once, in the isolated test browser. 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/SCHOOL/PROGRESS.txt`: `SCHOOL-FACTORY STEP 2 closed <date> — every lesson has an address: ?kid=<k>&go=lesson&week=<id>&day=<d>; the come-back carries them from now on.`
### STEP 3 — The come-back: one paragraph, a link per lesson, one cost line
**FOR NICK:** every day's report arrives as links to the pages and what the day cost — nothing to go find. · **Tier:** FRONT
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1) and STEP 2's address shape is on disk.
**Builder:** GLM 5.3 (zai) via `projects/ops/cheap-task.mjs` · **Builder backup:** Qwen · **Checker:** Sonnet · **Checker backup:** DeepSeek.
**Files you may touch:** the `comeback` stage inside `factory.mjs`. **Never** the closing-message lint (`projects/ops/skippy-jobs/lib/check-closing-message.mjs`, the ops lane's), any lesson file.
**Do exactly this:**
1. `--stage comeback --week <id> --day <d> --out <file>` reads the ledger for that day and the publish stage's artefact, and writes: one paragraph of what is now true for the children (the topic, the two lessons each, what a walk found and fixed, in plain words, no path, no jargon); then one line per lesson — `<Child> · <Day> · Lesson <1|2> — <title> — https://skippytutor.pages.dev/?kid=<k>&go=lesson&week=<id>&day=<d>`; then `This day cost: <h> h <m> min wall · <n> M helper tokens measured (<m> M claimed) · <k> pieces built twice · <r> re-walks · <g> grade`; a lesson not yet published gets a line saying which stage it is at instead of a link.
2. Run the closing-message lint on the file; fix until PASS.
**DEFINITION OF DONE:** the come-back for one real day is one paragraph, one link per lesson and one cost line, and passes the closing-message lint.
**PROOF:** `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage comeback --week week12 --day mon --out /tmp/comeback.txt && node projects/ops/skippy-jobs/lib/check-closing-message.mjs /tmp/comeback.txt` → `PASS`, and `command grep -c "https://skippytutor.pages.dev/?kid=" /tmp/comeback.txt` → `4` · **FAILS IF:** the lint fails, a header appears, a path appears, or a lesson line lacks its address.
**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, once, and read the paragraph cold as someone who never saw the thread. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.
### STEP 4 — The inventory lock: kit modes, frame keys and tile-art keys locked before a journey is written
**FOR NICK:** no lesson promises a picture or a tool move that does not exist (the "pictures pulse" and "SHORT or LONG" promises of 2026-09-12). · **Tier:** FRONT
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1).
**Builder:** Qwen via `projects/ops/cheap-task.mjs` · **Builder backup:** DeepSeek · **Checker:** Sonnet · **Checker backup:** GLM 5.3 (zai).
**Files you may touch:** the `inventory` stage inside `factory.mjs`; the new `--inventory` mode in `weekly-plan-drafts/tools/journey-lint.mjs`; `weekly-plan-drafts/tools/fixtures/journeys/` (one fixture naming a made-up mode). **Never** a kit, a frames file, a tile-art file or a journey (the School lane's).
**Do exactly this:**
1. `--stage inventory --week <id>` reads the week's kit file for its mode list (the kit's own `modes` table or the `case` labels of its mode switch, by the kit's own source — never a hand-typed list), the two frames files for their frame keys, and the tile-art file for its tile-text keys, and writes `weekly-plan-drafts/w<N>-build/INVENTORY.json` `{kits:{<kit>:[modes]}, frames:{noah:[keys], willow:[keys]}, tiles:[texts], lockedAt}`; a missing kit or frames file is a refusal naming it, never an empty list.
2. `journey-lint.mjs --inventory`: every VISUAL line's kit mode, every frame key and every picture name in the eight journeys must appear in INVENTORY.json; one FAIL line per name outside it; the last line `INVENTORY: <n> journeys · <k> names outside the lock · PASS|FAIL`.
3. The `journeys` stage in `factory.mjs` runs the lint with `--inventory` as its check; the `lessons` stage passes the mode list and tile texts into the writer's brief verbatim (the 18:47Z rule: only the kit's real modes; tiles never carry picture keys).
4. Fixture: a journey naming `plantkit:seed` mode `dance` → FAIL naming `dance`.
**DEFINITION OF DONE:** every kit mode, frame key and tile text a journey may name is locked in INVENTORY.json before the journeys stage can run, and the lint fails a journey that names anything outside it.
**PROOF:** `node "$L/weekly-plan-drafts/tools/journey-lint.mjs" --week=week12 --days=mon,monB,tue,tueB --inventory` → `INVENTORY: 8 journeys · 0 names outside the lock · PASS`; `node "$L/weekly-plan-drafts/tools/journey-lint.mjs" --selftest` → the existing line plus `INVENTORY FIXTURE: FAIL on dance` · **FAILS IF:** the real journeys pass while the fixture also passes, or the inventory is written from a hand-typed list.
**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, once; open INVENTORY.json and confirm the kit's mode list against the kit file's own source. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.
### STEP 5 — The writer checklist as a machine check on the drafts, and the child-read record taken before tile art, walks and publish
**FOR NICK:** no half-sentence, wrong picture, one-parent prompt or grown-up word reaches the children; the reader reads before anything is drawn or walked, not after a publish. · **Tier:** FRONT
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1).
**Builder:** Qwen via `projects/ops/cheap-task.mjs` · **Builder backup:** GLM 5.3 (zai) · **Checker:** Sonnet · **Checker backup:** DeepSeek.
**Files you may touch:** `weekly-plan-drafts/tools/draft-check.mjs` (new; promotes the pattern of the 2026-09-10 scratch check that measured bytes, parts, words and speech rules on a draft), `weekly-plan-drafts/tools/fixtures/drafts/` (nine fault fixtures and one clean), the `draftcheck` and `read` stages inside `factory.mjs`. **Never** the lesson standard's text, the lesson gate, the speech lint, any draft.
**Do exactly this:**
1. `draft-check.mjs --week <id> --days <d,…> [--facts weekly-plan-drafts/w<N>-build/FACTS.txt]` reads each draft file as the assembler does and checks, per draft: (a) no emoji in tile, item, choice, card-name or group text (the emoji ranges the speech lint already names, applied to those keys); (b) every part title and every heading a complete phrase — it may not end in a comma or in `and`, `the`, `a`, `of`, `to`, `is`, `not`, `one`, `this`; (c) each canon fact in FACTS.txt present verbatim at least once across the week and each of its listed banned variants absent; (d) a reflect or share prompt naming one parent must name both — "Mama or Dada"; (e) the US-spelling list the strict evidence check exports, applied to the draft text; (f) `session` written as `about <n> hours` or `about <n> minutes`; (g) sentence caps per child (Willow average under 12 and never over 20; Noah average under 16 and never over 28) over every child-facing string; (h) every quiz question's fact restated on its own page (the quiz part's own text contains the fact its `explain` names); (i) a `share` part with more than one group carries `itemLabels`. Last line `DRAFT CHECK: <n> faults · … · PASS|FAIL`.
2. `--selftest` over the fixtures: RED on 9 of 9, GREEN on the clean one.
3. In `factory.mjs`: the `draftcheck` stage runs it after `lessons` and before `assemble`; the `read` stage runs right after `assemble` — the read-only reader (Sonnet) reads every child-facing string of the assembled bundle against the four questions and writes `weekly-plan-drafts/proofread/<week>.txt` closed; `tileart`, `walks`, `grade` and `publish` are refused until the record is closed and newer than the bundle. After any later text fix, the `read` stage word-diffs the bundle against its last read and hands the reader only the changed strings; the record is re-closed, never re-dated by the tool.
**DEFINITION OF DONE:** the draft check fails each of the nine writer faults on its fixture and passes the clean one, and the factory refuses tile art, walks, grade and publish until the proof-read record is closed and newer than the bundle.
**PROOF:** `node "$L/weekly-plan-drafts/tools/draft-check.mjs" --selftest` → `DRAFT CHECK: RED on 9 of 9 fault fixtures · GREEN on the clean fixture · PASS`; `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage tileart --week week12 --day mon` with the record touched older than the bundle → `STAGE tileart REFUSED — read not done: …/proofread/week12.txt older than the bundle` · **FAILS IF:** any fixture passes, or tile art runs on an unread bundle.
**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, once; read one fixture and confirm it carries exactly the fault its name claims. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.
### STEP 6 — Walks before publish, on the harness, through the isolated test browser, four at a time, re-walking only what changed
**FOR NICK:** a bug like the Finish button with no size is found before the children could meet it, not the morning after — and a walker never again loses its pictures to a hidden window. · **Tier:** FRONT
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1).
**Builder:** DeepSeek via `projects/ops/cheap-task.mjs` · **Builder backup:** Qwen · **Checker:** Sonnet · **Checker backup:** GLM 5.3 (zai). The roster edit is by hand (agent definitions are control-plane; PROGRESS 2026-09-12T18:20Z) and the agent file regenerated.
**Files you may touch:** the `walks` stage inside `factory.mjs`; the `sup-kid-walker` row of `projects/ops/agents/roster.json` and its generated file `.claude/agents/sup-kid-walker.md`. **Never** the walk-verdict tool's rules, any walk record.
**Do exactly this:**
1. `--stage walks --week <id> --day <d> --base <harness>` composes one walker brief per lesson (both children, both lessons) that names the instrument — the project's isolated test browser through `standard/test-chrome.mjs` and `standard/shot.mjs`, the pattern of `strongroom-journey.mjs` — forbids the shared browser pane by name, names the walk folder and its `walk.txt` as the artefact, and states "write the walk file first, hand back a pointer"; dispatches in waves of four (STEP 12's wave rule); when the four land, runs the walk-verdict tool over the folders; `--dry-run` prints the briefs and artefact paths without dispatching.
2. Re-walk rule: after a fix, `--stage walks --changed` reads the fix's changed part ids from the ledger row and composes briefs for those parts only; a whole re-walk only when the world or kit file changed.
3. Roster: the walker's `tools_because` and its brief template name the isolated test browser as the only instrument; regenerate the agent file; `command grep -c "Browser pane" .claude/agents/sup-kid-walker.md` → 0 and `command grep -c "test-chrome" .claude/agents/sup-kid-walker.md` → at least 1.
**DEFINITION OF DONE:** the walks stage composes four briefs naming the isolated test browser and an artefact path each, dispatches in waves of four, and computes the verdicts from the walk files; the walker's agent file no longer names the shared pane.
**PROOF:** `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage walks --week week12 --day mon --base http://127.0.0.1:8788 --dry-run` → `WALKS: 4 briefs · instrument test-chrome · wave 4 · artefacts named 4 of 4 · PASS`; `command grep -c "Browser pane" .claude/agents/sup-kid-walker.md` → `0` · **FAILS IF:** a brief lacks the instrument or the artefact path, or the wave is larger than four.
**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, once; read one composed brief cold and confirm a walker following it could not open the shared pane. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.
### STEP 7 — The shell drive: Start → every part → Finish → the comprehension check, as each child, both worlds, before any publish; mouse drag on every part type whose words say drag
**FOR NICK:** the Finish button with no size and the seeds that would not drag are caught by a machine before a publish, not by a walker after one. · **Tier:** FRONT
**Start when:** none — start now.
**Builder:** Qwen via `projects/ops/route-build.mjs` (anchored additions) · **Builder backup:** DeepSeek · **Checker:** Sonnet · **Checker backup:** GLM 5.3 (zai).
**Files you may touch:** `weekly-plan-drafts/tools/hook-drive.mjs` and `weekly-plan-drafts/tools/hook-drive-willow.mjs` (the new `--finish` mode each); the control probe inside `weekly-plan-drafts/tools/lesson-evidence.mjs` (mouse drag for every part type whose words say drag, not only `share`). **Never** a world, kit or lesson file; never a threshold in the strict check.
**Do exactly this:**
1. `--finish <week> <day>`: open the lesson as the child, press Start, complete every required part through its own control (the existing per-type drivers), then measure the shell's Finish control's on-screen box (must be at least 44 px tall and wholly visible — the vault walk's rule of 2026-09-11), press it, confirm the comprehension check opens and answer its questions from the screen; print `… Finish <w>×<h> · check opened · <n> of <n> answered · PASS`.
2. The evidence probe: where a part's words say drag, drag with a MOUSE pointer as well as touch on every part type that carries tiles (`slots`, `sort`, `share`, `match`, the kits' pictures) and count a tile that does not land as a `control` fault.
3. `factory.mjs` runs both `--finish` drives inside the `assemble` stage's gate list, so no publish stage is reachable without them.
**DEFINITION OF DONE:** both shell drives reach a sized, visible Finish control and an answered comprehension check on a real lesson for each child, and the strict check counts a tile that will not mouse-drag as a fault on every tile-carrying part type.
**PROOF:** `node "$L/weekly-plan-drafts/tools/hook-drive.mjs" http://127.0.0.1:8788 --finish week12 mon` → `HOOK DRIVE … Finish 127×44 · check opened · 3 of 3 answered · PASS`; the same for `hook-drive-willow.mjs`; `node "$L/weekly-plan-drafts/tools/lesson-evidence.mjs" --selftest` → RED on the fixture whose `slots` tile refuses a mouse drag · **FAILS IF:** Finish measures 0×0 or is not wholly visible, the check does not open, or the mouse-drag fixture passes.
**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, 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/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt`: `SCHOOL-FACTORY STEP 7 closed <date> — the shell drive (Start to Finish to the check) runs as each child before any publish; mouse drag is probed on every tile type.`
### STEP 8 — The look graded once, at the end, on every state
**FOR NICK:** the look is graded once, on everything — including what appears after a tap — after the walks, not three times over the same weakness. · **Tier:** FRONT
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1) and the walks stage of STEP 6 is on disk.
**Builder:** Qwen via `projects/ops/route-build.mjs` · **Builder backup:** DeepSeek · **Checker:** Sonnet · **Checker backup:** GLM 5.3 (zai). The grade itself is the creative director's (invoked by name by the overseer, once per day of lessons).
**Files you may touch:** the new `--states` mode in `weekly-plan-drafts/tools/lesson-evidence.mjs`; the `grade` stage inside `factory.mjs`. **Never** the creative director's gates or agent file.
**Do exactly this:**
1. `--states`: for every part, capture the first state and then each after-tap state the control probe reaches (a sorted tray, a matched pair, an opened bean, a cut stem, a day-5 jar, a cooked leaf, a finished share), at 1280×800 for both children, into the lane's evidence folder with a manifest listing every state captured and every state NOT reached, so nothing is graded blind and nothing unmeasured is silent.
2. The `grade` stage runs only after `walks` closed with all three verdicts held; it composes the creative director's brief with the manifest and the capture folder; her verdict file is the artefact; a HOLD loops the named builder and re-runs `--states` on the changed parts only; a second grade is never composed for the same day.
**DEFINITION OF DONE:** the capture set for a day of lessons includes every after-tap state the probe reaches with a manifest of the unreached ones, and the factory composes exactly one grade per day, after the walks.
**PROOF:** `node "$L/weekly-plan-drafts/tools/lesson-evidence.mjs" --states --weeks=week12 --days=mon --kids=both --base=http://127.0.0.1:8788` → `STATES: 4 lessons · <N> parts · after-tap captures <N> of <N> · unreached listed <k> · PASS`; `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage grade --week week12 --day mon` with the walks artefact missing → `STAGE grade REFUSED — walks not done: …` · **FAILS IF:** a first-state-only capture set, an unlisted unreached state, or a grade composed before the walks.
**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, once; open three after-tap captures and confirm they show the after state. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.
### STEP 9 — The ledger report and the cost line
**FOR NICK:** he sees what a day of lessons cost — minutes, tokens, anything built twice — in one line, every time, against the target. · **Tier:** FRONT
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1) and STEP 3's come-back stage is on disk.
**Builder:** GLM 5.3 (zai) via `projects/ops/cheap-task.mjs` · **Builder backup:** Qwen · **Checker:** Sonnet · **Checker backup:** DeepSeek.
**Files you may touch:** the `--report` mode and the ledger writer inside `factory.mjs`. **Never** a ledger row already written (append-only; a correction is a new row with `corrects: <line>`).
**Do exactly this:**
1. `--report --week <id> [--day <d>]` sums LEDGER.jsonl per lesson, per day and per week: wall minutes (the day's wall is first dispatch to publish exit, not the sum of pieces), tokens measured and tokens claimed in separate columns, pieces built twice (`rework: true` rows), re-walks, grades; prints a table with a `vs target` column (target per day: 180 min · 4 M measured tokens · 0 built twice · 1 grade · re-walks ≤ lessons) and exits 0 within target, 1 over.
2. Every worker brief the tool composes ends with the instruction to write a `USAGE: <tokens>` line into its artefact; the tool reads the worker transcript's usage first when the harness leaves one beside the artefact and marks the row `measured`, else `claimed`.
3. The come-back's cost line is rendered from this report, never typed.
**DEFINITION OF DONE:** the report prints per-lesson, per-day and per-week totals with measured and claimed tokens apart and a target column, and the come-back's cost line comes from it.
**PROOF:** `node "$L/weekly-plan-drafts/tools/factory.mjs" --report --week week12 --day mon` → a table whose header carries `minutes`, `tokens measured`, `tokens claimed`, `built twice`, `re-walks`, `grades`, `vs target`, exit 0 when within target; `node "$L/weekly-plan-drafts/tools/factory.mjs" --report --week week12 --day mon --fixture over-target` → exit 1 with the over columns named · **FAILS IF:** measured and claimed tokens are summed together, or the over-target fixture exits 0.
**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, once; recompute one day's totals by hand from the ledger lines and compare. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** the moment the first factory day closes within target, post one dated line into `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt`: `SCHOOL-FACTORY first day <date> — <week> <day> both children through the factory: <minutes> min · <tokens> tokens · <k> built twice · report in the week's build folder.`
### STEP 10 — A progress entry committed by the tool that writes it
**FOR NICK:** nothing he notices — the record stops losing entries to the automatic snapshot. · **Tier:** POLISH (it bit twice on 2026-09-12, so it runs alongside the FRONT steps under §U's bite clause)
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1).
**Builder:** DeepSeek via `projects/ops/cheap-task.mjs` · **Builder backup:** GLM 5.3 (zai) · **Checker:** Sonnet · **Checker backup:** Qwen.
**Files you may touch:** the `--record` mode inside `factory.mjs`. **Never** the snapshot job, any git configuration, any other lane's record except by appending through this mode.
**Do exactly this:**
1. `--record "<entry>" --into <record path>` appends the dated entry (the clock read by the tool, never typed — PROGRESS 2026-09-12T18:20Z's note), then in the same command `git add <that file>`, `git commit -m "<lane>: progress entry <date>" -- <that file>`, `git push`, then `git log -1 --format=%h -- <that file>` and confirms the entry's first line is in that commit's diff; prints `RECORD: appended · committed <hash> · found by git log · PASS`; a failed push is printed and the entry stays committed locally with a red row — never silently lost.
2. `--dry-run` performs the same on a scratch copy of the record in a scratch repository under the machine's temporary area.
3. Every stage of the factory records its own stopping point through this mode (Rule 20: the cloud copy is the record at every stopping point).
**DEFINITION OF DONE:** an entry written by the tool is committed by pathspec in the same command and found in git before the tool exits.
**PROOF:** `node "$L/weekly-plan-drafts/tools/factory.mjs" --record "factory dry run" --into projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt --dry-run` → `RECORD: appended · committed <hash> · found by git log · PASS` · **FAILS IF:** the entry is on disk but not in the commit, or the commit touches any other file.
**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, once, and read the commit's file list. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.
### STEP 11 — One builder per file at a time, and the handoff to the ROUTER lane
**FOR NICK:** nothing he notices — a good edit stops being reverted because another agent touched the same file. · **Tier:** POLISH (bit on 2026-09-10 and 2026-09-12; runs alongside the FRONT steps)
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1).
**Builder:** GLM 5.3 (zai) via `projects/ops/cheap-task.mjs` · **Builder backup:** DeepSeek · **Checker:** Sonnet · **Checker backup:** Qwen.
**Files you may touch:** the `--lock`, `--unlock` and `--lock-selftest` modes inside `factory.mjs` and its lock table in the week's build folder. **Never** `projects/ops/route-build.mjs` or `projects/ops/cheap-task.mjs` (the ROUTER lane's).
**Do exactly this:**
1. `--lock <file> --holder <name>` grants when no live lock holds the file, refuses naming the holder and the time otherwise; `--unlock <file> --holder <name>` releases; a lock older than 90 minutes is reported stale and released with a red row; every stage that dispatches a builder takes the lock for each file in its fence first and releases at the artefact.
2. `--lock-selftest`: grant, second refused with the holder named, release, re-grant.
3. Post one dated line into `projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PROGRESS.txt`: `SCHOOL-FACTORY STEP 11 <date> — request: scope route-build's clean-tree check to the target file's folder (it reverts a good edit when another lane's autopush changes an unrelated file: SCHOOL PROGRESS 2026-09-12T01:18Z, 2026-09-10T02:34Z), count a 60-second eligibility timeout as a failure row, and accept repo-relative and absolute paths as the same override target (2026-09-12T19:10Z). The factory holds one builder per file on its own side meanwhile.`
**DEFINITION OF DONE:** a second builder asking for a locked file is refused by name, and the ROUTER lane holds the dated request.
**PROOF:** `node "$L/weekly-plan-drafts/tools/factory.mjs" --lock-selftest` → `LOCK: first holder granted · second refused naming the holder · release · re-grant · PASS`; `command grep -c "SCHOOL-FACTORY STEP 11" projects/ops/life-os/REGROUP-2026-09-08/plans/ROUTER/PROGRESS.txt` → `1` · **FAILS IF:** two holders on one file, or the ROUTER record lacks the line.
**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, once. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** the ROUTER line above is the handoff.
### STEP 12 — Waves of four and the retry rule
**FOR NICK:** nothing he notices — a usage-limit death costs one re-dispatch, not a night. · **Tier:** POLISH (bit three times in two days; runs alongside the FRONT steps)
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1).
**Builder:** Qwen via `projects/ops/cheap-task.mjs` · **Builder backup:** GLM 5.3 (zai) · **Checker:** Sonnet · **Checker backup:** DeepSeek.
**Files you may touch:** the wave dispatcher and `--wave-selftest` inside `factory.mjs`. **Never** the agent definitions, the dispatch gate.
**Do exactly this:**
1. Every stage that dispatches more than one worker (journeys, lessons, walks) dispatches at most four at a time; before each dispatch the tool writes the worker's expected artefact path and a window (writer 20 min, reader 15 min, walker 60 min — the measured pieces plus half) into the ledger row; every brief ends "write your artefact to disk first, then hand back its path — a hand-back with no file is a miss".
2. At the window's end a missing artefact is re-dispatched once with the same brief and `retries: 1`; a second miss writes a red row naming the lesson and the stage, and the stage stays open — never a silent gap, never a guess.
3. `--wave-selftest`: nine stub jobs, one of which withholds its artefact on the first run and delivers on the second; a tenth withholds twice.
**DEFINITION OF DONE:** no stage dispatches more than four workers at once, every worker's artefact path is written before dispatch, a missing artefact is re-dispatched once, and a second miss is a red row.
**PROOF:** `node "$L/weekly-plan-drafts/tools/factory.mjs" --wave-selftest` → `WAVE: 9 jobs in waves of 4 · 1 missing artefact re-dispatched once · second miss red · PASS` · **FAILS IF:** a wave of five, a re-dispatch loop past one, or a missing artefact with no red row.
**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, once. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.
### STEP 13 — The environment stage: no scheduled job under Documents; the voice engine's proof-of-run
**FOR NICK:** nothing he notices — the voice cannot die unnoticed for three weeks again, and a scheduled job is never installed where the Mac will refuse to run it. · **Tier:** POLISH (bit on 2026-09-12; runs alongside the FRONT steps)
**Start when:** `projects/personal/learning-app/weekly-plan-drafts/tools/factory.mjs` exists (CREATED BY STEP 1).
**Builder:** DeepSeek via `projects/ops/cheap-task.mjs` · **Builder backup:** Qwen · **Checker:** Sonnet · **Checker backup:** GLM 5.3 (zai).
**Files you may touch:** the `env` stage inside `factory.mjs` and a fixture plist under `weekly-plan-drafts/tools/fixtures/factory/`. **Never** any real plist in the user's LaunchAgents folder, the voice installer, the site's worker.
**Do exactly this:**
1. `--stage env` reads every plist in the user's LaunchAgents folder (and, with `--plist <file>`, a named one) and refuses any whose program, arguments, working directory or log paths resolve under the user's Documents folder, naming the file and the key; then reads the live voice door — `https://skippytutor.pages.dev/say` for a two-word line as each child — and requires a 200 with an `x-voice-engine` header naming an engine, else a red row `VOICE: silent`; prints `ENV: launchd jobs under Documents <n> · voice /say <status> engine <name> · PASS|FAIL`.
2. `factory.mjs` runs the env stage before `publish`; a red env row stops the publish stage with its reason.
3. Fixture: a plist whose StandardOutPath is under Documents → `FAIL` naming it.
**DEFINITION OF DONE:** the env stage refuses a scheduled job under Documents by file and key and reads the live voice engine's proof-of-run before any publish.
**PROOF:** `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage env` → `ENV: launchd jobs under Documents 0 · voice /say 200 engine openai · PASS`; `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage env --plist "$L/weekly-plan-drafts/tools/fixtures/factory/under-documents.plist"` → `FAIL` naming `StandardOutPath` · **FAILS IF:** the fixture passes, or a silent voice door reads as PASS.
**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, once. PASS closes the step. Do not accept the builder's pasted output; do not summon anyone else.
**Handoff (if any):** none.
**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 check existed that could not fail | every new check ships with a fixture it turns RED on (thirteen refusals, nine draft faults, the mouse-drag fixture, the under-Documents plist, the over-target report) before it is trusted | STEPS 1, 5, 7, 9, 13 PROOF lines |
| The builder graded its own work and passed it | every step's checker is a different model in a different session and re-runs the proof; the creative director grades what a builder drew, once | §3b table; STEP 8 |
| Concurrent sessions clobbered each other's work in a shared file | one builder per file through the lock table; a progress entry is committed by pathspec in the same command that writes it | STEPS 10, 11 |
| A finding existed only in the session's output and died with it | every worker brief ends "write your artefact to disk first, then hand back its path"; a missing artefact is a re-dispatch, never a guess | STEP 12; §3 contract |
| Session rules never reached the subagents doing the work | the factory composes every brief from a template that carries the ROLE line, the MACHINE-RULES travel block, the fence, the definition of done and the proof | STEP 1 step 2 |
| A pipeline broke silently and looked identical to a working one | the env stage reads the live voice door's engine header before every publish; a silent engine is a red row that stops the publish | STEP 13 |
| Rules existed but were psychologically dormant at answer-time | the writer checklist and the inventory lock are machine checks that run before assembly, not sentences in a brief | STEPS 4, 5 |
| One instance of a defect class was fixed while its siblings stayed broken | mouse drag is probed on every tile-carrying part type, not the one type the last fix touched | STEP 7 step 2 |
| Done was declared before the live surface was checked | the publish stage is unreachable until walks, grade and the shell drives closed; the come-back carries links only for lessons live on production | STEPS 1, 3, 6, 7 |
| The review didn't cover the shipped artifact | the proof-read record must be newer than the bundle, and after any text fix the reader re-reads the changed strings before tile art, walks and publish can run again | STEP 5 step 3 |
| A number was published without the population it was counted over | the cost line names wall minutes, measured tokens and claimed tokens separately, with the day and the lesson count beside them | STEP 9 |
| Novel: a downstream piece built before its upstream input was locked (seven sequence faults in the postmortem) | the stage order with a gate between each; the inventory lock before journeys; the read before art; the walks before publish; one grade after the walks | STEPS 1, 4, 5, 6, 8 |
| Novel: a worker's picture choice (emoji) standing in for a drawn object | tiles never carry picture keys or emoji (draft check a); tile art is keyed by the tile's own text and locked in the inventory | STEPS 4, 5 |
## 5 · Topology and roles
- **OVERSEER-AUTHORITY:** none named (the superintendent's seat on the School 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.** None of the four arises in this plan; the publish stage sends nothing as Nick and deletes nothing.
- Thread layout: one overseer thread (Fable — the superintendent's session or the Fable session Nick names); builders as cheap dispatches through `projects/ops/cheap-task.mjs` (new files, new modes) and `projects/ops/route-build.mjs` (anchored edits); checkers as Sonnet VERIFIER dispatches that run the proof scripts; the creative director invoked by name once per day of lessons.
- Overseer: Fable · Workers: GLM 5.3 (zai), DeepSeek, Qwen; Opus only under a recorded cheap failure row for that step; Sonnet readers and walkers · Cap: 8 per session, ~40 machine-wide
- State files location: `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL-FACTORY/STATE.md` (Questions and Assumptions as sections inside it; a PLAN-CHANGES.md beside it when a §3 contract changes)
- **Board card id:** none yet
- **Artefact consumers:** the factory tool → the superintendent (every school day from the first one after STEP 1) · INVENTORY.json → the journey lint and the writer briefs · the draft check → the assemble stage's gate · the walk folders → the walk-verdict tool → the grade stage · the ledger → the report → the come-back's cost line → Nick · the lesson address → the come-back → Nick · the ROUTER line → the ROUTER lane's record · this file's STEPS block → the progress screen
- **Write-contention (parallel lanes in a shared checkout):** the app repository autopushes every few minutes and the workspace repository snapshots periodically; every step commits its own file by pathspec the moment its proof passes; `factory.mjs` is one file with modes, so STEPS 3, 4, 5, 6, 8, 9, 10, 11, 12, 13 each add a mode through its own lock (STEP 11's lock table, or the overseer serialising them until it exists — one mode landing at a time); the app's main file is touched by STEP 2 only; checkout proven writable at each pass (`git status` clean of conflict markers).
**Per-stage topology — counts DECLARED at plan time:**
| Stage | Overseer | Sub-overseers | Workers |
|---|---|---|---|
| Framing (STEP 1) | 1 | 0 | 2 |
| Elements (STEPS 2, 3, 4, 5) | 1 | 0 | 4 |
| Tests (STEPS 6, 7, 8) | 1 | 0 | 3 |
| Output (STEP 9) | 1 | 0 | 2 |
| Polish (STEPS 10–13) | 1 | 0 | 4 |
| First factory day (FINISH LINE d) | 1 | 0 | 8 |
**The walk-away contract — a stranger resumes the drive from files alone:**
- **STATE FILE:** `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL-FACTORY/STATE.md`
- **HEARTBEAT ROW:** none — this lane rides the School lane's record (`projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt`) through the dated handoff lines STEPS 1, 2, 7 and 9 post
- **MORNING-REPORT LINE:** "SCHOOL · Lesson Factory — step n of 13 — <what is true> — first factory day: <minutes> · <tokens> · <built twice>"
## 6 · Evals — what "working" means, decided now
| Capability | Check (exact command or procedure) | Pass looks like |
|---|---|---|
| (1) the fixed order with a gate between stages | the factory selftest | `out-of-order refused 13 of 13 · in-order ran 14 of 14` |
| (2) the inventory lock | the journey lint with `--inventory` on the real journeys and on the made-up-mode fixture | `0 names outside the lock · PASS`; the fixture `FAIL on dance` |
| (3) the writer checklist on the drafts | the draft check's selftest | `RED on 9 of 9 fault fixtures · GREEN on the clean fixture` |
| (4) the read before art, walks and publish | ask the tile-art stage with the record older than the bundle | `STAGE tileart REFUSED — read not done` |
| (5) walks before publish through the isolated browser | the walks stage dry run; the walker agent file | `instrument test-chrome · wave 4 · artefacts named 4 of 4`; `Browser pane` count 0 |
| (6) the shell drive to Finish and the check | both hook drives with `--finish` | `Finish <w>×<h> · check opened · <n> of <n> answered · PASS`, height ≥ 44 |
| (7) one grade, after the walks, on every state | the `--states` capture and the grade stage asked before the walks | `after-tap captures N of N`; `STAGE grade REFUSED — walks not done` |
| (8) the lesson address | the URL driver's `--lesson` mode | `noah … opened · willow … opened · bad link → hub · PASS` |
| (9) the come-back shape | the comeback stage then the closing-message lint | lint `PASS`; four address lines |
| (10) the ledger and the report | `--report` on the first factory day; the over-target fixture | a table with measured and claimed apart and a `vs target` column; fixture exit 1 |
| (11) one builder per file | the lock selftest | `second refused naming the holder` |
| (12) waves and the retry rule | the wave selftest | `waves of 4 · re-dispatched once · second miss red` |
| (13) the record committed by the tool | `--record … --dry-run` | `committed <hash> · found by git log` |
| (14) the environment stage | `--stage env` on the machine and on the fixture plist | `launchd jobs under Documents 0 · voice /say 200 engine <name>`; fixture `FAIL` naming the key |
| (15) the target for a school day | the report on the first factory day, both children | `vs target` all within: ≤ 180 min · ≤ 4 M measured tokens · 0 built twice · 1 grade · re-walks ≤ 4 |
## 7 · THE ONE DECISION LIST FOR NICK
- none — every variable in §1a carries his dated words or an opened fact; nothing here spends money, rotates a credential, destroys anything or sends as him.
**Not asked, because you already answered:** the links in the come-back (2026-09-13, "next time you can just bring up the links to the pages") · the order and the goal (2026-09-13 01:40Z, "avoid rework and get it dialed in from the start line") · Fable designs, Opus writes, cheap builds (2026-09-10, 2026-09-11) · both screens 1280×800 landscape (2026-09-12) · the reading bar (2026-09-12) · the punch list as the standard (2026-09-12) · the documentation gate off tonight (2026-09-12, "its already off").
## 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.
## Your loop
Every pass: every step whose START WHEN inputs exist and which is not yet CLOSED is running, up to the cap → each builder runs its own PROOF, hands to its checker → PASS closes it, FAIL loops it → repeat until the FINISH LINE is proven. STEP 1 first, then STEPS 2 and 7 beside it (they need nothing), then everything that needs the tool the moment it is on disk.
## SUMMARY — a few plain-English lines, read by the status generator
**TRUE NOW (re-established 2026-09-14 by a regroup pass that re-ran every proof first-hand; 13 steps, 13 re-run, 22 minutes).** This lane is a written plan and nothing else. The one piece of software the entire plan is built around — a tool that runs a school day's work through in the right order, so nothing is written before the thing it depends on exists — does not exist anywhere. That was confirmed two independent ways: searching the whole file history, and checking both registries that exist precisely to answer "does this already exist somewhere we forgot". Every separate feature that could be checked without it was also confirmed missing: a working link straight into one lesson, a locked list of allowed pictures and tools, a sentence checker, a "did the child finish" checker, and the note this lane owed another lane. **Nothing about how school days actually get built has changed since this plan was written.** The by-hand process from before is still the only thing producing real lessons for the children today, and it is still producing them.
**LEFT.** All thirteen steps. 🔴 CORRECTED BY A COLD READER: an earlier version of this line said six steps wait on the central tool, and listed step 1 among them — but step 1 is what BUILDS that tool, so it cannot wait on itself. Reading each step's own start-when line, TEN steps wait on it: everything except 1, 2 and 7. **Steps 1, 2 and 7 can be picked up right now with no ambiguity.**
**NEXT PICKUP.** Start building the central ordering tool. A full day passed between the plan describing it and this audit, and not one line of it was written. Nothing else in this lane is worth touching first, because six steps are waiting behind it and the other six describe features of it.
**No decision is owed by Nick here.** This lane is behind because nobody picked it up, not because it was blocked on him.
## STEPS
```
1. [TOOL] The factory tool: the fixed order, a gate between stages, a ledger row per piece — 0%
DEFINITION OF DONE: thirteen out-of-order stage requests refused by naming the missing artefact; fourteen stages run in order on a planted scratch week; one ledger row per stage run
PROOF: `node "$L/weekly-plan-drafts/tools/factory.mjs" --selftest` → FACTORY: 14 stages · out-of-order refused 13 of 13 · in-order ran 14 of 14 · ledger rows 14 · PASS
REGROUP 2026-09-14 · UNPROVEN: ARTEFACT GONE — the factory tool was never built. Confirmed absent on disk, absent from all git history, and absent from BOTH ownership registries (the 64-system business/health registry and the family document catalogue), checked specifically to try to prove this verdict wrong. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
2. [UI] The lesson address — 0%
DEFINITION OF DONE: both children's addresses open straight into the named lesson in a real browser; a bad address lands on the hub; every gate green
PROOF: `node "$L/weekly-plan-drafts/tools/school-drive-url.mjs" --lesson http://127.0.0.1:8788 week12 mon` → LESSON LINK: … · PASS
REGROUP 2026-09-14 · FAILED — the lesson-link code path does not exist in the site's boot handler, checked directly. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
3. [UI] The come-back: one paragraph, a link per lesson, one cost line — 0%
DEFINITION OF DONE: the come-back for one real day passes the closing-message lint and carries one address per lesson
PROOF: `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage comeback --week week12 --day mon --out /tmp/comeback.txt && node projects/ops/skippy-jobs/lib/check-closing-message.mjs /tmp/comeback.txt` → PASS
REGROUP 2026-09-14 · UNPROVEN: ARTEFACT GONE — depends entirely on the missing factory tool of step 1. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
4. [TOOL] The inventory lock before a journey is written — 0%
DEFINITION OF DONE: kit modes, frame keys and tile texts locked in INVENTORY.json before the journeys stage; the lint fails a journey naming anything outside it
PROOF: `node "$L/weekly-plan-drafts/tools/journey-lint.mjs" --week=week12 --days=mon,monB,tue,tueB --inventory` → INVENTORY: 8 journeys · 0 names outside the lock · PASS
REGROUP 2026-09-14 · FAILED — the tool this step claims to extend has no such feature at all, checked directly. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
5. [TOOL] The writer checklist on the drafts, and the read before art, walks and publish — 0%
DEFINITION OF DONE: the draft check RED on nine fixtures and GREEN on the clean one; tile art, walks, grade and publish refused until the proof-read record is closed and newer than the bundle
PROOF: `node "$L/weekly-plan-drafts/tools/draft-check.mjs" --selftest` → DRAFT CHECK: RED on 9 of 9 fault fixtures · GREEN on the clean fixture · PASS
REGROUP 2026-09-14 · FAILED — the file this step is supposed to create does not exist. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
6. [TOOL] Walks before publish, through the isolated test browser, four at a time — 0%
DEFINITION OF DONE: four briefs naming the instrument and an artefact path each; waves of four; verdicts from the walk files; the walker's agent file no longer names the shared pane
PROOF: `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage walks --week week12 --day mon --base http://127.0.0.1:8788 --dry-run` → WALKS: 4 briefs · instrument test-chrome · wave 4 · artefacts named 4 of 4 · PASS
REGROUP 2026-09-14 · FAILED — the one part checkable without the missing tool contradicts the claim. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
7. [UI] The shell drive to Finish and the check, both worlds; mouse drag on every tile type — 0%
DEFINITION OF DONE: both drives reach a sized, visible Finish and an answered check on a real lesson; the strict check counts a tile that will not mouse-drag on every tile type
PROOF: `node "$L/weekly-plan-drafts/tools/hook-drive.mjs" http://127.0.0.1:8788 --finish week12 mon` → … Finish 127×44 · check opened · 3 of 3 answered · PASS (and the Willow drive the same)
REGROUP 2026-09-14 · FAILED — the required feature was never added to the existing driver, checked directly. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
8. [UI] The look graded once, at the end, on every state — 0%
DEFINITION OF DONE: the capture set carries every after-tap state the probe reaches with a manifest of the unreached; exactly one grade per day, after the walks
PROOF: `node "$L/weekly-plan-drafts/tools/lesson-evidence.mjs" --states --weeks=week12 --days=mon --kids=both --base=http://127.0.0.1:8788` → STATES: … after-tap captures N of N · PASS
REGROUP 2026-09-14 · FAILED — the required feature is absent, and its prerequisite step is also unbuilt. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
9. [TOOL] The ledger report and the cost line — 0%
DEFINITION OF DONE: per-lesson, per-day, per-week totals with measured and claimed tokens apart and a target column; the come-back's cost line rendered from it
PROOF: `node "$L/weekly-plan-drafts/tools/factory.mjs" --report --week week12 --day mon` → the table, exit 0 within target
REGROUP 2026-09-14 · UNPROVEN: ARTEFACT GONE — depends entirely on the missing factory tool of step 1. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
10. [TOOL] A progress entry committed by the tool that writes it — 0%
DEFINITION OF DONE: the entry is committed by pathspec in the same command and found in git before the tool exits
PROOF: `node "$L/weekly-plan-drafts/tools/factory.mjs" --record "factory dry run" --into projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt --dry-run` → RECORD: appended · committed <hash> · found by git log · PASS
REGROUP 2026-09-14 · UNPROVEN: ARTEFACT GONE — depends entirely on the missing factory tool of step 1. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
11. [TOOL] One builder per file at a time, and the handoff to the ROUTER lane — 0%
DEFINITION OF DONE: a second builder on a locked file is refused by name; the ROUTER lane's record carries the dated request
PROOF: `node "$L/weekly-plan-drafts/tools/factory.mjs" --lock-selftest` → LOCK: … second refused naming the holder … PASS
REGROUP 2026-09-14 · FAILED — the required handoff note is confirmed absent from the other lane's record. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
12. [TOOL] Waves of four and the retry rule — 0%
DEFINITION OF DONE: at most four workers per wave; artefact paths written before dispatch; one re-dispatch for a missing artefact; a second miss is a red row
PROOF: `node "$L/weekly-plan-drafts/tools/factory.mjs" --wave-selftest` → WAVE: 9 jobs in waves of 4 · 1 missing artefact re-dispatched once · second miss red · PASS
REGROUP 2026-09-14 · UNPROVEN: ARTEFACT GONE — depends entirely on the missing factory tool of step 1. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
13. [TOOL] The environment stage: no scheduled job under Documents; the voice engine's proof-of-run — 0%
DEFINITION OF DONE: a scheduled job under Documents refused by file and key; the live voice door's engine read before any publish
PROOF: `node "$L/weekly-plan-drafts/tools/factory.mjs" --stage env` → ENV: launchd jobs under Documents 0 · voice /say 200 engine openai · PASS
REGROUP 2026-09-14 · UNPROVEN: ARTEFACT GONE — depends entirely on the missing factory tool of step 1. Re-run first-hand; evidence in regroup-audit-2026-09-14.txt
```
## NEXT (recorded so nothing is lost; not worked under this plan)
- The Hub's progress screen shows this lane's cost line beside its step count (the PROJECT-MANAGEMENT lane's screen generator).
- A weekly line in the morning report comparing the factory's cost per day across weeks.
- The ROUTER lane's answer to STEP 11's request lands; when it does, the factory's own file lock can retire.
- A walker instrument that captures the child's own device (a Chromebook, when one arrives).
## POSTMORTEM
**The 2026-09-14 regroup — every failure, every confusion, every blocker, with its concrete example.**
- **The single failure that explains all thirteen rows: the plan was written, checked, filed — and then nobody started it.** This is not a build that went wrong. It is a build that never began, while its own tracking sheet sat at a tidy 0% that reads identically to "in progress, early". **Keep:** a plan finished and handed off needs a named session actually holding it that same day, or the handoff did not happen.
- **Twelve of the thirteen steps were written as checks on a tool that did not exist yet.** That is the correct way to write them, but it means a single unbuilt dependency silently invalidates the whole sheet, and no status view shows that shape. **Keep:** a plan whose steps nearly all depend on one unbuilt thing should say so in its own summary, in one line, so a reader sees one blocker rather than thirteen open items.
- **What went well: the audit's own discipline.** Before recording "this tool was never built", the verifier went and checked both ownership registries — the exact places that exist to catch "we already built this and forgot" — and found nothing. A verdict of absence is only worth recording when the search that would have disproved it was actually run.
- **The confusion worth naming:** this lane and the separately-tracked school lane have similar names and adjacent folders, and a reader can easily audit one believing it is the other. They are different bodies of work with different steps.
- **No blocker, and that is the finding.** Nothing external stopped this. No approval was owed, no credential was missing, no vendor refused anything.
**The earlier postmortem this plan was built from** is is `POSTMORTEM.md` beside this file — twenty rework rows, four classes, the measured cost of two days and the target derived from it. This plan's own postmortem is written here as its last step, when the FINISH LINE is proven: what failed, what was confused, what to keep. "Nothing worth extracting" is a good answer.
# STATE — SCHOOL: Lesson Factory · updated 2026-09-13T02:05Z by the Fable planning session that wrote the plan
## Who is driving this (fill before any lane opens — a routing rule pointing at nobody routes to Nick)
- **Overseer session:** the superintendent (Fable) — the School lane's own overseer session, or the Fable session Nick names for this lane; a lane addresses it through a dated line in this file's Questions section and in `projects/ops/life-os/REGROUP-2026-09-08/plans/SCHOOL/PROGRESS.txt`
- **Lanes re-read this file:** at the start of every pass, and before dispatching any step whose "Start when" names the factory tool
- **Unowned-blocker owner:** the superintendent — nothing is carved out of every lane's scope; the routing tools' scope fix is handed to the ROUTER lane by STEP 11 with a named line
- **Checkout last proven writable:** 2026-09-13T02:05Z — this file and PLAN.md written into the shared checkout; `git status` clean of conflict markers on this folder; re-proven each pass
## Where it stands
- 2026-09-13 02:05Z: the lane is OPEN and nothing is built. The postmortem of School 2.0 (2026-09-10 to 2026-09-13) is written beside this file — twenty rework rows in four classes, the measured cost of two days of lessons, and the target for a factory day derived from the measured pieces. The plan is written in the workspace's plan shape and passes its checker. The first step (the factory tool with the fixed stage order and a gate between stages) can start now; the lesson address (STEP 2) and the shell drive (STEP 7) need nothing and can start beside it. The School lane keeps building the plants week's Wednesday–Friday under its own plan; the first school day NOT yet written when STEP 1 lands is the factory's first day.
- The three things Nick will notice first, in order: a link to every lesson page (STEP 2), a come-back made of those links plus one cost line (STEP 3), and lessons that no longer arrive with wrong pictures or half-sentences (STEPS 4 and 5).
## Stage contract (Fable writes this in STEP 1's first hour; the builder starts from it)
The fourteen stages in order, each with its input artefact, its worker, its output artefact and its refusal line. Frozen in PLAN.md §3; the exact file shapes are filled in here before the tool is dispatched.
| # | Stage | Input artefact | Worker | Output artefact |
|---|---|---|---|---|
| 1 | brief | the School lane's topic decision | the superintendent | `w<N>-build/BRIEF.txt` (nine inputs, day plans, FACTS.txt canon) |
| 2 | judge | BRIEF.txt | sup-topic-judge (Sonnet) | the judge's file ending NOTHING LEFT TO CUT |
| 3 | inventory | the week's kit, frames and tile-art files | the tool itself (no model) | `INVENTORY.json` |
| 4 | journeys | INVENTORY.json + the judged brief | the superintendent | eight journey files, lint PASS with `--inventory` |
| 5 | breaker | the eight journeys | spec-breaker (cold) | the findings file, every would-misbuild marked applied |
| 6 | lessons | the corrected journeys + INVENTORY.json | sup-lesson-writer (Opus), waves of four | the draft files |
| 7 | draftcheck | the drafts | the tool itself | `DRAFT CHECK: 0 faults` |
| 8 | assemble | the drafts | the tool itself + the existing gates + the two shell drives | the bundle, manifests; lesson gate 0, firewall 130, speech clean, breaks 0, strict 0, shell drives PASS |
| 9 | read | the assembled bundle | a read-only reader (Sonnet) | `proofread/<week>.txt` closed, newer than the bundle |
| 10 | tileart | INVENTORY.json tile texts | sup-visual-builder (Fable) | the tile-art file, every tile drawn or listed as an answer |
| 11 | walks | the bundle on the harness | sup-kid-walker (Sonnet) ×4 per wave, isolated test browser | one walk folder per lesson, all three verdicts HELD |
| 12 | grade | the walks + the `--states` captures | creative-director (Sienna), once | her file ending PASS |
| 13 | publish | everything above | the tool runs deploy-live.sh | exit 0, strict 0 on production, bytes identical |
| 14 | comeback | the ledger + the live addresses | the tool itself | the message, closing-message lint PASS |
## Lanes
| Lane | Objective (short) | Owner/session | State (building·blocked·review·done) | Blocker (if any) | Integration-ready? |
|---|---|---|---|---|---|
| A · THE LINE | the factory tool: order, gates, lock, waves, ledger, come-back, record, env | the superintendent | STEP 1 done, proven | none | yes — factory.mjs is on disk and tracked |
| B · THE LINK | the lesson address and its driver mode | the superintendent | STEP 2 done, proven | none | yes — ?go=lesson&week=&day= drives green |
| C · THE CHECKS | the draft check, the shell drive, the state captures, the walker's instrument | the superintendent | not started | none | no |
## Questions (surfaced to Nick only at a true hard stop or completion — WHERE I LOOKED is not optional)
| # | Category | Question | WHERE I LOOKED (named searches) | Our recommended answer | Blocking? | Status |
|---|---|---|---|---|---|---|
| — | — | none open | the School lane's record, handoff and round plan; the punch list; the lesson standard; the plan skill; the model matrix | — | no | — |
## Assumptions (verified before use, or marked)
| # | Date | Assumption | Why we chose it | What would prove it wrong | Makes easier | Makes harder | Lane |
|---|---|---|---|---|---|---|---|
| 1 | 2026-09-13 | A worker's token usage is readable from its transcript only when the harness writes one beside the artefact; otherwise the worker's own USAGE line is a claim | the record's per-piece figures were the superintendent's reading, not a tool's | a harness field that exposes usage per dispatch | the ledger ships now | measured and claimed tokens must stay in separate columns | A |
| 2 | 2026-09-13 | The kit's mode list can be read from the kit file's own source (its modes table or mode switch) rather than hand-typed | a hand-typed list is the drift the inventory lock exists to stop | a kit whose modes are not enumerable from its source | one source of truth per kit | the inventory stage must know each kit's shape | A |
| 3 | 2026-09-13 | The first factory day is the first school day not yet written when STEP 1 lands | the School lane is mid-way through the plants week's Wednesday–Friday | the School lane finishing the week before STEP 1 lands — then the water week's Monday is the first day | no lesson is written twice for the sake of a test | the target is measured on a real day, so the day's own faults count against it | A |
## Contract-change log (mirror of PLAN-CHANGES.md heads, newest first)
- none — the §3 contracts stand as written at lane-open
## Done lanes (archived)
- none
## Target hash / anchor map
- DESIGN FIDELITY GATE: N/A — nothing rendered by this plan; the lesson screens are graded once per day by the creative director inside STEP 8
## Folded in 2026-09-13 22:47Z — the Codex audit (Skippy-School-Team-Handoff.docx; Nick: "just guidance for you to catch more and account for it in the system")
Status-only addition: nothing above changes; these become checks the factory runs, in the stage that already owns them.
- Science review BEFORE assembly (the plan's cold read gains a content-reviewer pass): every keyed quiz answer must be supported by the case's own evidence; a reasonable alternative hypothesis is never marked wrong. Found tonight and corrected in the plants week: the no-bees tomato case (tomatoes self-pollinate) and the sealed-jar case ("tired air" was not proved; heat and trapped water are the likelier cause). Wording refinements queued for the next plants pass, not tonight: name carbon dioxide and oxygen on Noah's leaf model; veins as transport bundles (water in, sugar out); pollen source and receiver, never "must reach another flower"; the helicopter drop as a test to repeat, not a promised result; fur-hitchhiker vs eaten-fruit as two animal rides; count the seeds actually found, never promise a fixed count; a processed pantry grain is not a planting seed.
- The answer-key lint (Sienna round 6, 2026-09-13): on a sort, match or order part neither `help` nor `open.say` may enumerate the items with their answers — the rule only. Tonight: 14 help columns and 7 cold opens rewritten by the cheap lane, 29 of 29 first try. The factory's lesson gate should flag "Here are all N" / "so you never have to guess" / an item list that matches the part's own answers.
- Every tool's finish condition = the instruction's last required action, never its first (the bean cleared on the first of four taps; CUT cleared before the dots; the leaf cleared before the light was removed). Fixed tonight where a one-line rule reached it; the rest is the Monday app job.
- Promised things must exist on the screen: fields the app never reads (`open.pic`, `wordsPic`) are inert — the assembler should refuse a draft that sets a field no renderer reads, and a sentence that says "look at the X beside these words" must have an X.
- ONE progress system (Monday's first app job, one reference lesson per child, proved on the tablets, then rolled out): "Finish" means end this session and keeps a Resume; "Lesson complete" only when the required parts are done; "Day complete" only when both lessons are; the bar, the map, the badge and the closing card tell one story; Next means the next step, never a silent jump backward; resume restores work, not ticks; the craft-step position is keyed by child + week + lesson + activity, not by part number.
- Wrong answers get a causal hint, not a colour ("Ice makes it cold. A bean needs warmth to start growing"), on sort, match, share and the closing check alike; the closing check must not advance past a wrong answer without saying why.
- Label drift lint: the calendar week everywhere (week selector said Week 9 while header and hub said Week 12), the browser title is the current lesson title (it still read "Week 1: Space"), no internal day key ever reaches a child ("COMPLETE · FRIB").
- Accessible names on every token, card and drawn control; a tap alternative to every drag; labels legible at tablet size.
- The regression list every publish runs: fresh entry into every lesson (right child, week, title, first step, one obvious next action); end a session at 0, halfway and after the required work and check every completion surface agrees; for each tool, the first action alone must not clear it and the full action must; wrong answers in sort/match/quiz/share/check give a useful hint and a way forward; leave a task partway, reload, return; both Friday lessons in order to the week's end message. Plus, once per week, watch each child do one lesson without adult help and record where they ask "what do I do?".
- What the audit did not cover, so the factory keeps its own: picture quality (Sienna's six gates), sound (RULE 42: muted test browsers; audible quality still untested), and the cost ledger.
## Handoff into the factory — written 2026-09-13 23:51Z by the plants-week superintendent (status-only)
The plants week is LIVE for Monday 14 Sep (publish 6, stamp c82ac115-2330; production strict 0 faults 20/20 and week9 mon–tue 8/8; Sienna round 7 final look in progress, its record goes to plans/SCHOOL/evidence/). The full trail is the SCHOOL lane's PROGRESS.txt (15:05Z–23:50Z) and the cost ledger plans/SCHOOL/evidence/w12-wedfri-ledger.tsv.
- FIRST JOB, Monday: build the generator this plan describes (factory.mjs) — nothing here runs from one brief yet; tonight every step (writers, tile artists, fixers, walkers, Sienna, publish) was briefed and landed by hand, and the hand work is where most of the overseer's tokens went.
- SECOND JOB, Monday: the shared progress/completion overhaul from the Codex audit (one reference lesson per child, proved on the tablets, then rolled across the week) — Nick was offered "force it tonight"; unless he did, it is queued here.
- WHAT NOW WORKS AND SHOULD BE REUSED, NOT REBUILT: the cheap lane lands small exact-string edits first try (52/52 tonight) when the brief carries the whole old field and the whole new field and the proof counts a raw new-only sentence (1) and an old-only sentence (0), pre-checked before the run — the runner is scratch text-trim/make-runner.mjs and belongs in tools/; whole-file swaps never land by the cheap lane (9/9 failed) — the factory needs a "copy this proven file" step; the walk verdicts, the strict evidence run, the frames drive, the tile map walk, the sentence measure, the proof-read record and deploy-live.sh are all real gates that ran green tonight.
- KNOWN, NOT DONE: Noah's 14 lesson pages are long (Codex UX 07 — the current task should own the screen); the science wording refinements listed above; Willow's Mon–Tue columns say "drag" while Thursday's header rules tap-only (four columns — decide once, in the app's own words); the three memory columns that write out every pair (Sienna passed them; Codex would gate them behind a help step); the week selector says Week 9 while header and hub say Week 12; audible sound quality untested (every test browser is muted by rule).
- COST OF THIS WEEK'S WED–FRI, measured: see the ledger — roughly 13 M tokens across ~70 dispatches, ~10 h of overseer time; the cheap-first shape (cheap drafts + Opus edit, cheap cold reads, drawing reuse, scripted walks, one Sienna round) is the trial Nick has been asked to approve for next week's first day.
## 2026-09-14 16:28Z — NICK'S TEMPLATE BATCH, added to his punch list, NOT started
Nick walked the live plants week on his own screen and gave seven template rules, framed as "UI changes that we need to
standardize on the templates before we build anything else" and "make all this stuff universal". They are written in
full, with his verbatim words and the measured counts, as items 18-24 of plans/SCHOOL/evidence/punchlist-nick-2026-09-12.txt
(that file's own standing rule: nothing is acted on until he says the list is complete). In one line each:
18 the two columns of a page end together (92 parts leave 160px+ of dead column; worst 785px; his exception — where the
text needs the length the words column SCROLLS, it never leaves a hole); 19 the header owns its own line and then
both columns start on the same baseline (63 of 124 two-column parts are out of line, every one with the text too
high, worst 550px); 20 every card in a row is one height, top and bottom (28 uneven rows, worst 84px); 21 a
collapsible block opens by default when it already fits beside its neighbour (9 of 32 fit today, 5 more within
50px); 22 the Spanish words box is a two-column table, not a run-on sentence; 23 the break, Story Time and lesson
opening pages get real two-column compositions instead of one narrow stack; 24 photographs for "what does this look
like", drawings for "here is how you do it", never mixed inside one section — with a recommended one-section trial
before anything wider. Also settled there: what Willow's "Want a harder one?" button does, and a template rule it
needs (it currently wipes her progress on that page when she taps it).
These are THE template pass this plan already names as its job ("standardize a small library of task-shaped screens").
They land before the next week is built, in both worlds, and the plants week inherits them at its next publish.
## 2026-09-14 17:31Z — Nick's rulings that bind the next week's build
- WILLOW'S NEXT WEEK IS WRITTEN HARDER. His words: "i do think her stuff needs to be a bit harder next week". This is
content, not layout, and it binds whoever writes her child journeys BEFORE a line is drafted — not a late pass.
- PHOTOGRAPHS ARE ADOPTED per section and per type, in BOTH worlds ("3 universal"): a section that only shows what
things look like goes to photographs; anything showing something happening, or carrying steps, stays drawn; a mixed
section stays drawn. The measured trial and the first sections to convert are in plans/SCHOOL/evidence/punchlist-nick-2026-09-12.txt.
- THE LOST-WORK FIX IS OUT OF THIS PASS AND ALREADY BUILDING ("2 ok"); the counters, the Finish button and the Next
button stay IN this pass so they are built once, on the new templates.
## 2026-09-15 02:15Z — STEPS 1 AND 2 ARE BUILT AND PROVEN (status-only; nothing above is removed)
STEP 1 — the factory tool exists at weekly-plan-drafts/tools/factory.mjs, force-added to git (the tools folder is
covered by a wildcard ignore, so it would otherwise have lived only on this Mac — RULE 20). Its self test: 13 of 13
out-of-order stages refused BY NAME with the missing artefact's real path, 14 of 14 stages ran in order, 14 ledger
rows, a stage re-run over an existing artefact marked rework. Driven against the REAL plants week it refused
"lessons" for Wednesday because no breaker file exists — an honest finding, since those drafts were written without
a breaker pass.
A blind checker (Sonnet, briefed cold, never shown the build) re-ran the proof and found TWO real defects, both now
fixed and re-proven: (1) a week id carrying no digits threw a raw stack trace instead of refusing in the tool's own
voice; (2) the composed brief was going out WITHOUT the standing rules block — the plan's own STEP 1 text requires
it and nothing in the proof commands would have caught its absence. The rules are now read live from
projects/ops/MACHINE-RULES.md at compose time, so a brief written next month carries next month's rules, and a
brief REFUSES to compose if the rulebook cannot be read rather than going out bare.
STEP 2 — index.html gains an openLessonLink arm on the boot handler. Four addresses drive green in a real browser
on the harness: both children straight into the right lesson with the tab naming the lesson, an unknown day to that
child's hub, an unknown week to that child's hub. An unknown week is deliberately never stored: weekMeta and
weekLessons both fall back to week 1, so a typo would have opened the wrong week's lesson silently.
🔴 A CONSTRAINT THE NEXT BUILD MUST KNOW: WILLOW'S APP SHELL IS AT ITS CEILING. The lesson gate caps her shell
(index.html + her stylesheet + her world file) at 900,000 bytes. STEP 2 failed that gate three times on SIZE ALONE,
never on behaviour, and each fix was cutting comment text; it now sits at 899,975 with 25 bytes to spare. Every
further feature in the app's main file now costs another one. This is a decision for Nick before the next week is
built, not during it.
CHEAP LANE, MEASURED TONIGHT ON THESE TWO STEPS: creating a whole new file failed three times (two runs killed from
outside before writing anything — no terminal row, the SIGTERM class; one run and a control probe both wandered the
repo and hit the step budget without writing a byte), consistent with the recorded 9-of-9 whole-file failure rate,
and was applied by hand under a recorded route override. The two anchored edits of STEP 2 failed twice for a REAL
reason the brief did not account for — the byte cap above — not a vendor fault; the lane was right to revert.