T001–T006: Phase 1 setup (workspace, Biome, TS, Vitest, layer boundary enforcement)

This commit is contained in:
Lukas
2026-03-03 12:54:29 +01:00
parent ddb2b317d3
commit 7dd4abb12a
27 changed files with 2655 additions and 35 deletions

View File

@@ -0,0 +1,128 @@
# Tasks: Advance Turn
**Input**: Design documents from `/specs/001-advance-turn/`
**Prerequisites**: plan.md (required), spec.md (required)
**Organization**: Tasks follow the phased structure from plan.md. There is only one user story (US1 — Advance Turn, P1), so phases map directly to the plan's milestones.
## Format: `[ID] [P?] [Story?] Description`
- **[P]**: Can run in parallel (different files, no dependencies)
- **[US1]**: User Story 1 — Advance Turn
- Exact file paths included in every task
---
## Phase 1: Setup (Milestone 1 — Tooling)
**Purpose**: Initialize pnpm monorepo, Biome, TypeScript, Vitest, and layer boundary enforcement
- [X] T001 Initialize pnpm workspace and root config — create `pnpm-workspace.yaml`, `.nvmrc` (Node 22), root `package.json` (with `packageManager` pinning pnpm 10.6 and scripts: check, test, lint, format, typecheck), `biome.json`, and `tsconfig.base.json` (strict, composite, path aliases)
- [X] T002 [P] Create `packages/domain` package skeleton — `packages/domain/package.json` (`@initiative/domain`, no deps), `packages/domain/tsconfig.json` (extends base, composite), empty `packages/domain/src/index.ts`
- [X] T003 [P] Create `packages/application` package skeleton — `packages/application/package.json` (`@initiative/application`, depends on `@initiative/domain`), `packages/application/tsconfig.json` (extends base, references domain), empty `packages/application/src/index.ts`
- [X] T004 [P] Create `apps/web` package skeleton — `apps/web/package.json` (React 19, Vite 6.2, depends on both packages), `apps/web/tsconfig.json`, `apps/web/vite.config.ts`, `apps/web/index.html`, `apps/web/src/main.tsx`, `apps/web/src/App.tsx` (placeholder)
- [X] T005 Configure Vitest — add `vitest` as root dev dependency, create `vitest.config.ts` at root (workspace mode or per-package), verify `pnpm test` exits 0
- [X] T006 Create layer boundary check — `scripts/check-layer-boundaries.mjs` (scans domain/application for forbidden imports) and `packages/domain/src/__tests__/layer-boundaries.test.ts` (wraps script as Vitest test)
**Checkpoint**: `pnpm install` succeeds, `biome check .` runs, `tsc --build` compiles, `pnpm test` exits 0 with layer boundary test green.
---
## Phase 2: Domain Implementation — User Story 1: Advance Turn (Priority: P1) (Milestone 1)
**Goal**: Implement the complete AdvanceTurn domain logic as a pure function with all 8 acceptance scenarios and invariant tests.
**Independent Test**: Pure state transition — given an Encounter value and AdvanceTurn action, assert resulting Encounter and emitted domain events. No I/O, persistence, or UI needed.
- [ ] T007 [US1] Define domain types in `packages/domain/src/types.ts``CombatantId` (branded/opaque), `Combatant`, `Encounter` (combatants, activeIndex, roundNumber), factory `createEncounter` enforcing INV-1, INV-2, INV-3
- [ ] T008 [P] [US1] Define domain events in `packages/domain/src/events.ts``TurnAdvanced`, `RoundAdvanced`, `DomainEvent` union (plain data, no classes)
- [ ] T009 [US1] Implement `advanceTurn` in `packages/domain/src/advance-turn.ts` — pure function `(Encounter) => { encounter, events } | DomainError`, implements FR-001 through FR-005
- [ ] T010 [US1] Write tests for all 8 acceptance scenarios + invariants in `packages/domain/src/__tests__/advance-turn.test.ts` — scenarios 18, INV-1 through INV-5, event ordering on round wrap
- [ ] T011 [US1] Export public API from `packages/domain/src/index.ts` — re-export types, events, `advanceTurn`, `createEncounter`
**Checkpoint (Milestone 1)**: `pnpm check` passes (format + lint + typecheck + test + layer boundaries). All 8 scenarios + invariants green. No React/Vite imports in domain or application.
---
## Phase 3: Application + Web Shell (Milestone 2)
**Goal**: Wire up the application use case and minimal React UI with a "Next Turn" button.
- [ ] T012 Define port interface in `packages/application/src/ports.ts``EncounterStore` port: `get(): Encounter`, `save(e: Encounter)`
- [ ] T013 Implement `AdvanceTurnUseCase` in `packages/application/src/advance-turn-use-case.ts` — accepts `EncounterStore`, calls `advanceTurn`, saves result, returns events
- [ ] T014 Export public API from `packages/application/src/index.ts` — re-export use case and port types
- [ ] T015 Implement `useEncounter` hook in `apps/web/src/hooks/use-encounter.ts` — in-memory `EncounterStore` via React state, exposes encounter state + `advanceTurn` action, hardcoded 3-combatant demo
- [ ] T016 Wire up `apps/web/src/App.tsx` — display current combatant, round number, combatant list with active indicator, "Next Turn" button, emitted events
**Checkpoint (Milestone 2)**: `pnpm check` passes. `vite build` succeeds. Clicking "Next Turn" cycles combatants and increments rounds correctly.
---
## Dependencies & Execution Order
### Phase Dependencies
- **Phase 1 (Setup)**: No dependencies — start immediately
- **Phase 2 (Domain)**: Depends on Phase 1 completion
- **Phase 3 (App + Web)**: Depends on Phase 2 completion (needs domain types and `advanceTurn`)
### Within Phase 1
- T001 must complete first (workspace and root config)
- T002, T003, T004 can run in parallel [P] after T001
- T005 depends on T001 (needs root package.json)
- T006 depends on T002 and T005 (needs domain package + Vitest)
### Within Phase 2
- T007 must complete first (types needed by everything)
- T008 can run in parallel [P] with T007 (events are independent types)
- T009 depends on T007 and T008 (uses types and events)
- T010 depends on T009 (tests the implementation)
- T011 depends on T007, T008, T009 (exports all public API)
### Within Phase 3
- T012 first (port interface)
- T013 depends on T012 (uses port)
- T014 depends on T013 (exports use case)
- T015 depends on T014 (uses application layer)
- T016 depends on T015 (uses hook)
---
## Parallel Opportunities
```text
# After T001 completes:
T002, T003, T004 — all package skeletons in parallel
# After T007 starts:
T008 — domain events can be written in parallel with types
# Independent stories: only one user story (US1), so parallelism is within-phase only
```
---
## Implementation Strategy
### MVP First (Milestone 1)
1. Complete Phase 1: Setup (T001T006)
2. Complete Phase 2: Domain (T007T011)
3. **STOP and VALIDATE**: `pnpm check` passes, all 8 scenarios green
### Full Feature (Milestone 2)
4. Complete Phase 3: App + Web Shell (T012T016)
5. **VALIDATE**: `pnpm check` passes, app runs in browser
---
## Notes
- All task IDs (T001T016) match plan.md — no scope expansion
- Single user story (US1: Advance Turn) — no cross-story dependencies
- Tests (T010) are included as specified in plan.md and spec.md
- Domain package must have zero React/Vite imports (enforced by T006)