refactor(layering): R83 forbids package exports that publish test support
What
R83 package-export-surface measures its offenders against the merge-base and keeps no list: 99 test-only exports at head against 99 at the merge-base (daemon 80, capture-kit 6, and 13 across eight other packages). R11's devDependency support for test-file imports (what lets the daemon's @agent-device/proxy dependency flip to devDependencies) is carried by #3659, the next layer in this stack.
No workspace package except @agent-device/test-kit may publish test support in exports. A package's tests live beside the module they test; fixtures one package uses stay inside it; fixtures shared across packages go to test-kit.
- Merge-base ratchet, no list. The offender set (
<package> <export key>) is measured at the merge-base throughmergeBaseRatchetsand at head by the same function. An offender at head that the merge-base did not publish fails; a cleanup needs no edit, and nothing sits above what main holds. The check's output reports99 test-only export(s) remain against 99 at the merge-base. - Classification judges the export key and every file the entry resolves to under every condition (
types,import,default, nested), expanding*patterns against the tracked package files. It flags fixtures (*.fixtures.ts,*.fixture.ts,*-fixture*.ts,fixtures.ts),__tests__/,testing/,test/,*.test.*/*.spec.*, and keys or paths withfixture(s),fake(s),mock(s),stub(s),harness,test-support,test-host. Production names that merely mention test (./test-command,./session-test-*,./android-clipboard-support) are pinned as accepted. - One production predicate.
isProductionSourceFileintracked-sources.tsowns "is this production source": TypeScript under rootsrc/or a packagesrc/, minus__tests__/andtest/trees,*.test.*/*.spec.*,*.fixtures.*and test-kit. The layering scans, the eager-closure reader, R11's consumer ban and R13 all call it;isTestSupportFile,isTestConsumerFileand R13'sisProductionSource/isNonProductionConsumerare deleted. It has its own table test. Side effect: fixtures and test-kit leave the layering scans (1880 โ 1800 files), sotest-kitleavesUNRANKED_ZONESand the gateway fixtures row leavesROOT_MODULE_ZONES. - Consumer ban moved into the R11 import walk (same sites, same
vi.mockscan) and checks all condition targets: production source may not import a test-only export or@agent-device/test-kit.test/andscripts/are not production sources. - Removed:
TEST_ONLY_FIXTURE_SUBPATHS, the./testing/namespace check and R13'sANDROID_TEST_ONLY_SUBPATHS(R13 leaves test-only subpaths to R83 and filters them from its exact-export comparison).
Seed at head (99, re-measured at the merge-base after the daemon test relocations)
| package | offenders |
|---|---|
| daemon | 80 (./testing/*, incl. ./testing/server/daemon-runtime and ./testing/daemon-command-registry) |
| capture-kit | 6 |
| daemon-contracts | 2 |
| host-kit | 2 |
| platform-android | 2 |
| platform-apple | 1 |
| replay-port | 2 |
| selectors | 2 |
| contracts | 1 |
| device-selection | 1 |
#3647 and #3650 drop only production replay-port/session-journal exports (session-test-*, session-event-*), none of them R83 offenders, so the seed is unaffected.
Evidence
package-export-surface.test.tsplants 22 test-support shapes and 7 look-alike production shapes; fixtures intypesonly, indefaultonly and in a nested condition; string-valued and missingexports; wildcards with and without a matching fixture file; ratchet growth, cleanup, moved file, renamed key and a brand-new package; and the consumer ban at root and package sites (including.spec, fixtures files,scripts/).- Planted a catch-all
./*โ./src/*.tspluspackages/kernel/src/planted.fixtures.ts;check:layeringnamed it under R83. Reverted. - Gates:
pnpm check:quick,check:layering(361 node tests),check:fallow,check:gate-manifest,check:affected --run, repo-widepnpm format. - Workers: tests that pin a package's exact export list (e.g. selectors in
package-boundaries.test.ts) edit in the same PR that drops the export.
๐ค Generated with Claude Code