Files
initiative/specs/031-quality-gates-hygiene/tasks.md

212 lines
13 KiB
Markdown

# Tasks: Quality Gates & Code Hygiene
**Input**: Design documents from `/specs/031-quality-gates-hygiene/`
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, quickstart.md
**Tests**: No test tasks — feature spec does not request TDD or explicit tests. Verification is via `pnpm check`.
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
## Format: `[ID] [P?] [Story] Description`
- **[P]**: Can run in parallel (different files, no dependencies)
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
- Include exact file paths in descriptions
## Phase 1: Setup
**Purpose**: Install new dependencies and prepare infrastructure
- [x] T001 Install `@vitest/coverage-v8` as dev dependency via `pnpm add -D @vitest/coverage-v8` in root `package.json`
- [x] T002 Add `coverage/` to `.gitignore`
---
## Phase 2: User Story 1 — Constitution codifies early enforcement (Priority: P1) 🎯 MVP
**Goal**: The constitution and CLAUDE.md explicitly state that all automated quality gates MUST run at the earliest feasible enforcement point (currently pre-commit via Lefthook's `pnpm check`).
**Independent Test**: Read the updated constitution and CLAUDE.md and confirm the early-enforcement language is present.
### Implementation for User Story 1
- [x] T003 [P] [US1] Update the Development Workflow section of `.specify/memory/constitution.md` to add an early-enforcement rule: "All automated quality gates MUST run at the earliest feasible enforcement point (currently pre-commit via Lefthook). No gate may exist only as a CI step or manual process." Bump version 2.2.0 → 2.2.1 (PATCH). Update the "Last Amended" date.
- [x] T004 [P] [US1] Update the Conventions section of `CLAUDE.md` to add: "All quality gates are enforced at pre-commit via Lefthook's `pnpm check` — the project's single earliest enforcement point. No gate may exist only as a CI step or manual process." Also update the `pnpm check` comment in the Commands section to reflect the full set of gates once all stories are complete.
**Checkpoint**: Constitution and CLAUDE.md reflect the early-enforcement principle.
---
## Phase 3: User Story 2 — Test coverage thresholds (Priority: P1)
**Goal**: `pnpm check` enforces v8 coverage thresholds (lines: 70%, branches: 60%) with auto-ratchet.
**Independent Test**: Run `pnpm check` and verify coverage is reported, thresholds are enforced, and `thresholds.autoUpdate` is configured.
### Implementation for User Story 2
- [x] T005 [US2] Add `test.coverage` configuration to `vitest.config.ts`: provider `"v8"`, enabled `true`, thresholds `{ lines: 70, branches: 60, autoUpdate: true }`. See research.md R1 for exact syntax.
- [x] T006 [US2] Run `pnpm test` to verify coverage passes with the configured thresholds. If thresholds are too high for current coverage, lower them to just below actual coverage (the `autoUpdate` will ratchet them up). Commit any auto-updated threshold values in `vitest.config.ts`. Note in CLAUDE.md (during T019) that contributors must commit auto-ratcheted threshold changes as part of their changeset.
**Checkpoint**: `vitest run` reports coverage and enforces thresholds. No script changes needed — `vitest run` in `pnpm check` automatically includes coverage.
---
## Phase 4: User Story 3 — Cognitive complexity (Priority: P2)
**Goal**: Biome enforces `noExcessiveCognitiveComplexity` at threshold 15. All existing violations are refactored.
**Independent Test**: Run `biome check .` and verify no cognitive complexity violations.
### Implementation for User Story 3
- [x] T007 [US3] Enable `noExcessiveCognitiveComplexity` in `biome.json` under `linter.rules.complexity` with `level: "error"` and `options.maxAllowedComplexity: 15`. Nest inside existing `linter.rules` — add `"complexity"` key alongside `"recommended": true`.
- [x] T008 [P] [US3] Refactor `renderEntries` function (complexity 23) in `apps/web/src/adapters/bestiary-adapter.ts` to reduce cognitive complexity to ≤15. Extract helper functions for distinct entry type handling.
- [x] T009 [P] [US3] Refactor `loadEncounter` function (complexity 17) and rehydration callback (complexity 22) in `apps/web/src/persistence/encounter-storage.ts` to reduce cognitive complexity to ≤15. Extract field-rehydration logic into a helper function.
- [x] T010 [P] [US3] Refactor `checkLayerBoundaries` function (complexity 22) in `scripts/check-layer-boundaries.mjs` to reduce cognitive complexity to ≤15. Extract per-layer checking into separate functions.
- [x] T011 [P] [US3] Refactor `buildSourceMap` function (complexity 19) in `scripts/generate-bestiary-index.mjs` to reduce cognitive complexity to ≤15. Extract file-loading and source-parsing into helpers.
- [x] T012 [US3] Verify all 5 violations are resolved by running `npx biome lint --only='lint/complexity/noExcessiveCognitiveComplexity' .` and confirming zero errors.
**Checkpoint**: `biome check .` passes with cognitive complexity rule enabled.
---
## Phase 5: User Story 4 — Dependency audit (Priority: P2)
**Goal**: `pnpm check` includes `pnpm audit --audit-level=high` to catch high-severity CVEs at pre-commit.
**Independent Test**: Run the updated `pnpm check` and verify the audit step executes and passes.
### Implementation for User Story 4
- [x] T013 [US4] Add `pnpm audit --audit-level=high` to the `check` script in root `package.json`. Place it as the first step in the chain (before `knip`) for early failure on CVEs: `"check": "pnpm audit --audit-level=high && knip && biome check . && tsc --build && vitest run && jscpd"`.
**Checkpoint**: `pnpm check` runs audit as first gate.
---
## Phase 6: User Story 5 — Biome-ignore hygiene (Priority: P2)
**Goal**: Zero blanket `biome-ignore lint:` comments. Reduce a11y ignores in `combatant-row.tsx` from 8 to ≤4.
**Independent Test**: Search codebase for `biome-ignore lint:` (blanket form) — zero results. Count ignores in `combatant-row.tsx` — ≤4.
### Implementation for User Story 5
- [x] T014 [P] [US5] Fix the blanket `biome-ignore lint:` on line 65 of `packages/domain/src/set-initiative.ts`. Restructure the sort comparator to eliminate the need for non-null assertions: handle the `aHas && bHas` branch by extracting `a.c.initiative` and `b.c.initiative` into properly narrowed local variables (e.g., use an early-return pattern or explicit `as number` casts). The goal is to remove the ignore entirely, not replace it with a rule-specific one.
- [x] T015 [US5] Refactor `apps/web/src/components/combatant-row.tsx` to reduce the 8 a11y `biome-ignore` comments (4 pairs of `useKeyWithClickEvents` + `noStaticElementInteractions`). Strategy: the 3 inner wrapper divs (initiative, AC, HP sections) exist solely for `e.stopPropagation()` — replace them with a small reusable wrapper component or restructure the outer row's click handler to check `event.target`/`currentTarget` and skip propagation when a child interactive element is clicked. For the outer row div, add `role="button"`, `tabIndex={0}`, and an `onKeyDown` handler (Enter/Space) to satisfy both a11y rules. Target: ≤4 total ignores (down from 8).
- [x] T016 [US5] Verify no blanket `biome-ignore lint:` remains anywhere in the codebase by searching all `*.ts`, `*.tsx`, `*.js`, and `*.mjs` files.
**Checkpoint**: Zero blanket ignores. `combatant-row.tsx` has ≤4 ignores.
---
## Phase 7: User Story 6 — Biome a11y rules (Priority: P3)
**Goal**: Enable relevant non-recommended Biome a11y rules in `biome.json`.
**Independent Test**: Review `biome.json` for explicit a11y rule enablement. Run `biome check .` — no new violations.
### Implementation for User Story 6
- [x] T017 [US6] N/A — `noNoninteractiveElementInteractions` does not exist in Biome 2.0. All available a11y rules are already covered by `recommended: true`. No non-recommended a11y rules to enable. in `biome.json` under `linter.rules.a11y` with `level: "error"`. This is the only relevant non-recommended stable a11y rule in Biome 2.0 (research.md R3 confirmed no nursery a11y rules exist). Add `"a11y"` key alongside existing `"complexity"` under `linter.rules`.
- [x] T018 [US6] N/A — no new rule enabled, no violations to fix. from `noNoninteractiveElementInteractions` in existing code. Run `npx biome lint --only='lint/a11y/noNoninteractiveElementInteractions' .` to identify violations. Fix each by adding appropriate ARIA roles, converting to semantic elements, or adding rule-specific (never blanket) biome-ignore comments with justification.
**Checkpoint**: `biome check .` passes with the new a11y rule enabled.
---
## Phase 8: Polish & Cross-Cutting Concerns
**Purpose**: Final verification and documentation updates
- [x] T019 Update the `pnpm check` comment in the Commands section of `CLAUDE.md` to list all six gate types: `# Merge gate — must pass before every commit (audit + knip + biome + typecheck + test/coverage + jscpd)`
- [x] T020 Run `pnpm check` end-to-end and verify all gates pass: audit, knip, biome (including cognitive complexity + a11y rules), typecheck, vitest (with coverage thresholds), and jscpd.
- [x] T021 Clean up any agent-context markers added to `CLAUDE.md` by the speckit agent-context script (lines referencing `031-quality-gates-hygiene` with technology stack entries that duplicate existing Tech Stack documentation).
---
## Dependencies & Execution Order
### Phase Dependencies
- **Phase 1 (Setup)**: No dependencies — start immediately
- **Phase 2 (US1)**: No dependencies — can run in parallel with Phase 1
- **Phase 3 (US2)**: Depends on Phase 1 (needs `@vitest/coverage-v8` installed)
- **Phase 4 (US3)**: No dependencies on other phases — biome.json change + refactors are self-contained
- **Phase 5 (US4)**: No dependencies — `package.json` check script change is independent
- **Phase 6 (US5)**: No dependencies — biome-ignore fixes are independent
- **Phase 7 (US6)**: Should follow Phase 6 (US5) — combatant-row refactoring in US5 may affect which elements trigger the new a11y rule
- **Phase 8 (Polish)**: Depends on all previous phases — final verification
### User Story Dependencies
- **US1 (P1)**: Independent — documentation only
- **US2 (P1)**: Depends on Setup (T001)
- **US3 (P2)**: Independent — modifies `biome.json` + refactors
- **US4 (P2)**: Independent — modifies `package.json` check script
- **US5 (P2)**: Independent — modifies source files only
- **US6 (P3)**: Soft dependency on US5 — modifies `biome.json` (same file as US3, different section) and may interact with US5's combatant-row refactor
### Within Each User Story
- Config changes before code refactors (US3: enable rule → refactor violations)
- Verification task after implementation tasks
- Each story independently completable and verifiable
### Parallel Opportunities
**Cross-story parallelism** (after Setup):
- US1, US3, US4, US5 can all run in parallel (different files)
- US2 can run in parallel with all except must wait for T001
- US6 should follow US5 (shared file concerns)
**Within-story parallelism**:
- US3: T008, T009, T010, T011 (4 independent refactors across different files)
- US5: T014 can run in parallel with T015 (different files)
---
## Parallel Example: User Story 3
```text
# After T007 (biome.json rule enablement), launch all refactors in parallel:
Task: "Refactor renderEntries in apps/web/src/adapters/bestiary-adapter.ts"
Task: "Refactor loadEncounter + rehydration in apps/web/src/persistence/encounter-storage.ts"
Task: "Refactor checkLayerBoundaries in scripts/check-layer-boundaries.mjs"
Task: "Refactor buildSourceMap in scripts/generate-bestiary-index.mjs"
```
---
## Implementation Strategy
### MVP First (User Stories 1 + 2)
1. Complete Phase 1: Setup (install coverage dep)
2. Complete Phase 2: US1 — Constitution + CLAUDE.md
3. Complete Phase 3: US2 — Coverage thresholds
4. **STOP and VALIDATE**: `pnpm check` runs with coverage enforcement
5. The two P1 stories deliver the most impactful gates
### Incremental Delivery
1. Setup → US1 + US2 → Coverage + documentation MVP
2. Add US3 → Cognitive complexity enforced
3. Add US4 → Dependency audit enforced
4. Add US5 → Lint hygiene cleaned up
5. Add US6 → A11y rules comprehensive
6. Polish → Final verification
7. Each increment leaves `pnpm check` passing
---
## Notes
- [P] tasks = different files, no dependencies
- [Story] label maps task to specific user story for traceability
- No test tasks generated — verification is via `pnpm check` and manual search
- The `check` script in `package.json` is modified by US4 (T013) — be aware that US2's coverage wires in automatically via config, not script changes
- US3 and US6 both modify `biome.json` — if implementing in parallel, merge carefully (different rule sections: `complexity` vs `a11y`)
- Commit after each task or logical group