Implement the 031-quality-gates-hygiene feature that strengthens automated quality gates by adding pnpm audit to the check script, v8 coverage thresholds with per-directory auto-ratchet (domain 96%, adapters 71%, persistence 87%), Biome cognitive complexity enforcement (max 15), and keyboard accessibility for the combatant row, while cleaning up all blanket biome-ignore comments, refactoring 5 overly complex functions into smaller helpers, and codifying the early-enforcement principle in the constitution and CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
35
specs/031-quality-gates-hygiene/checklists/requirements.md
Normal file
35
specs/031-quality-gates-hygiene/checklists/requirements.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Specification Quality Checklist: Quality Gates & Code Hygiene
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-03-10
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- All items pass. The spec references specific files (set-initiative.ts, combatant-row.tsx, biome.json) as context for the hygiene fixes, which is appropriate for a code-quality feature — these are the *targets* of the work, not implementation prescriptions.
|
||||
- Success criteria SC-001 mentions tool names (knip, biome, tsc, vitest, jscpd, pnpm audit) because this feature is specifically about configuring quality tooling — these are the *subject matter*, not implementation details.
|
||||
35
specs/031-quality-gates-hygiene/data-model.md
Normal file
35
specs/031-quality-gates-hygiene/data-model.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Data Model: Quality Gates & Code Hygiene
|
||||
|
||||
**Feature**: 031-quality-gates-hygiene
|
||||
**Date**: 2026-03-11
|
||||
|
||||
## Overview
|
||||
|
||||
This feature has no new domain entities. It modifies configuration files, documentation, and refactors existing code for lint compliance. No data model changes.
|
||||
|
||||
## Configuration Artifacts
|
||||
|
||||
### Vitest Coverage Config (vitest.config.ts)
|
||||
|
||||
New `test.coverage` block:
|
||||
- `provider`: "v8"
|
||||
- `enabled`: true
|
||||
- `thresholds.lines`: 70 (initial, auto-ratchets)
|
||||
- `thresholds.branches`: 60 (initial, auto-ratchets)
|
||||
- `thresholds.autoUpdate`: true
|
||||
|
||||
### Biome Config (biome.json)
|
||||
|
||||
New rules under `linter.rules`:
|
||||
- `complexity.noExcessiveCognitiveComplexity`: error, maxAllowedComplexity: 15
|
||||
- `a11y.noNoninteractiveElementInteractions`: error
|
||||
|
||||
### Check Script (package.json)
|
||||
|
||||
Updated `check` script adds `pnpm audit --audit-level=high` step.
|
||||
|
||||
## New Component
|
||||
|
||||
### StopPropagation Wrapper (apps/web/src/components/)
|
||||
|
||||
A small presentational component that wraps children in a `<div>` with `onClickCapture` to stop propagation, replacing the current pattern of `<div onClick={e => e.stopPropagation()}>` that requires biome-ignore comments. Exact implementation TBD during task execution — alternatives include using `role="presentation"` or restructuring the click handling logic in the parent row.
|
||||
81
specs/031-quality-gates-hygiene/plan.md
Normal file
81
specs/031-quality-gates-hygiene/plan.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Implementation Plan: Quality Gates & Code Hygiene
|
||||
|
||||
**Branch**: `031-quality-gates-hygiene` | **Date**: 2026-03-11 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `/specs/031-quality-gates-hygiene/spec.md`
|
||||
|
||||
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/plan-template.md` for the execution workflow.
|
||||
|
||||
## Summary
|
||||
|
||||
Strengthen the project's automated quality gates by: (1) codifying the early-enforcement principle in the constitution and CLAUDE.md, (2) adding Vitest v8 coverage thresholds with auto-ratchet, (3) enabling Biome's cognitive complexity rule and an additional a11y rule, (4) adding dependency auditing to the check script, and (5) cleaning up all blanket biome-ignore comments and reducing a11y suppressions in combatant-row.tsx. All gates enforce at pre-commit via `pnpm check`.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.8 (strict mode, `verbatimModuleSyntax`)
|
||||
**Primary Dependencies**: Vitest 3.2.4, Biome 2.0, Lefthook, Knip, jscpd. New: `@vitest/coverage-v8`
|
||||
**Storage**: N/A (config/documentation changes only)
|
||||
**Testing**: Vitest with v8 coverage provider (to be added)
|
||||
**Target Platform**: macOS/Linux dev environments (pre-commit hooks)
|
||||
**Project Type**: Web application (React 19 + Vite 6)
|
||||
**Performance Goals**: N/A — tooling config changes
|
||||
**Constraints**: All changes must pass `pnpm check` at pre-commit
|
||||
**Scale/Scope**: 10 files modified, 5 functions refactored for complexity
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Status | Notes |
|
||||
| --------- | ------ | ----- |
|
||||
| I. Deterministic Domain Core | PASS | No domain logic changes. `set-initiative.ts` fix is a lint-only refactor preserving identical behavior. |
|
||||
| II. Layered Architecture | PASS | No layer boundary changes. Refactors stay within their existing layers. |
|
||||
| III. Clarification-First | PASS | All requirements are explicitly specified by the user. No assumptions needed. |
|
||||
| IV. Escalation Gates | PASS | All items are in the spec. |
|
||||
| V. MVP Baseline Language | PASS | Constitution amendment uses PATCH clarification language, not permanent bans. |
|
||||
| VI. No Gameplay Rules | PASS | No gameplay mechanics involved. |
|
||||
| Dev Workflow: automated checks | PASS | This feature strengthens the check pipeline. |
|
||||
| Dev Workflow: CLAUDE.md sync | PASS | CLAUDE.md will be updated to reflect the early-enforcement principle. |
|
||||
| Dev Workflow: README sync | PASS | No user-facing capability changes — README update not required. |
|
||||
|
||||
**Post-Phase 1 re-check**: All gates still pass. The constitution itself is being amended (PATCH), which follows the amendment procedure (propose → update → verify → review → commit).
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/031-quality-gates-hygiene/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0 output — technical research
|
||||
├── data-model.md # Phase 1 output — config artifacts
|
||||
├── quickstart.md # Phase 1 output — implementation guide
|
||||
└── tasks.md # Phase 2 output (via /speckit.tasks)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
# Files modified by this feature:
|
||||
.specify/memory/constitution.md # PATCH amendment (early enforcement)
|
||||
CLAUDE.md # Add early-enforcement operational note
|
||||
biome.json # Add complexity + a11y rules
|
||||
vitest.config.ts # Add v8 coverage with thresholds
|
||||
package.json # Add @vitest/coverage-v8, update check script
|
||||
.gitignore # Add coverage/ directory
|
||||
|
||||
# Refactored for cognitive complexity:
|
||||
apps/web/src/adapters/bestiary-adapter.ts
|
||||
apps/web/src/persistence/encounter-storage.ts
|
||||
scripts/check-layer-boundaries.mjs
|
||||
scripts/generate-bestiary-index.mjs
|
||||
|
||||
# Refactored for biome-ignore hygiene:
|
||||
packages/domain/src/set-initiative.ts
|
||||
apps/web/src/components/combatant-row.tsx
|
||||
```
|
||||
|
||||
**Structure Decision**: No new directories or structural changes. All modifications target existing files. The only potential new file is a small `StopPropagation` wrapper component if the combatant-row refactor warrants extraction.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
No constitution violations to justify.
|
||||
40
specs/031-quality-gates-hygiene/quickstart.md
Normal file
40
specs/031-quality-gates-hygiene/quickstart.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Quickstart: Quality Gates & Code Hygiene
|
||||
|
||||
**Feature**: 031-quality-gates-hygiene
|
||||
**Date**: 2026-03-11
|
||||
|
||||
## What This Feature Does
|
||||
|
||||
Strengthens the project's automated quality gates by adding coverage thresholds, cognitive complexity limits, dependency auditing, and cleaning up lint suppressions — all enforced at pre-commit via `pnpm check`.
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. **Constitution + CLAUDE.md** — Add early-enforcement principle (documentation only)
|
||||
2. **Biome config** — Enable cognitive complexity rule + a11y rule
|
||||
3. **Refactor violations** — Fix 5 cognitive complexity violations + biome-ignore hygiene
|
||||
4. **Coverage** — Install `@vitest/coverage-v8`, configure thresholds
|
||||
5. **Audit** — Add `pnpm audit` to check script
|
||||
6. **Verify** — Run `pnpm check` end-to-end
|
||||
|
||||
## Key Files to Modify
|
||||
|
||||
| File | Change |
|
||||
| ---- | ------ |
|
||||
| `.specify/memory/constitution.md` | PATCH: add early-enforcement rule to Development Workflow |
|
||||
| `CLAUDE.md` | Reflect early-enforcement principle |
|
||||
| `biome.json` | Add complexity + a11y rules |
|
||||
| `vitest.config.ts` | Add coverage config with thresholds |
|
||||
| `package.json` | Add `@vitest/coverage-v8` dep, update check script |
|
||||
| `packages/domain/src/set-initiative.ts` | Remove blanket biome-ignore |
|
||||
| `apps/web/src/components/combatant-row.tsx` | Reduce a11y ignores |
|
||||
| `apps/web/src/adapters/bestiary-adapter.ts` | Refactor `renderEntries` complexity |
|
||||
| `apps/web/src/persistence/encounter-storage.ts` | Refactor `loadEncounter` + rehydration complexity |
|
||||
| `scripts/check-layer-boundaries.mjs` | Refactor `checkLayerBoundaries` complexity |
|
||||
| `scripts/generate-bestiary-index.mjs` | Refactor `buildSourceMap` complexity |
|
||||
| `.gitignore` | Add `coverage/` directory |
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
pnpm check # Must pass with all new gates
|
||||
```
|
||||
107
specs/031-quality-gates-hygiene/research.md
Normal file
107
specs/031-quality-gates-hygiene/research.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Research: Quality Gates & Code Hygiene
|
||||
|
||||
**Feature**: 031-quality-gates-hygiene
|
||||
**Date**: 2026-03-11
|
||||
|
||||
## R1: Vitest v8 Coverage Configuration
|
||||
|
||||
**Decision**: Use `@vitest/coverage-v8` with config-based enablement under `test.coverage`.
|
||||
|
||||
**Rationale**: Vitest 3.x (project uses 3.2.4) supports v8 coverage natively but requires the `@vitest/coverage-v8` peer package installed separately. Config-based `coverage.enabled: true` ensures coverage runs automatically with `vitest run`, integrating into the existing `pnpm check` script without CLI changes.
|
||||
|
||||
**Configuration**:
|
||||
```typescript
|
||||
test: {
|
||||
coverage: {
|
||||
provider: "v8",
|
||||
enabled: true,
|
||||
thresholds: {
|
||||
lines: 70,
|
||||
branches: 60,
|
||||
autoUpdate: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Key details**:
|
||||
- `thresholds.autoUpdate: true` modifies the config file in-place when coverage exceeds thresholds — contributors must commit the updated file.
|
||||
- No change to `pnpm check` script needed — `vitest run` already runs tests and will now include coverage.
|
||||
- `coverage` directory should be added to `.gitignore`.
|
||||
|
||||
**Alternatives considered**:
|
||||
- CLI flag `--coverage`: Rejected — requires changing the `check` script and doesn't support autoUpdate persistence.
|
||||
- Istanbul provider: Rejected — v8 is the default and faster for Node.js workloads.
|
||||
|
||||
## R2: Biome Cognitive Complexity Rule
|
||||
|
||||
**Decision**: Enable `noExcessiveCognitiveComplexity` with default threshold 15 and refactor 5 existing violations.
|
||||
|
||||
**Rationale**: The rule uses SonarSource's Cognitive Complexity methodology. Threshold 15 is the industry standard default. The rule is not included in `recommended: true` — it must be explicitly configured.
|
||||
|
||||
**Configuration**:
|
||||
```json
|
||||
"complexity": {
|
||||
"noExcessiveCognitiveComplexity": {
|
||||
"level": "error",
|
||||
"options": {
|
||||
"maxAllowedComplexity": 15
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Existing violations** (5 functions):
|
||||
|
||||
| File | Function | Score |
|
||||
| ---- | -------- | ----- |
|
||||
| `apps/web/src/adapters/bestiary-adapter.ts:257` | `renderEntries` | 23 |
|
||||
| `apps/web/src/persistence/encounter-storage.ts:21` | `loadEncounter` | 17 |
|
||||
| `apps/web/src/persistence/encounter-storage.ts:55` | rehydration callback | 22 |
|
||||
| `scripts/check-layer-boundaries.mjs:51` | `checkLayerBoundaries` | 22 |
|
||||
| `scripts/generate-bestiary-index.mjs:30` | `buildSourceMap` | 19 |
|
||||
|
||||
All must be refactored before the rule can be enforced.
|
||||
|
||||
## R3: Biome A11y Rules Review
|
||||
|
||||
**Decision**: Enable `noNoninteractiveElementInteractions` explicitly. No nursery-stage a11y rules exist in Biome 2.0.
|
||||
|
||||
**Rationale**: Biome 2.0 ships 35 a11y rules, 33 of which are `recommended: true`. The two non-recommended stable rules are:
|
||||
- `noNoninteractiveElementInteractions` — directly relevant to this React UI (flags interactive handlers on non-interactive elements)
|
||||
- `noRestrictedElements` — not applicable (requires a config of restricted elements, used for design system enforcement)
|
||||
|
||||
There are no nursery-stage a11y rules available. The spec's FR-011 (enable nursery rules) resolves to enabling the one relevant non-recommended stable rule instead.
|
||||
|
||||
## R4: pnpm audit
|
||||
|
||||
**Decision**: Add `pnpm audit --audit-level=high` to the `pnpm check` script.
|
||||
|
||||
**Rationale**: Current dependency tree passes cleanly (0 advisories). The command runs quickly and requires no additional dependencies. Placing it first in the chain provides early failure on CVEs before slower checks run.
|
||||
|
||||
**Offline consideration**: `pnpm audit` requires network access. If the registry is unreachable, the command fails. This is an acceptable trade-off — pre-commit enforcement is the primary goal, and offline commits are rare.
|
||||
|
||||
## R5: Biome-Ignore Audit
|
||||
|
||||
**Decision**: Fix 1 blanket ignore, reduce 8 a11y ignores in combatant-row.tsx.
|
||||
|
||||
**Current state** (10 total ignores):
|
||||
|
||||
| File | Count | Type |
|
||||
| ---- | ----- | ---- |
|
||||
| `packages/domain/src/set-initiative.ts:65` | 1 | Blanket `biome-ignore lint:` |
|
||||
| `apps/web/src/components/combatant-row.tsx` | 8 | Rule-specific (4 pairs of `useKeyWithClickEvents` + `noStaticElementInteractions`) |
|
||||
| `apps/web/src/adapters/bestiary-adapter.ts:375` | 1 | Rule-specific (`noExplicitAny`) |
|
||||
|
||||
**Fix strategies**:
|
||||
- **set-initiative.ts**: Replace `biome-ignore lint:` with proper type narrowing — the guard on line 64 checks `aHas && bHas`, so TypeScript can narrow if the comparison is restructured (e.g., early return for undefined cases, or explicit `as number` cast with rule-specific ignore).
|
||||
- **combatant-row.tsx**: The 3 inner divs (lines 444-446, 481-486, 491-496) exist solely to call `e.stopPropagation()`. These can be replaced with a small `StopPropagation` wrapper component that uses `onClickCapture` or similar pattern, or the outer row's click handler can check `event.target` to skip when a child interactive element was clicked. The outer row div (lines 405-407) could be converted to a `<div role="button" tabIndex={0} onKeyDown={...}>` to satisfy both rules, or the click handler can be moved to a semantic element.
|
||||
- **bestiary-adapter.ts**: Keep as-is — rule-specific ignore for `noExplicitAny` on raw JSON is justified.
|
||||
|
||||
## R6: Constitution Early Enforcement Principle
|
||||
|
||||
**Decision**: PATCH amendment to constitution Development Workflow section (version 2.2.0 → 2.2.1).
|
||||
|
||||
**Rationale**: This is a clarification of the existing rule "No change may be merged unless all automated checks pass." The new language makes explicit that gates must run at the earliest feasible enforcement point (pre-commit), not just in CI.
|
||||
|
||||
**Versioning**: PATCH — non-semantic clarification per the constitution's versioning policy.
|
||||
144
specs/031-quality-gates-hygiene/spec.md
Normal file
144
specs/031-quality-gates-hygiene/spec.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Feature Specification: Quality Gates & Code Hygiene
|
||||
|
||||
**Feature Branch**: `031-quality-gates-hygiene`
|
||||
**Created**: 2026-03-10
|
||||
**Status**: Draft
|
||||
**Input**: User description: "Refine constitution early enforcement principle, add Vitest coverage thresholds, Biome cognitive complexity rule, pnpm audit in check script, fix biome-ignore hygiene, review Biome a11y rules"
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Constitution codifies early enforcement (Priority: P1)
|
||||
|
||||
A contributor reads the constitution and CLAUDE.md and understands that every automated quality gate MUST run at the earliest feasible enforcement point — currently pre-commit via Lefthook's `pnpm check`. No gate may exist only as a CI step or manual process.
|
||||
|
||||
**Why this priority**: This principle governs all other items in the feature; without it, the remaining gates lack authoritative backing.
|
||||
|
||||
**Independent Test**: Can be verified by reading the updated constitution and CLAUDE.md and confirming the early-enforcement language is present.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the constitution's Development Workflow section, **When** a reader reviews it, **Then** there is an explicit rule stating all automated quality gates MUST run at the earliest feasible enforcement point (currently pre-commit).
|
||||
2. **Given** CLAUDE.md, **When** a reader reviews it, **Then** the early-enforcement principle is reflected operationally in the Commands or Conventions section.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Test coverage thresholds prevent regressions (Priority: P1)
|
||||
|
||||
A contributor runs `pnpm check` and coverage is measured automatically. If coverage drops below the configured thresholds (initial: lines 70%, branches 60%), the check fails. Thresholds ratchet upward automatically as coverage improves.
|
||||
|
||||
**Why this priority**: Coverage thresholds are the most impactful new gate — they catch untested code before it reaches the repository.
|
||||
|
||||
**Independent Test**: Can be tested by running `pnpm check` and verifying coverage is reported and thresholds are enforced.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the project with current test suite, **When** a contributor runs `pnpm check`, **Then** test coverage is measured using the v8 provider and reported.
|
||||
2. **Given** coverage thresholds configured at lines: 70, branches: 60, **When** a change drops coverage below either threshold, **Then** `pnpm check` fails.
|
||||
3. **Given** `thresholds.autoUpdate: true`, **When** coverage exceeds the configured thresholds, **Then** the thresholds are automatically ratcheted upward in the config file.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Cognitive complexity catches overly complex functions (Priority: P2)
|
||||
|
||||
A contributor writes a function with cognitive complexity exceeding 15. Biome flags it during `pnpm check`, prompting the contributor to refactor.
|
||||
|
||||
**Why this priority**: Directly supports the Deterministic Domain Core principle by keeping pure functions small and understandable, but is less impactful than coverage gates.
|
||||
|
||||
**Independent Test**: Can be tested by writing a function with complexity > 15 and verifying Biome reports an error.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** Biome configured with `noExcessiveCognitiveComplexity` at threshold 15, **When** `pnpm check` runs against a function exceeding that threshold, **Then** Biome reports a lint error.
|
||||
2. **Given** all existing code in the repository, **When** the rule is enabled, **Then** no existing code violates the threshold (or violations are addressed).
|
||||
|
||||
---
|
||||
|
||||
### User Story 4 - Dependency audit catches known CVEs (Priority: P2)
|
||||
|
||||
A contributor runs `pnpm check` and known high-severity dependency vulnerabilities are flagged before commit.
|
||||
|
||||
**Why this priority**: Security hygiene is important but the blast radius is smaller than coverage or complexity — it catches external dependency issues rather than code quality.
|
||||
|
||||
**Independent Test**: Can be tested by running `pnpm check` and verifying audit runs with `--audit-level=high`.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** `pnpm audit --audit-level=high` is wired into `pnpm check`, **When** a contributor runs `pnpm check`, **Then** the audit step executes.
|
||||
2. **Given** no high-severity advisories exist in the current dependency tree, **When** `pnpm check` runs, **Then** the audit step passes.
|
||||
3. **Given** a high-severity advisory exists, **When** `pnpm check` runs, **Then** the check fails and the advisory is reported.
|
||||
|
||||
---
|
||||
|
||||
### User Story 5 - Biome-ignore comments are hygienic (Priority: P2)
|
||||
|
||||
All `biome-ignore` comments in the codebase specify the exact rule being suppressed. Blanket `biome-ignore lint:` comments are eliminated. The number of total ignores is reduced where possible.
|
||||
|
||||
**Why this priority**: Code hygiene — blanket ignores silently suppress future rules and mask technical debt.
|
||||
|
||||
**Independent Test**: Can be tested by searching the codebase for `biome-ignore` comments and verifying none use blanket `lint:` form.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** `packages/domain/src/set-initiative.ts` line 65, **When** the blanket `biome-ignore lint:` is removed, **Then** the code is refactored so no ignore is needed (e.g., inline undefined checks or narrowing).
|
||||
2. **Given** `apps/web/src/components/combatant-row.tsx` with 8 a11y ignores (4 pairs of `useKeyWithClickEvents` + `noStaticElementInteractions`), **When** the clickable wrapper divs are refactored, **Then** the number of biome-ignore comments is reduced (ideally eliminated) by using semantic elements or a shared wrapper component.
|
||||
3. **Given** the full codebase, **When** searched for `biome-ignore lint:` (blanket form without a specific rule), **Then** zero results are found.
|
||||
|
||||
---
|
||||
|
||||
### User Story 6 - Biome a11y rules are comprehensive (Priority: P3)
|
||||
|
||||
The Biome configuration explicitly enables relevant non-recommended a11y rules that benefit this React UI app, beyond what `recommended: true` provides by default.
|
||||
|
||||
**Why this priority**: Incremental improvement — the app already has `recommended: true` covering stable a11y rules. Non-recommended rules add coverage but are lower urgency.
|
||||
|
||||
**Independent Test**: Can be tested by reviewing `biome.json` and verifying a11y nursery rules are explicitly listed.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** Biome 2.0 with `recommended: true`, **When** a developer reviews `biome.json`, **Then** relevant non-recommended a11y rules are explicitly enabled.
|
||||
2. **Given** the newly enabled rules, **When** `pnpm check` runs against the existing codebase, **Then** no new violations are introduced (or all violations are fixed).
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- What happens when `pnpm audit` finds a vulnerability in a dev-only dependency? The `--audit-level=high` flag filters by severity, not dependency type — dev-only high-severity CVEs still fail the check.
|
||||
- What happens when `thresholds.autoUpdate` writes to the config file? The updated thresholds file should be committed by the contributor as part of their change.
|
||||
- What happens if Biome nursery rules produce false positives? Individual false positives can be suppressed with rule-specific `biome-ignore` comments (never blanket ignores).
|
||||
- What happens if `pnpm audit` is slow or unavailable offline? The audit command may fail if the registry is unreachable; contributors working offline may need to skip the check temporarily (acceptable trade-off for pre-commit enforcement).
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: The constitution's Development Workflow section MUST include an explicit early-enforcement principle stating all automated quality gates run at the earliest feasible enforcement point.
|
||||
- **FR-002**: CLAUDE.md MUST reflect the early-enforcement principle operationally.
|
||||
- **FR-003**: Vitest MUST be configured with the v8 coverage provider and initial thresholds of lines: 70, branches: 60.
|
||||
- **FR-004**: Vitest coverage MUST have `thresholds.autoUpdate: true` enabled so thresholds ratchet upward.
|
||||
- **FR-005**: Coverage enforcement MUST be wired into `pnpm check`.
|
||||
- **FR-006**: Biome MUST have `noExcessiveCognitiveComplexity` enabled with the default threshold (15).
|
||||
- **FR-007**: `pnpm audit --audit-level=high` MUST be added to the `pnpm check` script.
|
||||
- **FR-008**: The blanket `biome-ignore lint:` in `set-initiative.ts` MUST be replaced with either a rule-specific ignore or (preferably) a code fix that eliminates the need for any ignore.
|
||||
- **FR-009**: The 8 a11y ignores in `combatant-row.tsx` MUST be reduced by refactoring clickable wrapper divs to use semantic elements or a shared wrapper component.
|
||||
- **FR-010**: No `biome-ignore lint:` (blanket form) may exist anywhere in the codebase after implementation.
|
||||
- **FR-011**: Relevant non-recommended Biome a11y rules MUST be explicitly enabled in `biome.json`.
|
||||
- **FR-012**: All changes MUST pass `pnpm check` after implementation.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The v8 coverage provider is compatible with the existing Vitest setup (Vitest 3.x supports v8 natively).
|
||||
- Initial coverage thresholds (lines: 70, branches: 60) are conservative enough that the current test suite passes without needing to add tests.
|
||||
- Biome 2.0's `noExcessiveCognitiveComplexity` default threshold of 15 is reasonable for existing code — no existing functions exceed it.
|
||||
- The project's current dependencies have no high-severity advisories (pnpm audit passes cleanly).
|
||||
- Non-recommended Biome a11y rules are stable enough for pre-commit enforcement and won't produce excessive false positives.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: `pnpm check` enforces all six gate types: unused code (knip), formatting/linting (biome), type checking (tsc), tests with coverage (vitest), copy-paste detection (jscpd), and dependency audit (pnpm audit).
|
||||
- **SC-002**: Test coverage thresholds are enforced and automatically ratchet upward — no manual maintenance needed.
|
||||
- **SC-003**: Zero blanket `biome-ignore lint:` comments remain in the codebase.
|
||||
- **SC-004**: The total number of `biome-ignore` comments in `combatant-row.tsx` is reduced from 8 to 4 or fewer.
|
||||
- **SC-005**: The constitution and CLAUDE.md explicitly document the early-enforcement principle.
|
||||
- **SC-006**: All existing code passes `pnpm check` with the new gates enabled — no regressions.
|
||||
211
specs/031-quality-gates-hygiene/tasks.md
Normal file
211
specs/031-quality-gates-hygiene/tasks.md
Normal file
@@ -0,0 +1,211 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user