id: PX-PLAN-IRCFFLM3
type: plan
title: Test Quality Tooling
spec: PX-SPEC-DZLVGRW4
status: draft
depends_on:
PX-PLAN-XQFSKHHG
Outcome
Coverage, property testing, mutation testing, snapshot discipline, and fuzzing are available as non-gating, author-driven tools with per-extension reporting.
Verified current state: @vitest/coverage-istanbul installed but no coverage task; no property, mutation, or fuzz tooling.
Behaviors
flowchart TB
q1["1. Per-extension coverage report with unimported files"] --> q2["2. Seeded property tests in unit layer"]
q2 --> q3["3. Selective mutation runs with cache"]
q1 --> q4["4. Snapshot policy enforced"]
q2 --> q5["5. Fuzz harness for hostile-input boundaries"]
//:coverage and //extensions/<name>:coverage run the unit and contract projects with all: true scoped to owned production files, reporting line, branch, function per extension; provider chosen after timing V8 vs Istanbul on the full suite and recorded in the task description.
Add fast-check as devDependency; test/property.ts supplies a seeded runner that records seed and counterexample on failure and stores minimal cases under __tests__/regressions/.
First targets: the-system frontmatter and Markdown normalization, nushell argument rendering, web URL and path safety, strata state machine.
//extensions/<name>:mutate <file|range> via Stryker with Vitest runner, incremental cache per extension invalidated by source and test hashes; //:mutate-all optional report; never in hooks or CI.
Snapshot rules: inline or file snapshots only for normalized rendered output; a lint test rejects snapshots containing absolute paths, timestamps, IDs, or bundle content; updates require an explicit --update task.
//extensions/<name>:fuzz <target> wraps a coverage-guided runner for parsers exposed to hostile input (web fetch parsing, the-system document parsing); findings minimized into deterministic regression tests.
Structure
flowchart LR
unit["unit project"] --> cov["coverage per extension"]
prop["test/property seeded runner"] --> unit
prop --> reg["__tests__/regressions"]
mut["Stryker per-extension cache"] --> unit
fuzz["fuzz runner"] --> reg
snap["snapshot lint"] --> unit
No percentage gate; coverage guides investigation.
Mutation targets pure unit-testable code only.
Long fuzz campaigns run manually or on a schedule, never in hooks or normal CI.
Verification
Coverage report lists unimported owned files and per-extension line, branch, function numbers.
A failing property test prints a replay seed and yields a stored minimal regression case.
Repeated mutation run on unchanged code uses the cache; a source change invalidates it.
Snapshot lint fails on a snapshot containing an absolute path.
Fuzz run on a target completes and any finding lands as a normal test.
---
id: PX-PLAN-IRCFFLM3
type: plan
title: Test Quality Tooling
spec: PX-SPEC-DZLVGRW4
status: draft
depends_on:
- PX-PLAN-XQFSKHHG
---
## Outcome
Coverage, property testing, mutation testing, snapshot discipline, and fuzzing are available as non-gating, author-driven tools with per-extension reporting.
Verified current state: `@vitest/coverage-istanbul` installed but no coverage task; no property, mutation, or fuzz tooling.
## Behaviors
```mermaid
flowchart TB
q1["1. Per-extension coverage report with unimported files"] --> q2["2. Seeded property tests in unit layer"]
q2 --> q3["3. Selective mutation runs with cache"]
q1 --> q4["4. Snapshot policy enforced"]
q2 --> q5["5. Fuzz harness for hostile-input boundaries"]
```
1. `//:coverage` and `//extensions/<name>:coverage` run the `unit` and `contract` projects with `all: true` scoped to owned production files, reporting line, branch, function per extension; provider chosen after timing V8 vs Istanbul on the full suite and recorded in the task description.
2. Add `fast-check` as devDependency; `test/property.ts` supplies a seeded runner that records seed and counterexample on failure and stores minimal cases under `__tests__/regressions/`.
First targets: `the-system` frontmatter and Markdown normalization, `nushell` argument rendering, `web` URL and path safety, `strata` state machine.
3. `//extensions/<name>:mutate <file|range>` via Stryker with Vitest runner, incremental cache per extension invalidated by source and test hashes; `//:mutate-all` optional report; never in hooks or CI.
4. Snapshot rules: inline or file snapshots only for normalized rendered output; a lint test rejects snapshots containing absolute paths, timestamps, IDs, or bundle content; updates require an explicit `--update` task.
5. `//extensions/<name>:fuzz <target>` wraps a coverage-guided runner for parsers exposed to hostile input (`web` fetch parsing, `the-system` document parsing); findings minimized into deterministic regression tests.
## Structure
```mermaid
flowchart LR
unit["unit project"] --> cov["coverage per extension"]
prop["test/property seeded runner"] --> unit
prop --> reg["__tests__/regressions"]
mut["Stryker per-extension cache"] --> unit
fuzz["fuzz runner"] --> reg
snap["snapshot lint"] --> unit
```
Touched: `package.json` (fast-check, Stryker, fuzz runner devDependencies), `vitest.config.ts` coverage block, `test/property.ts`, `test/snapshot-lint.test.ts`, `mise.toml` and `extensions/*/mise.toml` (`coverage`, `mutate`, `fuzz`, `snapshot-update`), `stryker.config.*` per extension template.
## Constraints
- No percentage gate; coverage guides investigation.
- Mutation targets pure unit-testable code only.
- Long fuzz campaigns run manually or on a schedule, never in hooks or normal CI.
## Verification
- [ ] Coverage report lists unimported owned files and per-extension line, branch, function numbers.
- [ ] A failing property test prints a replay seed and yields a stored minimal regression case.
- [ ] Repeated mutation run on unchanged code uses the cache; a source change invalidates it.
- [ ] Snapshot lint fails on a snapshot containing an absolute path.
- [ ] Fuzz run on a target completes and any finding lands as a normal test.