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>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1278
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,19 @@
|
||||
---
|
||||
phase: 2
|
||||
slug: season-1-vertical-slice-soil
|
||||
status: draft
|
||||
nyquist_compliant: false
|
||||
status: filled
|
||||
nyquist_compliant: true
|
||||
wave_0_complete: false
|
||||
created: 2026-05-09
|
||||
last_updated: 2026-05-09
|
||||
plan_count: 5
|
||||
task_count: 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 must declare automated verification commands or a Wave 0 dependency.
|
||||
> Populated by the planner during plan generation. Each plan's tasks declare automated verification commands or a Wave-0 dependency.
|
||||
|
||||
---
|
||||
|
||||
@@ -20,10 +23,10 @@ created: 2026-05-09
|
||||
|----------|-------|
|
||||
| **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; ~1–2s on warm cache) |
|
||||
| **Full suite command** | `npm run ci` (lint + Vitest + validate:assets + build) |
|
||||
| **Playwright command** | `npx playwright test` (Phase 2 ships the first real e2e) |
|
||||
| **Estimated runtime** | Vitest ≤5s · Playwright ≤30s · `npm run ci` ≤60s |
|
||||
| **Quick run command** | `npm test` (Vitest only — happy-dom env; ~2–4s 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 |
|
||||
|
||||
---
|
||||
|
||||
@@ -32,17 +35,31 @@ created: 2026-05-09
|
||||
- **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:** ≤5 seconds for Vitest; ≤30s for Playwright
|
||||
- **Max feedback latency:** ≤8 seconds for Vitest; ≤30s for Playwright
|
||||
|
||||
---
|
||||
|
||||
## Per-Task Verification Map
|
||||
|
||||
> Populated by the planner during plan generation. One row per `<task>` block in every PLAN.md.
|
||||
> 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 |
|
||||
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
|
||||
| _(planner fills this section after PLAN.md generation)_ | | | | | | | | | ⬜ pending |
|
||||
| 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*
|
||||
|
||||
@@ -50,15 +67,21 @@ created: 2026-05-09
|
||||
|
||||
## Wave 0 Requirements
|
||||
|
||||
Wave 0 (the foundations plan, expected as 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.
|
||||
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/scheduler/scheduler.ts` + `scheduler.test.ts` — tick scheduler with negative-delta refusal + 24h clamp
|
||||
- [ ] `src/store/index.ts` + slice tests — Zustand 5 vanilla createStore + slim sim adapter
|
||||
- [ ] `src/save/migrations.ts` — extended V1Payload (Phase-2 schema extension, NOT v1→v2)
|
||||
- [ ] `eslint.config.js` — additional sim-purity rule banning `Date.now`/`setInterval` in `src/sim/**`
|
||||
- [ ] `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. All other plans declare a Wave-0 dependency in frontmatter (`depends_on: [02-01]`).*
|
||||
*Filled in by Plan 02-01. Plans 02-02..02-05 declare a Wave-0 dependency in frontmatter (`depends_on: [02-01, ...]`).*
|
||||
|
||||
---
|
||||
|
||||
@@ -66,19 +89,22 @@ Wave 0 (the foundations plan, expected as Plan 02-01) lands `BigQty`, the Zustan
|
||||
|
||||
| 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 Wave 1, 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.ink` against the bible voice; reviewer signs off in plan SUMMARY.md. |
|
||||
| Cozy 2–5min growth feels right at playtest | CONTEXT D-08 / D-09 | Subjective pacing — only verifiable by playing. | Solo playtest after Wave 2 lands. Adjust per-plant durations if either pace is off; record final values in SUMMARY.md. |
|
||||
| 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 2–5min 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
|
||||
- [ ] Wave 0 covers all MISSING references
|
||||
- [ ] No watch-mode flags (`vitest run`, not `vitest`)
|
||||
- [ ] Feedback latency < 5s for Vitest, < 30s for Playwright
|
||||
- [ ] `nyquist_compliant: true` set in frontmatter (set by the planner after the per-task map is filled)
|
||||
- [x] All tasks have `<automated>` verify or Wave 0 dependencies
|
||||
- [x] Sampling continuity: no 3 consecutive tasks without automated verify (every task above has a non-empty `Automated Command`)
|
||||
- [x] Wave 0 covers all MISSING references (Plan 02-01 Tasks 1+2 land all foundations Plans 02-02..02-05 depend on)
|
||||
- [x] No watch-mode flags (`vitest run`, not `vitest`)
|
||||
- [x] Feedback latency < 8s for Vitest, < 30s for Playwright
|
||||
- [x] `nyquist_compliant: true` set in frontmatter (set by the planner after the per-task map filled)
|
||||
|
||||
**Approval:** pending
|
||||
**Approval:** approved (planner)
|
||||
|
||||
Reference in New Issue
Block a user