13 KiB
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
- T001 Install
@vitest/coverage-v8as dev dependency viapnpm add -D @vitest/coverage-v8in rootpackage.json - 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
- T003 [P] [US1] Update the Development Workflow section of
.specify/memory/constitution.mdto 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. - T004 [P] [US1] Update the Conventions section of
CLAUDE.mdto add: "All quality gates are enforced at pre-commit via Lefthook'spnpm check— the project's single earliest enforcement point. No gate may exist only as a CI step or manual process." Also update thepnpm checkcomment 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
- T005 [US2] Add
test.coverageconfiguration tovitest.config.ts: provider"v8", enabledtrue, thresholds{ lines: 70, branches: 60, autoUpdate: true }. See research.md R1 for exact syntax. - T006 [US2] Run
pnpm testto verify coverage passes with the configured thresholds. If thresholds are too high for current coverage, lower them to just below actual coverage (theautoUpdatewill ratchet them up). Commit any auto-updated threshold values invitest.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
- T007 [US3] Enable
noExcessiveCognitiveComplexityinbiome.jsonunderlinter.rules.complexitywithlevel: "error"andoptions.maxAllowedComplexity: 15. Nest inside existinglinter.rules— add"complexity"key alongside"recommended": true. - T008 [P] [US3] Refactor
renderEntriesfunction (complexity 23) inapps/web/src/adapters/bestiary-adapter.tsto reduce cognitive complexity to ≤15. Extract helper functions for distinct entry type handling. - T009 [P] [US3] Refactor
loadEncounterfunction (complexity 17) and rehydration callback (complexity 22) inapps/web/src/persistence/encounter-storage.tsto reduce cognitive complexity to ≤15. Extract field-rehydration logic into a helper function. - T010 [P] [US3] Refactor
checkLayerBoundariesfunction (complexity 22) inscripts/check-layer-boundaries.mjsto reduce cognitive complexity to ≤15. Extract per-layer checking into separate functions. - T011 [P] [US3] Refactor
buildSourceMapfunction (complexity 19) inscripts/generate-bestiary-index.mjsto reduce cognitive complexity to ≤15. Extract file-loading and source-parsing into helpers. - 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
- T013 [US4] Add
pnpm audit --audit-level=highto thecheckscript in rootpackage.json. Place it as the first step in the chain (beforeknip) 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
- T014 [P] [US5] Fix the blanket
biome-ignore lint:on line 65 ofpackages/domain/src/set-initiative.ts. Restructure the sort comparator to eliminate the need for non-null assertions: handle theaHas && bHasbranch by extractinga.c.initiativeandb.c.initiativeinto properly narrowed local variables (e.g., use an early-return pattern or explicitas numbercasts). The goal is to remove the ignore entirely, not replace it with a rule-specific one. - T015 [US5] Refactor
apps/web/src/components/combatant-row.tsxto reduce the 8 a11ybiome-ignorecomments (4 pairs ofuseKeyWithClickEvents+noStaticElementInteractions). Strategy: the 3 inner wrapper divs (initiative, AC, HP sections) exist solely fore.stopPropagation()— replace them with a small reusable wrapper component or restructure the outer row's click handler to checkevent.target/currentTargetand skip propagation when a child interactive element is clicked. For the outer row div, addrole="button",tabIndex={0}, and anonKeyDownhandler (Enter/Space) to satisfy both a11y rules. Target: ≤4 total ignores (down from 8). - T016 [US5] Verify no blanket
biome-ignore lint:remains anywhere in the codebase by searching all*.ts,*.tsx,*.js, and*.mjsfiles.
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
- T017 [US6] N/A —
noNoninteractiveElementInteractionsdoes not exist in Biome 2.0. All available a11y rules are already covered byrecommended: true. No non-recommended a11y rules to enable. inbiome.jsonunderlinter.rules.a11ywithlevel: "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"underlinter.rules. - T018 [US6] N/A — no new rule enabled, no violations to fix. from
noNoninteractiveElementInteractionsin existing code. Runnpx 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
- T019 Update the
pnpm checkcomment in the Commands section ofCLAUDE.mdto list all six gate types:# Merge gate — must pass before every commit (audit + knip + biome + typecheck + test/coverage + jscpd) - T020 Run
pnpm checkend-to-end and verify all gates pass: audit, knip, biome (including cognitive complexity + a11y rules), typecheck, vitest (with coverage thresholds), and jscpd. - T021 Clean up any agent-context markers added to
CLAUDE.mdby the speckit agent-context script (lines referencing031-quality-gates-hygienewith 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-v8installed) - Phase 4 (US3): No dependencies on other phases — biome.json change + refactors are self-contained
- Phase 5 (US4): No dependencies —
package.jsoncheck 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.jsoncheck 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
# 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)
- Complete Phase 1: Setup (install coverage dep)
- Complete Phase 2: US1 — Constitution + CLAUDE.md
- Complete Phase 3: US2 — Coverage thresholds
- STOP and VALIDATE:
pnpm checkruns with coverage enforcement - The two P1 stories deliver the most impactful gates
Incremental Delivery
- Setup → US1 + US2 → Coverage + documentation MVP
- Add US3 → Cognitive complexity enforced
- Add US4 → Dependency audit enforced
- Add US5 → Lint hygiene cleaned up
- Add US6 → A11y rules comprehensive
- Polish → Final verification
- Each increment leaves
pnpm checkpassing
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 checkand manual search - The
checkscript inpackage.jsonis 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:complexityvsa11y) - Commit after each task or logical group