Why this matters: I red-team my own plans before executing them. Condensed from the project ledger (.superpowers/sdd/rounds/2026-07-21-build-plan-review/build-plan-review.md): a three-agent adversarial panel reviewed the increment-4 build plan against the shipped codebase before any increment-4 code was written. The findings, including the one against my own north star metric, are unsoftened.
Run 2026-07-21. Three-agent panel: two red-team critics (one for the entire project, one for increment 4 onward) and one champion who defends the plan and separates real problems from nitpicks. Grounded against shipped code at main 9b227b9. Full panelist findings: panel-critic-project.md, panel-critic-inc4.md, panel-champion.md.
The bar is a personal, local, single-user, $0, non-production project. Production-grade concerns (scale, multi-tenancy, security compliance, HA, observability) were killed at the door. Every finding carries a severity (ARCHITECTURE = changes a schema, seam, dependency, or build order; DETAIL = resolvable inside a feature), the champion’s verdict (REAL / REAL-BUT-CHEAP / NITPICK), and the cheapest test or decision that settles it. Agreed nitpicks are collected in one section so you can skip them.
Both critics independently flagged the same top three (the north star is not measurable, the coach transport is load-bearing and slow, the F26 profile is an unfrozen keystone), and the champion conceded all three rather than defending them. When a critic and the champion agree, treat it as settled.
P1. The north star is currently unmeasurable, and inverted. [ARCHITECTURE · REAL · headline]
The north star is “every session teaches me something I can name.” Shipped reality: the only lesson artifact is debriefLesson.ts, which the coach authors. There is nowhere she names the lesson herself, so the learning mechanism the north star is built on (active recall, her words) is absent, and “something I can name” has quietly become “something the coach names for me.” The metric “Notebook entries with real content ÷ sessions” is then either trivially ~1.0 (a coach tag always exists) or vacuously 0 (no her-authored content ever). The denominator is also half-open: endSession() is defined but never called, so there is no session-close rule. F24 (the Notebook) is marked shipped but its free-text half is effectively unbuilt.
P2. The coach’s synchronous CLI-per-call transport is load-bearing and slow. [ARCHITECTURE · REAL] (Resolved 2026-07-22, see technical-decisions.md: a warm in-process backend replaced the per-call spawn, and a separate measurement pass found the deeper issue was missing facts, not transport speed alone.)
Coach replies run 6-9 seconds through a headless claude call, against the PRD’s own 2-second ship gate. The judge path is safe (code-computed verdict, never waits on the model, confirm never blocked), so this is quarantined to the narration path. But narration is where increment 4 lives: F26 profile narration, F28 opening explainers, F13 phrasing, F16 chat. Five surfaces would each inherit blank-screen-then-paragraph.
P3. “Player is always white” is hardcoded into the honesty logic. [ARCHITECTURE-if-F46 · REAL, low urgency]
The zero-fabrication proofs (especially opportunity.ts and the seed-ply logic) assume pv[0]/pv[2]/… are her moves. True today. But the advertised F46 import seam, or ever playing black, would silently invert every “you win the piece” claim into a confident lie. The games.source schema flag is seam-ready; the analysis pipeline is not.
P4. The magic-moment two-game lesson loop (H1’s showcase) is not deliverable as one clean v1 arc. [DETAIL · REAL] The PRD’s aha is a two-game arc: coach recreates the taught mistake next game, unaided success is detected and rewarded. Pieces of the re-test (drills, biased situations) are increment-4, and the in-game re-test bias ships off in v1. The hypothesis that carries the north star is the hardest thing to actually stage.
P5. Render-only honesty has no fact list for general-knowledge prose. [ARCHITECTURE · REAL] The honesty gate works because every claim derives from a chess.js replay of engine output. F28’s “the coach explains the opening” is general chess theory, not a replay-derived fact. There is nothing to validate it against, so it is the one planned surface where the coach can fabricate inside the guardrails.
Whether the two-ELO model actually separates playing from comprehension in practice (F26/F13 are unbuilt); live coach latency under real usage beyond the gate sample; whether the render-only gate has any current hole outside the F28 general-knowledge case.
I1. F26 (player profile) is an unfrozen keystone, and the plan builds it in the wrong order. [ARCHITECTURE · REAL-BUT-CHEAP]
F13, F28, F31, and all coach personalization read the profile. assembleFactList has no profile input today. Yet the master plan’s increment-4 order is F27 drills first, and the profile shape is never frozen. If the shape is wrong, four features rebuild on top of it.
I2. The north-star capture gap gates “it remembers.” [ARCHITECTURE · REAL] P1 seen from the future. Increment 4 is literally “it remembers,” and what it remembers is the Notebook + profile. If the Notebook never captures a lesson she named, increment 4 is building memory on top of nothing. Close the capture gap before F26/drills read from it.
I3. The coach transport is the same slow, sometimes-offline backend every coach-dependent inc-4 feature builds on. [ARCHITECTURE · REAL] (Resolved 2026-07-22, see P2.) P2 seen from the future. F28 explainers, F13 phrasing, and F26 narration all inherit it, and the offline path silently degrades them to templates. Decide it once, up front.
I4. F13’s “suggested moves reach a 10% Maia play-rate at the advice level” needs a policy distribution the current engine seam does not expose. [ARCHITECTURE · REAL-BUT-CHEAP, needs a spike] The engine seam returns a single best move (argmax), not a per-move probability distribution. F13’s acceptance criterion assumes access to Maia’s move play-rates. If lc0/Maia can emit a policy distribution on her Mac, this is a config flag; if not, F13’s core rule is unbuildable as written.
I5. The drill pass/fail rubric (F27) is undefined, and drills are the plan’s first increment-4 build. [DETAIL · REAL-BUT-CHEAP] “A failed drill breaks the streak” needs a definition of pass and fail (first-move-correct? whole-line? within N seconds?). ts-fsrs scheduling keys off it. Write the rubric before F27, not during.
I6. The playing-ELO and comprehension-ELO update rules are not specified. [DETAIL · REAL-BUT-CHEAP] F26 says the profile “tracks” both ELOs; nothing says how they move after a game or drill. Write the update rule as a labeled starting value (like the +200 gap already is), then tune.
Whether lc0/Maia emits a per-move policy distribution on her Mac (I4’s linchpin); the exact ts-fsrs API surface vs F27’s rules; whether the two-ELO separation holds empirically once built.
The champion’s job was to stop the review from importing production expectations a personal $0 single-user app rejects. These were raised, examined, and killed:
The review converts to five pre-build decisions and one sequencing change. All are cheap; the point is to make them before code, not after a rebuild.
Pre-build decisions (settle before increment 4 coding):
endSession() so the denominator closes. (P1/I2)Sequencing change to the master plan’s increment-4 order:
The net: increment 4’s risk is not the features, it is three foundations the plan under-specifies (the lesson-capture surface, the coach transport, the profile shape). Settle those three and the rest of increment 4 is execution.