refactor(layering): R84 requires each new unit test to be named for a module beside it

Merged
TH
4h ago
+320-19

Stacked on #3667 (test-kit single entry) on #3659 (R11 devDependencies) on #3645 (R83).

What

R84 test-colocation: every *.test.ts under root src/ and packages/<pkg>/src/ sits in the directory of a module (or in that directory's __tests__/) and is named <module>.test.ts or <module>-<aspect>.test.ts. It measures its offenders against the merge-base and keeps no baseline file, the same way R83 does: 202 unmatched tests at head against 202 at the merge-base. Growth fails; moving or renaming a test into place needs no edit (base entries are carried to head coordinates through renamedSince, so a rename is the same entry). Burn-down belongs to the package and root workers; this PR only lands the rule.

Conventions (stated in the rule's doc comment, so it needs no exemptions)

  • Cross-module package tests (conformance drivers, package-surface tests) are named for the package entry: index-<aspect>.test.ts, beside index.ts. index is an ordinary module to the rule.
  • Repo-level guards (tests that assert on Swift sources, manifests, docs or scripts) move to test/repo-guards/, under the exempt test/ tree, and run in unit-core.
  • Exempt: test/, scripts/, package-local test/ trees. The rule reads only src/ and packages/*/src/, and not test-kit, whose sources are test support.
  • A module is any production source file (isProductionSourceFile): fixtures and __tests__/ helpers never count as the module a test is named for.

Evidence

  • test-colocation.test.ts: matched by module and by -aspect; unmatched for a different name, a glued suffix or a dotted suffix; __tests__ judged against the parent directory (and not against modules inside __tests__); index-<aspect> matched only with an index.ts beside it; fixtures never the module; test/, scripts/ and package test trees unmeasured; growth fails, a cleanup or unchanged set passes; the message names both conventions.
  • Planted src/__tests__/planted-unmatched.test.ts in the real tree: check:layering named it under R84. Removed.
  • The measurement runs through mergeBaseRatchets (LayeringRatchets.unmatchedTests); measureRatchets now takes the tracked tree ({ manifests, files }) that R83 and R84 share.
  • Gates: pnpm check:quick, check:layering (384 node tests), check:fallow, check:gate-manifest, check:affected --run, repo-wide pnpm format.

🤖 Generated with Claude Code