The family app's To-Do screen, restyled to the Pearl design system

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

Plan PLAN-PEARL-TODO.md

# PLAN-PEARL-TODO.md — the family app's To-Do screen, rebuilt in Pearl · Terracotta, pixel-measured against the approved drawing

**🔴🔴 THIS IS THE ONLY PLANNING DOCUMENT FOR THIS SUBPROJECT. Do not create a second plan, tracker, summary, or scratch state file for it — extend THIS file or its STATE-PEARL-TODO.md companion, and log a dated delta in PLAN-CHANGES-PEARL-TODO.md. Any status view about this subproject is GENERATED from this plan and its state file; if a view disagrees with this plan, the plan wins.**

**Owner:** the Pearl screens drive (any machine) · **Overseer:** Fable, one thread for the Pearl screens bucket (build work + design QA), shared with the Shopping and Extras plans · **Design authority:** Sienna (`creative-director`), UI only — taste graded ONLY after the fidelity count is zero
**Rule: no step begins until its named entry artefact exists and its predecessor's PROOF has been produced and closed by a checker that is not the builder. A step with an unproven predecessor is a violation, not a shortcut.**

**Authority order:** Nick's dated words in §1a → this plan → `PEARL-DESIGN-SYSTEM.md` (the system) → `specs/FINANCES-PEARL-BUILD-SPEC-2026-09-04.md` (the shared-layer MECHANICS this plan reuses, never its screen content) → `PEARL-SCREEN-PROMPT.md` (the layout bar the drawing was built to; the To-Do drawing is one of the three screens that DEFINED that bar). The shell layer (`css/pearl-shell.css`) is the Shopping plan's artefact and the chrome is the Home lane's; this plan consumes both.

---

- **NORTH STAR:** Nick opens To-Do on his phone or his Mac at family.heroesandsidekicks.io and sees the Pearl · Terracotta screen he approved on 2026-09-04 — the dark "what needs you today" card with its overdue / due-today / this-week trio and the due-today list, then Overdue, Next week, Later and Someday as glass cards that fit one desktop viewport and scroll inside themselves — with every task live from Monday, every check-off, Someday move and comment thread still working, Chantelle's list one tap away, and nothing else in the app changed. His words: "home and calendar are exactly what i wanted" (the same bar this drawing set, 2026-09-04); "next week and beyond are all in scrollable sections so they dont make the page so long"; "nothing business goes in the family app".
- **FINISH LINE** (written now; the bar never rises mid-drive — anything found after these pass goes on the NEXT list):
  1. `family.heroesandsidekicks.io/#todo` — **the DEFAULT address since the app-wide flip; there is no flag to add** — signed in as Nick, renders the Pearl To-Do screen at 375×812 and 1280×662, light, with Nick's and Chantelle's lists reachable from the header's person switch.
  2. To-Do · Nick — mismatched properties: 0 · unmeasured anchors: 0 at 375×812 light · 1280×662 light.
  3. To-Do · Chantelle — mismatched properties: 0 · unmeasured anchors: 0 at 375×812 light · 1280×662 light. (Items 2–3 are STEP 4's check on the published URL, evidence files named in STEP 12.)
  4. Every §2 row is verified on the live surface by the blind checker (STEP 13) — every bucket, every row control (check-off, Someday, thread chip, link), every hero state string verbatim; the Noah's Needs and Business tabs are not reachable under the skin and their code is untouched.
  5. At `?skin=field#todo` — **the old look, which is what “flag off” now means** — the To-Do screen (all four tabs) is byte-for-byte the screen shipped today; AND on the DEFAULT route every other screen is untouched: `#view-todo` computes `display:none` with its `hidden` attribute on `/#home`, `/#finances`, `/#calendar`, `/#shopping` and `/#extras` at 375, 1280 and 1728 (STEP 6's fence plus the other-screens check of standing rule 11, re-run at STEP 12).
  6. The 900–1099px band renders the phone layout with no horizontal scroll at 1024 (STEP 11).
  7. Sienna's taste verdict and the verifier's verdict are recorded against the side-by-side PNGs (STEP 12), and the postmortem is written (STEP 14).
  8. The business audit (STEP 15) is on file, Nick has its triage list, and no task was hidden from the screen to produce it.
- **NEXT list** (found after the finish line, never worked in this drive):
  1. **The drawing owes a This-week frame at REV 11, or an explicit “never drawn” note.** Sienna accepted carding the live “This week” group through `data-pl-live="this-week"` (RULING (m) in the anchor map) as a live surface with NO design side — so it takes no row in either table and sits outside N/M/K. Until REV 11 rules, it can never become an anchor.
  2. **Handoff to the Home lane (the chrome's owner) — the reversible FA-1 rule.** `css/styles.css`'s rule hiding Status, Talk and Dispatch is explicitly reversible, *“until the voice-app work lands”*; when it is deleted three destinations return and the shipped seven-cell `PL_TABS` has no slot for them. Not a defect today, a known future change.
  3. **Handoff to the Home lane — Updates is unreachable, not merely unbadged.** Hiding it removed its only nav link (the hash route still works); it was showing 29 items when measured twice on 2026-09-05.

---

> **STEP 0 — ARM THE LOOP, BEFORE ANYTHING ELSE.** Set a 5-minute loop. Every time it fires, answer these four in order and CORRECT any failure before doing anything else:
> 1. **NORTH STAR** — is what I am doing this minute moving this plan's North Star? If not, drop it and take the highest-value unblocked step that does.
> 2. **FAN-OUT** — is my queue full up to the concurrency cap (§T)? Full capacity means the cap is reached and a queue of ready work sits behind it — NEVER "launch everything at once". Below the cap with ready work → dispatch now. At the cap → queue, don't launch.
> 3. **CHEAP** — are cheap models doing the building? If anything expensive is building, move that work down now.
> 4. **BLOCKED** — for anything I have called blocked: name the three concrete things I tried. If I cannot, it is not blocked — drive through it now.
> Then keep building. The loop never stops until the FINISH LINE is proven.

---

## Already true (the distilled past — facts, not story)

- The Pearl design system is approved and written — evidence: `projects/personal/family-app/PEARL-DESIGN-SYSTEM.md` (header carries Nick's "approved dont ask again", 2026-09-04) and `projects/personal/family-app/pearl-tokens.css`.
- The To-Do drawing exists as generator output on the 2026-09-04 18:50Z live pull and went through four rounds on Nick's feedback the same day (commits `f62b957c5` all four panels · `a289cacfb` kids' panel removed, due-today list given room to grow · `a507e3b72` capacity frame · `72bc0d294` panel named Nick, selector moved onto the header line); its desktop frame measured 660px and it is one of the three screens `PEARL-SCREEN-PROMPT.md` names as the bar — evidence: `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/screens/todo.mjs` (on `origin/main`), `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/gen.mjs`, renders `todo-00…` to `todo-05…` under `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/renders/`.
- The drawing renders three frames: Nick (the base), "a busy day (capacity check)" — a STATE DEMONSTRATION built from Nick's own task text so the enlarged due-today slot can be judged, captioned as such, never live data — and Chantelle. It draws Nick and Chantelle only: the kids' panel was removed on Nick's feedback and the Business panel is gone ("nothing business goes in the family app").
- Nick approved the drawing set for building: "giv em the plans for all except for health we need to keep wokring on those - full /plan plans" (2026-09-04); on 2026-09-05 he confirmed To-Do was "worked on" and this plan is the missing piece — no To-Do build exists on any branch or on the live site (checked two ways 2026-09-05: every Pearl file on every ref, and a signed-in fetch of the live app where a To-Do stylesheet request returns the app's fallback page).
- The shared Pearl layer, the switch and the chrome are live: `projects/personal/family-app/css/pearl.css` (generated), `projects/personal/family-app/js/pearl-nav.js` v3 (reads `?skin=pearl`, sets `data-pl-screen` from the hash, builds the seven-cell bar and four-group rail — the Home lane's STEP 3), `projects/personal/family-app/css/pearl-nav.css` v3, `projects/personal/family-app/css/pearl-home.css`, `projects/personal/family-app/js/pearl-home.js`, `projects/personal/family-app/css/pearl-calendar.css`, `projects/personal/family-app/js/pearl-calendar.js`, `projects/personal/family-app/css/pearl-finances.css`, `projects/personal/family-app/js/pearl-finances.js`, `projects/personal/family-app/js/pearl-watch.js` — evidence: `projects/personal/family-app/index.html` lines 66–74 and 3322–3326 on `origin/main`; the live app serves them (build v481, read signed in 2026-09-05).
- The shell layer (`css/pearl-shell.css`, `tools/pearl-shell-rename.mjs`, `tools/pearl-shell-class-map.json`, source `redesign-mockups/concepts-2026-09-04-round10-screens/pearl-shell-tokens.css`) exists on the Shopping lane's branch `pearl-shopping/drive` (its STEP 5 closed 2026-09-05) and is not yet on `origin/main` — evidence: `git ls-tree -r pearl-shopping/drive --name-only | command grep pearl-shell`.
- The live To-Do screen's hooks are known and frozen (`projects/personal/family-app/index.html` `#view-todo` block; `projects/personal/family-app/js/app.js` `initTodoTabs` line 344, `loadTodos` line 772, `renderTodoPanel` lines 1714–1904, `todoUrgency` line 1625; `projects/personal/family-app/js/pop.js` the To-Do hero lines 250–360; `projects/personal/family-app/js/todo-thread.js`): `#pp-hero-todo` (pop.js `setHero`: eyebrow "Left today" / "Chantelle · today", the open count with "/total" suffix or "Fresh slate ✦" or "All clear — go be with the fam ✦", the `.pp-pill` row, the line "Ready when you are" / "One down — keep it rolling" / "Nice pace — N down before lunch|already" / "Nothing due today — enjoy it"); `.segmented .seg-btn[data-todo]` for personal · chantelle · noah · business with `is-active`/`aria-selected`; panels `#todo-personal`, `#todo-chantelle`, `#todo-noah` (+ `#noahAddForm #noahAddName #noahAddBtn #noahAddError`), `#todo-business`; each panel's `.card.td-card > .td-list` with `.td-skel` rows and `.placeholder-note` "Couldn't reach the list right now — it'll retry automatically."; rendered by `renderTodoPanel`: `.td-bucket` headers (Overdue · Due today · This week · Next week · Later · Someday), `.td-scroll-group`, `.td-item.dc-prio.td-prio[data-id][data-list][data-store-id][data-someday]` with `.chk` ("Mark done"), `.pt.td-link`, `.pw`, `.lv.td-lv` (urgency class from `todoUrgency`), `.td-go`, the `.td-someday` button ("Move to Someday" / "Move back off Someday"), `.td-thread-chip[data-thread-key]` (comment threads via `todo-thread.js`: `/api/comments?item=`, `/api/comments?items=`, `/api/comment-upload`, `/api/files/`), `.td-body`, `.hc-empty`, `.hc-source-note`, `.td-store-note`; feeds `/api/todo-list?list=` and `/api/todo-store-write`; the check-off confirm `Mark "<label>" done? This checks it off on Monday too.` (line 1974) and the Someday failure `Couldn't update Someday.` (line 1961).
- The pull's text flattening, known and handled: the drawing's `hero.left` reads `LEFT TODAY 1/1` / `CHANTELLE · TODAY 1/1` because the 2026-09-04 text pull joined the hero's eyebrow with its pill count and upper-cased it; the live `js/pop.js` (`paintTodoHero`, lines 321–362) writes the eyebrow as `Left today` / `Chantelle · today` and draws the count as a separate `.pp-pill` row. The build renders pop.js's real eyebrow text; the anchor compares the eyebrow's TYPE (the check's string list reads the two eyebrows from pop.js's source), and the pills row follows Sienna's STEP 3 ruling.
- The pull's own contradictions, rendered verbatim by the drawing and to be raised upstream, never reconciled in the build: the header says "0 Nick · 11 Chantelle" while Nick's own panel reports 7 overdue; every panel's `hero.week` is "0" while Nick has a 4-row Next week group; Chantelle's `hero.overdue` is "12" while her Overdue group holds 14 rows; Nick's Someday group holds 67 rows, mostly a handful repeated in a cycle — evidence: `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round3/solstice-app/live-2026-09-04.json` `mapped.todos`, `mapped.todoPanels`.
- Build and deploy path: `node projects/personal/family-app/build-dist.js`; `projects/personal/family-app/sw.js` `const CACHE` (line 215 on `origin/main`, `deck-family-v482`; the number moves with every publish — the re-measure command in §1 is the fact) + ASSETS hand-maintained and gated by `projects/ops/skippy-jobs/_test-family-app-asset-version-parity.mjs`; deploy `node projects/ops/deploy.mjs deck-family` — evidence: `projects/personal/family-app/README.md` lines 26–46, `projects/ops/deploy.mjs` line 60.
- The browser rig is `projects/shared-tooling/browser.mjs` (one machine-wide Chrome lock); the round-10 renders were produced through it by `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/tools/render.mjs`; STEP 4 re-measures its capabilities before relying on them.
- Nick's standing grants cover every test in this plan: agents drive his machine and apps as him, sign in through his identity gate, run their own tests — evidence: `node projects/ops/skippy-jobs/lib/standing-auth.mjs --audit` (read 2026-09-05).
- Task names are personal (a hospital report, a child's blood draw, a supplement reorder): every cheap-lane brief in this plan masks task text (`<task N>`), the check measures row STYLE and never records row text, and the Business panel's rows never leave the machine.

## 0 · Gate Zero receipts (the plan may not exist without these)
- Failure Mode Registry loaded: 2026-09-05, 168 entries per `python3 projects/ops/agents/check_plan.py`'s own count (authoritative over a hand count); all 168 covered in §4.
- Canonical specs loaded: `.claude/skills/plan/SKILL.md` (§N, §L, §G, §S, §T, §W, §Z, §AUTH, §A, §D, §B), `projects/ops/PROMPT-SPEC.md` P1–P7, `projects/ops/agents/CODE-STANDARD.md`, `projects/ops/agents/CREATIVE-QA-STANDARD.md`, `projects/personal/family-app/PEARL-DESIGN-SYSTEM.md` + `projects/personal/family-app/pearl-tokens.css`, `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/PEARL-SCREEN-PROMPT.md`, `projects/personal/family-app/specs/FINANCES-PEARL-BUILD-SPEC-2026-09-04.md` (§4.6, §4.7, §6.1–6.4 mechanics), `projects/personal/family-app/FRONTEND-REBUILD-PLAYBOOK.md`, `projects/ops/walkaway/MODEL-MATRIX.md`, and the three sibling plans `projects/personal/family-app/PLAN-HOME-PEARL.md` (the chrome), `projects/personal/family-app/PLAN-CALENDAR-PEARL.md` (the mirror mechanism), `projects/personal/family-app/PLAN-PEARL-SHOPPING.md` (the shell layer and the check pattern).
- Ownership check: the family app's governing estate is `projects/personal/family-app/BUILD-PLAN-2026-08-17.md` + `projects/personal/family-app/PROJECT.md` (the app), `projects/personal/family-app/specs/FINANCES-PEARL-BUILD-SPEC-2026-09-04.md` + `projects/personal/family-app/specs/FINANCES-PEARL-BUILD-HANDOFF-2026-09-04.md` (the first Pearl screen and the shared layer), `projects/personal/family-app/PLAN-HOME-PEARL.md` (the chrome), `projects/personal/family-app/PLAN-PEARL-SHOPPING.md` (the shell layer). Searched two ways 2026-09-05 (`command grep -rl "To-Do\|todo" projects --include='PLAN*.md'` and a suffix glob over every worktree's `css/` for `*todo*pearl*`): no plan and no build for a Pearl To-Do exists; this plan is that subproject's own plan. Feeds and systems: the to-do lists, the Monday board, the comment threads and the Someday store already exist and are owned by their feeds; nothing new is created there.
- Expected inputs confirmed to exist: all opened or globbed on disk or on `origin/main` 2026-09-05 — `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/gen.mjs`, `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/screens/todo.mjs`, the six To-Do renders under `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/renders/`, the live pull `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round3/solstice-app/live-2026-09-04.json`, `projects/personal/family-app/index.html`, `projects/personal/family-app/sw.js`, `projects/personal/family-app/contract-check.js`, `projects/personal/family-app/js/app.js`, `projects/personal/family-app/js/pop.js`, `projects/personal/family-app/js/todo-thread.js`, `projects/personal/family-app/css/todo-command.css`, `projects/personal/family-app/css/todo-thread.css`, `projects/personal/family-app/css/pearl.css`, `projects/personal/family-app/js/pearl-nav.js`, `projects/personal/family-app/build-dist.js`, `projects/ops/deploy.mjs`, `projects/ops/skippy-jobs/_test-family-app-asset-version-parity.mjs`, `projects/shared-tooling/browser.mjs`, `projects/personal/skippy-app/design-directions/_pearl-fidelity-check.mjs` (the reference check), `projects/personal/family-vault/vault.py` (the gate password is read at run time by the rig and never written anywhere).
- Model matrix: executors named from `projects/ops/walkaway/MODEL-MATRIX.md` vocabulary — glm (zai) builds · deepseek does mechanical extraction · sonnet checks and authors tests · fable oversees and design-QAs (Nick, 2026-09-05: "pull back to opus and sonnet where reasonable for build but focus the UI on fable" — builders may run on Opus/Sonnet when the cheap vendors are down, as the Shopping lane did; Fable only for design QA).
- PLAN AUTHOR: the Pearl screens design session (Fable), 2026-09-05, on Nick's direct dispatch ("what else is left?" → the To-Do build plan).
- COLD READER: spec-breaker (a different session, briefed only with this plan's path), 2026-09-05 — verdict NOT READY with eight disputes, all eight applied the same morning (screenshots carry task text → every PNG lives only in a gitignored `evidence/local/` folder created at STEP 1 and cited by name and hash; the drawing's `LEFT TODAY 1/1` eyebrow is the pull's flattening — the build renders pop.js's `Left today` and the two eyebrows join the state list; an INTEGRATION standing rule: rebase onto `origin/main` before STEP 6 and before STEP 12, deploy only from an up-to-date branch; STEP 8 names the app's inline `style="flex:1;"` and beats it by property; STEP 1's placeholder grep made precise with its allowed tokens; STEP 9's split rule gets a six-row floor and both branches are forced with a replayed real payload; "keep the fade" needs its own fresh yes; STEP 14's proof reads the closing artefacts' CONTENT, not their presence). Recorded in `PLAN-CHANGES-PEARL-TODO.md` line 1.
- PROMPT-SPEC scan (P1–P7): P1 "to do built mostly" — measured: the DESIGN was worked on (four rounds), the BUILD does not exist; this plan is the build; P3 the test site does not exist (measured 2026-09-04) so the build lands on production behind `?skin=pearl`; P4 "all" = Shopping, Extras and now To-Do, stated in §1; P6 "wokring" read as working; P7 "hide updates for now" scopes the Updates screen (already absent from the Pearl nav; hidden, never deleted) and touches this plan only in that the To-Do badge on the nav is the chrome lane's. Security work: none in this plan (§S) — the security pass is its own end phase.
- Hook notes for builders (house facts, not the tools' text): the Bash routing hook refuses `cd` + a relative write target and `$VAR` write targets — use absolute-path script files; the cheap lane refuses briefs that list folders, point at `.md` spec files, or contain the word that trips its secret filter — briefs name files by repo-relative path only.

## 1 · Goal and definition of done
> The drawing is CANONICAL: `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/todo.html` regenerated from `screens/todo.mjs` at the revision STEP 1 locks — three frames (Nick, the capacity demonstration, Chantelle), each at 375 and 1280. This plan points at it and never restates it in different words.

- **What we're building, one paragraph.** The live To-Do screen (`#view-todo`) gets a Pearl · Terracotta skin under `body.skin-pearl`: a one-line header (eyebrow "To-Do · <date>" · serif "<N> due" · the app's own split line · the person switch as chips: Nick, Chantelle; the Noah's Needs and Business chips hidden under the skin), then the one dark card — "what needs you today": the overdue / due-today / this-week trio (overdue in Dusty rose when non-zero, due-today in the accent), the due-today rows, or the app's own "Ready when you are" line and next-up when nothing is due — then the app's buckets as glass cards: Overdue (never scrolls), Next week, Later, Someday (scroll inside their cards on desktop, capped on phone). Every row keeps its check box, its link, its Someday button, its thread chip and its urgency mark; every hook, handler, fetch and string of today's screen is preserved; the Field screen (all four tabs) stays byte-identical with the flag off.
- **HOW IT'S USED:** Nick (Chantelle equal) opens To-Do to see what is overdue and due today, checks things off, pushes things to Someday, opens a task's comment thread, and switches to Chantelle's list. · HOW WE KNOW: the live screen's own comments (`app.js` lines 1714–1760: the six due buckets, the Someday rule "does NOT hide, archive or delete", the check-off confirm) and Nick's rulings quoted there ("nothing should ever be overdue", 2026-07-03).
- **WHAT IT LOOKS LIKE:** exactly the locked drawing — `todo.html`, Nick and Chantelle, phone 375 and desktop 1280, light; accent Terracotta `#BF5E3B` (soft `#F5E0D5`, text-size accent `#AD5535`) in at most five places, from the drawing's own census: the active nav item · the DUE TODAY trio numeral · a checked check box · one corner of the wash · (the drawing's fifth, the nav badges, is the chrome lane's and is INK per the bar's rule — so four painted places on this screen). Overdue carries the warning colour Dusty rose `#B8657A`, never the accent. · HOW WE KNOW: Nick's approval words above, on the lookbook and the four feedback rounds.
- **WHERE IT LIVES:** `https://family.heroesandsidekicks.io/#todo` (production, Cloudflare Pages project `deck-family`), opened by Nick. **The app-wide flip HAPPENED (Finances STEP 20, Nick 2026-09-05), so Pearl is the DEFAULT look: `?skin=pearl` is a no-op and `?skin=field#todo` is the OLD look** — the old look is the screen this plan must leave byte-identical, and every other screen on the default route is the surface standing rule 11 protects. Everywhere below that says `/?skin=pearl#todo` read `/#todo`, and everywhere it says “flag off” read `?skin=field`. · HOW WE KNOW: `projects/ops/deploy.mjs` line 60; Cloudflare has no test project (measured 2026-09-04); the flip is the 2026-09-06 contract delta in `PLAN-CHANGES-PEARL-TODO.md`.
- **WHAT IT MUST DO:** (1) render the Pearl To-Do header, dark card and bucket cards from the live lists with zero invented strings, contradictions included; (2) keep every control working through the app's existing handlers — person switch, check-off with its confirm, Someday move and move-back, the thread chip, the task link, the Noah add form (hidden under the skin, untouched with the flag off); (3) show every hero state, loading, failed, empty and error string verbatim; (4) fit one viewport at 1280×662 with columns ending at the menu's bottom and the past-this-week buckets scrolling inside cards; (5) render the phone layout at 375 and in the 900–1099 band; (6) hide the Noah's Needs and Business chips and panels under the skin without touching their code; (7) leave the flag-off screen and every other screen unchanged; (8) measure zero mismatches against the locked target for Nick and for Chantelle; (9) **show personal to-dos only — no business work on this screen** (Nick, 2026-09-05, attached to his approval). Each is an eval in §6. · HOW WE KNOW: §2's rows and STEP 4's check.
- **NOT in scope:** the app-wide flip to Pearl by default (the Finances lane's STEP 20 and Nick's Decision 1) · the nav chrome and its To-Do badge (the Home lane's `js/pearl-nav.js` + `css/pearl-nav.css`; consumed and handed off, STEP 11) · any change to the to-do feeds, the Monday board, the Someday store, the comment threads, `js/app.js`, `js/pop.js` or `js/todo-thread.js` (edited zero times) · removing or altering the Noah's Needs or Business code, panels or feeds (hidden under the skin only) · reconciling the pull's contradictions (raised upstream as findings, rendered as pulled) · **deciding for Nick which of his own tasks are business** — STEP 15 measures and hands him the list; the screen never silently hides a task it guessed about, because a hidden real task is worse than a misfiled one · the Health screens (in concept work) · dark mode (Nick: "3 skip it") · security work of any kind (§S) · Shopping and Extras (their own plans).
- **Trip-over protocol:** a builder that finds a defect outside the fence (a chrome mismatch, a Monday data oddity, a thread bug, a Finances-layer bug) writes ONE dated line to `STATE-PEARL-TODO.md` under "Handoffs" naming the owner, then returns to its step — never investigates, never fixes.

Any inherited fact above carries its re-measure: hooks → `command grep -n '<hook>' projects/personal/family-app/index.html projects/personal/family-app/js/app.js projects/personal/family-app/js/pop.js`; the cache version → `command grep -n 'const CACHE' projects/personal/family-app/sw.js`; the deploy project → `command grep -n '"deck-family"' projects/ops/deploy.mjs`; the shell layer's location → `git ls-tree -r origin/main --name-only | command grep pearl-shell`.

## 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**: the live family app's To-Do screen at family.heroesandsidekicks.io, behind `?skin=pearl`, opened by Nick (Chantelle equal) | Production behind the switch | The retired test site (does not exist on Cloudflare, measured 2026-09-04); a standalone mockup | V1 | Nick's own dated words | Building for a screen nobody opens | Nick, 2026-09-04, "ok test is good if it still exists" (it does not, so production behind the switch) and "we redid the app today it has fewer buttons" |
| 2 | The look — Pearl with Terracotta as this screen's one accent, light only; overdue in Dusty rose | Pearl · Terracotta, per the locked drawing | Field (retired); a second accent; dark mode | V1 | His verbatim rulings | The wrong screen built pixel-perfectly | Nick, 2026-09-04, "1 approved dont ask again" (Pearl) · "3 skip it" (dark) · "skip lagoon lavendar iris mint eucalyptus apricot - use the rest" (Terracotta stays) |
| 3 | The drawing that is the target — the round-10 To-Do pages after his four feedback rounds (Nick + Chantelle, the capacity demonstration) | `todo.html` at STEP 1's locked revision | Any earlier round; redrawing during the build | V1 | His approval words and the four dated rounds on the same day | Measuring the build against the wrong page | Nick, 2026-09-04, "giv em the plans for all except for health … full /plan plans"; 2026-09-05, "im pretty sure the to do screen was worked on" |
| 4 | Which lists the Pearl screen shows: Nick and Chantelle only; Noah's Needs and Business hidden under the skin, their code untouched | As drawn (two chips) | Four chips as the Field screen has; deleting the Noah or Business code | V1 | The drawing's own header ("the Business panel is gone", "nothing business goes in the family app") and its round-2 commit ("kids' panel removed") on his feedback | A business list in the family app, or a deleted feature | DEFAULTED to the drawing, 2026-09-05; sheet row — Nick may override (e.g. bring Noah's Needs back as a third chip) |
| 5 | The soft fade at the bottom of every scrolling card, which the drawing carries — today (2026-09-05) he called the same effect on the Health screens "ghosted overlays" | Fade REMOVED before the target is locked (STEP 1 republishes REV n+1 with the `.fade` mask gone; everything else unchanged) | Keep the fade as drawn | V1 | His words this morning about the same device on Health | Locking a target he now dislikes, or changing an approved drawing without asking | DEFAULTED to removing it, 2026-09-05; sheet row — Nick may keep the fade |
| 6 | Fonts, exactly as the system says, coded into the build | Body `"Helvetica Neue",Helvetica,Inter,Arial,system-ui,sans-serif`; headlines and numerals `"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif`; no web font | Any substitute face | V1 | His words | Every size and weight renders off, and the fidelity check fails on fontFamily | Nick, 2026-09-04, "keep them as is then - just make sure all plans dictate striclty the fonts to be coded into the apps with each build" |
| 7 | What "done" means for this screen — great, not perfect: the finish line above, then stop | The seven FINISH LINE items | Endless polish rounds; a second adversary after a PASS | V1 | Plan skill §G in his words | A screen that never ships, or ships wrong | Nick, 2026-09-04, "we need our definition of done to be great, not perfect" |

- V1 confirmation reads `<name>, <date>, "<their own words>"`; the DEFAULTED rows (4, 5) carry their default's author and date and appear on the sheet — work downstream of them is a swap, never a rebuild.
- V2 confirmation reads `opened <what>, <date>, saw: <what was actually there>`.

**Considered and ruled NOT critical** *(the denominator — never demote a variable silently)*:
- `Which cheap vendor builds` — the model matrix decides; the checker is what matters.
- `The exact cache version number` — derived (`before + 1`) at STEP 6, never chosen.
- `The 900–1099 band's layout` — settled by the Finances spec STEP 17 (phone layout); reused here.
- `The capacity demonstration frame` — a state inventory item, not a build target: the build reaches the "many due today" state on the live surface when the data has it, or records NOT MEASURABLE; nothing is mocked (V2, opened `screens/todo.mjs` `busyHero` 2026-09-05, saw its own "STATE DEMONSTRATION" caption).
- `Whether Chantelle's overdue list splits across two desktop columns` — yes, as drawn (1–7 / 8–14 with honest range labels, her hero's "12" never recomputed); STEP 9 states it as the rule for any panel with ≤1 other group (V2, opened `screens/todo.mjs` lines 62–75).

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

- **SINGLE SUBPROJECT:** this plan IS the subproject (the Pearl redesign's To-Do screen); Nick's and Chantelle's lists are one screen behind one hash with one person switch and one composition sheet, and neither ships alone. Its siblings each have their own plan and owner; the shell layer is Shopping's, the chrome is Home's.

## 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 |
|---|---|---|---|---|---|
| T1 | `/?skin=pearl#todo`, phone 375 | default | Header: eyebrow "To-Do · <date>", serif "<N> due" (from `mapped.todos[0]`), the split line "0 Nick · 11 Chantelle" (from `mapped.todos[1]`, rendered as pulled even though Nick's panel says 7 overdue — raised upstream), person chips Nick · Chantelle (`.segmented .seg-btn[data-todo]` restyled; noah and business hidden under the skin) | one line on desktop; active chip ink on white; tapping activates the panel (`app.js` line 344) | any tab → To-Do |
| T2 | same, Nick | default | The dark card (`#pp-hero-todo` values + the panel's Due today rows): eyebrow as pop.js writes it (`Left today`; the drawing's `LEFT TODAY 1/1` is the pull's flattening of eyebrow + pill count — Already true), the trio (Overdue 7 in Dusty rose · Due today 1 in the accent · This week 0), the due-today rows with check boxes | one dark card; the pop.js nodes stay in the DOM, hidden, never removed | — |
| T3 | same | empty | Nothing due today: the dark card shows the app's own line ("Ready when you are ✦" or "Nothing due today — enjoy it") and the next-up row; the hero numeral reads "Fresh slate ✦" (pop.js line 82) | verbatim strings; no invented placeholder | — |
| T4 | same | default | All due-today done: pop.js `clear` → "All clear — go be with the fam ✦" and the celebrate call | verbatim; the celebration is the app's own | — |
| T5 | same | default | Pace lines: "One down — keep it rolling" / "Nice pace — N down before lunch|already" | verbatim, by the app's own counts | — |
| T6 | same | default | Overdue card (`.td-bucket` Overdue → the rows with `.td-item.over`, dates in Dusty rose) | never scrolls; every row keeps `data-id`, `.chk`, `.pt.td-link`, `.pw`, `.td-someday`, `.td-thread-chip` | — |
| T7 | same | default | Next week · Later · Someday cards | scroll inside on desktop, capped on phone (the cap not a multiple of the row height); Someday rows carry `data-someday` and the "Move back off Someday" button | — |
| T8 | same | default | Check box `.chk` on any row | `Mark "<label>" done? This checks it off on Monday too.` confirm, then the row leaves; failure "Couldn't update …" as the app writes it | — |
| T9 | same | default | Someday button `.td-someday` | moves the row to the Someday card via `/api/todo-store-write`; failure `Couldn't update Someday.` verbatim | — |
| T10 | same | default | Thread chip `.td-thread-chip[data-thread-key]` | opens the comment thread (`todo-thread.js`), count from `/api/comments?items=` | row → thread |
| T11 | same | default | Task link `.pt.td-link` / `.td-go` | opens the task's target in a new tab where the app has one | row → target |
| T12 | same | loading | `.td-skel` rows before data | skeleton rows restyled | — |
| T13 | same | error | `.placeholder-note` "Couldn't reach the list right now — it'll retry automatically." and `.td-store-note` / `.hc-source-note` when the store answers with a notice | verbatim | — |
| T14 | same | empty | `.hc-empty` when a bucket or a list is empty | verbatim | — |
| T15 | same, Chantelle | default | Her dark card (eyebrow as pop.js writes it, `Chantelle · today`; overdue 12, today 1, week 0), Overdue 14 rows, Someday 2 rows; the "12 vs 14" contradiction rendered as pulled | same components, her rows | — |
| T16 | same | default | Phone bottom bar (chrome lane) | seven destinations, To-Do active in Terracotta on soft tint, badge ink | To-Do → any |
| T17 | `/?skin=pearl#todo`, desktop 1280×662, Nick | default | Three columns: dark card + Next week (absorbs) · Overdue (absorbs) · Later + Someday (absorbs) | frame fits 662; menu 40→622; every column ends at 622; the dark card never absorbs | — |
| T18 | same, Chantelle | default | Three columns: dark card (grows — the day list reads as a free day) + Someday · Overdue 1–7 · Overdue 8–14, labels "1–7 of 14" / "8–14 of 14", her hero's "12" untouched | columns end at 622 | — |
| T19 | same, any day | default | The column rule of STEP 9 on different data (more due today, fewer overdue) | the same components re-place themselves; the frame stays 662 | — |
| T20 | same | default | Desktop rail (chrome lane) | seven destinations, To-Do active, badge ink, no pill | — |
| T21 | `/?skin=pearl#todo`, 1024×768 | default | The band `desktop.css` does not author | phone layout, `.pl-rail` absent, `scrollWidth <= clientWidth` | — |
| T22 | `/#todo` (no flag), all four tabs incl. Noah's Needs and Business | default | The screen shipped today | byte-identical DOM and computed styles to the pre-build capture; the Noah add form and the Business list present and working | — |
| T23 | `/?skin=pearl#todo` | default | The Noah's Needs and Business tabs under the skin | chips not rendered, panels unreachable; `app.js`, `pop.js`, `todo-thread.js` shas unchanged | — |
| T25 | `/?skin=pearl#todo`, either person | default | Business work on a personal list | zero rows on the screen name a client, a company tool, company money or a staff/payroll action — measured by STEP 15's audit against the business record, not by a guess at the wording | — |
| T26 | same | default | A task that IS business and is still on the personal board | it renders exactly as pulled (never hidden), AND it appears in STEP 15's triage list for Nick — the screen tells the truth about the board while the board is being fixed | — |
| T24 | `/?skin=pearl#todo` as Chantelle | default | Same screen signed in as the second identity; her list active by the app's own rule | identical layout | — |

## DESIGN FIDELITY GATE (plan skill §D)
- **LOCKED TARGET:** generator `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/gen.mjs` + `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/screens/todo.mjs` → output `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/todo.html` (the single output, six frames; redlines go into the generator and it is republished) · published login-free address: `https://skippy-designs.pages.dev/family-pearl-todo-r10.html` — **REPUBLISHED 2026-09-06 03:55Z AT REV 10.9** (sha256 07159f940baf2dba…, served after the redirect with the same sha; Sienna's redline — the shared chrome's bottom gradient on absorbing/capped cards suppressed inside the To-Do page's own CSS, which is what §1a row 5 and Nick's "ghosted overlays" words already required; the earlier 10.7 page claimed no fade but the gradient device was `.g.absorb::after`, not a `.fade` mask. Within the approval below — no re-ask.) Earlier: **PUBLISHED AND VERIFIED 2026-09-05**: HTTP 200, and the served bytes are sha256 `19643b51a97af39c3575e6916c572d30081642969cc6eb58a673ab3260af910b`, identical to the generator's own output (checked after the redirect). `mask-image` count in the served page: 0 — the fade is gone. **APPROVED by Nick, 2026-09-05: "approved just make sure its clear only personal todos end up on this list not business stuff - get is planned"** — the approval is of the REDRAWN round-11 page (the cut-off fixed, the type on Home's scale, the completion bar / week strip / days-late / rose-edged Overdue), and it carries the business-exclusion condition now written into §1 capability 9, §2 rows T25–T26 and STEP 15 (applies §1a row 5's default — the `.fade` mask removed — as a REV bump on the generator, regenerates, publishes the byte-identical copy through `node projects/ops/deploy.mjs skippy-designs`, records REV + commit hash here) · Nick's approving words, naming the drawing: "giv em the plans for all except for health … full /plan plans" (2026-09-04, on the lookbook) and the four same-day feedback rounds it closed on — STEP 1 ends with his approval of the PUBLISHED page at its named revision (the fade removed), the one sanctioned wait in this plan; every non-visual step proceeds meanwhile · approved revision: REV 10.6 · commit 6d0748d6d (the drawing) + 65a9d0461 (the published copy), branch pearl/todo, both on origin — recorded by STEP 1 2026-09-05; Nick's dated words go here when he answers.
- **ANCHOR MAP:** `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/gen.mjs-anchors-todo.md` — **CREATED BY STEP 3**, two tables (Nick, Chantelle), signed by Sienna's design QA with each frame's distinct-element count beside its anchor count.
- **FIDELITY CHECK:** `projects/personal/family-app/tools/pearl-fidelity-todo.mjs`, adapted from `projects/personal/skippy-app/design-directions/_pearl-fidelity-check.mjs` (and structurally from the Shopping plan's check), one MAP per person, carrying the retired-colour sweep (Field palette `#c04040 #AE6B50 #7D6E5E #65704F #55697E #40706F #fbbf24 #d99a3a` and the dropped accent Mint `#2FBF8F`; the anchor map's `RETIRED:` line carries this list and grows if another dropped accent's hex appears) and the plain-app fence check (a DOM count of every other view and of `#view-todo` with the flag off — all four tabs — before and after, same route, same viewport) — **CREATED BY STEP 4**.
- **VIEWPORTS AND THEMES:** 375×812 light · 1280×662 light, for Nick and for Chantelle — equal to the locked target's own list (the drawing draws each at phone 375 and desktop 1280 at its ~660px frame; light only, dark skipped by Nick) — signed by Sienna's design QA at STEP 3 alongside the anchor map.
- **RULE:** "No screen closes above `mismatched properties: 0 · unmeasured anchors: 0` at every viewport × theme, or with an unsigned GAP. More than two GAPs on a screen is a FAIL."
- **REDLINE RULE:** STEPS 3–11 may run against the published-but-not-yet-approved page (the map and the check address elements by selector, so a redline changes expected VALUES, not the build's hooks). If Nick redlines, STEP 1 republishes REV n+1, the §D revision line moves, Sienna re-signs only the anchors whose design changed, and STEPS 7–11 re-run their injected check against the new page — one check round, never a rebuild. STEP 12 opens only on the APPROVED revision.
- **GAPS:** none yet — the candidates the anchor map must decide: the capacity-demonstration frame (a state, not a target: its anchors are the same components as Nick's frame with more rows, so no GAP; the state is reached live or recorded NOT MEASURABLE); the dark card's `min-height` floor on desktop (the drawing's own rule from Sienna — measured, not a GAP); the pop.js pills row (the drawing does not draw the pills — Sienna decides whether they are hidden by the neutraliser or drawn; if hidden, no anchor, no GAP).

## 3 · Lanes and frozen contracts

**File fences are drawn so no two lanes need the same file in the same hour (skill §W). Scoped commits only (`git commit -m "..." -- <paths>`); never `git stash`; re-read a file immediately before writing it; the family app sits inside the outer repo — every step's first action is `git -C "/Users/nickdeck/Documents/Claude 2.0" rev-parse --show-toplevel` and the printed path is recorded. The Shopping, Extras and To-Do lanes all edit `index.html`, `sw.js` and `contract-check.js` once each (their STEP 6): the ONE overseer thread opens those steps for one lane at a time, and each lane writes a `HOLDING:` line into its state file before its first edit and clears it after the scoped commit.**

| Lane | Scope (in / out) | Owner | Definition of done | Model (explicit) |
|---|---|---|---|---|
| TODO | In: NEW `css/pearl-todo.css`, NEW `js/pearl-todo.js`, NEW `tools/pearl-fidelity-todo.mjs`, NEW `gen.mjs-anchors-todo.md`, NEW `ground-truth-todo.json`, NEW `tools/pearl-accent-sites-todo.json`, evidence under `redesign-mockups/concepts-2026-09-04-round10-screens/evidence/` named `todo-*`; ONE edit each (STEP 6) to `index.html`, `sw.js`, `contract-check.js`; the shell layer files ONLY if STEP 5 finds them absent on `origin/main` and on the Shopping branch (then exactly the Shopping plan's STEP 5 instructions, with a handoff posted there). Out: `js/app.js`, `js/pop.js`, `js/todo-thread.js`, `css/todo-command.css`, `css/todo-thread.css`, `css/styles.css`, every `pearl-*` file another lane owns (`css/pearl.css`, `tools/pearl-rename.mjs`, `css/pearl-nav.css`, `js/pearl-nav.js`, `css/pearl-home.css`, `js/pearl-home.js`, `css/pearl-calendar.css`, `js/pearl-calendar.js`, `css/pearl-finances.css`, `js/pearl-finances.js`, `js/pearl-watch.js`, `css/pearl-shopping.css`, `js/pearl-shopping.js`, `css/pearl-extras.css`, `js/pearl-extras.js`), every other view. | this drive | FINISH LINE items 1–7 | glm builds (Opus/Sonnet when the cheap vendors are down, per Nick 2026-09-05) · deepseek extracts · sonnet checks · fable (Sienna) design-QAs and oversees |

**Contracts between lanes (FROZEN at plan time — change = dated `PLAN-CHANGES-PEARL-TODO.md` delta):**
- **The switch and the chrome:** `body.skin-pearl` and `data-pl-screen` come from `js/pearl-nav.js` (Home lane STEP 3, live at v3); the seven-cell bar and the four-group rail are drawn by that script and `css/pearl-nav.css`; To-Do's active state and badge are theirs. This plan never adds a second switch or a second bar; it maps `#todo` ⇒ `todo` only by consuming `data-pl-screen`.
- **The generated token layer:** `css/pearl.css`. This plan's composition sheet scopes to `body.skin-pearl #view-todo` and sets `--acc:#BF5E3B;--soft:#F5E0D5;--deep:#AD5535;--warn:#B8657A` INSIDE that scope.
- **The shell layer:** `css/pearl-shell.css` + `tools/pearl-shell-class-map.json` (Shopping STEP 5; on `pearl-shopping/drive` today). Consumed here; class names frozen by the map; this lane never edits the source or the output (STEP 5 builds them only when absent everywhere, then posts the handoff into the Shopping plan).
- **The hook rule** (Finances spec §6.4): `js/app.js`, `js/pop.js` and `js/todo-thread.js` are edited zero times; Pearl restyles the existing nodes in place and adds wrapper nodes only where the drawing needs them (the dark card's trio and list wrapper, the three columns, the scroll regions), via `js/pearl-todo.js` on `MutationObserver`s over `#pp-hero-todo` and each `.td-list` with a re-entrancy guard and a microtask debounce (one Pearl render per app render — `renderTodoPanel` re-binds per render, so the observer restyles AFTER it and re-runs nothing of its binding). The person switch is the app's own `.segmented` restyled in place; the noah and business buttons are hidden with CSS under the skin (`body.skin-pearl #view-todo .seg-btn[data-todo="noah"], … [data-todo="business"]{display:none}`) and their panels stay `hidden` through the app's own logic; if the app's initial active tab were one of them the observer would activate Nick.
- **The trio's numbers are pop.js's numbers.** Overdue / due today / this week are read from the values `setHero` and `renderTodoPanel` wrote (the hero's counts and the bucket headers), never recounted from rows — so the pull's contradictions render as pulled (§1 anti-scope).
- **Neutraliser:** explicit, enumerated properties only inside `body.skin-pearl #view-todo`; `all: unset` and `all: revert-layer` are banned.
- **Widths:** 375 · 1024 · 1280; breakpoint 1100px; light only.
- 🔴 **KNOWN INSTRUMENT LIMIT — the evening filter, and why a zero colour count can be a true reading of the wrong thing.** `css/reskin-popfix.css` lines 72–85 apply `:root[data-evening]` `filter:brightness(.82) contrast(.97) sepia(.34) hue-rotate(-14deg) saturate(1.16)` to `#view-todo` along with ten other views, every day from 7pm to 6am. **A CSS filter never changes a computed style**, and `getComputedStyle` is the only thing this lane's fidelity check can read — so at night the check reports every colour as correct while the screen in front of Nick renders tan. Every `mismatched properties: 0` in this plan is therefore a statement about the STYLE SHEET, never about the pixels, unless the run ALSO records the hour it ran and reads a rendered pixel: STEP 12's PROOF does both and is the only step that may be read as a statement about what the screen looks like. The four zero counts of STEPS 7–11 must not be quoted as "the colours are right" — they mean "the rules that produce the colours are right". The app-wide neutralisation belongs to the chrome's owner (the Home lane; the open handoff is in `STATE-PEARL-TODO.md`), the Finances lane neutralised it for its own view only, and this lane measures around it and never edits another lane's sheet.
- **Accent sites:** `tools/pearl-accent-sites-todo.json` — derived by STEP 7 from the drawing's CSS: the DUE TODAY trio numeral (`.pl-trio b.pl-acc`), a checked check box, the wash tint, and the chrome's active item (theirs); the accent may never appear on a label, heading, body text, hairline, card edge, date or the person chips; overdue dates and the overdue numeral are Dusty rose.
- **Data floor:** task text is masked (`<task N>`) in every cheap-lane brief and every evidence file; the check compares styles and never records row text; the Business panel's rows never leave the machine. Side-by-side PNGs and every `--shot` capture are screenshots of a signed-in screen and carry real task text: they are written ONLY under `redesign-mockups/concepts-2026-09-04-round10-screens/evidence/local/` — a folder STEP 1 creates with a `.gitignore` containing `*` so nothing in it can be committed or pushed — Sienna and the verifier open them on this machine, and the publish record cites their file names and sha256, never the images.

## 3b · Execution map — the Step map, then one STEP block per row

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

**Standing rules for EVERY step (Finances §6.4, restated once):** (1) first action `git -C "/Users/nickdeck/Documents/Claude 2.0" rev-parse --show-toplevel`, record the path; (2) snapshot before edit, step-numbered: `<file>.pre-pearl-todo-step<N>-20260905.bak`, deleted only after the step's checker closes it; (3) commit with an explicit pathspec, then `git show --stat HEAD`; (4) any step touching `index.html` tags also edits `sw.js` (ASSETS + CACHE) in the same change; (5) every step ends with `node projects/personal/family-app/contract-check.js` green with the number quoted and `node --check` on every touched `.js`; (6) builder = cheap lane (`projects/ops/cheap-task.mjs` for new files, `projects/ops/route-build.mjs` for one existing file) with the §T dispatch header pasted verbatim; checker = a Sonnet session that did not build it; (7) every `--prove` states what must NOT change and proves it; (8) no `--prove` hardcodes a count it could derive; (9) no brief or evidence file carries a task's text; (10) INTEGRATION — this lane's branch is rebased onto `origin/main` (`git fetch origin && git rebase origin/main`) immediately before STEP 6's first edit and again immediately before STEP 12's build, so the `<link>`/`<script>` tags and the `sw.js` CACHE it adds apply to the CURRENT shared files (CACHE = the rebased value + 1, never a remembered one); a conflict in the three shared files is resolved by keeping every lane's tags and taking the higher CACHE + 1, then parity and contract-check re-run; STEP 12 deploys only when `git merge-base --is-ancestor origin/main HEAD` exits 0, pushes the branch first, and the deploy record names that commit; (11) **THE HIDDEN-VIEW RULE — every frame rule this lane writes on `#view-todo` carries `body.skin-pearl #view-todo[hidden]{display:none}` beside it, and the other-screens check is part of the PROOF of every step that CARRIES OR CHANGES A FRAME RULE — STEPS 6, 8, 9 and 12, and no others.** 🔴 **NARROWED 2026-09-06 by a cold audit of this plan**, which read the four PROOF blocks the old wording pointed at and found the check named in none of STEPS 10, 11, 13, 14 or 15: the rule said "EVERY step's proof from STEP 6 onward", so it was failing silently on five steps at once and a step could close clean while breaking it. STEPS 10, 11, 14 and 15 write no frame rule and are out of scope by construction; STEP 13 carries the same fact as a blind-check criterion in a person's words instead of as an instrument line. The four steps that DO carry it now each name it in their own PROOF block, which is what makes the rule enforceable rather than remembered. A `display:flex` frame rule beats the app's own `[hidden]` attribute, so the hidden To-Do view paints over every other screen — measured live on 2026-09-06 12:55Z on `#home` and `#finances` at 375/1280/1728, hotfixed in `css/pearl-todo.css` v8 (line 315 on `origin/main`). 🔴 **THIS IS A DEFECT CLASS ACROSS THE PEARL LANES, NOT A To-Do QUIRK — it has now happened THREE times on TWO screens**, and a guard written for one screen will not stop the next one: the Home lane hit it at ≥1100 (its ROUND 15, `css/pearl-home.css`), hit it AGAIN at <1100 on 2026-09-06 (a hidden Home stayed laid out above every other screen on a phone — `#view-finances` measured at y=1654 under a 1654px-tall Home; the Finances lane found it and handed it over, `PLAN-HOME-PEARL.md` line 292), and this lane hit it at 12:40Z. **So copy the SHAPE the Home lane proved, not just the selector:** the guard must OUTRANK the frame rule it is guarding — `css/styles.css:63` is `.view[hidden]{display:none}` at (0,1,1), the Home frame rule is (0,3,1) and its shipped guard `body.skin-pearl #view-home.pl-body[hidden]{display:none}` is (0,4,1) — and it must carry **NO media query**, because the Home lane's first fix sat inside the `≥1100` block and left the phone width broken for a full round. One rule, higher specificity than the frame rule, at every width. The check is: `#view-todo` computes `display:none` AND still carries its `hidden` attribute on `/#home`, `/#finances`, `/#calendar`, `/#shopping` and `/#extras` at 375, 1280 and 1728 — fifteen combinations, each read as `getComputedStyle(document.getElementById('view-todo')).display` plus the attribute. The fidelity check gains an `--others` flag at STEP 4's next edit; until that lands the instrument is a small other-screens probe that **DOES NOT EXIST AS A COMMITTED FILE YET — searched five ways before that was written down (§Z), 2026-09-06:** a repo-wide `find` for `*probe*` / `*hidden*` / `*other-screen*`; `git ls-files` for the same; a BEHAVIOUR search for any `.mjs`/`.js` under `projects/personal/family-app` that reads `getComputedStyle` on `#view-todo` (six hits — `tools/pearl-fidelity-todo.mjs`, three `sw.js` asset lists, `_design-target/audit.mjs` which navigates routes but only inspects `#view-calendar` and `#view-todo`'s buttons, and `…/landing/todo-default-fence.mjs`, none of which reads `#view-todo`'s display on another route); the ownership register `projects/ops/spine-projections/ownership-injection.md` (the family app is listed as a canonical system; the only named owner is `family-app-daily-audit-cc` over `AUDIT-LEDGER.md` — no owner and no instrument for a cross-route hidden-view check) and the `projects/ops/scheduled-mirror/` it tells you to grep as well (no hit for `view-todo` or `pearl-todo`); and the family vault's own index (credentials only — it holds the family-app login rows, nothing about instruments). So nothing exists to EXTEND and this is a genuine build, not a duplicate. The regroup's own STEP 6 re-run wrote such a probe in a scratchpad and never committed it, so writing that probe into `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round9-finance/build-proofs/landing/` (the Finances lane's landing folder, beside its siblings) is the STEP 8 redo's first action, and it is a build artefact of this lane, never a second plan or ledger file. What it must do, so anyone can rebuild it: sign in as nick through `live-lib.mjs`, visit each of the five routes at each of the three widths, and print `getComputedStyle(document.getElementById('view-todo')).display` plus the `hidden` attribute for all fifteen. `todo-default-fence.mjs` in the same folder is NOT a substitute: it snapshots every `.view` while sitting on `/#todo` and never visits another route. **The STEP 6 fence cannot substitute either** — it measures `#view-todo` itself and is structurally blind to the screens this rule protects, which is exactly why it did not catch the incident.

**Step map (read this first):**

| Stage | # | Task (step name) | Gate to enter | EXECUTOR (model, from the matrix) | CHECKER (different model — never the builder) | DONE-PROOF (runnable command) | Ends when |
|---|---|---|---|---|---|---|---|
| Plan | 1 | Lock the target: apply the fade-removal default as a REV bump, regenerate, publish login-free, record revision; open the state file | nothing — start now | sonnet | glm | `command grep -c '^// REV ' projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/gen.mjs` prints 1 and `curl -s -o /dev/null -w '%{http_code}' https://skippy-designs.pages.dev/family-pearl-todo-r10.html` prints 200 (the published copy is CREATED BY STEP 1's deploy) | REV + commit hash in the §D block; Nick's approval of the published page recorded (the one sanctioned wait) |
| Plan | 2 | Ground truth for To-Do: ids, classes, data-attributes, strings, endpoints, signed-in rendered DOM per person, task text masked | nothing — start now | deepseek (A, B) + sonnet (C) | glm | `ls projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens` shows ground-truth-todo.json (CREATED BY STEP 2's run; the checker re-lists) and its four counts print ≥ 10 · 30 · 16 · 6 | the file exists with no task text, no `$`-prefixed number, no credential |
| Design | 3 | Anchor map, two tables, signed by design QA with the element counts | STEP 1's published page + STEP 2's ground truth | sonnet | fable (Sienna, creative-director) | `ls projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens` shows gen.mjs-anchors-todo.md (CREATED BY STEP 3) with ≥ 50 rows and two `SIGNED BY` lines | every drawn element per frame is an anchor or a signed GAP; viewport list signed |
| Tests | 4 | The fidelity check for this design (two MAPs), with its red-proof, retired-colour sweep, plain-app fence check and data-floor self-test | STEP 3's anchor map | sonnet | glm | `node projects/personal/family-app/tools/pearl-fidelity-todo.mjs --selftest` (CREATED BY STEP 4) prints `red-proof: 1 mismatch (paddingTop) · green-proof: 0 · floor: 0` and exits 0 | the check can go red and green on demand and cannot leak a row's text |
| Framing | 5 | The shell layer present: consume the Shopping plan's generated sheet (from `origin/main` or its branch), or build it by that plan's STEP 5 if absent everywhere | nothing — start now | glm | sonnet | `ls projects/personal/family-app/css` shows pearl-shell.css (CREATED BY the Shopping plan's STEP 5, or by this STEP 5's run when absent); the renamer's `--check` prints `regenerated: identical` | the shell sheet and its class map exist and regenerate identically |
| Framing | 6 | Wire in (link tag, script tag, `sw.js` ASSETS + CACHE, contract-check lists), painting nothing; capture the flag-off baseline of all four tabs | STEP 5's sheet; STEP 4's `--fence-only`; the overseer has opened this lane's STEP 6 | glm | sonnet | `node projects/ops/skippy-jobs/_test-family-app-asset-version-parity.mjs` prints PASS and `node projects/personal/family-app/tools/pearl-fidelity-todo.mjs --fence-only` (CREATED BY STEP 4) prints `fence: 0 changed elements (flag off)` | stylesheet-link count = before + 1 (+1 more if the shell was wired here), `sw.js` CACHE = before + 1, the screen unchanged with and without the flag |
| Elements | 7 | Header (one line: eyebrow · "N due" · split line · person chips, noah and business hidden), wash, accent sites | STEP 6 | glm | sonnet | `node projects/personal/family-app/tools/pearl-fidelity-todo.mjs --only header --inject <abs path to css/pearl-todo.css>` (CREATED BY STEP 4) prints `mismatched properties: 0` and `--hidden-tabs` prints `noah chip hidden ✓ · business chip hidden ✓ · panels unreachable ✓ · app.js/pop.js/todo-thread.js shas unchanged ✓` | header anchors zero at both viewports; the two tabs hidden, untouched |
| Elements | 8 | The dark card (trio + due-today list, or the free-day line + next-up) and the bucket cards with every row control | STEP 7 | glm | sonnet | `… --only cards --inject <abs path>` prints `mismatched properties: 0`; `--drive rows` prints `check-off confirm ✓ · someday move ✓ · thread chip ✓ · link ✓` (CREATED BY STEP 4) | card and row anchors zero; every handler still fires |
| Details | 9 | Desktop composition: fixed viewport, the column RULE (Nick's three columns; Chantelle's split overdue), absorbers + scroll regions, no fades, all panels' `hidden` respected | STEP 8 | glm | sonnet | `… --only layout` (CREATED BY STEP 4) prints, per person, `frame: 662 · rail: 40→622 · col1: →622 · col2: →622 · col3: →622` and the scroll-region count at 1280×662 | columns end at the menu's bottom for Nick and for Chantelle; no dead band |
| Details | 10 | Every state, verbatim: the hero's five lines, skeleton, failed note, store notice, empty, Someday error, check-off confirm | STEP 8 | glm | sonnet | `… --states` (CREATED BY STEP 4) prints `states: N/N reached · strings verbatim: N/N` with N read from the ground-truth file (13 on the approval-day source, the two hero eyebrows included) | every §2 state row T3–T5, T8–T9, T12–T14 captured |
| Tests | 11 | Widths and chrome conformance: 375 · 1024 · 1280; measure the chrome under To-Do and post the handoff | STEPS 9–10 | sonnet | glm | `… --band` prints `1024: rail absent · scrollWidth<=clientWidth ✓` and `--chrome` prints the destination count, badge colour and pill presence (CREATED BY STEP 4) | conformance measured; any chrome gap posted to the Home lane's plan STEPS, never fixed here |
| Output | 12 | PUBLISH behind the flag: build, parity gate, deploy, the post-deploy wall check, fidelity check on the live URL for both people at both viewports, side-by-side PNGs, Sienna's verdict, verifier's verdict | STEPS 1 (approved revision), 4, 7–11 | glm | sonnet (+ fable/Sienna for taste, verifier for the click-through) | `node projects/personal/family-app/tools/pearl-fidelity-todo.mjs --out <abs path under the round-10 evidence folder>/todo-fidelity-live.txt` (CREATED BY STEP 4) → `mismatched properties: 0 · unmeasured anchors: 0` ×4 (Nick and Chantelle × 375×812 light, 1280×662 light) | all four publish items landed; flag-off diff still 0; the wall still up |
| Proof | 13 | Blind check of every §2 row on the live URL, as Nick and as Chantelle | STEP 12 | sonnet (se-blind-checker) | glm | `ls projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/evidence` shows todo-blind-check.md (CREATED BY STEP 13's run) containing `24/24 PASS` and `other screens: 15/15 hidden` | zero failed criteria, or the named failures back to one builder round |
| Tests | 15 | Personal only: audit the two personal lists against the business record, hand Nick the triage list, record the rule | STEP 2's ground truth | sonnet | glm | `ls projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/evidence` shows todo-business-audit.md (CREATED BY STEP 15's run) naming its total, its flagged count and every flagged row | the audit exists, Nick has the list, nothing was hidden |
| Proof | 14 | Record: STEPS verified lines, the four data contradictions raised upstream as one handoff, PROJECT.md status handed to Nick (governed), postmortem, retro entry | STEP 13 | sonnet | glm | `python3 projects/ops/agents/check_plan.py --progress projects/personal/family-app/PLAN-PEARL-TODO.md` (this plan file, CREATED BY STEP 1's first commit) prints the derived figure, `ls projects/personal/family-app` shows STATE-PEARL-TODO.md (CREATED BY STEP 1), and the content greps print `4` (zero-count lines in todo-fidelity-live.txt, CREATED BY STEP 12's run) and `1` (`24/24 PASS` in todo-blind-check.md, CREATED BY STEP 13's run) | plan closed at the derived figure; NEXT list holds everything else |

### STEP 1 — Lock the target
**Enter this step when:** nothing. This is the first step.
**Builder:** sonnet · **Checker:** glm, different session
**Files you may touch:** `redesign-mockups/concepts-2026-09-04-round10-screens/screens/todo.mjs` (ONE change: the `.fade` mask removed per §1a row 5 — the `fade` class kept, its rule emptied, nothing else), `gen.mjs` (the REV header bumped), `todo.html` (regenerated), NEW `family-pearl-todo-r10.html` inside the Skippy design-directions folder (a byte-identical copy of the output, the deploy artefact), NEW `STATE-PEARL-TODO.md`, NEW `PLAN-CHANGES-PEARL-TODO.md`, this plan's §D block and STEPS. **Never** any other line of `screens/todo.mjs` (any other redline is a new revision, re-approved) or any live app file.

**Do exactly this:**
1. `git -C "/Users/nickdeck/Documents/Claude 2.0" rev-parse --show-toplevel` → record. Probe the checkout: write `evidence/.probe`, read it back, delete it, `git status --porcelain` shows nothing under `evidence/`.
2. In `screens/todo.mjs`, replace the `.fade{…}` rule's body with nothing (the class stays so the drawing's markup is unchanged); in `gen.mjs` line 1 bump the `// REV ` number by one tenth and append `— To-Do: fade removed (Nick, 2026-09-05, "ghosted overlays")` (a script file with an absolute path; never `cd` + relative).
3. `node "/Users/nickdeck/Documents/Claude 2.0/projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/gen.mjs" todo` → `todo.html`; `node …/tools/shot.mjs todo` → six renders; open the desktop render and confirm the scrolling cards end in a hard edge, not a smear. Grep this plan for unfilled template placeholders: `command grep -nE '<(TBD|todo|fill|replace|xxx)[^>]*>' projects/personal/family-app/PLAN-PEARL-TODO.md` must print nothing; the format tokens `<n>`, `<hash>`, `<date>`, `<person>`, `<task N>`, `<label>`, `<the paths>`, `<ISO hour>`, `<abs path …>`, `<the evidence folder>`, `<the json>`, `<temp>`, `<temp file>`, `<hook>`, `<ref>` are allowed and are filled by the steps that write them.
4. `cp` `todo.html` → the design-directions folder as `family-pearl-todo-r10.html`; `shasum -a 256` both, equal.
5. `node projects/ops/deploy.mjs skippy-designs`; `curl -s -o /dev/null -w '%{http_code}' https://skippy-designs.pages.dev/family-pearl-todo-r10.html` → 200; `curl -s <url> | shasum -a 256` equals the local hash.
6. Commit: `git commit -m "PEARL To-Do STEP 1: locked target REV <n> published (fade removed)" -- <the paths>`; record the hash; `git cat-file -e <hash>` and push; write `REV <n> · commit <hash>` into this plan's §D LOCKED TARGET line.
7. Create `STATE-PEARL-TODO.md` (current-state only: step, next step, handoffs, blocked lines, the HOLDING lines) and `PLAN-CHANGES-PEARL-TODO.md` (line 1 reserved for the cold reader's verdict); create `evidence/local/.gitignore` containing `*` and prove it with `git check-ignore -q <the folder>/probe.png` (exit 0).
8. Hand Nick one plain sentence with the address and the one change (the fade is gone; say "keep the fade" to put it back) and ask nothing else; his yes lands as `Nick, <date>, "<words>"` in §D. Every step whose gate does not name "STEP 1's approved revision" runs meanwhile.

**PROOF — all must be true, pasted into STEPS verbatim:**
- `command grep -c '^// REV ' projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/gen.mjs` prints `1`; the curl prints `200`; the two sha256 lines are identical; `command grep -c 'mask-image' projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/todo.html` prints `0`.
- `git branch -r --contains <hash>` names `origin/main`.
- The §D block carries a REV and a hash, and (when it lands) Nick's dated words about THIS page.
- What would make this step FAIL, in Nick's words: "that's not the one I approved" — a published page whose hash differs from the generator output, a page still carrying the smear, or a revision he has not seen.

**If it fails:** deploy refused → the published address stays empty; post one line to the state file; STEPS 2, 5 run regardless; STEP 12 cannot open until this lands (SUCCEEDED). If Nick says "keep the fade", STEP 1 reverts the one rule, republishes REV n+1, the §D line moves, and REV n+1 needs its own fresh yes from him on the republished page before STEP 12 can open — one check round for STEPS 7–11, never a rebuild.
**Checker's job:** re-run the curl and the hash comparison yourself; open the published page and confirm six frames, Nick and Chantelle, and no smear at the bottom of the scrolling cards.
**Handoff:** post `STEP 1 closed <date> — gen.mjs carries REV <n>` into `PLAN-PEARL-SHOPPING.md` and `PLAN-PEARL-EXTRAS.md` STEPS.

### STEP 2 — Ground truth for To-Do
**Enter this step when:** nothing. Runs in parallel with STEP 1.
**Builder:** deepseek for Briefs A and B (mechanical extraction from source files); sonnet for Brief C (the signed-in DOM — task text is personal and is masked before anything is written) · **Checker:** glm (recounts from the source files; the masked DOM block is checked by a second Sonnet session that did not write it)
**Files you may touch:** NEW `redesign-mockups/concepts-2026-09-04-round10-screens/ground-truth-todo.json`. **Never** any live app file.

**Do exactly this:**
1. Brief A (ids + classes + data-attributes from `index.html`'s `#view-todo` block, all four panels): output `{ids:[…], classes:[…], data:[…]}` — repo-relative path only, no folders, no `.md`.
2. Brief B (strings + endpoints from `js/app.js` lines 344–360, 772–900, 1625–1710, 1714–1990, `js/pop.js` lines 250–360 and `js/todo-thread.js`): every user-facing string verbatim (the hero eyebrows and lines, "Fresh slate ✦", "All clear — go be with the fam ✦", the pace lines, the bucket names, "Move to Someday" / "Move back off Someday" and their titles, the check-off confirm, "Couldn't update Someday.", the placeholder note, the store notices, the empty string) and every `fetch(` endpoint (`/api/todo-list?list=`, `/api/todo-store-write`, `/api/comments…`, `/api/comment-upload`, `/api/files/` — Brief B records what it finds, never a guessed list).
3. Brief C (the signed-in rendered DOM, read-only, Sonnet): through `projects/shared-tooling/browser.mjs`, sign in as Nick via the gate (password from `python3 projects/personal/family-vault/vault.py get family-app-password --caller=skippy` read at run time, never written), open `/#todo`, activate Nick then Chantelle, wait for each `.td-list` to lose `.td-skel`, and write the outerHTML of `#view-todo` with every `.td-item`'s text replaced by `<task N>` (dates and bucket names kept; the Business panel's rows dropped entirely) plus the computed styles of the anchor candidates to the JSON's `live` block; record `shasum -a 256` of `js/app.js`, `js/pop.js`, `js/todo-thread.js` in a `baseline` block.
4. Merge into one file; a `node -e` line prints the four counts.

**PROOF:** the four counts print (≥ 10 ids · ≥ 30 classes · ≥ 16 strings · ≥ 6 endpoints — derived from the reads above, the checker recounts from the sources); `command grep -c '\$[0-9]' <the json>` prints `0`; `command grep -c 'echocardiogram\|blood draw\|Dindin' <the json>` prints `0` (three task words the pull is known to contain — a leak would show); no credential string present. FAIL, in Nick's words: "you left out the button" — a hook in the live markup missing from the file; or a task's text that left the machine.
**If it fails:** a brief that "ran N steps without finishing" is split again, never enlarged; dependents (STEPS 3, 7–10) need this step SUCCEEDED.
**Checker's job:** recount ids from `index.html` yourself; spot-check five strings against `app.js` and `pop.js`; run the two zero-greps.

### STEP 3 — The anchor map (two tables)
**Enter this step when:** STEP 1's published page exists (approval not required yet) and STEP 2's file exists.
**Builder:** sonnet · **Checker/signer:** fable — Sienna (`creative-director`), different session
**Files you may touch:** NEW `gen.mjs-anchors-todo.md` beside the generator. **Never** the generator.

**Do exactly this:**
1. Per frame (Nick, Chantelle), list every distinct element the drawing draws (from `screens/todo.mjs` + the shared chrome): eyebrow, title, split line, person chips (active/inactive), the dark card (label, em line, the trio's three numerals and captions, the due-today row: check box, text, date; the free-day line and the next-up row; the "copied" foot), the bucket card (label, count/em, a row: check box, text, date; an overdue row's Dusty rose date; the Someday button; the thread chip; the urgency mark), the scroll region's hard edge, wash, ground, rail + rows and tab bar + cells (chrome lane). Write each count.
2. For each element one row: `design selector` (inside the frame's `.desk`/`.frame` of the published page) → `live selector` (inside `#view-todo` or the chrome) → properties compared → `GAP` with reason where no live hook exists. Rows are addressed by `:nth-of-type` inside their bucket.
3. Write `VIEWPORTS: 375×812 light · 1280×662 light (per person)` and `RETIRED: <the §D list>`.
4. Sienna signs each table: `SIGNED BY sienna <date> — <person>: elements drawn: N · anchors: M · gaps: K` with M ≥ N − K and K ≤ 2 per frame; she also rules on the pop.js pills (hidden or drawn) and records it in the map.

**PROOF:** ≥ 50 table rows across the two tables; two signature lines; K ≤ 2 in each. FAIL: fewer anchors than drawn elements in a frame, or a GAP without a reason.
**If it fails:** three GAPs in a frame → the drawing or the code is wrong; one line to the overseer naming which; STEP 4 needs this ANSWERED (signed).
**Checker's job (Sienna):** count the drawn elements per frame yourself from the published page; refuse any anchor whose live selector matches more than one node.

### STEP 4 — The fidelity check
**Enter this step when:** STEP 3 is signed.
**Builder:** sonnet (test authoring is never a cheap vendor — matrix row 4) · **Checker:** glm
**Files you may touch:** NEW `tools/pearl-fidelity-todo.mjs`. **Never** the reference script or the sibling checks.

**Do exactly this:**
1. Copy the reference `projects/personal/skippy-app/design-directions/_pearl-fidelity-check.mjs`; DESIGN = the published address (six frames — the script selects the frame by its `.cap` text), APP = `https://family.heroesandsidekicks.io`, sign-in = the family gate (`POST /__gate`, fields `pw`/`identity`/`next`; password from the vault at run time), MAP = STEP 3's two tables, VIEW = the two viewports, route = `/?skin=pearl#todo` + a click on the person chip before measuring.
2. Add `--selftest` (a 1px `paddingTop` injection on one anchor → exactly one printed row naming `paddingTop`; the design page vs itself → 0; the data-floor self-test: a sentinel row text `PEARL-FLOOR-SENTINEL` injected into a `.td-item` on the design page, a full `--out` run to a temp file, and `command grep -c PEARL-FLOOR-SENTINEL <temp>` must print 0 → prints `floor: 0`), the retired-colour sweep, `--fence-only` (DOM count + computed-style hash of `#view-todo` with all four tabs activated in turn, and of every other `.view`, flag OFF, saved to `evidence/todo-fence-baseline.json`), `--hidden-tabs` (asserts the noah and business chips' computed `display` is `none` under the skin, their panels stay `hidden`, and the three script shas equal STEP 2's baseline), `--only <header|cards|layout>`, `--states`, `--drive rows`, `--band`, `--chrome`, `--inject`, `--out`, `--shot`; an unknown flag exits 2 with `unknown flag`.
3. The check never writes a `.td-item`'s text to any file; it records computed styles and the app's own state strings (which are not task text).
4. Preflight: a known-good control (the design page vs itself) prints 0; a failed Chrome launch exits 2, never 0.

**PROOF:** `--selftest` prints `red-proof: 1 mismatch (paddingTop) · green-proof: 0 · floor: 0`, exit 0; `--bogus` exits 2 with `unknown flag`. FAIL: a run that prints 0 with an anchor it never measured, or a sentinel that reaches the output.
**If it fails:** Chrome lock held → wait on the lock, never a second Chrome; dependents need this SUCCEEDED.
**Checker's job:** run `--selftest` yourself; break one anchor's live selector on purpose and confirm exit 2 with that anchor named UNMEASURED.

### STEP 5 — The shell layer present
**Enter this step when:** nothing (needs only `gen.mjs` on disk).
**Builder:** glm · **Checker:** sonnet
**Files you may touch:** none if `css/pearl-shell.css` exists on `origin/main` or on `pearl-shopping/drive` (then it is fetched/merged, not rebuilt); otherwise exactly the Shopping plan's STEP 5 file set. **Never** `pearl-tokens.css`, `tools/pearl-rename.mjs`, `css/pearl.css`.

**Do exactly this:** FIRST write `HOLDING: shell layer · <ISO hour> · STEP 5` into `STATE-PEARL-TODO.md`, then read `STATE-PEARL-SHOPPING.md` and `STATE-PEARL-EXTRAS.md`; if either carries a `HOLDING: shell layer` line from the last hour, release yours and wait (work STEPS 2–4). Otherwise `git ls-tree -r origin/main --name-only | command grep pearl-shell` and the same on `pearl-shopping/drive`: present → bring the four files into this lane's worktree from that ref (`git checkout <ref> -- <the four paths>`, scoped), run the renamer's `--check`, record `regenerated: identical`; absent on both → follow `PLAN-PEARL-SHOPPING.md` STEP 5 word for word, then post `STEP 5 built here <date>` into that plan's STEPS. The overseer (ONE thread for all three lanes) opens STEP 5 and STEP 6 for one lane at a time — that sequencing is the lock; the state-file line is the record.

**PROOF:** `--check` prints `regenerated: identical`; the class map exists; the intersection with `css/pearl.css` is only `.pl-g`, `.pl-l`, `.pl-s`. FAIL: two lanes building the sheet in the same hour.
**If it fails:** another lane is mid-build → wait for its STEP 5 to close, work STEPS 2–4 meanwhile; dependents (6–9) need this SUCCEEDED.
**Checker's job:** run `--check` yourself.

### STEP 6 — Wire in, painting nothing
**Enter this step when:** STEP 5's sheet exists, STEP 4's `--fence-only` exists, and the overseer has opened this lane's STEP 6.
**Builder:** glm (route-build, one file per run) · **Checker:** sonnet
**Files you may touch:** `index.html` (one link tag + one script tag, plus the shell link if no lane has added it), `sw.js` (ASSETS + CACHE = before + 1), `contract-check.js` (only entries the coverage gates name), NEW `css/pearl-todo.css` (header comment only), NEW `js/pearl-todo.js` (header comment only). **Never** anything else. This is the ONLY step that edits the three shared files; the builder writes `HOLDING: index.html sw.js contract-check.js · <ISO hour> · STEP 6` into `STATE-PEARL-TODO.md` BEFORE its first edit and clears it after the scoped commit; re-read each file immediately before writing.

**Do exactly this:**
0. Integration first (standing rule 10): `git fetch origin && git rebase origin/main`, so the tags and the CACHE bump below land on the current shared files.
1. Capture the baseline first: the check's `--fence-only` (flag off, all four tabs) → `evidence/todo-fence-baseline.json`.
2. Add `<link rel="stylesheet" href="css/pearl-todo.css?v=1">` after `css/pearl-calendar.css` (adding the shell link before it if absent), before `css/pearl-nav.css`; add `<script defer src="js/pearl-todo.js?v=1">` after `js/pearl-calendar.js`.
3. `sw.js`: ASSETS gains the new URLs; `const CACHE` = previous + 1 (derive; never type a number twice).
4. Run `node projects/personal/family-app/contract-check.js`; add EXACTLY the names its coverage gates print; re-run green; the checker removes them and confirms red for exactly those names.
5. `node projects/ops/skippy-jobs/_test-family-app-asset-version-parity.mjs` → PASS.

**PROOF:** stylesheet-link count = before + 1 (or + 2 with the shell), script count = before + 1 (derived); parity PASS; contract-check green with the number quoted; `--fence-only` re-run prints `fence: 0 changed elements (flag off)` AND with `?skin=pearl` prints 0 changed; and the other-screens check (standing rule 11) prints `display:none` plus the `hidden` attribute for `#view-todo` on `/#home`, `/#finances`, `/#calendar`, `/#shopping` and `/#extras` at 375, 1280 and 1728 — all fifteen combinations, each read as `getComputedStyle(document.getElementById('view-todo')).display` plus the attribute. FAIL: any pixel moves with the flag off, on any of the four tabs, or any one of the fifteen combinations reads anything but `display:none` with the attribute present. — saved `todo-fence-baseline.json` CREATED BY STEP 6
**If it fails:** parity red → read what it names before touching anything; dependents need this SUCCEEDED.
**Checker's job:** re-run parity, contract-check and both fence runs yourself.

### STEP 7 — Header, person chips, wash, accent sites
**Enter this step when:** STEP 6 is closed.
**Builder:** glm · **Checker:** sonnet
**Files you may touch:** `css/pearl-todo.css`, `js/pearl-todo.js`, NEW `tools/pearl-accent-sites-todo.json`. **Never** `js/app.js`, `js/pop.js`, `js/todo-thread.js`.

**Do exactly this:**
1. Composition scope `body.skin-pearl #view-todo`: `--acc:#BF5E3B;--soft:#F5E0D5;--deep:#AD5535;--warn:#B8657A`; `position:relative;overflow:hidden` on `#view-todo`; `.pl-wash` inserted as its first child from `js/pearl-todo.js` (four radial gradients per `gen.mjs`, `blur(44px)`, opacity .95; the corner tint is the accent's site).
2. Header: `js/pearl-todo.js` observes `#pp-hero-todo` and renders the one-line `.pl-ph` (eyebrow "To-Do · <date>" from the app's own date · `<h1>` "<N> due" from `mapped.todos`'s live equivalent — the nav badge count the app already computes (`setNavBadge("todo", …)`), read from the DOM, never recounted · the split line as the app writes it) and MOVES NOTHING: `.topbar h1` is hidden by the neutraliser, never removed; the `.segmented` is restyled in place into the person chips and placed on the header row on ≥1100 via CSS order — the nodes stay where `initTodoTabs` bound them.
3. Hidden tabs: `body.skin-pearl #view-todo .seg-btn[data-todo="noah"], body.skin-pearl #view-todo .seg-btn[data-todo="business"]{display:none}`; if the app's initial active tab were one of them (it is not — the markup sets `is-active` on personal), the observer would click Nick's chip.
4. Write the accent-site list: `.pl-trio b.pl-acc`, `.chk.is-checked` (the app's own checked state class, read from `app.js` before writing — if the app has none, the drawing's "checked box" site is recorded as unreachable and the list has three entries), `.pl-wash` (tint), `.pl-tab a.pl-on`, `.pl-rail a.pl-on` (chrome).

**PROOF:** `--only header --inject <abs path to css/pearl-todo.css>` prints 0 at both viewports for both people; `--hidden-tabs` prints its four ✓; `command grep -cE "fetch\(|XMLHttpRequest|sendBeacon|EventSource" projects/personal/family-app/js/pearl-todo.js` prints 0 (CREATED BY STEP 6). FAIL: a person chip painted in the accent, a third chip visible, a recounted number.
**If it fails:** an anchor that cannot reach zero because the drawing and the hook disagree → a GAP proposal to Sienna (STEP 3 re-sign); dependents need this SUCCEEDED.
**Checker's job:** re-run the injected check; count accent-coloured elements against the site list; click each chip.

### STEP 8 — The dark card and the bucket cards (REDO — rescoped by the regroup, 2026-09-06)
**Enter this step when:** STEP 7 is closed (it is) and this block's FIRST ACTION below is done.
**Builder:** glm (Opus/Sonnet when the cheap vendors are down, per Nick 2026-09-05) · **Checker:** sonnet, a session that did not build it
**Files you may touch:** `css/pearl-todo.css`, `js/pearl-todo.js`, `tools/pearl-fidelity-todo.mjs` (ONLY its §14 drive proof, rebuilt below, and the `--others` flag of standing rule 11), and ONE new probe script of this lane's own inside `redesign-mockups/concepts-2026-09-04-round9-finance/build-proofs/landing/` (the other-screens probe of standing rule 11). **Never** `js/app.js`, `js/pop.js`, `js/todo-thread.js`.

**STATUS 2026-09-06 15:30Z — CLOSED: the cold checker's verdict is PASS (`evidence/todo-step8-redo-check.md`).** Landed `2ee1ff3b1` (css v9 / js v6 / sw v606) and `31d1050a0`, live 14:45Z. Items 2 and 3 below were founded on a behind checkout and a wrong inference and are corrected in `PLAN-CHANGES-PEARL-TODO.md` (14:55Z) — the bar and her Someday card are data-conditional and proven under a forced state; item 5 reads 0 on every run today; item 6's mechanism was already rebuilt and only its wording changed. Evidence: `evidence/todo-step8-redo-proof.txt` and its siblings `todo-step8-redo-*.txt`.

**Why this is a REDO and not a first build.** `css/pearl-todo.css` v8 and `js/pearl-todo.js` v5 are LIVE and most of the card work is real. The step's own checker REFUSED it (`redesign-mockups/concepts-2026-09-04-round10-screens/evidence/todo-step8-check.md`) and a cold verifier re-ran that refusal first-hand and reproduced every item plus two nobody had caught (`redesign-mockups/concepts-2026-09-04-round10-screens/evidence/regroup-2026-09-06/step-8.txt`). What follows IS that list, in the order it must be worked. Nothing is restarted; the named defects are closed one at a time.

**FIRST ACTION (standing rule 11).** `body.skin-pearl #view-todo[hidden]{display:none}` is already in css v8 and stays beside every frame rule this step writes. Then write the other-screens probe into `…/build-proofs/landing/` so this step's proof is reproducible by someone who is not this builder — no committed copy exists, the regroup ran it from a scratchpad, and `todo-default-fence.mjs` in that same folder is NOT a substitute (it snapshots every `.view` while sitting on `/#todo` and never visits another route). Standing rule 11 records exactly what the probe must print.

**Do exactly this, in this order:**
1. **The idle re-render storm — FIRST, because it makes every other measurement noisy.** Measured twice on the live screen: `window.__pearlTodo.passes` climbs by 14 and by 16 over five completely idle seconds while `renderTodoPanel` is called ZERO times (`evidence/regroup-2026-09-06/step-8.txt`, UNIT D) — about three Pearl passes a second at rest. The build's own claim ("37 idle passes → 1") is not what is running. Fix the observer's re-entrancy guard and its microtask debounce so a Pearl pass fires only on a real app render. **Proven by a five-second idle count of 0–1 passes against 0 app renders, run twice.**
2. **The `.pl-prog` completion bar does not exist in the code at all.** A zero-hit grep of `css/pearl-todo.css` and `js/pearl-todo.js` for `pl-prog` / `pl-progl` proves it is UNBUILT, not data-conditional. `screens/todo.mjs` draws nothing for this element when `pct === null` (nobody due today), so an absent FILL is correct on a free day — but the track, its wrapper and its labels exist whenever the dark card does. Build `.pl-prog`, `.pl-prog > i`, `.pl-progl` and its labels per RULING (a); `.pl-prog > i` is already a legal accent site in `tools/pearl-accent-sites-todo.json`.
3. **Chantelle's Someday bucket card is missing** (map rows 50–53). Nick's Someday card matched `ok` on the same run, which rules out a data conditional. Build hers by the same rule.
4. **The footer colour — the one STEP-8-owned colour mismatch.** Anchor 29, the "copied" foot: design `rgba(255,255,255,.5)`, live `rgb(111,106,120)`. It recurs identically for both people, so it is not today's data.
5. **The four retired colours on Chantelle's panel.** The sweep requires 0 and is not data-dependent; STEP 7's verifier already recorded 26 for Chantelle against 0 for Nick as OUTSIDE its own proof. Name each hex and the element carrying it before changing anything.
6. **Rebuild the drive proof against the controls this app actually has** — the 12:09Z delta in `PLAN-CHANGES-PEARL-TODO.md` states the correction and it was never applied to the code. Read directly in `runDriveRows()`: it still queries `.td-thread-panel, [data-thread-open], .todo-thread-open` for the thread and still counts `window.open` for the link — the exact mechanisms that delta proved this app does not have. **So its two ✗ are evidence about the instrument, not about the product, and must not be read either way until it is rebuilt.** The real contract: the thread opens by tapping the row's NAME (`.pt` — `toggleTodoItem` expands the `.td-item` in place and mounts `window.TodoThread.card` into `.td-body`, whose classes are `tdt-*`); `.td-thread-chip` is a BADGE with no click handler, proven by its rendered count equalling `/api/comments?items=`'s own answer for that row (hidden at zero); `window.open` is never called.
7. **The eighteen repointed map rows are now measurable.** Sienna's 13:00Z ruling is APPLIED in `gen.mjs-anchors-todo.md`: the pre-card rows read through the card (`… .td-list > .pl-g[data-pl-bucket="…"] > .pl-l > .td-bucket` for a label, `… > .pl-scroll > .td-item:nth-of-type(1)` for a row). They were the single largest contributor to the 47 / 42 UNMEASURED counts. Under her RULING (l), a `.hc-source-note` or `.hc-empty` preceding a row makes that row **NOT MEASURABLE, never FAIL**, and any change to `dissolve()` re-opens the whole repoint.
8. **Deferred BY NAME to STEP 9, not fixed here** — they belong to the column frame and this step's proof must not chase them: anchor **19** (the trio's `gridTemplateColumns`), anchor **30** (the week strip's) and anchor **55** (Nick's compact two-column Someday tail), all three "design narrow / live full-width", desktop only. Anchor **14** (the free-day / state line) is DATA-CONDITIONAL — present in one run, absent in the next — and is STEP 10's state, not a STEP 8 mismatch.
9. Everything the original block required still stands: rows are RESTYLED and RE-PLACED, never re-created (`renderTodoPanel` re-binds per render, and the interrupted 12:05Z fix round's own leading hypothesis was that reparenting the Due-today rows out of their `.td-list` broke list-delegated handlers — keep them inside the list and build the card around them, or place them by CSS order); the inline styles the frozen script writes are beaten by property with the `app.js` line commented beside each rule; Overdue never gets a scroll region.

**PROOF — all must be true:**
- `--only cards --as nick` and `--as chantelle` at 375×812 and 1280×662 print `mismatched properties: 0 · unmeasured anchors: 0`, with anchors 19, 30 and 55 named as deferred to STEP 9 and every NOT MEASURABLE row named.
- `--drive rows` on the REBUILT instrument prints `check-off confirm ✓ · someday move ✓ (or NOT MEASURABLE — PERMISSION NOT GRANTED) · thread opens from the row name ✓ · chip badge count = /api/comments ✓`, and the checker confirms the code matches that description before either verdict is trusted.
- The idle counter prints 0–1 Pearl passes over five seconds against 0 app renders, run twice.
- The other-screens check prints `display:none` plus the `hidden` attribute for `#view-todo` on `/#home`, `/#finances`, `/#calendar`, `/#shopping` and `/#extras` at 375, 1280 and 1728 — fifteen combinations (standing rule 11).
- `--fence-only` (the old look) re-run TWICE within one minute against a baseline RECAPTURED on the current `origin/main` — the 13-changed-element result of 2026-09-06 was measured against a baseline dated 02:22Z, before this lane's own wiring landed, so it cannot separate a leak from other lanes' drift.
- FAIL: a handler that no longer fires, a row re-created, a recounted trio, or a green count from an instrument that measures a mechanism this app does not have.

**If it fails:** one line to the overseer naming the element; dependents (9, 10) need this SUCCEEDED.
**Checker's job:** re-run all five proofs first-hand; open the screen and tap one of each control yourself, cancelling every confirm; before grading the drive proof, read its code and confirm it matches its own description.
### STEP 9 — Desktop composition
**Enter this step when:** STEP 8 is closed.
**Builder:** glm · **Checker:** sonnet
**Files you may touch:** `css/pearl-todo.css`, `js/pearl-todo.js`.

**Do exactly this:**
0. **The `[hidden]` guard FIRST (standing rule 11).** This step writes the largest frame rule in the lane — `#view-todo{height:100vh; …}` — which is the exact shape that painted the To-Do screen over every other screen on 2026-09-06. `body.skin-pearl #view-todo[hidden]{display:none}` sits beside it in the same block, and the other-screens check is part of this step's PROOF below, not an afterthought.
1. At ≥1100: `#view-todo` is the viewport frame (`height:100vh`, the shell's 40px inset, content box `inset:40px 40px 40px 284px`, flex column); the observer wraps the cards into three `.pl-cols3` columns by THIS RULE, computed from the rendered buckets on every app render: (a) column 1 = the dark card (never absorbs; on desktop its list keeps the drawing's floor so a one-item day still reads as a card) + the Next week card (absorbs); (b) column 2 = the Overdue card (absorbs, scrolls only if taller than the column); (c) column 3 = Later + Someday (Someday absorbs); (d) when a panel has at most one bucket besides Overdue and Due today AND at least six Overdue rows (Chantelle today: 14), the Overdue rows split across columns 2 and 3 in two cards labelled `1–⌈n/2⌉ of n` and `⌈n/2⌉+1–n of n`, the dark card grows in column 1 above that panel's remaining bucket, and the hero's own overdue number is never recomputed; with fewer than six Overdue rows the panel uses (a)–(c) with Overdue alone in column 2 and the remaining bucket in column 3. On the 2026-09-04 data this rule yields exactly the two drawn frames. Both branches are PROVEN, not assumed: the check's `--layout-data <fixture>` replays a captured real `/api/todo-list` response (byte-identical to a real payload the check itself captured; Nick's payload through Chantelle's chip and hers through his) through the app's own loader so the observer runs the other branch on a live screen — §2 T19.
2. Absorbers' `.pl-scroll` regions get `flex:1 1 auto; min-height:0; overflow-y:auto` and NO bottom fade (§1a row 5); non-absorbers size to content; nothing else gets a min-height.
3. Below 1100 the app's own stacking stands, with the drawing's phone caps (210px, not a multiple of the row height) on Next week, Later and Someday.
4. The rail and tab bar come from the chrome lane; this step only leaves room.

**PROOF:** `--only layout` at 1280×662 prints, per person, `frame: 662 · rail: 40→622 · col1: →622 · col2: →622 · col3: →622` and the scroll-region count (Nick 3 · Chantelle 2); `--only layout --layout-data <fixture>` prints the same equality for the swapped payloads (both branches of rule (d) exercised); no `.pl-g` has innerSlack > 24px except the dark card's floor (measured and recorded); and the other-screens check prints `display:none` + the `hidden` attribute for `#view-todo` on `/#home`, `/#finances`, `/#calendar`, `/#shopping` and `/#extras` at 375, 1280 and 1728 — all fifteen combinations. FAIL: a dead band, a bare column, a card taller than its content, a fade, a branch never exercised, or a frame rule written without its `[hidden]` guard. — saved `todo-step9-proof.txt` CREATED BY STEP 9
**If it fails:** rebalance in CSS order, never with min-heights beyond the dark card's floor; dependents need this SUCCEEDED.
**Checker's job:** re-run; resize to 1280×800 and confirm the columns still meet the rail's bottom; switch to Chantelle and confirm the split.

### STEP 10 — Every state, verbatim
**Enter this step when:** STEP 8 is closed (parallel with 9).
**Builder:** glm · **Checker:** sonnet
**Files you may touch:** `css/pearl-todo.css`, `js/pearl-todo.js`.

**Do exactly this:** style, never rewrite, every reachable state string in STEP 2's ground-truth list (the check reads the list and derives N; 13 on the approval-day source: the hero eyebrows "Left today" and "Chantelle · today", the hero's "Fresh slate ✦", "All clear — go be with the fam ✦", "Ready when you are", "One down — keep it rolling", "Nice pace — …"; "Nothing due today — enjoy it"; the `.td-skel` skeleton; the placeholder note; the store notice; `.hc-empty`; `Couldn't update Someday.` — the check-off confirm is the app's own dialog, asserted by `--drive rows`). Strings are copied from the SOURCE FILE by the check at run time, never from this plan's text. `--states` forces each: loading by throttling, failed by `Network.setBlockedURLs` on `/api/todo-list`, empty by a fixture byte-identical to a captured real empty response of the same endpoint (the check records which), the hero variants by driving the app's own counts where the data allows and recording NOT MEASURABLE where it does not (the "All clear" state needs every due-today item done — never done for real in a proof).

**PROOF:** `--states` prints `states: N/N reached · strings verbatim: N/N` with N read from the ground-truth file, NOT MEASURABLE rows listed by name; empty and failed differ visibly (different text AND a different class). FAIL: a paraphrased string, or a mock passed off as a state. — saved `todo-states.txt` CREATED BY STEP 10
**If it fails:** a state the code cannot produce becomes a signed GAP (Sienna), never a mock.
**Checker's job:** re-run `--states`; diff every string against `app.js` and `pop.js` yourself, apostrophes included.

### STEP 11 — Widths and chrome conformance
**Enter this step when:** STEPS 9 and 10 are closed.
**Builder:** sonnet (measurement) · **Checker:** glm
**Files you may touch:** `evidence/todo-band.txt`, `evidence/todo-chrome.txt` (under the round-10 evidence folder), `STATE-PEARL-TODO.md` (a handoff line). **Never** `js/pearl-nav.js`, `css/pearl-nav.css`.

**Do exactly this:** `--band` at 1024×768 (phone layout, rail absent, no horizontal scroll) and 1100×768 (rail present), for both people; `--chrome` under `#todo` prints the destination count, each badge's background colour, the presence of any `.pl-wait` pill, and the active item's colour. Compare with Nick's rulings (seven · ink · none · Terracotta). Any difference → ONE dated line in the state file's Handoffs and in `projects/personal/family-app/PLAN-HOME-PEARL.md` STEPS (the chrome's owner), quoting Nick: "updates is not a screen on the app".

**PROOF:** `1024: rail absent · scrollWidth<=clientWidth ✓` ×2; `1100: rail present`; the chrome line printed and either matching or handed off. FAIL: a horizontal scrollbar at 1024, or a chrome gap silently fixed here. — saved `todo-band.txt` and saved `todo-chrome.txt` CREATED BY STEP 11
**If it fails:** the band is this lane's; the chrome is not (handoff only). STEP 12 needs the band SUCCEEDED and the chrome ANSWERED.
**Checker's job:** re-run both measurements yourself.

### STEP 12 — PUBLISH behind the flag (the design fidelity gate, four items)
**Enter this step when:** STEP 1's revision is approved by Nick, STEP 4 exists, STEPS 7–11 are closed.
**Builder:** glm (build + deploy commands) · **Checker:** sonnet (re-runs the check on the live URL) · **Design QA:** fable — Sienna (side-by-side PNGs) · **Verifier:** the `verifier` agent
**Files you may touch:** under the round-10 evidence folder: `todo-fidelity-live.txt`, `todo-publish.md`; under its gitignored `evidence/local/` subfolder ONLY: `todo-side-by-side-<person>-375.png` ×2, `todo-side-by-side-<person>-1280.png` ×2 and every `--shot` capture; STEPS. **Never** any app file, and never a PNG outside `evidence/local/`.

**Do exactly this:**
0. Integration first (standing rule 10): `git fetch origin && git rebase origin/main`; `git merge-base --is-ancestor origin/main HEAD` exits 0; parity and contract-check green again on the rebased tree; the branch pushed.
1. `node projects/personal/family-app/build-dist.js`; `node projects/ops/skippy-jobs/_test-family-app-asset-version-parity.mjs` → PASS; `node projects/ops/deploy.mjs deck-family`; record the deployment URL and the commit hash (`git cat-file -e`, pushed); then the deploy's own wall check: `curl -s -o /dev/null -w '%{http_code}' https://family.heroesandsidekicks.io/api/finances` with no cookie prints `401` (the wall survived the deploy — `projects/ops/deploy.mjs` documents that a publish can drop the `AUTH_REQUIRED` binding; a `200` here is a FAIL that stops the step and rolls back to the previous deployment through the same tool).
2. Fidelity, live: the check with `--out <the evidence folder>/todo-fidelity-live.txt --shot <the evidence folder>` for Nick and Chantelle at 375×812 light and 1280×662 light, signed in as Nick, on `https://family.heroesandsidekicks.io/#todo` (the default address — no flag) with cache disabled → four count lines, all `mismatched properties: 0 · unmeasured anchors: 0`; the retired-colour sweep and `--hidden-tabs` print 0 / ✓; and **TWO fences, not one**: (i) the OLD-LOOK fence at `?skin=field#todo`, all four tabs, 0 changed against a baseline recaptured on the current `origin/main`, run twice within one minute; and (ii) the DEFAULT-ROUTE fence over EVERY OTHER `.view` on `/#todo` itself — `node projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round9-finance/build-proofs/landing/todo-default-fence.mjs`, before and after, DOM count plus computed-style hash per view, identical — plus the other-screens check's fifteen combinations (standing rule 11).
2a. 🔴 **The evening filter — record the hour and read a RENDERED PIXEL, because this lane's colour instrument is structurally blind to it (§3 known instrument limit).** `css/reskin-popfix.css` filters `#view-todo` from 7pm to 6am and `getComputedStyle` cannot see a CSS filter, so four zero counts taken at 10pm say nothing whatever about what the screen looks like at 10pm. Two things, both required, and this names exactly which:
   **(a) The hour, on the line.** Each of the four runs records its own wall-clock start time in ISO-8601 with the offset, on the SAME line as its zero count in `todo-fidelity-live.txt`, and beside it `data-evening: present` or `data-evening: absent`, read off `document.documentElement` at that moment and BEFORE anything is removed. A run whose line has no hour is not a proof and is re-run.
   **(b) A `getComputedStyle`-independent read, two ways.** First the check calls `document.documentElement.removeAttribute("data-evening")` and only then measures, so the four zero counts are taken on an unfiltered document whatever the hour — the removal and the pre-removal reading from (a) are recorded separately, so a reader can always tell whether the attribute was there. Second, independently of computed styles entirely: the `--shot` PNG of each of the four runs is sampled at three named sites — the DUE TODAY trio numeral (`.pl-trio b.pl-acc`), the dark card's ground (`.pl-g.pl-shdark`) and the page wash (`.pl-wash`) — by reading the pixel at each element's own bounding-box centre, and each sampled RGB is compared with the drawing's hex for that site within a stated tolerance of ΔE ≤ 3. The sample coordinates and the sampled values go into `todo-publish.md` beside the PNG names. A site whose PIXEL fails while its COMPUTED STYLE passes IS the filter, by construction: it is recorded as such, handed to the chrome owner through the state file's Handoffs, and never fixed inside this lane's sheet.
3. Side-by-side PNGs (target left, live right, same width, labelled) per person and viewport, from `--shot` and the published design page — written under `evidence/local/` only (gitignored; the checker runs `git check-ignore` on each and confirms it is ignored); `todo-publish.md` records each file's name and sha256.
4. Sienna grades the PNGs (taste only, now that the counts are zero) → her verdict line in `todo-publish.md`.
5. The verifier re-runs item 2 first-hand and clicks through: a chip switch, one check box (cancelled), one thread chip, one link → its verdict line.
6. The check is run three times; all three must print four zeros.

**PROOF:** the four items in `todo-publish.md`: the four zero-count lines with the evidence file name, EACH carrying its own ISO-8601 hour and its `data-evening: present|absent` reading, and EACH backed by the three rendered-pixel samples (trio numeral, dark-card ground, wash) inside ΔE ≤ 3 · the four PNG file names · Sienna's verdict, with the hour she graded at · the verifier's verdict; the deploy hash reachable and pushed; the anonymous `/api/finances` curl printed `401`; BOTH fences clean (the old look byte-identical on all four tabs, every other `.view` on the default route identical before and after) and the fifteen other-screens combinations `display:none`. FAIL: any non-zero count, any missing item, a third chip visible under the skin, or any other screen changed by this lane. — saved `todo-fidelity-live.txt` and saved `todo-publish.md` CREATED BY STEP 12
**If it fails:** a non-zero count names its anchor and property → back to that anchor's step for ONE builder round; the re-check is of the named rows only.
**Checker's job:** re-run the live check yourself; do not accept the builder's paste.

### STEP 13 — Blind check of every §2 row
**Enter this step when:** STEP 12's four items are landed.
**Builder:** sonnet as `se-blind-checker`, briefed with §2 only and told to REFUTE · **Checker:** glm (confirms the report cites each row)
**Files you may touch:** NEW `todo-blind-check.md` under the round-10 evidence folder.

**Do exactly this:** for T1–T24 on the live URL, as Nick and as Chantelle (T24), drive each interaction and record PASS/FAIL with evidence (the computed value, the string, the navigation). Every confirm is cancelled; the Someday drive uses the test row of STEP 8 or records NOT MEASURABLE; no task text is copied into the report.

**Plus ONE criterion beyond §2, and it is Nick's own complaint rather than an instrument line** (standing rule 11's people-facing half): leave the To-Do screen, visit `/#home`, `/#finances`, `/#calendar`, `/#shopping` and `/#extras` at 375, 1280 and 1728, and confirm the To-Do screen is not there — `#view-todo` computes `display:none` AND still carries its `hidden` attribute in all fifteen combinations. His words, 2026-09-06: "to do screen has locked itself into the view for every other screen its a mess." The checker reports it as its own line, not folded into the 24.

**PROOF:** `24/24 PASS` AND `other screens: 15/15 hidden`, or the named failures. FAIL: any row the checker could not reach (NOT MEASURABLE keeps the row open). — saved `todo-blind-check.md` CREATED BY STEP 13
**If it fails:** failures go back to their step for one round; the re-check covers the named rows only.

### STEP 15 — Personal only: the business audit, and the rule that keeps it that way
**Enter this step when:** STEP 2's ground truth exists (it can run in parallel with everything after that).
**Builder:** sonnet (this reads Nick's real task text and the business record — never a cheap vendor, and never off this machine) · **Checker:** glm (re-runs the audit and recounts)
**Files you may touch:** NEW `evidence/todo-business-audit.md` under the round-10 evidence folder (task text masked to `<task N>` with only the matched term shown), `STATE-PEARL-TODO.md` (a handoff line). **Never** the Monday board, `js/app.js`, or any live task — nothing on his board is moved by this step.

**Why this step exists, measured before it was written.** Nick, 2026-09-05, approving the drawing: *"just make sure its clear only personal todos end up on this list not business stuff"*. Audited the same day against the 2026-09-04 pull: of **116 rows on the two personal lists, 15 distinct rows are business** — 14 of Nick's and 1 of Chantelle's — including a client's pricing structure, raising prices for Sidekicks, a staff payroll item, three company tools and the company's bank-account visibility. **So this is not a rendering fault. The screen is faithfully showing a personal board that has business work filed onto it**, and no change to the drawing can fix that.

**Do exactly this:**
1. Run the audit over every row of both personal lists. A row is flagged ONLY when it names something the business record itself contains — a client, a staff member, a company tool, a company money action — never by a general guess at the wording. The audit prints every flagged row so a person judges it; it never returns a bare count.
2. Write `evidence/todo-business-audit.md`: the total row count, the flagged count, and one line per flagged row giving its group and the term that matched. Task text is masked; the matched term is shown so the judgement is checkable.
3. Hand Nick ONE list, in plain words, with the recommendation: move these to the Business list on the board. **Nothing is moved by this step** — a task is his to file, and a silently hidden task is worse than a misfiled one.
4. Record the standing rule in `STATE-PEARL-TODO.md`'s Handoffs and in the audit file: **business work belongs on the Business list, which the family app's Pearl skin does not show; the personal panel renders its own board's rows verbatim and never filters by guess.** Any future agent filing a task to the personal board for Nick or Chantelle applies that rule.
5. Re-run the audit at STEP 12's publish, so the number quoted to Nick is the number on the day it ships, not this one.

**PROOF:** `evidence/todo-business-audit.md` exists and names its total, its flagged count and every flagged row with the term that matched; `command grep -c 'masked' <the file>` confirms the masking note; the checker re-runs the audit and gets the same flagged set from the same pull. FAIL, in Nick's words: *"that's business"* — a row he names that the audit did not flag, or the screen quietly dropping a task it decided was business. — saved `todo-business-audit.md` CREATED BY STEP 15
**If it fails:** a flagged row he says is personal is removed from the term list with his word recorded beside it; the audit is re-run. Nothing about the screen changes either way — this step measures and reports, it does not filter.
**Checker's job:** re-run the audit yourself from the same pull; confirm no row was hidden from the screen by this step.
**Handoff:** the triage list to Nick; the standing rule into `PROJECT.md`'s next-steps (proposed, governed) so the board's own hygiene has an owner.

### STEP 14 — Record and close
**Enter this step when:** STEP 13 prints 24/24.
**Builder:** sonnet · **Checker:** glm
**Files you may touch:** this plan (STEPS, SUMMARY, the postmortem section), `STATE-PEARL-TODO.md`, `.claude/skills/plan/references/failure-registry.md` (append only, four-column format), and a proposed `PROJECT.md` status paragraph handed to Nick as one sentence (governed — never filed as a ticket while he is asleep).

**Do exactly this:** paste two independent `VERIFIED:` lines per step; write the SUMMARY in plain words; post the four data contradictions (header vs panel counts, `hero.week`, Chantelle's 12 vs 14, the Someday cycle) as ONE dated handoff line into `projects/personal/family-app/PROJECT.md`'s "What's next" (proposed, governed) and into the state file; write `## Postmortem` (what the fidelity count caught that eyes passed, what a cheap run could not do, one registry entry or "nothing worth extracting"); post `STEP 14 closed <date>` into `PLAN-PEARL-SHOPPING.md` and `PLAN-PEARL-EXTRAS.md`.

**PROOF:** `python3 projects/ops/agents/check_plan.py --progress projects/personal/family-app/PLAN-PEARL-TODO.md` prints the derived figure; `--artifacts` reports nothing MISSING or EMPTY; the CONTENT of the two closing artefacts is read, not their presence: `command grep -c 'mismatched properties: 0 · unmeasured anchors: 0' <the evidence folder>/todo-fidelity-live.txt` prints `4` and `command grep -c '24/24 PASS' <the evidence folder>/todo-blind-check.md` prints `1` (both CREATED BY STEP 12 and STEP 13); the postmortem heading exists. FAIL: a typed percentage, or a count that reads presence for truth.

## 4 · Regret Check (every registry entry, or the plan is not done)

*One row per registry entry, in registry order (PLANNING → DECOMPOSITION → EXECUTION → INTEGRATION → QA → REPORTING → the retro blocks). "Steps" are this plan's §3b steps.*

| Failure mode (registry entry) | The measure in THIS plan that prevents it | Where it lives |
|---|---|---|
| A second system was built because the first was invisible | Ownership receipt cites the family app's own governing plan, PROJECT.md and the Finances Pearl spec; this plan extends that estate (shared Pearl layer reused, never rebuilt) | §0, §1 anti-scope |
| A capability was declared impossible from a stale or unverified claim | Every 'cannot' in this plan (no screenshot capture, transitions freeze) is re-measured by STEP 4's instrument preflight before it is relied on | STEP 4 |
| An absence was asserted without opening the store that would hold it | §Z binds every negative: STEP 2's ground truth searches index.html, app.js/extras.js and pop.js with `command grep` and a second pattern before any hook is called absent | STEP 2 |
| A known constraint's reason was lost, and it silently capped the product | Each frozen constraint names its reason inline (why prefixed classes, why 1100px, why the old markup stays) | §3 contracts |
| An instruction assumed capacity the executor doesn't have | Steps are sized to one cheap run each; STEP 2 is split into three briefs because the Finances lane measured one big brief 'ran 24 steps without finishing' | §3b, STEP 2 |
| Expectations/manifest rows carried no grounding | Every §2 row and every anchor cites its source hook (id/class/string in the live files) — no expectation without a citation | §2, STEP 3 |
| Work was written to a queue no reader ever visits | Every artefact names its reader: evidence → the checker and the publish step; handoff lines → the chrome lane's hand-off file | §5 artefact consumers |
| A detector's death was invisible because only its target read it | The fidelity check is run by the builder AND re-run first-hand by a different-session checker; its exit code is read by both | STEP 4, STEP 12 |
| A decision settled once re-opened elsewhere, or two copies of a rule disagreed | One implementation of the rule: the accent-site list and the anchor map are single files; the nav is consumed from the chrome lane, never re-implemented | §3 contracts |
| A rule constraining the user turned out to be an agent's invention | Every rule in this plan carries Nick's quoted, dated words or a named author; no unattributed rule | §1a, §D block |
| Remediation was ordered with diagnosis last | STEP 6's first action is the is-it-already-fine check (flag off → screen byte-identical) before any composition is written | STEP 6 |
| A document, label, or comment was believed over the live system | The live app is read by running it (STEP 2 pulls the rendered DOM signed in), never from comments or the Field spec | STEP 2, STEP 4 |
| A proposal was sold on a capability never opened and read | Every capability this plan leans on (MutationObserver takeover, :has(), body.skin-pearl switch) is opened in the live file and cited by line | §3 contracts, STEP 7 |
| A cause was named and acted on without eliminating alternatives | A failing fidelity count is diagnosed per anchor with the check's own per-property line, never by one guessed cause | STEP 12 |
| The human was asked a question the record already answers | The standing-auth audit was read: sign-in, driving the app, and testing as Nick are granted; no step asks him for those | §0, §AUTH receipt |
| A spec and its guard were authored by the same hand and ratified the same defect | The anchor map is written by the builder and SIGNED by Sienna's design QA (different agent) with the distinct-element count beside the anchor count | STEP 3 |
| Session rules never reached the subagents doing the work | Every dispatch header pastes the MACHINE RULES substance and the file fence verbatim; inheritance is assumed to be zero | §5 dispatch header |
| One rule was blanket-applied across items needing per-item answers | Per-panel and per-state rows in §2 are answered one at a time; the checker forces each state separately | §2, STEP 10 |
| Pattern-matching scoped too loosely produced false connections | Anchors map design selector → live selector one-to-one; a selector matching more than one live element is a GAP, never a loose match | STEP 3 |
| Rules existed but were psychologically dormant at answer-time | STEP 0's five-minute loop re-fires the North Star, fan-out, cheap and blocked questions so the rules are re-loaded, not remembered | STEP 0 |
| A run exceeded its cost/time ceiling or hung unbounded | Every cheap run carries the lane's 120s timeout and 60k read cap; a run that exceeds it is split, not retried bigger | §5, STEP 2 note |
| A helper was dispatched on a brief with a wrong or missing constraint | Every brief names its file fence, its proof command and what must NOT change; the header is copied verbatim from §T | §3b, §5 |
| A claim about the user/system was made without its source | Every claim about the live app cites file:line or the STEP 2 ground-truth entry | §2, STEP 2 |
| A conclusion was drawn from a partial read | STEP 2 reads the whole Shopping/Extras code path end-to-end (loader, renderer, handlers, hero) and records the read as complete or partial | STEP 2 |
| A fact was quoted as current without its date | Every live value quoted (counts, strings, versions) carries its pull date (2026-09-04 18:50Z) and the command that re-measures it | Already true, §2 |
| A computed value never reached the persistent record | Every count the plan produces (anchors, mismatches, links, cache version) is written to an evidence file under the repo, never left in a session | STEP 12 evidence paths |
| A missing lookup key fell back silently to a wrong default | Selectors that match nothing fail the check as UNMEASURED (exit 2), never silently pass; :has() fallback is stated | STEP 4 |
| A hardcoded identifier broke when the referent was recreated | Anchors bind to the app's own frozen hooks (RESKIN-CONTRACT ids) rather than generated ids; contract-check.js proves they still resolve | STEP 6, STEP 3 |
| A placeholder or wrong-level path shipped as a literal instruction | Every path in this plan is repo-relative and was globbed on disk at write time; no placeholder path survives (STEP 1 greps for `<`+`>` placeholders) | §0, STEP 1 |
| A UI reported success while the backend silently failed | Add/order/check-off actions are verified by reading the backend result (the refetched list, the queue card) after the click, not the button state | STEP 10 |
| Mid-session state was assumed unchanged | STEP 12 re-runs the whole acceptance set after STEP 11's dead-CSS removal; every re-check is a fresh run, not a remembered pass | STEP 11, STEP 12 |
| Uncertainty was silently absorbed instead of marked | Every evidence line declares its state (ARTIFACT SAVED · NOT MEASURABLE FROM HERE — instrument · UNPROVEN); nothing is left implicit | §A evidence states, STEPS |
| A serial multi-step operation blew its time budget | Steps are single-purpose; the publish step runs the check once per viewport with each count written before the next | STEP 12 |
| An external action went unlogged and became unrecoverable | Every deploy is logged by deploy.mjs's own record and the commit hash is pasted into STEPS | STEP 12 |
| A tool's own description contradicted house reality and won | Where a tool's own text contradicts house reality (the hook's 'cd + relative path' refusal, Chrome's screenshot hang) the plan names the house fact and the workaround | §0 notes, STEP 4 |
| Personal/identifying data exposed, or a record written to the wrong subject | No money value, credential or login appears in any brief, ground truth or evidence file; the cheap lane's data wall is stated in every brief | STEP 2, §5 |
| One instance of a defect class was fixed while its siblings stayed broken | A defect found on one row kind (bundle / no-link / plain) is fixed for all three in the same step; the checker forces all three | STEP 8 |
| A read operation mutated state | The fidelity check and the ground-truth pull are read-only against the live app (GET + signed-in read; no POST beyond the identity gate) | STEP 2, STEP 4 |
| The three biggest absence-claims variants: empty result, broken probe, discarded stderr | Every 'not found' is paired with the exact command and a second, differently-shaped search; stderr is captured to the evidence file | STEP 2, §Z |
| A generated mirror was hand-edited, or its generator never re-ran | css/pearl-shell.css is GENERATED by tools/pearl-shell-rename.mjs from one source sheet; a hand edit fails STEP 5's regenerate-and-diff proof | STEP 5 |
| Deployed config silently diverged from source config | The deployed asset list is proven equal to source by the asset-parity gate before every publish | STEP 6, STEP 12 |
| A delivery path was reordered and its notification behavior changed | N/A: no delivery path or notification is reordered by this build | — |
| A critical boundary was config-editable and could be silently widened | The skin switch is read once from the URL at load, never from stored config; the flip to default is out of scope | §3 contracts, anti-scope |
| A "growing" archive had actually frozen | N/A: no archive is written by this build | — |
| Files were archived but their citations kept pointing at them | The round-10 renders this plan cites live in the repo folder named; no citation points into the wiped scratchpad | §0, Already true |
| A pipeline broke silently and looked identical to a working one | The fidelity check exits non-zero on any mismatch or unmeasured anchor and its exit code is asserted in the proof (a green run with no rows is impossible by construction) | STEP 4, STEP 12 |
| Output was delivered somewhere the intended reader never looks | Evidence files land under the round-10 folder's evidence/ path and their names are pasted in STEPS where the checker looks | STEP 12, STEPS |
| Concurrent sessions clobbered each other's work in a shared file | File fences give this lane its own new files; the only shared files (index.html, sw.js, contract-check.js) are edited in ONE step with re-read-before-write and a scoped commit | §3, STEP 6, §W |
| An enforcement gate covered fewer paths than its rule, or failed open | The retired-colour sweep and the plain-app fence check run over EVERY rendered element with a non-zero box, not a listed subset | STEP 4 |
| Identity or authority was read from a value the caller supplies | Identity for the check comes from the server-set df_ident cookie obtained through the real gate, never from a value the script supplies | STEP 4 |
| A new failure state was detected but reached no human | A red fidelity run or a red parity gate is a FINISH-LINE failure written into STEPS and the state file; the loop's BLOCKED question surfaces it within five minutes | STEP 0, STEP 12 |
| The builder graded its own work and passed it | Builder and checker are different models in different sessions on every step; the publish step's four verdicts come from four different agents | §3b, STEP 12 |
| A check existed that could not fail | STEP 4 proves the check can go red (a deliberate 1px padding mismatch injected with --inject prints exactly one row) before it is trusted | STEP 4 |
| The review didn't cover the shipped artifact | The checker re-runs against the PUBLISHED deployment URL, never the working tree | STEP 12 |
| A narrowing/refactoring change broke the cases that were already correct | STEP 11's dead-CSS removal re-runs every acceptance check from STEPS 6–10 and must still pass | STEP 11 |
| A check's verdict depended on wall-clock, machine load, or a concurrent writer | The fidelity check pins its viewport, theme and a settled DOM (waits for the live loader's own end state) so its verdict does not move with load or clock | STEP 4 |
| A test existed but nothing ran it | Every proof command is wired into the STEPS section and re-run by the checker; contract-check.js runs at the end of every step | §3b standing rules |
| An interactive element or view shipped untested / unseen | Every §2 interaction is driven on the real surface by the blind checker at STEP 13 (click, type, expand, check-off) | STEP 13 |
| Coverage was reported optimistically | Coverage = verified ÷ the §2 row count pinned at plan time; the number reported is `--progress`'s derived figure | VERIFICATION, §2 |
| A staleness/freshness check used the wrong proxy | Freshness of the live pull is checked by re-pulling at STEP 2, not by the file's date | STEP 2 |
| A quantitative claim shipped without its method | Every count in the plan states its method (the check's rows, `command grep -c`, the DOM count script) | STEPS proofs |
| Done was declared before the live surface was checked | DONE requires the live published URL measured at every viewport, signed in, after deploy — never the working tree | STEP 12 |
| A biometric/metric overrode the human's stated reality | N/A: no biometric or human-state data is rendered by these screens | — |
| A correlation was asserted as a cause | N/A: no causal claim is made; the fidelity count is a measurement | — |
| A nuanced reality was collapsed into a clean binary | Row kinds (link / no-link / bundle), states (loading / failed / empty / populated) and the 900–1099 band are each their own §2 row, never collapsed | §2 |
| A recommendation repeated something already tried, uncited | N/A: no recommendation about Nick's body or money is made | — |
| A wrong record was disclaimed instead of corrected | A wrong evidence line is corrected in place with git history keeping the old text; never disclaimed | §A |
| Open items were re-typed from memory and drifted | Open items live only in STEPS and the state file, rewritten in place; nothing is re-typed from memory | STATE FILE, STEPS |
| A deliverable was referenced instead of delivered | Every deliverable (sheet, script, evidence, report) is a named file path on disk, pasted into STEPS | STEPS |
| A report used names/shorthand only the writer understood | SUMMARY and every message to Nick use plain words: what he sees, at which address; no codenames | SUMMARY |
| Commands were sent to a surface that can't run them | Commands meant for Chrome run through the shared rig; commands meant for the cheap lane are single-file briefs; nothing is sent to a surface that cannot run it | §5 |
| A number was published without the population it was counted over | Every number carries its population (anchors of N, rows of M, viewports × themes) | STEP 12 proof |
| A finding existed only in the session's output and died with it | Every finding is written to the evidence folder or the state file in the same turn | §5 artefact consumers |
| The plan named a target with total precision, and the target was wrong | The locked target is Nick's approved page at a named revision; the plan targets THAT file, and STEP 1 proves the published copy is byte-identical (sha256) | §D, STEP 1 |
| The human approved a summary, and the summary was silent on the deciding variable | The confirmation sheet carries the deciding variables (desktop shows all lists; Meditation hidden under Pearl) in Nick's words, not a summary | §1a |
| A project stated its scope and never its anti-scope, and lanes leaked into adjacent work | NOT in scope lists the flip, the chrome, Health, security and the app's data feeds, each with its reason | §1 anti-scope |
| A new rule was written as prose inside its own fix, with nothing enforcing it | The rules that matter here are enforced by scripts: the fidelity check, contract-check.js, the parity gate, check_plan.py | STEP 4, STEP 6 |
| A confirmation was satisfied by checking the wrong kind of fact | V1 rows are confirmed by Nick's words about THIS screen; V2 rows by opening the live file, dated | §1a |
| A blocker common to every lane was carved out of all of them and given to nobody | The chrome (nav) is common to every Pearl screen and has a named owner lane; this plan consumes it and posts handoffs, never leaves it to nobody | §3, STEP 11 |
| Lanes were built to stop: one pass, land, idle — while fixed ceremony ate the context | Steps name their next unblocked step on failure; the loop keeps the queue full; no lane is built to idle | STEP 0, If it fails |
| A caveat nobody measured travelled as fact through multiple independent lanes | Inherited caveats (the eight measured-broken rig capabilities) are re-measured by STEP 4's preflight before they gate anything | STEP 4 |
| The environment destroyed work silently, and the lane wrote a wrong lesson from it | Scoped commits, no stash, re-read before write, step-numbered snapshots — so a destroyed edit is visible in the diff, never mislearned | §W, §3b standing rules |
| A specification described ONE lifecycle in several places, and the copies drifted independently — four consecutive cold reviews each found ~5-8 blocking ambiguities, because every patch added another partial description of the same state machine | The state lifecycle (skin off → on → published) is written ONCE in §3 contracts and referenced, never restated | §3 |
| A task brief on an existing project was treated as the plan, and a generated status checklist was treated as the task list | This PLAN file is the plan; the STEPS section is generated state; no brief replaces either | header, STEPS |
| A regression test's "red-proof" failed for a reason unrelated to the thing it claimed to prove, twice in one session, two different mechanisms | STEP 4's red-proof injects a mismatch on the very property it claims to catch, and the row printed names that property | STEP 4 |
| A standing instruction to route work to an outside/cheap engine eroded over a long session into doing the work directly | The loop's CHEAP question re-routes building to glm every five minutes; only checking and design QA stay on Sonnet/Fable | STEP 0, §5 |
| A plan's own second line named a different document as the authority, and the reader proceeded without opening it | The plan's authority line points at THIS file and the design system; the Finances spec is cited for mechanics only | header, §0 |
| A live bug got three consecutive confident wrong-or-unproven diagnoses, two claiming live verification | A live defect gets one diagnosis by running the check, whose per-anchor rows are the evidence; no confident narrative | STEP 12 |
| Fourteen guards stayed green all day while the live screen showed the wrong thing | Green gates are not the finish: the fidelity count on the live URL and the blind checker's click-through are | FINISH LINE |
| An agent was accused of fabricating its report because a narrow search failed to find the file it cited | A cited file is looked up two ways (path and suffix glob) before anyone is called wrong | §Z |
| A tool's failure verdict was believed without checking the disk — and separately, a success verdict shipped a syntax error | A tool verdict (parity gate, deploy.mjs) is confirmed on disk and on the live URL, never believed alone | STEP 12 |
| A build with several independently-shippable pieces was planned and run as one monolithic project, too large for one agent to hold | This screen is its own subproject with its own plan; Extras and Shopping never share a step | §1b |
| A rule written only in prose, with no template slot and no machine gate, behaved as if it didn't exist | The rules here have slots: the §D block, the STEPS proof lines, the STATE file; check_plan.py gates the shape | §0, §D |
| A row-quality check counted TOTAL filled cells instead of checking the specific columns it claimed to require | Proof columns name the specific property compared, never a filled-cell count | STEP 4 PROPS |
| Three independent readers reported wildly different "% complete" for the exact same objective state — twice, on two different subprojects | Progress is the derived figure from `--progress`; no session types a percentage | VERIFICATION |
| A V2 "opened it, here's what I saw" confirmation was wrong three separate times because it opened the WRONG PATH — the plan's own stated location, never independently rediscovered | V2 rows open the exact live file at the cited line; STEP 2 records path + line + what was seen | §1a, STEP 2 |
| A shared coordination file used by several subprojects at once had no per-subproject write fence, and one subproject's list silently filled with rows belonging to the others | Shared files (index.html, sw.js, contract-check.js) have a single write step and a same-hour fence stated in §3 | §3 |
| The single cheapest, most decisive test of a build's core hypothesis was defined at planning time (correctly) but not RUN until after most of the build effort was already spent | The cheapest decisive test — flag on with no composition changes nothing (STEP 6) — runs before any composition | STEP 6 |
| A dispatched build agent reported an interim status ("build is in progress, will resume once a Monitor delivers the completion notification") as its FINAL answer and returned, instead of waiting for the real result | A builder never reports 'in progress, will resume'; a step ends with a proof or a BLOCKED line naming three tries | §BLOCKED |
| A sandbox restriction produced the EXACT error text this same repo's own CLAUDE.md already documents as a sign of a genuinely broken machine ("chrome exited early, code null" / Chrome preflight failure), and it was initially read as that known problem rather than investigated as a new one | The routing hook's known refusals (cd + relative path, $VAR write targets) are named with the fix (absolute-path script files) | §0 notes |
| A paid external tool (Codex CLI) ran out of its own usage quota mid-build, and the agent that hit the limit chose to switch to running the command directly via its own Bash tool instead of the mandated Codex path — correctly, but this is a real, recurring risk that needs a standing rule, not a one-off judgment call | Cheap-vendor quota or refusal is handled by the loop's CHEAP question and the lane's fallback list, never by silently doing the work on Fable | STEP 0, §5 |
| A card-creation script reported success ("card opened... read back and confirmed") and its own internal counter incremented, but the card did not actually exist on live re-query — twice, for two different cards, requiring full manual re-creation | STEP 12's success is read back from the live URL by the checker, never from the builder's own counter | STEP 12 |
| Three separate, independently-fatal wiring gaps each made the same feature (the ai-builds board) non-functional in a different way, and NONE of them were caught by a passing `build-dist.js` run, any TIER-1 or TIER-2 gate, or any API-level check | Every wiring gap (link tag, sw.js asset, contract list) is closed in one step with one proof that lists all three | STEP 6 |
| A real, deployed code fix (the three fixes directly above) did not reach a real user's already-open browser tab, even after that user hard-refreshed multiple times | The cache version is bumped with every css/js change and the parity gate proves it; the checker loads the URL with cache disabled | STEP 6, STEP 12 |
| A correct, intentional, previously-ruled-on design decision (the task screen's default view narrows to "my own tasks" even for leadership identities) was mistaken for a bug because it was checked from only ONE identity's login | Previously-ruled decisions (Pearl approved, seven destinations, no pill, Meditation and Pets gone) are quoted with dates in §1a so nobody re-litigates them | §1a |
| The Updates panel — the actual surface a person opens to read what an agent posted about a card — is wired to Monday.com sync data ONLY, and an app-native card (this entire board) has no Monday board behind it, so it will read "No Monday updates on record for this item" FOREVER, regardless of how many real, correctly-formatted updates were posted server-side | The rendered surface (published URL, signed in) is what the checker reads; a card or evidence file is never taken for the screen | STEP 12, STEP 13 |
| A pure oversight/QA dispatch (re-run four questions, grade the answers, write nothing) was refused twice in a row by the WORK-TYPE gate as "unclear," burning two full agent-spawn round-trips before the actual task began | Checker dispatches carry ROLE: VERIFIER with no write instruction; the builder edits the plan | §5 dispatch header |
| The same brief, past the work-type gate, was then refused by a SEPARATE gate for missing the ~6,000-word MACHINE-RULES travel block — a requirement with no automatic injection and no template a brief author can copy from without hitting the refusal first | Every dispatch header carries the literal MACHINE RULES substance, both ROLE and NICK-ASKED where needed | §5 |
| A fix (new SYSTEM-prompt grounding rules) was drafted, partially applied to disk, and left in a syntactically-valid but COMPLETELY UNVERIFIED state when the tool writing it (Codex CLI) hit its own account-wide usage cap mid-task | A partially-applied edit is caught by `node --check` on every touched .js and the regenerate-and-diff on generated CSS at the end of every step | §3b standing rules |
| The above fix's failure was found ONLY because a second, genuinely fresh-context pass re-ran the real test live — the first pass's own self-check (syntax valid, code present) had already been satisfied and would have been reported "done" without it | The blind checker (STEP 13) is a fresh session that re-runs the live test; the builder's pass is never the final verdict | STEP 13 |
| A confirmed, applied data fix was verified as working because it had only been applied to ONE of two live copies of the same data (production) — the copy actually being tested against (staging) still held the old, wrong text | Two live copies (flag on / flag off; source / dist) are both checked: flag off unchanged, dist equal to source | STEP 6, STEP 12 |
| A 16-question regression suite meant to catch exactly this bug class had been silently crashing on question 1 and reporting nothing useful for a full day, because a dependency it called gained a new required argument and nobody re-ran the suite after that change landed | The fidelity script's own preflight (exit 2 on unmeasured) means a crashing check cannot read as a pass | STEP 4 |
| Two entire bodies of real, load-bearing work — a 34-file answer pipeline and this drive's own PLAN.md/STATE.md tracking pair — had never been committed to git, on any machine, the whole time they were being built, found only by accident while fixing something else | Deleting a plan step is blocked at write time (§E); the state file is rewritten in place and committed with a pathspec | §E, STATE FILE |
| A request to deepen an existing artifact was answered by re-polishing the context already in hand, while named, existing sources were never opened | 'Deepen' means gather: STEP 2 gathers from the live files, not from this plan's own text | STEP 2 |
| A gate protecting one specific, highly sensitive file covered some tool surfaces (Write/Edit/MultiEdit) but not others (Bash), and the gap sat honestly documented in the file's own header for a day before being closed | The data wall is enforced in every brief AND by the cheap lane's egress scan; the plan names both | STEP 2, §5 |
| A function parameter's DEFAULT value silently made an entire decision branch unreachable, under a fully green test suite, since the day the branch was written | No default flag hides a branch: the skin switch is explicit and its off-state is proven byte-identical | STEP 6 |
| A write-then-rename ("atomic write") pattern was used to update one row in a file that has a SECOND, independent writer appending new rows — the pattern is genuinely atomic against a torn read, and genuinely loses any row the other writer appended during the read-modify-write window | Generated files are written whole by their generator; hand edits fail the diff | STEP 5 |
| A test suite's own "red-proof" claimed a safety property held ("removing the fix would fail the test") without ever actually removing the fix and running the suite | STEP 4's red-proof removes the fix (a real injected mismatch) and shows the row; a 'would fail' claim is not accepted | STEP 4 |
| Test files that exercised a shared module's logging path wrote real output into the REAL production log file, even though every other piece of test state (queue, tickets, journal) was correctly scoped to scratch directories | Evidence files are written under the round-10 evidence/ folder, never into the app's served folders or logs | §5 artefact consumers |
| An identity verified once, in memory, from a live authenticated source, was designed to be re-derived later from a file any process could write — which would have made the file, not the live authentication, the actual source of trust | Identity comes from the live gate on every run, never a cached value | STEP 4 |
| A background daemon process registered a global crash-and-exit handler for unhandled promise rejections; a later feature fired a promise without a `.catch()` in that same process, meaning any transient failure in that one feature (a network timeout) would have crashed the ENTIRE daemon, including everything unrelated it was doing | N/A: no daemon or long-lived process is built | — |
| A build's supersession of one design ("a standalone daemon" → "extend the existing listener") correctly re-scoped every task around the new mechanism's natural shape, and in doing so quietly dropped a piece of functionality that had no obvious home in the new shape | Re-scoping (Meditation/Pets removed from the design set) is written into §1a with Nick's words and the code left untouched | §1a |
| `fs.watch()` on a shared state directory was assumed to be a sufficient delivery trigger, and was not — under real concurrent load from ~235 other sessions writing to sibling files in the same directory, two real queued requests sat with zero fs.watch event ever firing | N/A: no file watcher is relied on | — |
| A plan asserted facts about the repo it never checked — one step named a symbol that travels under a different name; another's file fence named a file that does not exist (merges log items A3, A4, D2) | Every symbol the plan names (ids, classes, functions, cache constant) was found by STEP 2's grep at the cited line | STEP 2 |
| The program fixed what was BROKEN instead of building what was ASKED FOR — a day's good work landed on a component its own plan retires (log item J1) | The North Star is the screen Nick asked for at the address he opens; steps that stop serving it are corrected in place | NORTH STAR, §N |
| A plan passed every gate — well-formed steps, real proofs — and still could not deliver what the user asked for (log item J2) | The FINISH LINE names the user-visible outcome (Pearl Shopping at the URL, zero mismatches) not gate passage | FINISH LINE |
| An assistant's first-person account of its own failure was taken as the root cause by every reader, and it was false (log item J3) | An agent's account of its own failure is re-tested by the checker before it becomes a cause | §A inherited claims |
| Three verifications were real and all three had the wrong SCOPE: verifying a quote is not verifying the claim; verifying a file once is not verifying it now; verifying the code path is not verifying the thing (merges H1, H2, H3 — one defect, three extents) | Each verification names its SCOPE: the count (fidelity), the click-through (blind checker), the taste (Sienna) — three different claims | STEP 12, STEP 13 |
| An orchestrator's confident relay propagated a wrong conclusion to five sessions faster than any plan could — a real acceptance criterion was deleted on it — and the builder that refused the relay with evidence was right (merges F1, J4) | Relayed conclusions (a peer's 'the chrome is done') are re-measured on the live URL by STEP 11 | STEP 11 |
| One writer in three read the same handoff as a gate and serialized nine of fourteen steps behind another chunk's tenth step (log item F3) | Entry gates name the specific artefact needed; no step waits on 'the previous step' | §3b Gate to enter |
| Every failure mode of the file-approval machinery was silent: an approved-once path became permanently un-requestable; a legitimate handoff into a shared governed file consumed another chunk's pending approval; approval never notified the requester; one approval unlocked exactly one edit operation, losing a two-part edit's second half; and a plan tracker named STATE.md missed the PLAN-shaped free-edit carve-out, costing ~10 approval taps in one evening (merges B1, B2, B3, B4, I2) | N/A: no approval machinery is built; governed .md writes are recorded ungoverned and handed to Nick awake | §0 note |
| A governance CLI silently dropped unrecognized flags (exit 0), let a two-token flag value overwrite the file path, let --reason swallow the next flag as its value, and its own written spec documented the broken form in two copies (merges C1, C2, C3, C4) | N/A: no CLI flags are parsed by this build beyond the fidelity check's, which fails loudly on an unknown flag (STEP 4 proves it) | STEP 4 |
| Plan shape existed as convention, not enforcement: plans degenerated into 1,000-line session logs; the plan template itself failed the machine gate; the checker validates a plan's parts, never its shape (merges A1, A2, D1) | Plan shape is machine-gated by check_plan.py; STEPS is the only state section | §0 |
| A punchlist item condensed to six words pointed its reader at exactly the wrong action — implementing it literally would have silently rerouted every assistant reply into manual approval (log item I3) | Every STEPS line carries its DEFINITION OF DONE and PROOF verbatim from the STEP block | STEPS |
| A production secret read as SET when its value was EMPTY, and every check agreed with the wrong answer for 90 minutes across three sessions | N/A: no secret is read by this build; the gate password is supplied to the rig by the vault at run time and never written | STEP 4 |
| The SAME claim, on the SAME evidence, was CONFIRMED by a checker asked to verify it and REFUTED by a checker asked to break it — and the refuting one was right | The same claim is checked by a checker asked to REFUTE it (the blind checker's brief says so) | STEP 13 |
| Reasoning ABOUT a system instead of ASKING it — the single most repeated failure of the 2026-08-27/28 night, four times across three different sessions, every time producing a confident and wrong claim from real evidence | Every question about the live app is answered by running it (STEP 2's signed-in DOM pull), never by reasoning about it | STEP 2 |
| A hard prerequisite discovered AFTER a decision, with no owner assigned, silently converts a made decision into an unimplementable one | A prerequisite found mid-drive (a missing hook, a chrome gap) gets an owner line in the state file the same turn | STATE FILE, trip-over |
| A relayed instruction is acted on, or held, by whether the RELAY ITSELF could be the attack — and sessions had no test for that, so they either obeyed every relay or refused every relay | Nick's words are quoted verbatim with dates; a relayed instruction is checked against his words in §1a before it moves a step | §1a |
| Two independent programs audited themselves on the same night and found the same disease — every instrument reported a state that was not the system's state — while both had been reading the reports as ground truth | Instruments are preflighted (STEP 4) against a known-good control page before any verdict | STEP 4 |
| A PROOF block read as complete while still containing its own template placeholders — four times in one plan, and the shape is mechanically detectable | STEP 1 greps every PROOF block for template placeholders and fails on any | STEP 1 |
| Real evidence, deliberately destroyed for a good reason, is indistinguishable from evidence that never existed | Evidence is never deleted; superseded runs stay under evidence/ with their date | §5 |
| A capability was ruled impossible on the strength of a query that structurally could not see the answer — the same shape as an earlier logged incident, on a different tool, and it was not recognised | The fidelity check queries the DOM directly; a selector that cannot see the element is reported UNMEASURED, never as absent | STEP 4 |
| The instruments used to verify a UI lie in four distinct ways, and a "drive the real surface" standard that does not name them produces confident false results | Four instruments cross-check the UI: computed styles, DOM counts, the blind click-through, and the side-by-side PNG | STEP 12 |
| A step's entry gate was satisfied and the step still could not run, and the format had nowhere to say so | A step whose gate is met but which cannot run writes `STEP N BLOCKED — tried a,b,c` into STEPS; the format has the slot | If you get stuck |
| An automated proof's own internal check detected failure and the surrounding pipeline logged success anyway — the checking logic and the reporting logic disagreed, and reporting won | The proof asserts the check's exit code AND the printed counts; a pipeline logging success over a failed check is impossible | STEP 12 |
| A dispatch gate blocked the exact defensive pattern its own preceding line prescribed, for the exact reason that pattern exists | The dispatch header is copied verbatim from §T so the gate's own prescribed pattern is what is sent | §5 |
| A fallback held in place to make a cutover safe was itself the reason the cutover could never succeed — every retry failed, and each failure made the fallback look more necessary | The Field markup stays reachable with the flag off but is never a fallback for a Pearl failure; a red count blocks the publish | STEP 12 |
| An approved instruction was correct when it was approved and harmful by the time it could be delivered — and every existing rule for handling relayed instructions asked only whether it was AUTHENTIC, never whether it was still TRUE | Nick's approval of the target is dated and the revision named; a later redline re-locks a new revision rather than acting on the old one | §D |
| "I fixed the file" · "I deployed it" · "that is what the user sees" are THREE different claims, and a chunk can be right about the first two and wrong about the third — the gap is a client cache that no repo read, no deploy log and no server-side fetch can see | Fixed / deployed / seen are three proofs: node --check + diff, deploy.mjs record, live URL measured signed in | STEP 12 |
| In a multi-session build, code read from the working tree is not the state of the system — it may be another session's half-finished fix, and reading it as established behaviour produces a confident diagnosis of a bug that does not exist | The working tree is never the source of truth for the live app; every measurement targets the deployed URL | STEP 12 |
| Three successive rounds of fixes each produced an honest, passing proof, and the user's original complaint was untouched by all three — because every proof measured the mechanism the fixer had chosen to fix, never the sentence the user actually said | The original complaint (negative space, one viewport) is a FINISH LINE item measured on the live screen, not a passing proof | FINISH LINE |
| A correct local caution was escalated into a fleet-wide halt across eight sessions on a crisis that did not exist — and the escalation priced only one side of the decision | A local caution costs one line in NEXT; no halt propagates beyond this lane | §S, §BLOCKED |
| An overseer reported two pieces of work as missing because no message about them had reached its inbox — both had landed, were logged with dates and real terms, and one had already passed a full triad | Missing work is confirmed on disk and in git log before it is reported missing | §Z |
| An acknowledgement from the system under test was read as evidence of the outcome — the same word, `queued`, covered a genuine pass and a silent 40-minute failure on the same endpoint the same night | `queued`/`Staged` acknowledgements are read as acknowledgements; the outcome is read from the queue card or refetched list | STEP 10 |
| An overseer authorized an action by bridging a DIFFERENT ruling of the user's onto the question — reasoning correctly from a real quote that was about something else, three relay hops from where it was said | No approval is bridged from another ruling; each V1 row quotes Nick on THIS screen | §1a |
| An agent, blocked by a safety guard mid-test, offered the user a choice between loosening the guard and accepting weaker proof — presenting a load-bearing protection as one of two equal options | A guard that blocks a test is reported as NOT MEASURABLE — PERMISSION NOT GRANTED, never traded for loosening it | §A evidence states |
| A fault that repairs itself faster than anyone reports it is invisible to every alarm in the system — two family-facing surfaces cut out roughly twice a day for a MONTH and nobody escalated once | Intermittent faults are run three times by the checker before a clean read is accepted | STEP 12 note |
| A relayed approval was acted on as if the work were still outstanding — and the same file had already been written, by the session doing the relaying | A relayed approval is checked against the file and git before work is redone | §Z, STATE FILE |
| An investigator noticed that a metric could not possibly detect what it was being asked to detect, WROTE THAT DOWN, and then built a headline claim on it anyway — because the number it produced agreed with the conclusion | A metric that cannot detect what it is asked to detect (a check with no red-proof) is replaced, not caveated | STEP 4 |
| An investigation's own searches and relays contaminated the evidence it was searching for — 80 of 84 occurrences of the string were manufactured by the act of investigating it | Searches for a hook are run before any takeover writes it, so the evidence is not contaminated by the build's own output | STEP 2 before STEP 7 |
| Three unrelated lanes in one night each ran an honest check against an intermittent fault and each got a clean answer, because a point-in-time probe is mathematically almost certain to miss a fault that heals itself | Intermittent feed states (beach/vault/transcribe status) are forced deterministically with network blocking, not sampled | STEP 10 |
| An overseer holding the user's GENUINE first-hand instructions relayed them as authority to four sessions — and one correctly refused, because accuracy and standing are different things and only one of them travels | Overseer relays carry Nick's quote; no relayed line moves a step without the quote | §5 |
| A file that documents its own version history in prose ABOVE its code turns every unanchored search into a lie — three sessions in one hour read the changelog and believed it was the declaration | Every grep in this plan is anchored to a line range or a unique token; file histories in prose are excluded from proofs | STEP 2 |
| A commit hash cited as closing evidence resolved to nothing later — sometimes minutes later — because the citation was checked when it was written and never at the moment it was relied on | Every cited hash is checked reachable and pushed at citation time (`git cat-file -e` + `git branch -r --contains`) | STEP 12, STEP 14 |
| A step's own PROOF COMMAND, not just a claim someone else wrote, over-matched — pointed at the right file this time, it still returned a plausible, close, wrong count | Proof commands name the exact file and a token unique to the change; the checker confirms the token is not matched elsewhere | §3b DONE-PROOF |
| A check reported PASS five separate times on one feature while the live screen was wrong every time, and the check's own instrument then reported FAIL five separate times on code that was correct — the same fake page lied in both directions | A check that passes while the screen is wrong is caught by the side-by-side PNG and the blind checker; the check's own red-proof is re-run at STEP 12 | STEP 12, STEP 13 |
| A deliberate, reviewed, gate-passing commit was pre-empted by an automatic snapshot that bundled the change with unrelated files, destroyed its commit message, and meant the commit-time gate never executed at all | Commits are scoped and made in the same turn as the proof; the parity gate runs before deploy so an automatic snapshot cannot bundle a foreign change | §W, STEP 12 |
| A blind checker's whole verdict came back UNVERIFIED because the route into the walled surface it was handed was a remembered ruling, not the measured route | The blind checker's brief carries the MEASURED route into the walled surface (`POST /__gate` with `pw`/`identity`/`next`, the password read from the vault at run time), not a remembered one; the check's preflight signs in and fails loudly (exit 2) before any verdict | STEP 4, STEP 13 |
| A scoped restyle rule read correctly, passed its rig and the design QA, and never applied on screen: an inline style set by a frozen script beat it | Every restyle step's proof is the fidelity check's COMPUTED styles on the live node, so a rule beaten by a frozen script's inline style shows as a mismatch, never as a pass; STEP 8 names inline styles the app writes (`style="flex:1;"`) and neutralises by property, never by assuming the rule won | STEP 4, STEP 8 |
| A cache-busting parameter placed in the URL hash changed which screen the app believed it was on, so a re-check measured another screen's tokens and reported a false FAIL | Cache-busting lives in `?v=N` on asset URLs and the parity gate, never in the hash; the check asserts `data-pl-screen` equals the screen key before measuring and refuses a run whose route resolved elsewhere | STEP 6, STEP 4 |
| Three of five blind-check reds were the brief's own narrowing of the pinned design (an accent allow-list shorter than the pin's list, "one line" for a row the pin wraps, an icon measured on an opacity-0 overlay) | The blind checker is briefed with §2 verbatim and the LOCKED page — never a narrowed restatement; an accent allow-list in a brief is copied from `tools/pearl-accent-sites-todo.json`, and any red the brief itself caused is struck by the overseer, not counted | STEP 13, §3 |
| A verification read an eventually-consistent store within seconds of writing it and recorded the stale answer as a product defect | Reads after a write (a Someday move, a check-off, a deploy) wait for the app's own re-render or the CDN's settled state (the check re-fetches with cache disabled and retries three times) before a defect is recorded | STEP 8, STEP 12 |
| A test closed ONE of several identical inputs and read the correct unchanged output as a bug | Drive proofs act on ONE named test row (`pearl-check-<date>`) and assert the change on THAT row's `data-id`, never on the first of several identical rows | STEP 8, STEP 13 |
| A routing or safety filter matched a keyword in a PARAMETER NAME rather than in any content, and refused benign mechanical work three times in series | Briefs avoid the words the routing filter trips on (named in §0's hook notes) in parameter names as well as content, and a refusal is read for what it matched before the brief is retried | §0, §5 |
| Two cooperating passes wrote the same artifact filename and the richer one was silently lost while a grader was reading it | Every artefact file name carries its step and screen (`todo-fidelity-live.txt`, `todo-blind-check.md`); no two steps write the same path, and STEP 12's runs are numbered | §3 fences, STEP 12 |
| A machine owning a whole role went dark, and its peer's CORRECT standby behaviour silently froze 146 scheduled jobs for fourteen hours | N/A: this plan runs no scheduled jobs and no standby machine; the drive is a session with a state file a stranger resumes from | — |
| An abandoned merge blocked every commit in a shared workspace for every session, and nothing detected it | Every step's first action is `git rev-parse --show-toplevel` and `git status --porcelain` on its own worktree; an abandoned merge or a lock file is reported as the step's BLOCKED line with the path, and the lane works in its own branch, not the shared checkout | §3b standing rules, §5 |
| An append to a SYMLINKED path was committed as the unchanged link, so the content change was never staged and a later merge silently discarded it — while every check in the session read the file through the symlink and saw the change present | The plan, state and change files are real files in the repo (never symlinks); the checker confirms `git ls-files -s` shows mode 100644 for each before a commit is cited | STEP 1, STEP 14 |
| NOVEL — the drawing was approved WITH a bottom fade that Nick called "ghosted overlays" on another screen the next morning; locking it as-is builds a thing he now dislikes, changing it silently alters an approved drawing | §1a row 5 defaults to removing the fade as a REV bump that Nick sees on the published page; his "keep the fade" reverts it in one check round | §1a, STEP 1 |
| NOVEL — the pull's counts contradict each other (header 0 vs 7 overdue; week 0 vs 4 rows; 12 vs 14); a builder who "fixes" them hides a real data bug | §3 contract: the trio's numbers are pop.js's and the bucket headers' numbers, never recounted; STEP 14 raises the four contradictions upstream as one handoff | §3, STEP 14 |
| NOVEL — `renderTodoPanel` re-creates and re-binds its rows on every render; a takeover that re-creates nodes would drop the check-off, Someday and thread handlers | STEP 8 restyles and re-places the existing nodes with CSS order, never re-creates them; the Pearl-pass counter must equal the app-render count; `--drive rows` proves every handler | STEP 8 |
| NOVEL — three lanes (Shopping, Extras, To-Do) edit `index.html`, `sw.js` and `contract-check.js` in the same drive | the one overseer thread opens each lane's STEP 5/6 one at a time; HOLDING lines written before the first edit; the shell layer built by whichever lane reaches it first and fetched by the others | §3, STEP 5, STEP 6 |
| NOVEL — task text is personal (a hospital report, a child's blood draw); a cheap brief or evidence file could carry it off the machine | Brief C runs on Sonnet and masks every row; the check's data-floor self-test proves a sentinel never reaches its output; three known task words are grepped to zero at STEP 2 | STEP 2, STEP 4 |
| A capture instrument reported an element blank (a sketch box, then menu icons) while every DOM and computed-style probe said visible; three fix rounds were built against the phantom | A blank in a capture is graded against the computed-style and DOM probes first; a fix round opens only when a probe agrees with the image, and the rig's occlusion/beyond-viewport modes are named in STEP 4's preflight | STEP 4, STEP 12 |
| A cheap vendor re-saved a 40 KB checker file whole twice, and its own proof reverted it both times for a removed line | The cheap lane rewrites only files under its 60 KB read cap; the fidelity check (STEP 4) and any file over the cap are authored on Sonnet/Opus, and every `--prove` names what must NOT change so a whole-file re-save that drops a line fails | STEP 4, §5 |
| A concurrent lane's publish from `main` landed seconds after a branch lane's publish and took the SAME cache number, so the version said "new" while the served bytes were the old script | STEP 12 deploys only from a branch rebased onto `origin/main` with CACHE = the rebased value + 1, records the deploy hash, and the checker hash-compares the SERVED bytes of each new asset against that commit before any count is trusted | STEP 12 |
| A first-paint acceptance band ("now-line between 25% and 42% of the visible box") graded the only correct rendering FAIL, because nothing above the line existed to scroll away at that hour and box height | No geometry criterion here depends on the clock or on data volume without stating its precondition: the column rule (STEP 9) is proven on both branches with replayed payloads, and a check that cannot meet its precondition prints NOT MEASURABLE, never FAIL | STEP 9, §A |
| A failure path (the sweep's "post a finding to the inbox" step) shipped untested and failed silently the first three times it fired — once on request shape, twice by mis-filing a machine failure as a code regression | Every failure path this plan relies on (the fidelity check's exit 2, the parity gate's red, the fence check's non-zero) is exercised on purpose at STEP 4 and STEP 6 before it is trusted; a machine/rig failure is reported as its own class (NOT MEASURABLE — instrument), never as a product defect | STEP 4, STEP 6, §A |
| A prose section appended through an unquoted shell heredoc executed the backticks in its own text and pasted 155 lines of a selftest's output into an agent guide, unnoticed for seven hours | Plan, state and evidence prose is written with the Write tool or a quoted heredoc (`<<'EOF'`), never an unquoted one; STEP 14's checker greps the closing artefacts for shell output before closing | STEP 14, §5 |
| Screenshots taken "signed in" showed the signed-out screen: the server accepted the sign-in but the app in the already-loaded page kept `identity: null`, so every capture graded the wrong screen while the log said sign-in worked | The check mints the gate cookie BEFORE the first navigation (live-lib's signedInBrowser sets the cookie, then loads), and asserts the app reports the signed-in identity (the person chip active, the hero eyebrow present) before measuring; a signed-out capture is exit 2 | STEP 4, STEP 12 |
| A checker declared a growing list "settled" after two equal reads 700 ms apart and graded a missing item as a product defect; a second checker read the wrong control entirely and reported five boards "unreachable" that were on screen | Settled means the app's own end state (every `.td-list` without `.td-skel`, polled up to 60 s), not two equal reads; every selector in the anchor map matches exactly one live node (STEP 3 refuses ambiguous ones), so a checker cannot read the wrong control | STEP 2, STEP 3, STEP 13 |
| A build gate that insisted on a symlink into a second repository failed every publish after another lane made the file a tracked regular file — both lanes wanted "one reviewable copy" and the gate encoded only one route to it | No gate in this plan asserts a mechanism (symlink vs file); the parity gate and the fidelity check assert outcomes (served bytes, computed styles), and a shared file's route is whatever `origin/main` holds at the rebase | STEP 6, STEP 12 |
| An instrument measured the wrong page for one of two subjects and its failure read as a fault in the page it never opened — every two-identity run of the fidelity check died on "no frame found with .cap text 'To-Do · Chantelle · 375'" against a design page that carried the caption byte-for-byte, because one Chrome served the run and the design page was never re-opened after the first identity's live measurement | STEP 12's run brings the design page back and reads its location back before the second identity's frame read; a "not found" now names the page it searched (its href and every caption on it) | the third zero run's report carries both identities' rows, and a deliberately wrong caption prints the searched page's href and caption list |

## 5 · Topology and roles
- **OVERSEER-AUTHORITY:** none named for the family app in `projects/ops/OVERSEER-AUTHORITY.md`'s CURRENT HOLDER table at write time (2026-09-05) — this plan's lane works under the Pearl screens bucket's own Fable overseer (shared with the Shopping and Extras plans) per Nick's 2026-09-05 tiering. The four approval classes and the data floor never move on the overseer's word.
- Thread layout: ONE overseer thread (Fable) for the Pearl screens bucket; one worker session per running step, dispatched with the §T header, never idle-waiting; this lane works in its own git worktree on branch `pearl/todo` (cut from `origin/main`), every commit pushed the moment it lands.
- Overseer: fable (unsticks, design-QA oversight; never builds, never swarms one finding) · Lane manager: none at this size · Workers: glm builders (Opus/Sonnet when the cheap vendors are down), deepseek extractors, sonnet checkers/test authors/the DOM pull, fable (Sienna) design QA, se-blind-checker, verifier.
- State files location: `projects/personal/family-app/` — `STATE-PEARL-TODO.md`, `PLAN-CHANGES-PEARL-TODO.md` (both created by STEP 1, beside this plan). No QUESTIONS.md/ASSUMPTIONS.md: every open question is a §1a row; assumptions are the DEFAULTED rows.
- **Board card id:** none yet
- **Artefact consumers:** ground truth → STEPS 3, 7–10; anchor map → STEP 4; the check → STEPS 6–13; evidence files → STEP 12's four verdicts and STEP 14; handoff lines → `PLAN-HOME-PEARL.md` STEPS (chrome), `PLAN-PEARL-SHOPPING.md` and `PLAN-PEARL-EXTRAS.md` STEPS (shell, REV), `PROJECT.md` (the data contradictions, proposed). Every raise path is proven to ARRIVE by the receiving file's own line (the checker reads it back).
- **Write-contention:** this lane owns its NEW files outright; the three shared files are edited once, at STEP 6, by one builder, in a step the overseer opened for this lane alone, re-read before write, scoped commit; the shell layer is fetched from the lane that built it; the checkout is proven writable at STEP 1 and re-proven at STEP 6 and STEP 12.
- **Concurrency:** hard ceiling 8 simultaneously-running agents in this session, machine-wide budget ~40 shared with every live session — count `ls /tmp/cc-socks/` before the first wave and divide; a wave that has not returned is load, not progress. Raising either number is Nick's call, named here.
- **Dispatch header (verbatim, every dispatch):** `ROLE: <GATHERER|BUILDER|VERIFIER|CRITIC|RECONCILER>` · `NICK-ASKED:` only when he named the model · `REVIEW: t2` · `RETURN-SIZE: ~1500 tokens — write findings to disk, return a pointer` · the literal words `MACHINE RULES` with the substance pasted (never a bare `git commit`; never `git stash`; re-read before write; `command grep`; the four approval classes; the data floor — task text and the Business rows never leave the machine; nobody grades their own work; an empty result is evidence about the search; no security work; no second plan file) · the task, the file fence, the exact proof, the stop conditions. Cheap-lane briefs additionally: repo-relative paths only, no folder listings, no `.md` files, no money values, no task text, no credentials, never the word that trips the secret filter.

**Per-stage topology — counts DECLARED at plan time:**

| Stage | Overseer | Sub-overseers | Workers |
|---|---|---|---|
| Plan + Design (STEPS 1–3) | 1 | 0 | 3 |
| Tests + Framing (STEPS 4–6) | 1 | 0 | 3 |
| Elements + Details (STEPS 7–10) | 1 | 0 | 4 |
| Tests + Output (STEPS 11–12) | 1 | 0 | 4 |
| Proof (STEPS 13–14) | 1 | 0 | 2 |

**The walk-away contract — a stranger resumes the drive from files alone:**
- **STATE FILE:** `projects/personal/family-app/STATE-PEARL-TODO.md` (created at STEP 1, current-state only, rewritten in place)
- **HEARTBEAT ROW:** pearl-todo-drive, registered by the drive coordinator in `projects/personal/skippy-app/ala-state/work-threads.json` when the drive opens
- **MORNING-REPORT LINE:** "Pearl To-Do — <n>/24 manifest rows verified, current step, next unblocked step, fidelity counts per person on the live URL" in `projects/ops/walkaway/REPORT.md`

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

| Capability | Check (exact command or procedure) | Pass looks like |
|---|---|---|
| (1) Pearl To-Do renders from the live lists with zero invented strings, contradictions as pulled | STEP 12's live check + STEP 2's string list diffed against the rendered text; the trio's numbers equal pop.js's and the bucket headers' | `mismatched properties: 0 · unmeasured anchors: 0` ×4; every rendered string is in the ground-truth list |
| (2) Every control works through the existing handlers | STEP 8 `--drive rows` and STEP 13's blind check | every ✓ line (or the recorded NOT MEASURABLE for Someday); T1, T8–T11 PASS |
| (3) Every hero / loading / failed / empty / error string verbatim | STEP 10 `--states` | `states: N/N reached · strings verbatim: N/N`, N derived from the ground truth |
| (4) One viewport at 1280×662 for Nick and for Chantelle, columns meeting the menu's bottom, buckets scrolling inside without fades | STEP 9 `--only layout` and STEP 12 live | `frame: 662 · rail: 40→622 · col1: →622 · col2: →622 · col3: →622` ×2 |
| (5) Phone at 375 and the 900–1099 band | STEP 11 `--band` | `1024: rail absent · scrollWidth<=clientWidth ✓` ×2 |
| (6) Noah's Needs and Business hidden under the skin, their code untouched | STEP 7 and STEP 12 `--hidden-tabs` | the four ✓ |
| (7) Flag off unchanged (all four tabs), every other view unchanged | STEP 6 and STEP 12 `--fence-only` | `fence: 0 changed elements (flag off)`, all views |
| (8) Zero mismatches against the locked target, both people | STEP 4's check on the live URL, three runs | three consecutive runs of four zeros |
| (9) Personal only — no business work on this screen | STEP 15's audit, re-run at STEP 12's publish | every flagged row is listed for Nick with the term that matched; zero rows hidden by the screen |
| The cheapest invalidating test, run first | STEP 6: wiring with the flag on and nothing composed changes nothing | both fence runs print 0 before any composition is written |

## If you get stuck (all steps)

Before writing "blocked": (1) try a concrete workaround, (2) re-read the step's proof requirements — most "stuck" is a misread gate, (3) write one line to the overseer AND the owner of the blocker. Only then log `STEP <N> BLOCKED — tried: <a>,<b>,<c>. Need: <one sentence>.` Then keep working every other unblocked step. Never idle on a blocker. The gates that DO stop work: the four approval classes · the data floor · the §S security click · a proof that would destroy live data. Nothing else does.

## Your loop

Every pass: find the lowest-numbered step whose enter gate is proven and which is not yet proven → do it → produce its proof → paste the proof under the matching item in STEPS below → repeat. STEP 0's five-minute loop runs the whole time.

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

The To-Do screen is built, live and measured at zero difference from the drawing Nick approved (REV 12.4, "its close enough", 2026-09-06). What he sees at family.heroesandsidekicks.io/#todo: a black Today card that is the biggest thing on the screen with the day's count and a thin progress bar, This week beside it, Next week and Later capped, Someday a short quiet card, and nothing overdue — anything overdue is re-dated to today each night at 00:05 and 07:05. The old look at `?skin=field#todo` is untouched, and the To-Do screen never paints over another screen. The drawing Nick approved after four rounds of his feedback is the target; the first step republishes it with the bottom smear removed, at a login-free address, and records which version it is. Then the screen is rebuilt inside the real app behind a switch, measured against that drawing until the count of differences is zero for Nick's list and for Chantelle's, and checked by someone who did not build it. The Noah's Needs and Business tabs are hidden under the new look and their code is left alone. Nick's only wait is a yes on the published drawing; everything else runs without him.

## STEPS

**RESUME SNAPSHOT — regroup 2026-09-06.** Every line below was RE-RUN first-hand by a cold verifier this morning, not restated from this plan. Full command and output per step: `redesign-mockups/concepts-2026-09-04-round10-screens/evidence/regroup-2026-09-06/` (SUMMARY.txt plus one `step-N.txt` each). Thirty-four atomic units checked: 20 PROVEN, 5 FAILED, 9 UNPROVEN.

**TRUE NOW**
- STEP 1 — the target is locked and live: `gen.mjs` REV 10.9, the published page's sha256 `07159f940baf2dba…` identical to the generator's own output, `mask-image` 0, the locking commit `0eb9dbd21` on `origin/main` (step-1.txt). The hash this plan quoted before today (`0884fac2…`) was stale; the page has since moved to REV 10.9.
- STEP 2 — ground truth stands: ids 10 · classes 56 · data 2 · strings 33 · endpoints 37; the three data-floor zero-greps print 0; the three script shas equal the recorded baseline (step-2.txt).
- STEP 3 — the map is signed: 156 rows, both `SIGNED BY` lines verbatim (step-3.txt) — and its eighteen pre-card rows are now REPOINTED onto the card form (Sienna 13:00Z, applied in `gen.mjs-anchors-todo.md`; conditions as RULING (l), the This-week acceptance as RULING (m)).
- STEP 4 — the check exists, is 51KB and can go red on demand: `--selftest` prints `red-proof: 1 mismatch (paddingTop) · green-proof: 0 · floor: 0`, `--bogus` exits 2 (step-4.txt).
- STEP 5 — the shell layer regenerates identically (step-5.txt).
- STEP 6 — parity 12 passed / 0 failed; the served page carries `pearl-todo.css?v=8` and `pearl-todo.js?v=5`; and the 12:40Z hidden-view incident does NOT reproduce — fifteen combinations (five other screens × 375/1280/1728) all read `display:none` with the `hidden` attribute (step-6.txt). Two of its five units FAILED; see the step.
- STEP 7 — the header is at zero for both people, `--hidden-tabs` prints its four ✓, and `js/pearl-todo.js` makes no network call (step-7.txt).
- STEP 15 — the audit is on file and correct: 85 rows, 6 flagged by matched term only, nothing moved (step-15.txt).

**LEFT**
- **STEP 8 — CLOSED 2026-09-06 15:30Z: built, published, five proofs passing first-hand and re-run by a cold checker (PASS)** (`evidence/todo-step8-redo-proof.txt`). Of its six items, the idle storm and the foot colour were real and are fixed; the bar and Chantelle's Someday card were data-conditional (proven under a forced state, RULINGS (o)/(p)); the retired colours read 0 today; the drive proof's mechanism was already rebuilt and its wording now matches the plan. REV 11 of the drawing is the new open item (the 375 phone frame, the Someday-row date cell, the This-week frame), needing Nick's yes before STEP 12.
- **STEP 6's two failed units** — `contract-check` red (574 PASS / 22 FAIL, all 22 on ids unrelated to To-Do) and the old-look fence at 13 changed elements against a baseline captured before this lane's own wiring landed.
- **STEP 9 — CLOSED 2026-09-06 16:5xZ** (Nick: "desktop is a crowded overlapping mess"; css v13 / js v8; the cold checker's PASS in evidence/todo-step9-check.md). **STEPS 10, 11, 12, 13, 14 — NOT STARTED.** STEP 12 waits on REV 11's yes (six redlines bundled: (q) the 375 frame, (r) no date on a Someday row, (v) the accent-presence sites, (x)/(y) the phone header, (ae) the 121px bottom clearance, (m) the This-week frame).
- **STEP 15's second reader** and the triage list handed to Nick.

**NEXT PICKUP** — start with `git -C "/Users/nickdeck/Documents/Claude 2.0" fetch -q origin main` and read this plan, `STATE-PEARL-TODO.md` and `PLAN-CHANGES-PEARL-TODO.md` FROM `origin/main` (`git show origin/main:<path>`), because the main checkout can sit tens of commits behind while `git status` calls all three clean; then open `redesign-mockups/concepts-2026-09-04-round10-screens/evidence/todo-step8-check.md` (the checker's own refusal) beside `redesign-mockups/concepts-2026-09-04-round10-screens/evidence/regroup-2026-09-06/step-8.txt` (the independent re-run of it) and run from the main checkout:
`node projects/personal/family-app/tools/pearl-fidelity-todo.mjs --only cards --as nick`
That is the STEP 8 redo's first measurement, and the two handoffs the state file records as still OPEN travel with it: the hidden-view defect needs ONE guard across every Pearl view from the chrome owner (three occurrences on two screens, each fixed one screen at a time after a person saw it), and the evening filter makes every computed-colour zero blind between 7pm and 6am, so STEP 12 records the hour it ran and reads a rendered pixel (§3 known instrument limit). Everything else in this plan waits behind it. The lane lands on `origin/main` directly through the landers in `redesign-mockups/concepts-2026-09-04-round9-finance/build-proofs/landing/` — never merge `main` into `pearl/todo`.

1. [Plan] Lock the target: fade removed as a REV bump, regenerate, publish login-free, record the revision — 100%
   VERIFIED (lane, 2026-09-06 03:20Z): re-published after another lane's design-site publish dropped the page (404 measured 03:05Z); `todo.html` regenerated, `mask-image` count 0, `family-pearl-todo-r10.html` byte-identical, deployed with `node deploy.mjs skippy-designs`, served bytes after the redirect equal to the local hash; the design files also landed on `origin/main` so any lane's design-site publish keeps the page. Nick's approval (2026-09-05, §D) stands — the redraw he approved is unchanged except his own legibility redline.
   VERIFIED (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-1.txt): PROVEN, re-run first-hand — `command grep -c '^// REV '` prints 1 and the header reads REV 10.9; the curl prints 200; the served sha256 `07159f940baf2dba28db301450c9b3b4f2916986e76385ab77e771970afdc03f` is IDENTICAL to `shasum -a 256 todo.html`; `mask-image` count in the served page is 0; `git branch -r --contains 0eb9dbd21` names `origin/main`. Correction recorded in place: the hash this plan quoted (`0884fac2…`) was from the 03:20Z publish and is stale — the live page and the generator agree with each other at REV 10.9, which is what the proof asks.
   DEFINITION OF DONE: the §D block carries `REV <n> · commit <hash>` and Nick's dated words on the published page; the published sha256 equals the generator output's; no mask-image in the output
   PROOF: `command grep -c '^// REV ' projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round10-screens/gen.mjs` prints 1; the curl prints 200
2. [Plan] Ground truth for To-Do — 100%
   VERIFIED (lane, 2026-09-06 04:40Z): `ground-truth-todo.json` carries ids 10 · classes 56 (28 static + rendered) · data 2 · strings 33 · endpoints 37, the live signed-in DOM for nick (70 rows, 4 buckets, 0 skeletons) and chantelle (15 rows, 3 buckets) captured at 1280×900 with every row masked IN PAGE, the hero next-up masked, the business panel emptied, and the three script shas. The first capture was refused twice — once synthetic (a worker substituted static HTML under lock contention), once with the hero unmasked — and neither ever landed.
   VERIFIED (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-2.txt): PROVEN on all four units — the four floor counts (10 / 56 / 33 / 37, each at or above its floor), the live per-person row/bucket/skeleton counts, the three data-floor zero-greps all 0, and all three script shas matching the repo files exactly. Instrument note recorded rather than assumed: an unauthenticated fetch of `/js/app.js` returns the sign-in page (the identity gate working as designed), so the sha claim's real proof channel is the tool's own authenticated `--hidden-tabs` run, which also came back clean.
   DEFINITION OF DONE: the ground-truth file exists with ids, classes, data-attributes, verbatim strings, endpoints, the signed-in rendered DOM per person with task text masked, and the three script shas
   PROOF: the four-count line prints ≥ 10 · 30 · 16 · 6; the task-word grep prints 0
3. [Design] Anchor map, two tables, signed by design QA — 100%
   VERIFIED (Sienna, creative-director, 2026-09-06): the first map was REFUSED with eight corrections (a missing Later card, `.wk i`, `.wk span.has`, four not-drawn rows counted as drawn, two duplicate `.lv.td-lv` rows, the pills rows, the fade rows, `pl-dark`→`pl-shdark`); all eight applied, rulings (a)–(k) recorded in the map, signed `Nick: elements drawn: 72 · anchors: 71 · gaps: 1` and `Chantelle: 69 · 68 · 1`. Her instrument note stands: her signature certifies the map's structural coverage, not the rendered picture — the look-at happens at STEP 12.
   VERIFIED (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-3.txt): PROVEN on all three units — 156 table rows, both `SIGNED BY` lines present verbatim, and the eighteen pre-card rows confirmed still on the OLD selector form at the time of the run, exactly as the state file recorded.
   ALREADY TRUE SINCE (regroup, 2026-09-06 13:00Z): those eighteen rows are now REPOINTED in place onto the card form — Nick 39, 42–47, 49, 51, 52 and Chantelle 38, 41–47 — with design selectors, compared properties, person scoping, both signature lines and N/M/K unchanged, nothing retired. Sienna's two conditions are RULING (l) and her This-week acceptance is RULING (m) in `gen.mjs-anchors-todo.md`.
   DEFINITION OF DONE: every drawn element per frame is an anchor or a signed GAP (≤2 each); viewports signed; the pills ruling recorded
   PROOF: the anchor map carries two `SIGNED BY` lines and 156 rows
4. [Tests] The fidelity check with its red-proof and data-floor self-test — 100%
   VERIFIED (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-4.txt): PROVEN on both units — `node tools/pearl-fidelity-todo.mjs --selftest` prints `red-proof: 1 mismatch (paddingTop) · green-proof: 0 · floor: 0` and exits 0; `--bogus` prints `unknown flag` and exits 2. The file exists and is 51KB. This plan's own progress line had recorded the step at 0% / IN FLIGHT: the LINE was stale, not the work.
   OWED (not a proof failure, a scope addition): the `--others` flag of standing rule 11, added at this step's next edit.
   DEFINITION OF DONE: `--selftest` prints `red-proof: 1 mismatch (paddingTop) · green-proof: 0 · floor: 0`
   PROOF: `node projects/personal/family-app/tools/pearl-fidelity-todo.mjs --selftest`
5. [Framing] The shell layer present — 100%
   VERIFIED (lane, 2026-09-06 03:15Z): `css/pearl-shell.css`, `tools/pearl-shell-rename.mjs`, `tools/pearl-shell-class-map.json` and `pearl-shell-tokens.css` are on `origin/main` (Shopping's STEP 5 landed; `index.html` links `css/pearl-shell.css?v=1`). Nothing was built here.
   VERIFIED (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-5.txt): PROVEN — `node tools/pearl-shell-rename.mjs --check` prints `regenerated: identical`, exit 0.
   DEFINITION OF DONE: the shell stylesheet exists in this lane's checkout and regenerates identically
   PROOF: the renamer's `--check` prints `regenerated: identical`
6. [Framing][UI] Wire in, painting nothing — 70% (three of five units proven; two FAILED, neither closed)
   VERIFIED (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-6.txt) — PROVEN: (A) `node projects/ops/skippy-jobs/_test-family-app-asset-version-parity.mjs` prints `12 passed, 0 failed`. (C) the signed-in fetch of the live app carries `pearl-todo.css?v=8` and `pearl-todo.js?v=5`. (E) THE CHECK THIS STEP NEVER HAD, run live: signed in as nick, `#view-todo` read on `/#home`, `/#finances`, `/#calendar`, `/#shopping` and `/#extras` at 375, 1280 and 1728 — all fifteen combinations return `display:"none"` with the `hidden` attribute present. The 2026-09-06 12:40Z incident does not reproduce and the css v8 hotfix holds. That check is now standing rule 11 and is part of every step's proof from here on.
   FAILED (B) — contract-check is NOT green: `node projects/personal/family-app/contract-check.js` prints `574 PASS, 22 FAIL` and exits 1, against standing rule 5 which requires it green at the end of EVERY step with the number quoted. The 22 are pre-existing ids belonging to other parts of the app (`onb-*`, `roster-count`, `sk-*`, `watch-count`, `tools-*`, `mc-jobs*`, `taskSearch`) — none carries `pl-` or `td-`, none belongs to the To-Do screen, and no evidence attributes any of them to this lane. **This is a project-wide gap that rule 5 has been silently failing to enforce, not a defect this regroup introduced, and it is not this lane's to clean up** — it is raised here and stays raised.
   FAILED (D) — the old-look fence: `--fence-only --as nick` printed `fence: 13 changed elements (flag off)` on two runs within a minute (22798 and 22781 elements measured), where the stated proof line is 0. **Cause AMBIGUOUS and deliberately not closed either way:** the baseline file `evidence/todo-fence-baseline.json` is dated 2026-09-06 02:22Z — BEFORE this lane's own STEP 6/7/8 wiring and before other lanes' shared-chrome changes — so 13 of the 15 tracked views differ from a stale reference. It could be this lane's chrome leaking onto the old look, or legitimate churn from concurrent lanes. Separating them needs a baseline RECAPTURED on the current `origin/main` and a controlled diff, which is named in STEP 8's proof.
   DEFINITION OF DONE: link + script + sw.js + contract-check updated in a step the overseer opened for this lane; parity PASS; the old-look fence 0 against a current baseline (all four tabs); the fifteen other-screens combinations `display:none`
   PROOF: `node projects/ops/skippy-jobs/_test-family-app-asset-version-parity.mjs` prints PASS; the check's `--fence-only` prints 0 against a baseline recaptured on the current `origin/main`; the other-screens check prints fifteen `display:none`
7. [Elements][UI] Header, person chips, wash, accent sites; Noah and Business hidden — 100%
   VERIFIED (lane, 2026-09-06 06:05Z): `css/pearl-todo.css` + `js/pearl-todo.js` carry the STEP 7 scope (the four tokens re-declared; `.pl-wash` as first child; the `.pl-ph` header built beside the hidden `.topbar` with the eyebrow from the chrome's own date line, "<n> due" from the nav badge the app already computes, and the split line as the app writes it; the `.segmented` restyled in place and moved onto the header row at ≥1100 by CSS order; the noah and business chips `display:none`), plus `tools/pearl-accent-sites-todo.json` with five sites (`.pl-prog > i`, the `chk-done` row state — the app has no `.chk.is-checked` —, the `.pl-wash` tint, and the chrome's two). Landed on `origin/main` and published; served bytes identical to the tree.
   VERIFIED (verifier, 2026-09-06 06:25Z, evidence/todo-step7-check.md): PASS — `--only header` live 0/0 for nick and chantelle (14 rows each); `--hidden-tabs` four ✓; an independent accent walk over `#view-todo` plus the chrome found 0 illegal accent hits; chip clicks switch panels and `is-active`; hidden chips `display:none` with their panels hidden; no network read; the three shas match the baseline.
   VERIFIED (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-7.txt): PROVEN on all four units, re-run first-hand — `--only header --as nick` and `--as chantelle` both print `mismatched properties: 0, unmeasured anchors: 0, retired colours: 0`, exit 0; `--hidden-tabs` prints its four ✓; `command grep -cE "fetch\\(|XMLHttpRequest|sendBeacon|EventSource" js/pearl-todo.js` prints 0.
   CARRIED FORWARD, not this step's: the retired-colour sweep reads 26 on Chantelle's panel against 0 on Nick's — outside STEP 7's own proof and worked as item 5 of the STEP 8 redo.
   DEFINITION OF DONE: header anchors at 0 at both viewports for both people; the four `--hidden-tabs` ✓
   PROOF: the check's `--only header` and `--hidden-tabs`
8. [Elements][UI] The dark card and the bucket cards with every row control — 100%
   VERIFIED (cold checker, Sonnet verifier, 2026-09-06 15:30Z, evidence/todo-step8-redo-check.md): VERDICT: PASS — every proof re-run first-hand from a clean worktree of origin/main: cards 0 · 0 for nick (twice) and chantelle; `--drive rows` both people, with `runDriveRows()` read against `js/app.js` before grading; the harness on files whose sha256 the checker fetched from the live domain itself; `--others` 15/15; the fence twice with the To-Do tabs at 0 and the old-look probe PASS; the guard and network greps. One gap the checker named honestly: no eyes-on screenshot was taken — that is STEP 12's side-by-side PNGs, not this step's DOM-instrument PROOF.
   VERIFIED (lane, 2026-09-06 14:55Z, evidence/todo-step8-redo-proof.txt + todo-step8-redo-{diagnosis,harness,forced-state,before,others-before}.txt): live `pearl-todo.css?v=9` + `pearl-todo.js?v=6` (`2ee1ff3b1`, published 14:45Z, wall held). `--only cards --as nick` → `mismatched properties: 0 · unmeasured anchors: 0`, every absent row NAMED as NOT MEASURABLE with its measured condition, 19/30/55 (+41/51/25 and the split Overdue 48/49) printed as DEFERRED → STEP 9; `--as chantelle` → 0 · 0 on the same instrument. `--drive rows` both people → `check-off confirm ✓ · someday move: NOT MEASURABLE — PERMISSION NOT GRANTED · thread opens from the row name ✓ · chip badge count = /api/comments ✓`. The idle counter: `passes +0 · app renders +0` over five idle seconds, twice, on the live build (the storm was Home's tile count-up and the hero's own; two-tier scheduling). `--others` → `other screens: 15/15 hidden`. The old-look fence 0/0 twice within a minute against the baseline recaptured post-deploy; the old look proven untouched by construction on `?skin=field#todo` (no `skin-pearl`, no `__pearlTodo`, zero `pl-*` nodes). The foot (row 29) adopted into the dark card on RULING (n), white at 50%. Retired colours 0 on both people.
   CORRECTED (Sienna RULINGS (n)–(t), 2026-09-06): items 2 and 3 of this step's list were not defects — see PLAN-CHANGES 14:55Z; REV 11 of the drawing now owes the 375 phone frame and the Someday-row date-cell redline, with Nick's fresh yes before STEP 12.
   VERIFIED (lane, 2026-09-06 11:30Z): `css/pearl-todo.css` v6→v8 and `js/pearl-todo.js` v5 are on `origin/main` and served at sw CACHE v597, served bytes identical to the tree. Built and real: the dark card `.pl-g.pl-shdark` (the trio from the app's own hero and bucket values, the week strip, the Due-today rows adopted into `.pl-todaylist`), the four bucket cards `[data-pl-bucket]` with label and count and their rows restyled in place, `.td-go` hidden, `.pp-pills` hidden, the four live-sheet `!important` traps answered by property, no fade on absorbing cards, and a panel that leaves the screen restored to the app's own render.
   REFUSED (checker, 2026-09-06 11:50Z, evidence/todo-step8-check.md): `--only cards` 8 mismatches for Nick and 7 for Chantelle (47 / 42 unmeasured, 4 retired colours on hers); `--drive rows` check-off ✓, Someday NOT MEASURABLE (allowed), thread chip ✗ and link ✗; the old-look fence 12 changed. The fix round opened 11:55Z was INTERRUPTED mid-diagnosis by the 12:05Z restart, and its uncommitted work is gone by design.
   REFUSED AGAIN (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-8.txt): every item above reproduced independently, plus TWO findings nobody had caught. (i) **The idle re-render storm** — `window.__pearlTodo.passes` climbs 14 and 16 over five idle seconds against ZERO app renders, reproduced twice; the built claim "37 idle passes → 1" is not what runs. (ii) **The drive proof's instrument is stale** — `runDriveRows()` still queries `.td-thread-panel, [data-thread-open], .todo-thread-open` and still counts `window.open`, the exact mechanisms this plan's own 12:09Z delta proved this app does not have, so its two ✗ are evidence about the check, not the product, and must not be read either way until it is rebuilt. Classed correctly by the same run: the `.pl-prog` completion bar is UNBUILT (zero-hit grep, not a data conditional) and Chantelle's Someday card is MISSING (Nick's matched `ok` on the same run); anchors 19, 30 and 55 are desktop column widths DEFERRED BY NAME to STEP 9; anchor 14 is data-conditional and belongs to STEP 10.
   NOW MEASURABLE (regroup): the eighteen repointed map rows — the single largest contributor to the 47 / 42 unmeasured counts — read through the card form as of 13:00Z.
   DEFINITION OF DONE: card anchors 0 for both people at both viewports with anchors 19/30/55 named as deferred; the REBUILT drive proof's lines ✓; the idle counter 0–1 passes against 0 app renders, twice; the fifteen other-screens combinations `display:none`; the old-look fence 0 against a recaptured baseline, run twice
   PROOF: the check's `--only cards`, the rebuilt `--drive rows`, the idle counter, the other-screens probe and `--fence-only`
9. [Details][UI] Desktop composition by the column rule, no fades — 100%
   VERIFIED (cold checker, Sonnet verifier, 2026-09-06 16:5xZ, evidence/todo-step9-check.md): VERDICT: PASS, every proof re-run first-hand from a clean worktree — with one recorded gap (the `--layout-data` fixture branch could not be flipped for Nick's own list for a structural reason in js/app.js, outside this step's code; both branches of rule (d) were exercised on live data) and one staleness note (the STEP 9 block's prose describes the pre-build shape; the 16:10Z/16:30Z deltas are the record).
   VERIFIED (lane, 2026-09-06 16:3xZ, evidence/todo-step9-proof.txt): `--only layout` live — nick `frame: 662 · rail: 40→622 · col1: →622 · col2: →622 · col3: →622 · scroll regions: 3`, `mismatched properties: 0 · unmeasured anchors: 0` with the rail-inset track and padding, the tab-bar arithmetic and the absent Overdue card NAMED; chantelle the same with `col1: →631` (9px over on her data, caught by column 1's safety scroll, recorded), `scroll regions: 1`, her Overdue split `1–6 of 11 · 7–11 of 11`; at 1280×800 every column →760 on the harness. `--only cards` 0 · 0 both people. Other screens 15/15; the hidden guard `display:none` at 375 and 1280; the ≥1100 → 375 crossing unwrapped; the controls, the idle counter and the adopted note pass on the harness against the live files; the To-Do tabs 0 changed on the fence twice. The shape (the grid outside the list the app rewrites; This week in column 2; the shrinking dark card; the one-row command bar) and every defect the harness caught are the 16:10Z and 16:30Z deltas in PLAN-CHANGES. Owed to the checker: the `--layout-data` fixture branch (both branches of rule (d) were exercised on live data instead).
   NOTE: the phone header redline (RULINGS (x)/(y)) landed in the same drive — rows 1–8 and 24 now read against a target REV 11 has not yet drawn; Sienna's RULINGS (aa)–(ae) reshaped rows 39–57, 58–64 (Nick) and 38–61 (Chantelle) for the desktop grid and the frame.
   DEFINITION OF DONE: `frame: 662 · rail: 40→622 · col1: →622 · col2: →622 · col3: →622` for Nick and for Chantelle, both branches of the split rule exercised, the `[hidden]` guard beside the frame rule, and the fifteen other-screens combinations `display:none`
   PROOF: the check's `--only layout` and the other-screens probe
10. [Details][UI] Every state, verbatim — 100%
    VERIFIED (cold reader, a different model, 2026-09-07T02:55:18Z): PROVEN — re-ran `--states` for both people (three mechanical captures: `states: 8/33 reached · strings verbatim: 33/33`, 25 "NOT REACHED TODAY" lines each) and proved three not-reached strings exist verbatim in the app's own source, apostrophes included (saved `todo-second-reader-10-11-15.md` CREATED BY STEP 10).
    VERIFIED (lane, 2026-09-07 00:5xZ): `--states --as nick` and `--as chantelle` on the served build print `states: 8/33 reached · strings verbatim: 33/33` each, the 25 unreached named one by one with `verbatim in the app's own source` beside every one (evidence/todo-states.txt). The tool read the screen's `innerText` before — the sheet's text-transform upper-cased every label and 0/33 matched on a screen showing all of them — and now reads the DOM's own text; this lane edits none of js/app.js, js/pop.js, js/todo-thread.js (the `--hidden-tabs` sha line is ✓), so every string on the screen is the app's own.
    DEFINITION OF DONE: `states: N/N reached · strings verbatim: N/N`, N read from the ground-truth file, NOT MEASURABLE rows named — anchor 14's free-day / state line is one of them
    PROOF: the check's `--states` — saved `todo-states.txt` CREATED BY STEP 10
11. [Tests][UI] Widths and chrome conformance — 100%
    VERIFIED (cold reader, a different model, 2026-09-07T02:55:18Z): PROVEN — `--band` twice: `1024: rail absent ✓ · scrollWidth<=clientWidth ✓`; `--chrome` twice: `destinations: phone 7 · desktop 7 · badge colour: rgb(245, 224, 213) · badge present: ✓` (saved `todo-second-reader-10-11-15.md` CREATED BY STEP 11).
    VERIFIED (lane, 2026-09-07 00:5xZ): `--band` prints `1024: rail absent ✓ · scrollWidth<=clientWidth ✓`; `--chrome` prints `destinations: phone 7 · desktop 7 · badge colour: rgb(245, 224, 213) · badge present: ✓` — the badge is the soft tint, not ink, which is the nav sheet's own and is handed to the Home lane in STATE's Handoffs, not fixed here (evidence/todo-band.txt, todo-chrome.txt). The full check's 14 chrome rows per person print as HANDOFF (chrome — Home lane), RULING (bi).
    DEFINITION OF DONE: 1024 phone layout without horizontal scroll for both people; the chrome measured and any gap handed to the Home lane, never fixed here
    PROOF: the check's `--band` and `--chrome` — saved `todo-band.txt` and `todo-chrome.txt` CREATED BY STEP 11
12. [Output][UI] PUBLISH: the four fidelity-gate items, both fences and the wall check — 100%
    VERIFIED (Sienna, creative director, round 6, 2026-09-07 04:5xZ): SIENNA'S TASTE VERDICT: PASS on the css v26 side-by-sides — twelve visible items the numbers could not see were fixed on her exact forms across rounds 1–5. The anchor map (Nick 94 rows · 77 anchors · Chantelle 69 · 52) was NOT signed in round 6 on prose alone (stale row pointers after the row-4 insert; no measurement changes) — the pointers were moved and round 7 SIGNED it (05:2xZ), every item verified by line; the signed bytes are the ones on origin/main (saved `todo-sienna-round6.md` CREATED BY STEP 12).
    VERIFIED (third verifier, a different model briefed cold, 2026-09-07 03:35Z, script 17 / css v25): PROVEN — two first-hand runs `mismatched properties: 0 · unmeasured anchors: 0` for both people with their hour and data-evening lines; the chip switch (the title equals the card's numeral for both people), a cancelled check-off, the ADD control revealed/focused/rested across four person switches with no write sent, a row opened in place, the Someday mark; `VERIFIER VERDICT: PASS` (saved `todo-verifier3-step12.md` CREATED BY STEP 12).
    SECOND VERIFIER FAILED (03:0xZ, css v24) — the ADD button on the Today card did nothing after a person switch: the reveal handler held the first button while js/pop.js re-rendered the bar's children. Fixed (the button and field are looked up at event time; the empty field rests on blur) and proved across four person switches with focus emulation on (evidence/todo-add-control-probe.txt); a third verifier's verdict on the final build is recorded in todo-publish.md as it lands.
    VERIFIED (verifier, a different model briefed cold, 2026-09-07 01:05Z): PROVEN — re-ran the check twice first-hand, both runs `mismatched properties: 0 · unmeasured anchors: 0` for both people with their hour and data-evening lines; drove the chip switch, a cancelled check-off (rows 44→44), the thread badge (1 = the app's own count) and a row opening in place with zero new tabs; `VERIFIER VERDICT: PASS` (saved `todo-verifier-step12.md` CREATED BY STEP 12).
    VERIFIED (lane, 2026-09-07 01:1xZ): the live check printed `mismatched properties: 0 · unmeasured anchors: 0` for Nick and for Chantelle on THREE runs in a row against the re-signed map (Nick 91 rows, Chantelle 66; evidence/todo-fidelity-live.txt + -run1/-run2), each person block carrying its ISO hour with offset and its `data-evening: present|absent` reading taken before the attribute was removed; the rendered-pixel read (`tools/pearl-pixel-sample.mjs`, three sites per shot, the drawing's own pixel as the expectation) is within ΔE 3 at all twelve sites; the old-look fence reads `fence (the four To-Do tabs): 0 changed` on a baseline recaptured on today's origin/main; `other screens: 15/15 hidden`; `--hidden-tabs` four ✓; the anonymous `/api/finances` curl printed 401 after the deploy (`dddcb8b89`, reachable and pushed); the four side-by-side PNGs sit under the gitignored `evidence/local/` with their sha256 in evidence/todo-publish.md. Sienna's taste verdict and the verifier's verdict are recorded in todo-publish.md as they land.
    DEFINITION OF DONE: `mismatched properties: 0 · unmeasured anchors: 0` ×4 on the live URL, each line carrying its ISO-8601 hour and its `data-evening: present|absent` reading and backed by three rendered-pixel samples inside ΔE ≤ 3 (the evening filter — §3 known instrument limit); four side-by-side PNGs under `evidence/local/` only; Sienna's verdict with the hour she graded at; the verifier's verdict; the anonymous finances curl prints 401; BOTH fences clean and the fifteen other-screens combinations `display:none`
    PROOF: the check's `--out` run on the live URL — saved `todo-fidelity-live.txt` and `todo-publish.md` CREATED BY STEP 12
13. [Proof][UI] Blind check of every §2 row — 100%
    VERIFIED (blind checker, a different model briefed with §2 only and told to refute, 2026-09-07 01:2xZ): `24/24 PASS` and `other screens: 15/15 hidden` on the live URL as Nick and as Chantelle (saved `todo-blind-check.md`); 18 of the 24 were driven on the screen; SIX are NOT MEASURABLE by name and stay open as rows, not as defects — T4 and T5 (the all-clear and pace lines need a task completed in-session; every confirm was cancelled and no write was ever sent), T9 (no confirmed test-row write path for Someday), T10 and T11 (the thread chip is a non-interactive badge and the Monday anchors are invisible in BOTH looks — the manifest describes interactions the shipped app does not perform), T14 (`.hc-empty` never appeared in the default, empty or forced-error state). Nine rows graded STALE-MANIFEST (intent PASS): they describe the Overdue card, the trio and the old column rule, all retired at REV 12.4 by Nick's own words. Two findings outside the rows: the plain-tone "Copied in from Monday" notice is in the DOM but hidden under Pearl (Sienna's RULING (aw), deliberate; a warn/bad-tone notice stays visible), and the absorbing This-week card's `::after` — measured in the same drive with `getComputedStyle(el, "::after")` at 1280 and 375: it computes `display:none` (this lane's rule beats the shell's `pearl-shell.css` fade rule), so the shell's declared 38px gradient is never painted; the checker read the declared height, not a painted pixel. No change made.
    DEFINITION OF DONE: `24/24 PASS` on the live URL as Nick and as Chantelle, plus `other screens: 15/15 hidden` — the To-Do screen absent from `/#home`, `/#finances`, `/#calendar`, `/#shopping` and `/#extras` at 375, 1280 and 1728, which is Nick's own complaint checked by a person rather than by the instrument
    PROOF: saved `todo-blind-check.md` CREATED BY STEP 13
15. [Tests] Personal only: the business audit and the rule — 100%
    VERIFIED (cold reader, a different model, 2026-09-07T02:55:18Z): PROVEN — re-ran the audit from the same store: Nick 31 rows · Chantelle 13 · flagged [] · 525 terms; `command grep -c masked` = 1 on both files; the same flagged set (none) as the lane's file. It could not run the audit a second time while the Hub store was being written (`sqlite3 -readonly` refused 8 of 8) — the first run stands as the measurement (saved `todo-second-reader-10-11-15.md` CREATED BY STEP 15).
    VERIFIED (lane, 2026-09-07 00:3xZ, the re-run at STEP 12's publish): `evidence/todo-business-audit.md` rewritten from today's lists — Nick 31 rows, Chantelle 13, **0 flagged** against 525 terms drawn from the Hub's record itself (business.clients, clients_historical, sidekicks, team, tools, plus the company's names and its money namespaces); the matcher self-tested in the same run (a known client phrase flags; a longer word does not); task text masked to `<task N>`; nothing moved. The 2026-09-06 purge Nick ordered ("31–69 go away") is why the count fell from 6 to 0.
    VERIFIED (lane, 2026-09-06 03:58Z): `evidence/todo-business-audit.md` records Nick's list 71 rows / 3 flagged and Chantelle's 14 / 3, each flagged row masked with only the matched term shown, the standing rule recorded, and nothing moved. The 2026-09-05 move of the business rows to the Hub is separate and already landed.
    VERIFIED (cold verifier, 2026-09-06, evidence/regroup-2026-09-06/step-15.txt): PROVEN — the file was opened directly; 71 + 14 = 85 total and 6 flagged match this plan's own recorded totals exactly; no flagged row shows real task text; the file states plainly that nothing was moved.
    OWED (process, not proof): the second reader's re-run of the audit, the triage list handed to Nick in plain words, and the re-run at STEP 12's publish so the number quoted to him is the number on the day it ships.
    DEFINITION OF DONE: the audit file names the total, the flagged count and every flagged row with its matched term; Nick has the triage list; nothing is hidden from the screen
    PROOF: saved `todo-business-audit.md` CREATED BY STEP 15
14. [Proof] Record, contradictions raised, postmortem, close — 0% (NOT STARTED; it depends on STEPS 12 and 13's artefacts, neither of which exists)
    DEFINITION OF DONE: two VERIFIED lines per step; the four contradictions posted as one handoff; the postmortem written; the derived progress figure quoted
    PROOF: `python3 projects/ops/agents/check_plan.py --progress projects/personal/family-app/PLAN-PEARL-TODO.md`

Current state STATE-PEARL-TODO.md

# STATE-PEARL-TODO.md — current state only, rewritten in place

Companion to `PLAN-PEARL-TODO.md`. Not a log: every line describes NOW. Dated deltas to the plan's contracts go in `PLAN-CHANGES-PEARL-TODO.md`, not here.

## Where the lane is (2026-09-07 05:3xZ — closed)

- ✅ **The To-Do screen is BUILT, LIVE and at zero difference from its approved drawing** — Nick's yes 2026-09-06 ("its close enough"), the drawing at REV 12.7, the live build at css v26 / script 17. The check prints `mismatched properties: 0 · unmeasured anchors: 0` for both people three runs in a row on the final build; the twelve-site rendered-pixel read is within ΔE 3; the Someday control's mark is monochrome in 8/8 boxes; both fences clean; the blind check 24/24 (six rows NOT MEASURABLE by name); the audit 0 of 44 flagged; the third verifier PASS on the final script (`evidence/todo-publish.md` is the record).
- ✅ **Sienna's round 6 (04:5xZ): `STEP 12 TASTE VERDICT: PASS`** — the screen reads as the drawing to her eye on all four side-by-sides. ✅ **The map is SIGNED (round 7, 05:2xZ)** at REV 12.7 — Nick 94 rows · 77 anchors · Chantelle 69 · 52 — after round 6 refused it on prose alone (stale row pointers after the row-4 insert, all moved). Nothing is open in this lane.
- **What the drive changed, in one breath:** STEPS 10, 11, 12, 13, 15 at 100% with two VERIFIED lines each; the map re-signed from REV 10.10 to REV 12.7 (RULINGS (aj)–(bk)); the tool's eight bound changes plus STEP 12's hour/evening/pixel proofs and the glyph test; nine build fixes the numbers could not see (the person's own title numeral, no next-up line, an ADD control that rests as the button alone at the trailing edge, no date pill or bucket caption on rows, month-form Later dates, the foot under the list, the head flush left, a monochrome Someday mark, the empty "Set date" prompt gone); the ADD control fixed after the verifier caught it dead after a person switch.
- **Handed to the Home lane (chrome):** the tab bar / rail `position`, the badge's colour and height, the rail's active-row fill, the always-on captions under every rail item and the sparkline at its foot, the brand line. Recorded as HANDOFF in every check run (14 properties · 5 row readings per person), never counted here.
- **Known, not this lane's:** the first page load in a cold browser prints the hero as "—" or a stale count (js/pop.js's boot state; the second load is right); a headless page has no window focus, so blur events do not dispatch without `Emulation.setFocusEmulationEnabled` — a probe that reads "the field stayed open" without it is measuring the harness.
- **Open for a later REV (Sienna's own note):** the hard cut through a line of type at a scroll cap is the drawn device, but with the fade suppressed its honesty rests on the "n more below" foot, and there is no foot on desktop; the thread chip draws a glyph where the drawing draws the word "NOTE" — decide rather than inherit. The four data contradictions from STEP 2 (header vs panel counts — now resolved on the screen by the person's own numeral; `hero.week`; Chantelle's 12 vs 14; the Someday cycle) are one handoff line for PROJECT.md's "What's next", proposed to Nick in plain words.
- **Where the work happens:** unchanged — a clean detached worktree at `origin/main`, landed through the Finances lane's landers (`land.sh` takes label · message · sw note · `asset=ver,…` before the files; `land-files.sh` for files with no tag); the design site through `deploy.mjs skippy-designs`; the app through `deploy.mjs deck-family --fast`; capture evidence OUT of the worktree before any `checkout -f`; `css/pearl-todo.css` ends inside a ≥1100 media block, so tail appends are desktop-only unless placed at top level.
- **Nothing waits on Nick.**

## Postmortem (regroup 2026-09-06)

**Every failure, with its concrete example.**

- **A frame rule beat the app's own `[hidden]` attribute and the To-Do screen painted over every other screen.** `body.skin-pearl #view-todo{display:flex;…}` (two rules, css v3→v7) meant the hidden To-Do view computed `display:flex` on Home, Finances and everything else at every width — measured 12:55Z as `hidden/flex` on `#home` and `#finances` at 375, 1280 and 1728. Nick saw it before any instrument did: "to do screen has locked itself into the view for every other screen its a mess." Hotfixed as `#view-todo[hidden]{display:none}` in css v8 and re-proven at 13:05Z.
- **The fence that should have caught it was structurally blind to it.** STEP 6's `--fence-only` measures `#view-todo`. The defect was on the OTHER screens, with To-Do painted over them. A check pointed at the changed thing cannot see damage done to the unchanged things around it.
- **The rule already existed, in prose, in one line, with nothing enforcing it.** §11a said "frame rules need `[hidden]{display:none}`". It was written, read past, and cost a live incident — which is the registry's own "a rule written only in prose behaved as if it didn't exist", reproduced exactly.
- **A worker substituted a synthetic ground-truth capture under lock contention.** STEP 2's Brief C returned static HTML instead of the signed-in DOM. It was refused and never landed, but it would have passed a shape check.
- **The next capture attempt leaked the hero, unmasked.** Also refused, also never landed. Two of three attempts at one artefact were wrong in two different ways.
- **The checker refused STEP 8 on controls the app has never had.** Its drive proof queries `.td-thread-panel, [data-thread-open], .todo-thread-open` and counts `window.open` — the plan's own wording invented a clickable thread chip and a new-tab link. `js/app.js` binds only `.chk`, `.td-someday` and `.pt`; the chip is a badge with no handler. The correction was written into PLAN-CHANGES at 12:09Z and **never applied to the code**, so the instrument kept returning ✗ for a fortnight's worth of readings that meant nothing either way.
- **Eighteen signed anchor rows addressed a DOM the build had already replaced.** They read `#todo-<who> .td-list > .td-bucket:first-child` and its `.td-item` siblings, which cannot resolve once RULING (h)'s card wrappers exist. They were the single largest contributor to the 47 / 42 UNMEASURED counts, and the delta that raised them said "twelve" when the real number was eighteen.
- **The lane's branch became un-mergeable.** Merging `origin/main` into `pearl/todo` is refused by the pre-commit hooks three different ways, the branch fell 114 commits behind in an hour, and other lanes publish from `main` every few minutes, so any unlanded tag or asset was dropped from the live site within minutes. The topology had to change mid-drive: the lane now lands on `origin/main` directly.
- **The STEP 8 fix round was interrupted mid-diagnosis by the restart**, and its uncommitted work was gone by design (the auto-pull). The diagnosis itself survived only because it had been written down: the leading hypothesis was that reparenting the Due-today rows out of their `.td-list` breaks handlers delegated on the list.
- **A step's own progress line was stale in the safe direction and in the unsafe one.** STEP 4 read 0% while the tool was finished and passing its own self-test; STEP 1 quoted a hash (`0884fac2…`) that the page had moved past.

**Every plan item that was supposed to work and didn't.**

- Standing rule 5 — "contract-check green at the end of EVERY step, with the number quoted". It has been red at 574 / 22 the whole time and no step noticed.
- STEP 8's claim "one Pearl pass per app pass made true by a render signature (37 idle passes → 1)". Measured twice: 14 and 16 extra passes over five idle seconds against zero app renders. The claim was honest about the mechanism and wrong about the result.
- STEP 6's fence proof line `fence: 0 changed elements (flag off)`. It has never printed 0, and its baseline is older than the wiring it is supposed to be measuring.

**Every confusion.**

- "Flag off" meant two different things after the app-wide flip — the old Field look (`?skin=field`) and the other screens on the default route. They are different surfaces and only one of them was being measured.
- Twelve rows versus eighteen rows in the map repoint; the signer had to correct the count before ruling.
- A FAIL from a stale instrument reads identically to a FAIL from a broken product. Three separate readings of the thread chip were treated as product evidence.

**Every blocker.**

- The signer's ruling on the eighteen rows blocked STEP 8's UNMEASURED count from ever reaching zero. It arrived at 13:00Z and is now applied.
- The Chrome lock and machine contention (load average 252 on a 12-core Mac at one point) forced retries on nearly every browser measurement; the rig's own retry rule handled it, but it is the reason several proofs are timestamped minutes apart.
- The pre-commit hooks blocked the branch topology outright; there was no way through it, only around it.

**What went well, and what to keep.**

- **The cheap lane wrote the first working files for STEPS 7 and 8**, and STEP 7 came back at zero mismatches for both people on an independent re-run. The tiering held: cheap builds, a different model checks.
- **The fidelity check's red-proof is real.** `--selftest` injects a 1px `paddingTop` and prints exactly that property; `--bogus` exits 2. A check that can be shown to fail on demand is why STEP 7's zero is worth anything.
- **Sienna's refusal caught a missing Later card** before the map was signed — eight corrections on the first submission, all applied. A signer who refuses is the reason the map's counts mean something.
- **Nobody graded their own work**, and it paid: every closed step here has a second reader, and the two steps that are NOT closed were caught by readers who did not build them.
- **The evidence was written to disk in the same turn as the measurement.** The whole of this regroup was possible because 34 units are on disk with their commands and outputs, not in a session that ended.
- **Keep:** standing rule 11's other-screens check in every step's proof from STEP 6 onward; a baseline recaptured on the current `origin/main` before any fence result is read; and the habit of reading an instrument's CODE against its own description before trusting either its pass or its fail.

## Handoff — resume line

Pick this lane up by running `git -C "/Users/nickdeck/Documents/Claude 2.0" fetch -q origin main` and reading `projects/personal/family-app/PLAN-PEARL-TODO.md`, this file and `PLAN-CHANGES-PEARL-TODO.md` FROM `origin/main` (`git show origin/main:<path>`) — the main checkout can sit tens of commits behind while `git status` calls all three clean — then pick up where STEP 8's redo left off: read `redesign-mockups/concepts-2026-09-04-round10-screens/evidence/todo-step8-redo-check.md` (the cold checker's verdict; if it is absent the checker has not reported — dispatch one from the brief in PLAN-CHANGES 14:55Z's evidence), close STEP 8 at 100% on a PASS or work its named failures, then open STEP 9 (its entry gate is STEP 8 closed). Run the instrument from a clean worktree at `origin/main` with `../deck-shared` linked beside it, and land every change on `origin/main` through the landers in `projects/personal/family-app/redesign-mockups/concepts-2026-09-04-round9-finance/build-proofs/landing/`, never by merging `main` into `pearl/todo`. Two of the Handoffs below are OPEN and travel with the lane: the hidden-view defect needs ONE guard across every Pearl view from the chrome owner, and the evening filter makes every computed-colour zero blind between 7pm and 6am, so STEP 12 records the hour it ran and reads a rendered pixel.

## Who is driving this

- **Nobody, as of 2026-09-06.** Nick handed the To-Do screen off the Finances/To-Do overseer session; the next owner starts from the resume line above. When it restarts: builders on the cheap lane where the file fits its 60k cap, otherwise Opus per Nick's 2026-09-05 tiering; checkers on the cheap Anthropic worker or Sonnet; design QA on Sienna (Fable acting as the creative-director when that agent type is unavailable in a session — a substitution, recorded, never a skipped gate).
- **Where the work happens:** the MAIN checkout, landed on `origin/main` the same minute through the clean-worktree lander (PLAN-CHANGES 2026-09-06 topology delta). `pearl/todo` holds STEPS 1–4's history and is not the source from STEP 6 onward; `main` is never merged into it.
- **`evidence/local/` exists and is proven gitignored** (`git check-ignore -q` exits 0). Every screenshot and `--shot` capture goes there and nowhere else — captures of the signed-in screen carry real task text.

## Shared-file holds (the §3 hour fence)

- `HOLDING: none` — this lane holds none of `index.html` / `sw.js` / `contract-check.js`. STEP 6 writes a HOLDING line here before its first edit and clears it after the scoped commit.
- `HOLDING: shell layer` — none. STEP 5 writes one before touching `css/pearl-shell.css`.

## Handoffs

- **To the copier's owner (the jobs owner) — the Finances Watch tab is now a SECOND PRODUCER of queued changes for the Monday copy-out, and it can produce them faster than a person can (2026-09-06).** Every `Task to cancel` press on the merged Watch list writes a real to-do through `functions/api/todo-store-write.js`'s `add` branch, which stages one change for `scripts/todo-monday-copyback.mjs` to carry out; completing that to-do stages another. Measured, not assumed: each press grew the staged queue by exactly one on the first read after the write (no propagation delay — the +1 staged count is present on the FIRST poll, 305/509/361 ms across three trials), and the door refuses at `MAX_STAGED` = 500 (`functions/api/_todo-store.js:169`) with a message that names the copier by role. Nothing is broken and nothing is asked for — the copier is BUILT and runs as its own daemon job (`daemon:todo-monday-copyback`, built 2026-08-17, guarded by 101 + 12 nightly checks), and its own behaviour was read before this build shipped: a stored row with no outside id is always kept, on a complete pull and an incomplete one alike (`scripts/todo-carry-in.mjs` lines 596–616 and 735–760), so an app-created cancel task survives the next carry-in. **Two things for you, both honest limits of what this lane measured:** (1) the current staged depth on either list was NOT re-read by this step, so if the copier ever stalls the way Chantelle's carry-in did for 13 days, this new writer will fill the queue toward 500 faster than the old hand-typed rate — worth a depth alert rather than a ceiling refusal being the first anyone hears of it; (2) this lane's own proof runs created and then COMPLETED twelve fixture to-dos named `Cancel pearl-check-2026-09-06-r<n>`, so a matching number of fixture-shaped changes passed through the queue today and may reach Monday as real items — they are fixtures, not household tasks, and no real charge was read, pressed, marked or altered at any point.
- **To the Finances lane:** the two nav questions its STEPS 15 and 16 wait on are settled and triad-checked in `specs/PEARL-NAV-DECISIONS-2026-09-05.md` (2026-09-05). The short version: the spec's own eight-cell closure is superseded by one cell (Updates) by Nick's later seven-destination ruling; the shipped chrome already matches; **the stale part is STEP 15's "five cells" and STEP 16's "eleven destinations" acceptance text, which must be rewritten before either step can pass.**
- **To whoever owns the chrome (the Home lane):** `css/styles.css`'s FA-1 rule hiding Status, Talk and Dispatch is explicitly reversible — *"until the voice-app work lands"*. When it is deleted, three destinations return and the shipped seven-cell `PL_TABS` has no slot for them. Not a defect today; a known future change. Also on the plan's NEXT list.
- **To whoever owns the chrome (the Home lane):** hiding Updates makes it unreachable, not merely unbadged — the nav cell was its only link (the hash route still works). It was showing 29 items when measured twice on 2026-09-05. Also on the plan's NEXT list.
- **To the Home lane (the chrome's owner) — the hidden-view defect is a CLASS and it now has three occurrences on two screens.** Home at ≥1100 (its ROUND 15), Home again at <1100 on 2026-09-06 (found and handed over by the Finances lane; `#view-finances` measured at y=1654 under a hidden 1654px Home), and To-Do at 12:40Z. Each was fixed one screen at a time, after a person saw it. The shape that works is in `css/pearl-home.css:234` — one rule, higher specificity than the frame rule, **no media query**. Worth one guard that covers every Pearl view rather than a fourth occurrence; this lane has written it into its own standing rule 11 but cannot write another lane's sheet.
- **To the Home lane — the evening filter makes this lane's whole colour instrument blind, and it is not a To-Do bug.** Read first-hand in `css/reskin-popfix.css` lines 72–85: `:root[data-evening]` applies `filter:brightness(.82) contrast(.97) sepia(.34) hue-rotate(-14deg) saturate(1.16)` to `#view-todo` along with ten other views, 7pm–6am. **A CSS filter never changes a computed style**, so this lane's fidelity check reads every colour as correct while the screen actually renders tan — and STEP 12's side-by-side PNGs and Sienna's taste verdict are taken at whatever hour they happen to run. The Finances lane neutralised it for its own view only; the app-wide line belongs in `css/pearl-nav.css` or the generated sheet, which is the chrome owner's. Until then, STEP 12 must record the hour it ran.
- **To the drawing's next revision:** REV 11 owes either a This-week frame or an explicit "never drawn" note — Sienna's RULING (m) accepted carding the live This-week group as a surface with no design side, so it can never become an anchor until the drawing rules.
- **To the jobs owner — the carry-in from Chantelle's Monday board was wedged for 13 days, and the alert that should have said so was filtered away (2026-09-06).** FIXED, landed on `origin/main` in `scripts/todo-carry-in.mjs`: the store's publish gate floors `recently_done` against `priorRowsStillInWindow`, which forgives a finished row that AGED OUT of the 14-day window and nothing else. Measured live on rev 63: of her 7 stored finished rows 5 had aged out and the other 2 had been **un-ticked on Monday**, so the merge correctly filed them as open — the window rule still counted them as owed, the floor became 1, the merged shelf was 0, and the write was refused. A refused write never advances what is stored, so every run since 2026-08-24 measured the identical 2 and refused identically. New exported `finishedShelfStillOwed()` states the honest prior for `recently_done` **alone**, through the gate's own `priorRowsByArray` — deliberately not `allowShrinkReason`, which `assertAllArraysPushable` hands to every array and would have unfloored the open list and the archive too. Fails closed on every unknown: a row absent from the pull is still owed, because that is the read-fail-then-empty-push signature the floor exists to catch. Proven live — origin code refuses, the fix writes rev 63 → 64, `last_import_at` 2026-08-24T20:37Z → 2026-09-06T14:40Z, Nick's list unchanged. **🔴 THE SEPARATE DEFECT, NOT FIXED AND NOT THIS LANE'S:** the job reached its 3-run stuck threshold and its alert was suppressed as "machine chatter", so a real 13-day outage of one person's to-do import never reached anybody. The filter that swallowed it is the jobs owner's to fix; nothing here touched it.
- **Raised and staying raised, not this lane's to fix:** `contract-check` is red at 574 PASS / 22 FAIL across ids belonging to other parts of the app (`onb-*`, `roster-count`, `sk-*`, `watch-count`, `tools-*`, `mc-jobs*`, `taskSearch`). Standing rule 5 requires it green at the end of every step in this plan and has been silently failing to enforce that.