DimitriGilbert / DimitriGilbert/Formedible
Test & quality alignment plan
- Dominant language
- TypeScript
- Stars
- 47
- Forks
- 2
- PR merge metrics
- No merged PRs in 30d
Description
# Formedible — Test & Quality Alignment Plan
_Revised 2026-09-05: CI removed by owner decision — quality gates are local commands run by the owner/agents; no GitHub Actions._
- **Project:** Formedible (`/home/didi/workspace/Formedible`, origin `DimitriGilbert/Formedible`)
- **Date:** 2026-09-05
- **Generated by the fleet test-alignment initiative**
- **Audit report:** [../.test-alignment-audit/reports/Formedible.md](../.test-alignment-audit/reports/Formedible.md)
- **Fleet synthesis (decisions D1–D7):** [../.test-alignment-audit/synthesis-decisions.md](../.test-alignment-audit/synthesis-decisions.md)
This plan is **plan-only**. It is structured as phases of implementer/validator subagent tasks per the `subagent-orchestration` methodology: every phase has one implementer, one validator (fix loop, max 3 attempts), and explicit validation gates. Nothing here has been implemented yet.
---
## 1. Adopted decisions (D1–D7) and deviations
| Decision | Applies to Formedible? | How |
|---|---|---|
| **D1 — runner & layout** | **Yes, with the sanctioned node:test exception.** The synthesis (§4 delta, §5 risks) explicitly mandates: **do NOT migrate the node:test harness to vitest in v1.** The compile-then-run pattern (22 per-suite tsconfigs under `tests/`), architecture contract tests, hand-rolled fake timers, and bench harness are deliberate and donor-grade. What Formedible adopts from D1 is the **wiring** half: one headless, root-level, one-command test entry (Phase 1). New tests stay colocated; no mass renames; no vitest/jest configs are introduced. No `passWithNoTests` exists or will be added. |
| **D2 — coverage** | **Yes, with provider deviation.** Measure everywhere, gate nowhere (yet). Provider is **`node --test --experimental-test-coverage`** (per the synthesis §4 Formedible delta), *not* `@vitest/coverage-v8` — forced by the D1 exception (deviation noted). **Threshold ratchet (D2 state 2) is deferred**: node's experimental coverage has no native threshold enforcement; the baseline is recorded in AGENTS.md instead (Phase 4). Sanctioned contingency: `c8@^10` if richer reporters (text+json+html) or future thresholds are needed. |
| **D3 — lint & format** | **Yes.** ESLint 9 flat config + typescript-eslint ^8 + `eslint-plugin-react-hooks` (Phase 3). **Prettier is deferred** (sanctioned by D3 "optional in v1") — AGENTS.md will say so explicitly rather than pretend (Phase 0/6). ESLint retires the source-regex style assertions (`docs-regression.test.ts` console/alert/TODO regex; `field-store.test.ts` `onChangeRef` regex) — but only after equivalent lint rules exist; the TS-compiler-API architecture contract tests in `tests/architecture/` are **kept untouched**. |
| **D4 — quality metrics** | **Yes, subset.** `knip` (Phase 5 — registry/mono-repo export-surface drift is real here) + `jscpd` (launch-mommy canonical config, report-only `exitCode: 0` until a baseline exists). **CRAP/complexity is deferred with a deviation note**: `crap-score` consumes istanbul `coverage-final.json`, which neither node's experimental coverage nor v8-format c8 output provides; revisit only if an istanbul-format pipeline is adopted. Everything metric-shaped stays report-only (§5: never gate on an unmeasured number). |
| **D5 — no CI by design (owner decision 2026-09-05)** | **Yes.** **No GitHub Actions, no reusable-workflow caller, no local workflow fallback** — the earlier "thin caller of `DimitriGilbert/.github` `ts-ci.yml` (with a donor-shaped fallback)" plan is dropped entirely. Enforcement = the root-level headless verification entrypoint (`pnpm verify`: check-types → test, lint added in Phase 3), run by the owner and by agents; the plan's phase validation gates are run by implementer/validator agents before any phase is declared done. **e2e and bench are excluded from the default gate**: e2e is heavy (needs built app + Chrome) and bench is env-noise-prone (adjudication history in git/BENCH-FINDINGS.md) — both stay on-demand local commands. |
| **D6 — shared config** | **Adopt opportunistically.** Formedible is touching lint config in Phase 3, which is exactly D6's adoption trigger — if `@dg/config` is published by then, `eslint.config.js` extends its preset and `packages/config` re-exports rather than copies. If not yet published, Phase 3 ships a standalone flat config and convergence becomes a follow-up (never churn for its own sake). |
| **D7 — hooks** | **Yes (i.e., no hooks).** The local gate commands run by the owner/agents are the only authoritative gate. No lefthook is added in v1; AGENTS.md states "the headless local verification command is the quality gate, run by the owner/agents; hooks are local convenience" (Phase 2/6). A repo with no hooks and a green local `pnpm verify` is compliant per D7. |
### Explicit deviation log (summary)
1. **node:test stays** — this is the synthesis's own carve-out for Formedible (§4, §5); recorded here so no future phase "aligns" it to vitest by accident.
2. **Coverage provider = node's experimental flag** (not `@vitest/coverage-v8`), threshold ratchet deferred; `c8@^10` is the sanctioned fallback.
3. **Prettier deferred** and documented (D3-legal).
4. **CRAP deferred** (istanbul-format dependency unavailable in the node:test coverage path).
5. **Gate toolchain Node version: 24** (matches the repo's `@types/node ^24.10.2` catalog pin) — documented as the expected toolchain in AGENTS.md so the experimental coverage output is stable across runs.
6. **e2e and bench are on-demand local tiers**, never in the default gate — extending D5's former e2e guidance to the bench harness under the no-CI decision.
---
## 2. Phased plan
Execution model per the `subagent-orchestration` skill: the orchestrator dispatches one **implementer** per phase (or sub-phase), then one **validator** immediately after; failures go to a **fixer** with *all* validator findings at once, then re-validate (max 3 loops). Multi-sub-phase phases get per-sub-phase validation plus a **phase-wide validator**. Every implementer/fixer dispatch carries the NO-SLOP policy (no `any`/`as any`, no TODO/FIXME placeholders, no unused imports/vars, `import type` for type-only imports, external-then-local import order, no dev servers left running) and must run its phase's gatekeeping commands before reporting done. Phases are sequential; each gate must pass before the next phase starts.
Global constraints for every phase (from repo reality):
- Package manager: `pnpm` (`packageManager: pnpm@10.10.0`); new deps go into `pnpm-workspace.yaml` `catalog:` where sensible and are referenced as `catalog:`; installs are `pnpm install` at repo root (`-w` for root devDeps).
- Never edit generated files (`routeTree.gen.ts`, registry build output, `tests/**/.compiled`).
- **Never rewrite the node:test harness, per-suite tsconfigs, fake-timer utilities, or `tests/architecture/` contract tests.** They are KEEP items.
- The sync workflow (fix in `packages/formedible/src` → `build:pkg` → `node scripts/quick-sync.js` → `check-types`) must not be bypassed.
### Phase 0 — Docs truth: AGENTS.md phantom scripts (effort: S)
**Goal:** AGENTS.md documents only commands that exist; the "Testing" section matches reality. No behavior changes.
**Tasks (implementer):**
1. `AGENTS.md` — remove the phantom `pnpm run lint` / `pnpm run lint:web` / `pnpm run lint:pkg` bullets (they will be re-added truthfully in Phase 3). Verify every remaining command in "Build/Test Commands" against root `package.json` scripts; add missing ones worth documenting (`check-types:parser`, `check-types:ai-builder`, the `test:*` inventory pointer, `bench` family).
2. `AGENTS.md` — add a "Testing" section stating today's truth: runner is `node:test` via `tsx --test` / `node --test` with per-suite tsconfigs compiled by `tsc -p`; ~37 test files / ~516 cases across `tests/formedible`, `tests/architecture`, `tests/sync`, `tests/consumer-smoke`, `tests/e2e`, `tests/bench`, `packages/*/src/**/*.test.ts(x)`, `apps/web/tests`; **no coverage measurement yet** (status updated in Phase 4); the per-suite `test:*` scripts are the entry points until Phase 1 adds the aggregate.
3. `turbo.json` — leave the dead `lint` task in place for now but add a one-line comment is not possible in JSON; instead record in the phase report that `turbo.json` `lint` is resolved in Phase 3 (implemented via root script + task removal decision there). Do not delete it in this phase to keep Phase 0 docs-only.
4. Note for validator: also confirm no other doc files contradict AGENTS.md on test commands (`README.md` test mentions, `docs/formedible-test-expansion-plan.md` status headers).
**Validation gate (validator):**
- For every command documented in `AGENTS.md`, `pnpm run --help`-style existence check passes (script key exists in the relevant `package.json`); conversely, no documented command is phantom.
- `pnpm run check-types` passes (no code changed; confirms clean baseline).
- Validator reads `AGENTS.md` fully and cross-checks against root + per-package `package.json` scripts — not just grep.
**Role:** implementer → validator (fix loop ≤3).
---
### Phase 1 — Unified headless test entry: `pnpm test` (effort: M)
**Goal:** One root command runs the full headless suite (everything except e2e and bench), preserving the per-suite tsconfig compile-then-run pattern.
**Tasks (implementer):**
1. Root `package.json` — add a `test` script that chains, in this order (fast/pure first, heavier integration last), using the existing granular scripts so each tier keeps its own tsconfig:
- workspace package unit tests: `pnpm -r --if-present test` (covers `@formedible/formedible-parser`, `@formedible/builder`, `@formedible/ai-builder`, `@formedible/ai-picker` — note `ai-picker` needs its `scripts/register-package-alias-loader.mjs` import hook, which its own script already carries);
- `pnpm run test:formedible:types` then the formedible behavior suites: `normalization`, `validation`, `basic-fields`, `section-rendering`, `advanced-fields`, `advanced-fields:ssr`, `nested-fields`, `phase10`, `reactivity`;
- `pnpm run test:architecture`, `pnpm run test:sync` (includes `scripts/validate-sync-boundaries.js`);
- `pnpm --dir apps/web run test:docs` and `pnpm --dir apps/web run test:route-tree`;
- `pnpm run test:consumer-smoke` last (heaviest non-e2e tier: builds consumer fixtures).
Use a `&&` chain in the script (repo idiom — `check-types` already does exactly this). Do NOT add e2e (`test:e2e*`) or bench (`bench*`) to `test`.
2. Verify build prerequisites per tier: confirm whether `test:sync` / `test:consumer-smoke` require `pnpm run build:pkg` (+ `sync-components`) to have run first; if any tier needs a build artifact, prepend the minimal build steps inside `test` (or document the requirement in AGENTS.md if it makes the aggregate too slow — decide and record, don't leave ambiguous).
3. Optional but preferred: also add `test:fast` (aggregate minus `test:consumer-smoke`) for quick inner loops — measure both runtimes and record them so the owner/agents can pick the right aggregate per session.
4. `AGENTS.md` — document `pnpm test` (and `test:fast` if added) as the headless entry; keep the granular scripts listed.
5. Do not touch the suites themselves, the tsconfigs, or `.gitignore` (`tests/**/.compiled` is already ignored).
**Validation gate (validator):**
- `pnpm run check-types` passes.
- `pnpm test` passes end-to-end from a clean checkout state (delete `tests/**/.compiled` first to prove the aggregate compiles what it needs); record wall-clock time and per-tier times in the report.
- `pnpm test` actually runs ~516 cases (validator inspects the node:test summary totals, not just exit code 0) and includes zero e2e/bench tiers.
- `AGENTS.md` mentions match the scripts that exist.
**Role:** implementer → validator (fix loop ≤3).
---
### Phase 2 — Headless verification entrypoint + dependency hygiene (effort: M)
**Goal:** One local command runs check-types + the headless suite. Renovate one-liner lands. No new test tooling yet.
**Tasks (implementer):**
1. Root `package.json` — add `"verify": "pnpm run check-types && pnpm test"` — the headless verification entrypoint (D5). Later phases extend this same chain (lint in Phase 3); coverage and quality reports are deliberately kept out of it.
2. Add `engines` to root `package.json`: `"engines": { "node": ">=22", "pnpm": ">=10" }` (matches `packageManager` and the catalog `@types/node ^24` choice).
3. Add `renovate.json` at repo root: `{"$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["local>DimitriGilbert"]}` (DoD item 7; a Renovate preset is a non-workflow artifact, unaffected by the no-CI decision; no `"latest"` deps exist today — catalog pins — so no pin-fixing needed; validator re-verifies).
4. `AGENTS.md` — add the D7 statement: "the headless local verification command (`pnpm verify`) run by the owner/agents is the quality gate; hooks are local convenience. No git hooks are configured."
5. Do not add lint/coverage to the chain in this phase — later phases extend it.
**Validation gate (validator):**
- `pnpm run verify` passes end-to-end locally from a clean checkout state; both sub-commands exist in `package.json` scripts.
- Fail-fast proven once: the validator introduces a failing assertion in a scratch copy and confirms `pnpm verify` exits non-zero at the failing step (never on the real tree).
- The implementer/validator agents record the green output in the phase report before the phase is declared done (D5); no workflow file was created anywhere.
**Role:** implementer → validator (fix loop ≤3).
---
### Phase 3 — ESLint 9 flat config + retire source-regex style tests (effort: L)
**Goal:** Repo-wide lint with zero errors, wired into the headless entrypoint; the regex-based style assertions are replaced by real lint rules; AGENTS.md lint commands become real. **The `tests/architecture/` compiler-API contract tests are out of scope and must not be touched.**
**Sub-phase 3a — install, configure, get to zero errors:**
1. Install at root (add to `pnpm-workspace.yaml` catalog and reference via `catalog:`):
`pnpm add -D -w eslint@^9 typescript-eslint@^8 @eslint/js@^9 eslint-plugin-react-hooks@^5 eslint-plugin-import@^2 globals`
If `@dg/config` (D6) is published by now, extend its ESLint preset from `packages/config` instead of hand-rolling; otherwise standalone, convergence deferred.
2. Create root `eslint.config.js` (flat):
- `@eslint/js` recommended + `typescript-eslint` recommended + `eslint-plugin-react-hooks` flat presets.
- Type-checked `recommendedTypeChecked` via `projectService: true` **if feasible** across the monorepo's many tsconfigs; if projectService proves unreliable with the 22 test tsconfigs, ship `recommended` (non-type-checked) as v1 and record the ratchet to type-checked as an explicit follow-up — this is a D3-sanctioned "where feasible" decision, document which was chosen.
- `import/order` enabled (D3 standard preset). If the first run yields a very large violation count, land it as `warn` with a committed count baseline and a follow-up task to flip to `error` — a warn is a documented ratchet, not silenced enforcement.
- Product rules from AGENTS.md, encoded as lint: `no-restricted-syntax` banning `ImportExpression` (the "No `await import()`" rule), `no-alert` + `no-restricted-properties`/globals banning `confirm` (repo-wide), `no-console` **scoped to `apps/web/src/**`** (AGENTS.md elsewhere permits console logging for errors — respect that).
- Ignores: `**/dist/**`, `**/.compiled/**`, `**/routeTree.gen.ts`, `.bench-dist/**`, registry build output, bench fixtures' vendored code — **derive the generated-path ignore list from `tests/architecture/utils.ts`'s exact repository-relative ignore lists** (the audit notes generated output is ignored by exact path there; reuse that source of truth).
3. Root `package.json`: add `"lint": "eslint ."`. **Delete the dead `lint` task from `turbo.json`** (DoD item 4: implemented-or-deleted — the root script implements linting; no package defines `lint`, so the turbo task is dead weight).
4. Fix all violations (expected to be a large diff across ~57k LOC — this is the bulk of the effort). No `eslint-disable` without a same-line justification comment and validator sign-off; **never silence — fix the cause** (repo working agreement).
5. Update `AGENTS.md`: restore truthful lint commands (`pnpm run lint`), remove the now-false "unused vars: prefix with underscore to ignore ESLint warnings" if the config handles it differently (`no-unused-vars` argsIgnorePattern `^_` — implement accordingly so the doc stays true), and note the formatter decision: "No formatter (Prettier) as of 2026-09 — deliberately deferred per fleet D3; style remains per Code Style section."
**Sub-phase 3b — retire regex-based style tests:**
1. `apps/web/tests/docs-regression.test.ts`: remove the regex assertions that ban `console`/`alert`/TODO in app source **only where an equivalent lint rule now enforces the same property** (Phase 3a rules). Keep every other assertion in that file (it also guards docs/examples — audit §4). If any banned pattern is not fully covered by a lint rule (e.g. TODO-comment scanning), keep that specific regex assertion and note it.
2. `packages/builder/src/lib/formedible/field-store.test.ts`: remove the source-regex assertion on `form-builder.tsx` (`/onChangeRef = useRef\(onChange\)/`) if the behavior it approximates is covered elsewhere in the suite; if it is the only guard for that wiring, replace the regex with a behavioral assertion (render + interact) — do not simply delete coverage.
3. Grep for any other `assert.match(source, ...)` / `readFileSync(...).match(...)` style assertions in test files and apply the same rule: lint-ported → remove; not ported → keep and list in the phase report.
**Phase-wide validation (after 3a and 3b individually pass):**
- `pnpm run lint` exits 0 with zero errors (warns only where a documented ratchet exists).
- `pnpm run check-types && pnpm test` still green — the regex-test removal must not reduce passing case count below the Phase 1 recorded total minus exactly the assertions documented as removed.
- Validator reads `eslint.config.js`, `turbo.json`, the edited test files, and the AGENTS.md diff in full; verifies no `tests/architecture/` file changed (`git status` / `git diff --stat tests/architecture` empty).
- The `verify` chain from Phase 2 gains `pnpm run lint` before typecheck (one-line edit to the script).
**Role:** implementer+validator per sub-phase, then phase-wide validator (fix loop ≤3 each).
---
### Phase 4 — Coverage: measure, report, record baseline (effort: M)
**Goal:** Coverage measured and reported (report-only), baseline recorded in AGENTS.md. No thresholds (see D2 deviation).
**Tasks (implementer):**
1. Root `package.json`: add `"test:coverage"` running the behavior tiers under Node's builtin coverage, e.g. `node --import tsx --test --experimental-test-coverage tests/formedible/*.test.ts` per tier — but the tiers compile first (`tsc -p`) and the DOM suites need jsdom bootstrapping via tsx; implementer must verify per tier whether the flag works through `tsx --test` or requires `node --import tsx --test`. Scope coverage to tiers where it is meaningful: the formedible behavior suites + `pnpm -r --if-present test:coverage`-style package unit tests. Architecture/sync/consumer-smoke/e2e/bench tiers are excluded from coverage (they assert on source text/consumer builds, not runtime paths).
2. If node's experimental reporter is insufficient to produce a stable baseline artifact, adopt the sanctioned contingency: `pnpm add -D -w c8@^10` and wrap the same commands (`c8 --reporter=text --reporter=json --reporter=html node --import tsx --test ...`), output to `coverage/` (already gitignored). Record in AGENTS.md which provider is in use.
3. `AGENTS.md` Testing section: record the measured baseline (per-package lines% from the text report) and the policy: "Coverage is report-only; thresholds deferred until a threshold-capable provider is adopted; ratchet per fleet D2 begins then."
4. Do NOT add thresholds, do NOT wire coverage numbers into the gate (D2/§5).
**Validation gate (validator):**
- `pnpm run test:coverage` runs green and prints/includes a coverage summary whose numbers the validator independently sanity-checks (e.g. reruns one tier).
- Baseline numbers in `AGENTS.md` match the report output.
- `pnpm run check-types && pnpm test` still green; `git status` shows no coverage artifacts tracked (`.gitignore` already covers `coverage/`).
**Role:** implementer → validator (fix loop ≤3).
---
### Phase 5 — Quality metrics, report-only: knip + jscpd (effort: M)
**Goal:** Dead-export and duplication visibility as local reports; nothing gates.
**Tasks (implementer):**
1. `pnpm add -D -w knip jscpd@^4.2` (knip current 5.x line; add both to the workspace catalog).
2. Create `knip.json`: workspaces mode; entry points covering each package's `src/index.ts` (or package.json `exports`), `apps/web/src` routes/entry, `scripts/*.js` (`quick-sync.js`, `build-registries.js`, `build-release.js`, `validate-sync-boundaries.js`, `validate-public-registries.js`, `stitch-e2e-recordings.js`, `register-package-alias-loader.mjs`), and `tests/**` (tests are consumers of the packages — include them or configure `includeEntryExports` thoughtfully; the registry `build:registry` outputs must be excluded as generated). Expect iteration; the audit flags manual dead-weight sweeps (`DEAD-WEIGHT-REVIEW.md`) — knip should mechanize exactly those.
3. Create `jscpd` config (launch-mommy canonical per D4): `minLines: 8`, `minTokens: 80`, `reporters: ["html", "consoleFull"]`, `output: ./quality-report/jscpd`, `exitCode: 0` (report-only until a baseline exists), excludes: `**/__tests__/**`, `**/e2e/**`, `**/.compiled/**`, `**/dist/**`, `**/.bench-dist/**`, generated registry output, `tests/bench/fixtures/**` (intentional consumer mirrors).
4. Root `package.json`: `"quality:knip": "knip"`, `"quality:duplicates": "jscpd"`, `"quality:report": "pnpm run quality:knip; pnpm run quality:duplicates"` (semicolon chain — report-only by design; document that in AGENTS.md).
5. Run on demand: `pnpm run quality:report` is executed by the owner or during a periodic agent pass; reports stay local under `quality-report/` (git-ignored). Never part of `pnpm verify`, no workflow, no schedule (D5).
6. `AGENTS.md`: document the quality scripts and their report-only status.
7. **CRAP deferred** (deviation logged in §1): no istanbul-format coverage exists; revisit with c8/istanbul reporter decision.
**Validation gate (validator):**
- `pnpm run quality:knip` and `pnpm run quality:duplicates` both run to completion and produce local reports; neither gates/fails the chain.
- Validator reads `knip.json` + jscpd config in full; confirms `exitCode: 0` on jscpd, excludes match §1/D4 lists, and that no workflow file was created and nothing was wired into `pnpm verify`.
- `pnpm run check-types && pnpm run lint && pnpm test` still green.
**Role:** implementer → validator (fix loop ≤3).
---
### Phase 6 — Hygiene leftovers + docs convergence (effort: S)
**Goal:** Close DoD items 5, 6, 8 leftovers; AGENTS.md becomes the single truthful quality doc.
**Tasks (implementer):**
1. `apps/web/package.json`: remove the unused `@testing-library/react` and `@testing-library/dom` devDeps (audit P3: installed, never used by its plain node:test scripts) — removal, not adoption, since root tests deliberately hand-roll jsdom + act (KEEP; do not migrate them to RTL). Consolidate the jsdom drift: bump `apps/web` `jsdom` `^29.0.2` → root's `^29.1.0` (or hoist to a single catalog entry) so one jsdom major is in the lockfile.
2. Final `AGENTS.md` pass: Testing section states runner, aggregate command, tier list, coverage provider + baseline, lint command + ratchet notes, formatter deferral, the local-gate statement (`pnpm verify` run by the owner/agents), quality report scripts. Cross-check `README.md` for the same truths.
3. (Optional, non-blocking P3 stretch, only if trivially additive) e2e breadth: the site's purpose is documented examples yet e2e has 3 cases; adding one docs-example interaction case to `tests/e2e/docs-examples.test.ts` is welcome but must not delay the phase.
**Validation gate (validator):**
- `pnpm install --frozen-lockfile` regenerates cleanly after dep removal; `pnpm run check-types && pnpm run lint && pnpm test` green.
- `git grep "@testing-library" -- package.json apps/web/package.json` returns only intentional hits (root tests use their own utilities).
- Validator reads the final AGENTS.md against every phase's outcomes — DoD checklist below must be honestly ticked.
**Role:** implementer → validator (fix loop ≤3).
---
## 3. Alignment Definition of Done — current status for this repo
Fleet DoD (synthesis §3) with Formedible's status as of 2026-09-05:
| # | DoD item | Status today | Resolved by |
|---|---|---|---|
| 1 | Root-level headless test command exists, documented in AGENTS.md | **Missing** (~15 granular `test:*` scripts, no aggregate) | Phase 1 |
| 2 | Headless verification entrypoint running typecheck + test (+lint where installed) in one local command; agent records green before a phase is done | **Missing** (no aggregate command) | Phase 2 (lint added Phase 3) |
| 3 | Coverage measured + reported; baseline recorded in AGENTS.md; thresholds only per D2 states; never lowered | **Missing** (zero coverage anywhere) | Phase 4 (thresholds deferred — D2 deviation) |
| 4 | Language-appropriate lint installed, zero errors, wired into the headless entrypoint; dead turbo `lint` task implemented or deleted | **Missing** (no lint anywhere; `turbo.json` has dead `lint` task; AGENTS.md phantom `lint*` scripts) | Phase 3 |
| 5 | AGENTS.md "Testing" section matches reality — no phantom docs | **Stale** (phantom `lint`/`lint:web`/`lint:pkg`; no Testing section) | Phase 0, finalized Phase 6 |
| 6 | Dead scaffold test deps removed or used | **Partial** (`@testing-library/react` + `@testing-library/dom` unused in `apps/web`; jsdom version drift root ^29.1.0 vs web ^29.0.2) | Phase 6 |
| 7 | Renovate one-liner present; no `"latest"` deps | **Partial** (no renovate.json; deps clean — catalog-pinned, no floats found) | Phase 2 |
| 8 | Quality-report script (knip + jscpd, + CRAP where coverage exists) runnable locally, report-only | **Missing** (manual `DEAD-WEIGHT-REVIEW.md` sweeps only; CRAP n/a — deviation logged) | Phase 5 |
| 9 | Nothing satisfied by a fake pass (no `passWithNoTests`, no echo tests, no tautologies) | **Already satisfied** — keep it that way (real suite, real assertions; the source-regex anti-pattern is the one quality concern, addressed in Phase 3b) | Continuous |
**Overall DoD ETA after all phases:** local gate + docs + hygiene complete; coverage measured with baseline; the remaining open items are time-locked follow-ups (type-checked lint ratchet, `import/order` warn→error flip, coverage thresholds once a threshold-capable provider lands, `@dg/config` convergence, e2e breadth).
---
## 4. Risks & notes specific to this repo
1. **The harness is the product's moat — the biggest risk is "helpful" modernization.** Any phase that renames suites, unifies the 22 tsconfigs, swaps hand-rolled fake timers for a framework, or ports `tests/architecture/` to lint-plugin equivalents is out of scope and must be rejected in review. Only Phase 3b removes regex assertions, and only after equivalent rules exist.
2. **ESLint first-run blast radius (~57k src LOC).** Violation count is unknown; the phase is sized L for this reason. Mitigations are built in: recommended before type-checked (feasibility-gated per D3), `import/order` warn-with-baseline fallback, and a strict no-mass-`eslint-disable` stance. If 3a stalls past the fix budget, split by directory (`packages/*`, then `apps/web`, then `tests/`) into additional sub-phases rather than relaxing rules.
3. **Compile-then-run inflates aggregate test time** — each tier pays a `tsc -p` cost and the local gate has no caching for root-level suites (they are not workspace tasks). Phase 1 measures per-tier times and records which aggregate to prefer for quick loops, from data not hope.
4. **Bench is noise-prone by its own history** (BENCH-FINDINGS.md / fixed-sha A/B adjudication). It stays on-demand; `bench:regress` (needs a reference worktree) must never be wired into any gate.
5. **e2e needs a browser and a built app** — e2e is the only tier that may need network/browser setup; `agent-browser`'s install step must be verified by the implementer. It is deliberately not part of the default gate.
6. **Sync workflow coupling:** several suites validate synced copies (`copy-only-sync-contract`, `validate-sync-boundaries.js`). If the gate runs from a fresh checkout, any tier requiring `build:pkg` + `quick-sync.js` output must have that step prepended in `test` (Phase 1 task 2) or it will fail on a clean tree. This is the most likely "works on a dirty tree, fails clean" trap.
7. **node's `--experimental-test-coverage` output is unstable across Node majors** — the expected Node version is documented in AGENTS.md (24) and the c8 contingency exists precisely so a flag change cannot erase the baseline. Baseline numbers in AGENTS.md must state the provider + Node version used.
8. **No CI anywhere (D5).** The earlier org-repo reusable-workflow plan and its local-workflow fallback are moot; the gate is the local `pnpm verify` command and nothing else.
9. **Docs-heavy root** (BENCH-FINDINGS/DEAD-WEIGHT-REVIEW/PERF-*.md) — Phase 0/6 validators must also confirm these planning docs are not mistaken for current-truth command references; AGENTS.md remains the only normative commands doc.
10. **consumer-smoke network posture unknown** — if the tier installs anything from a registry at runtime, the gate needs an offline path (local fixture installs). Phase 1's validator must confirm; if it needs network, it moves out of the default aggregate into the on-demand tier.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading AGENTS.md, the root package.json scripts, turbo.json, and the cited audit report and synthesis decisions. Run the existing check-types and granular test commands to establish the baseline, then follow the phases in order. Done means each phase's validation gate passes, the documented commands exist, and the local headless verification flow covers the specified suites without adding CI.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- eslint, node.js, typescript
- Domain
- build-system, documentation, testing, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100