Files
josh 63d2d8d5f7 docs(02): create phase 2 plan — 5 plans across 3 waves
Phase 2 (Season 1 Vertical Slice — Soil) plan set:
- 02-01 (Wave 0): foundations (BigQty + Zustand 5 store + tick scheduler + V1Payload extension + save lifecycle hooks + Phaser EventBus + ESLint sim-purity rule)
- 02-02 (Wave 1, parallel): Begin → Plant → Grow vertical slice
- 02-03 (Wave 1, parallel): Harvest → Journal → Compost + Season 1 fragments + PIPE-02 verification
- 02-04 (Wave 2, parallel): Lura's 3 Ink-authored gate beats (1st/4th/8th harvest, STRY-10)
- 02-05 (Wave 2, parallel): Letter + Settings + boot-path save lifecycle + Playwright PIPE-07 e2e

All 24 Phase-2 REQ-IDs covered across the plan set. VALIDATION.md per-task verification map filled (15 tasks); nyquist_compliant: true.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 02:45:56 -04:00

10 KiB
Raw Permalink Blame History

phase, slug, status, nyquist_compliant, wave_0_complete, created, last_updated, plan_count, task_count
phase slug status nyquist_compliant wave_0_complete created last_updated plan_count task_count
2 season-1-vertical-slice-soil filled true false 2026-05-09 2026-05-09 5 15

Phase 2 — Validation Strategy

Per-phase validation contract for feedback sampling during execution. Populated by the planner during plan generation. Each plan's tasks declare automated verification commands or a Wave-0 dependency.


Test Infrastructure

Property Value
Framework Vitest 4.x (sim + unit + integration) + Playwright 1.x (e2e smoke, PIPE-07)
Config files vitest.config.ts, playwright.config.ts (Phase 1 already shipped both)
Quick run command npm test (Vitest only — happy-dom env; ~24s on warm cache as Phase-2 surface grows)
Full suite command npm run ci (lint + Vitest + validate:assets + build + check:bundle-split)
Playwright command npm run test:e2e (= npx playwright test; Phase 2 ships the first real e2e)
Estimated runtime Vitest ≤8s · Playwright ≤30s · npm run ci ≤90s

Sampling Rate

  • After every task commit: Run npm test (Vitest)
  • After every plan wave: Run npm run ci (full)
  • Before /gsd-verify-work: npm run ci AND npx playwright test must be green
  • Max feedback latency: ≤8 seconds for Vitest; ≤30s for Playwright

Per-Task Verification Map

Every PLAN task's automated verification command, mapped to a phase REQ-ID and any threat-model entry.

Task ID Plan Wave Requirement Threat Ref Secure Behavior Test Type Automated Command File Exists Status
02-01-T1 02-01 0 CORE-02, CORE-03, CORE-11, UX-11 T-02-01-02, T-02-01-03, T-02-01-05 drainTicks refuses negative; clamps 24h; benchmark <500ms unit npm run lint && npx vitest run src/sim/numbers/ src/sim/scheduler/ && npm run build Plan 02-01 creates pending
02-01-T2 02-01 0 UX-10 T-02-01-01, T-02-01-04 save lifecycle hooks fire synchronously on visibilitychange/beforeunload/season-transition unit + integration npm run lint && npx vitest run src/store/ src/save/migrations.test.ts src/save/lifecycle.test.ts && npm run ci Plan 02-01 creates pending
02-01-T3 02-01 0 (defended option) T-02-01-04 ESLint blocks Date.now() outside scheduler/clock.ts unit (lint integration) npm run lint && npx vitest run src/sim/__test_violation__/ && npm run ci Plan 02-01 creates pending
02-02-T1 02-02 1 GARD-01, GARD-02, CORE-02 sim/garden pure; growth state machine deterministic unit npm run lint && npx vitest run src/sim/garden/ && npm run build Plan 02-02 creates pending
02-02-T2 02-02 1 GARD-01, GARD-02, CORE-02 T-02-02-01 render layer + Garden scene wires scheduler + EventBus + store manual + build npm run lint && npm run build (manual smoke: npm run dev confirms tile grid + plant primitives visible) Plan 02-02 creates pending
02-02-T3 02-02 1 AEST-07, UX-01, GARD-01 T-02-02-02, T-02-02-03 BeginScreen + audio bootstrap synchronous in click; SeedPicker enqueues plantSeed; UI strings externalized integration npm run lint && npx vitest run src/ui/begin/ src/ui/garden/ src/content/ && npm run ci Plan 02-02 creates pending
02-03-T1 02-03 1 GARD-03, GARD-04, MEMR-01, MEMR-02, MEMR-03, MEMR-06 T-02-03-02, T-02-03-04 selector deterministic + gating + no-dup + exhaustion fallback; ≥10 fragments authored; harvest/compost pure unit + build npm run lint && npx vitest run src/sim/memory/ src/sim/garden/ src/content/ && npm run build Plan 02-03 creates pending
02-03-T2 02-03 1 MEMR-04, MEMR-05, UX-01 T-02-03-03 Journal DOM selectable + reveal-modal + journal-icon-after-first-harvest integration npm run lint && npx vitest run src/ui/journal/ && npm run ci Plan 02-03 creates pending
02-03-T3 02-03 1 PIPE-02 T-02-03-05 check-bundle-split.mjs asserts Vite emits a Season-1 chunk structural npm run lint && npm run build && node scripts/check-bundle-split.mjs && npx vitest run scripts/check-bundle-split.test.mjs && npm run ci Plan 02-03 creates pending
02-04-T1 02-04 2 STRY-06 T-02-04-03, T-02-04-04 inklecate compiles 4 .ink → JSON; ink-loader instantiates Story + binds variables snake_case unit + build npm run compile:ink && npm run lint && npx vitest run src/content/ink-loader.test.ts scripts/compile-ink.test.mjs && npm run ci Plan 02-04 creates pending
02-04-T2 02-04 2 STRY-01, STRY-10 T-02-04-01, T-02-04-06 lura-gate fires on harvest count 1/4/8 only; FakeClock advance alone does NOT fire (STRY-10) unit npm run lint && npx vitest run src/sim/narrative/ src/sim/garden/ && npm run build Plan 02-04 creates pending
02-04-T3 02-04 2 STRY-01, STRY-06, STRY-07 T-02-04-03 LuraDialogue + InkRenderer + gate-renderer wired; pending beat opens overlay; resolves on close integration npm run lint && npx vitest run src/ui/dialogue/ src/render/ && npm run ci Plan 02-04 creates pending
02-05-T1 02-05 2 UX-02, GARD-04, CORE-03 T-02-05-02, T-02-05-08 sim/offline + auto-harvest + letter Ink + letter-renderer all pure unit + build npm run compile:ink && npm run lint && npx vitest run src/sim/offline/ src/sim/garden/auto-harvest.test.ts src/ui/letter/letter-renderer.test.ts && npm run ci Plan 02-05 creates pending
02-05-T2 02-05 2 UX-02, UX-10, CORE-09 T-02-05-01, T-02-05-03, T-02-05-04 Letter overlay (Pitfall 9 audio bootstrap on dismiss) + Settings (Export/Import/Restore) + boot-path save lifecycle integration npm run lint && npx vitest run src/ui/letter/ src/ui/settings/ && npm run ci Plan 02-05 creates pending
02-05-T3 02-05 2 PIPE-07 T-02-05-01 Playwright e2e covering load → begin → plant → fast-forward → harvest → reveal → journal → reload → persist e2e npm run ci && npx playwright test tests/e2e/season1-loop.spec.ts Plan 02-05 creates pending

Status: pending · green · red · ⚠️ flaky


Wave 0 Requirements

Wave 0 (Plan 02-01) lands BigQty, the Zustand 5 store, the tick scheduler, and the Phase-2 V1Payload schema extension before any vertical slice can run. Until Wave 0 lands, every Wave-1+ task carries a Wave-0 dependency.

  • src/sim/numbers/big-qty.ts + big-qty.test.ts — BigQty wrapper unit tests
  • src/sim/numbers/format.ts + format.test.ts — UX-11 thresholds
  • src/sim/scheduler/clock.ts + clock.test.ts — FakeClock fixture; Date.now() owner
  • src/sim/scheduler/tick.ts + tick.test.ts — drainTicks, accumulator, CORE-02
  • src/sim/scheduler/catchup.ts + catchup.test.ts — 24h clamp + negative refusal (CORE-03 + CORE-11)
  • src/store/store.ts + store.test.ts — Zustand 5 vanilla createStore + 4 slices
  • src/store/sim-adapter.ts — slim sim → store adapter (sim never imports the store directly)
  • src/save/migrations.ts — extended V1Payload with unlockedPlantTypes / luraBeatProgress / offlineEvents / settings.persistenceToastShown (Phase-2 schema EXTENSION, not v1→v2)
  • src/save/lifecycle.ts + lifecycle.test.ts — UX-10 hooks (visibilitychange + beforeunload + saveOnSeasonTransition)
  • src/game/event-bus.ts — Phaser.Events.EventEmitter singleton (per Phaser 4 React-template pattern)
  • eslint.config.js — additional sim-purity rule banning Date.now/setInterval in src/sim/** (defended option; Plan 02-01 Task 3 ships)

Filled in by Plan 02-01. Plans 02-02..02-05 declare a Wave-0 dependency in frontmatter (depends_on: [02-01, ...]).


Manual-Only Verifications

Behavior Requirement Why Manual Test Instructions
AudioContext.resume() actually unlocks audio across Chrome / Firefox / Safari / Edge AEST-07 Browser autoplay policies vary; Vitest happy-dom does not exercise real audio context. After Plan 02-02 lands, manually load the dev build in Chrome / Firefox / Safari (last 2 stable). Click Begin. Confirm a console-logged audioContext.state === 'running'.
Letter prose reads in voice (not as a stat dump) UX-02 Tonal verification is a human review; CONTEXT D-17 explicitly calls this out. Author reviews /content/dialogue/season1/letter-from-the-garden.ink against the bible voice; reviewer signs off in Plan 02-05 SUMMARY.
Cozy 25min growth feels right at playtest CONTEXT D-08 / D-09 Subjective pacing — only verifiable by playing. Solo playtest after Plan 02-05 lands. Adjust per-plant durations if either pace is off; record final values in SUMMARY.
Lura's three beats read in voice + warmth-anchor tone STRY-01 + CLAUDE.md Tone Tonal verification. After Plan 02-04 lands, user reads compiled Lura beats in dev build during a real harvest cadence. Sign-off in Plan 02-04 SUMMARY.
Tile→DOM coord mapping under Phaser.Scale.FIT RESEARCH Assumption A5 (MEDIUM) Layout depends on actual canvas DOMRect; test in non-fullscreen window. Plan 02-02 Task 2 manual smoke: dev build → click empty tile → seed picker positioned visually over the tile in non-fullscreen + fullscreen.
inklecate compiles cleanly on Windows + macOS + Linux RESEARCH Assumption A6 (MEDIUM) First real inklecate invocation in the project. Plan 02-04 Task 1 first run on the dev machine; cross-platform verified post-merge via CI run.

Validation Sign-Off

  • All tasks have <automated> verify or Wave 0 dependencies
  • Sampling continuity: no 3 consecutive tasks without automated verify (every task above has a non-empty Automated Command)
  • Wave 0 covers all MISSING references (Plan 02-01 Tasks 1+2 land all foundations Plans 02-02..02-05 depend on)
  • No watch-mode flags (vitest run, not vitest)
  • Feedback latency < 8s for Vitest, < 30s for Playwright
  • nyquist_compliant: true set in frontmatter (set by the planner after the per-task map filled)

Approval: approved (planner)