diff --git a/AGENTS.md b/AGENTS.md index b46d4d2d98..f2ee773328 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -91,6 +91,13 @@ under `contracts/fixtures/`. - Tests mirror source topology one-to-one. Split a source module and its test together; do not add to the legacy `interaction.test.ts` or platform `index.test.ts` aggregations. Pure moves carry their tests unchanged; rename-only hunks owe no new coverage. +- A test sits beside the module it is named for: `.test.ts` or `-.test.ts` + (R84). Cross-module package tests are `index-.test.ts` beside `index.ts`; repo-level guards on + Swift sources, manifests, docs or scripts live in `test/repo-guards/`. +- Add no package `exports` entry that points at test support; R83 only lets the legacy count fall. A + fixture one package uses stays inside it, imported relatively; helpers shared across packages live only in `@agent-device/test-kit`, a + `devDependency` production source never imports and that never depends on `platform-*`, + `provider-*`, the daemon or root. ## Toolchain and worktree traps diff --git a/docs/agents/test-audit.md b/docs/agents/test-audit.md index b6d2ef3351..533841567c 100644 --- a/docs/agents/test-audit.md +++ b/docs/agents/test-audit.md @@ -24,7 +24,7 @@ Authoring rejects; audits hunt: - type pins posing as runtime assertions (`Equal = true`, `X ? true : never`, `satisfies` after erasure, `toHaveLength(n)` over literals); if erasing leaves nothing executable, use `void [fixtures]`; - mocks asserting their own inputs; negative controls passing for the wrong reason; - names promising more than the input exercises; -- dead code called only by tests; tests preserving test-only exports. +- dead code called only by tests; tests preserving test-only exports; a production package `exports` entry only tests import. ## Retention criteria diff --git a/docs/agents/testing.md b/docs/agents/testing.md index b42194f5d5..b13ba371e3 100644 --- a/docs/agents/testing.md +++ b/docs/agents/testing.md @@ -32,9 +32,8 @@ Two selection traps recur: - A workspace package manifest or TypeScript config can rewire all consumers, so the affected selector fails open to the full gate set on purpose. -Docs-only changes with no runtime behavior impact need no runtime tests or new tests asserting prose. -Keep required gates; after those and focused checks pass, repeat or broaden only for changes, -failures, or unresolved risks. +Docs-only changes need no runtime tests or tests asserting prose. After the required gates, repeat +or broaden only for changes, failures, or unresolved risks. ## Platform and live-device policy @@ -67,13 +66,15 @@ Read the entry file before running a lane. Do not copy its environment matrix he ## Shared test utilities -Before creating fixtures, look in `@agent-device/test-kit/` (generic) and -`packages/daemon/src/__tests__/support/` (daemon-shaped). Import named builders from their -defining module, never through a barrel. Shared `DeviceInfo`, session, snapshot, store, -runtime-fact, and mocked-binary values belong in a sibling fixture module, not in repeated -test literals. `PROPERTY_RUNS` and the touch-point/rect arbitraries live in -`@agent-device/selectors/snapshot-geometry-fixtures`; -`makeSnapshotState` lives in `@agent-device/capture-kit/snapshot-state-fixtures`. +Before creating fixtures, look in `@agent-device/test-kit` and +`packages/daemon/src/__tests__/support/`. Shared `DeviceInfo`, session, snapshot, store, +runtime-fact, and mocked-binary values belong in a sibling fixture module, not in repeated test +literals; a fixture one package uses is a `*.fixtures.ts` beside its module. The packages test-kit +builds on (kernel, contracts, capture-kit, host-kit, command-registry, daemon-contracts) cannot +declare it; their tests keep helpers local. Never import test-kit in the `vi.mock` factory of a +module its entry loads (`host-kit/process`, `command`, `file`): spy it in place, as +`pinOwnProcessStartTime` does. Fixtures a package still exports (R83's legacy count) stay +importable until moved. Use `mkdtempForTest` or `mkdtempForTestSync`. Global setup redirects `TMPDIR` for the run and removes it after every worker exits — skip per-test cleanup. An interrupted run may leave a @@ -109,8 +110,8 @@ For structured classifiers, pair the positive case with the closest negative. Wh can be identical with and without a typed reason, the negative test must prove the message alone cannot activate retry, fallback, or recovery. -Test through public interfaces where practical; never add production exports or test-only -dependency injection — a missing seam needs a real product seam. +Test through public interfaces; never add production exports or test-only dependency injection — a +missing seam needs a real product seam. ## Properties, fuzzing, and mutation @@ -119,9 +120,7 @@ another example. Keep examples for a real past bug or a named decision. Reuse `P budgets so property files stay inside the unit slow-test gate. Parser fuzz targets live in `scripts/fuzz/targets.ts`. Validation generators carry the invalid -outcome they planted, so silent acceptance and wrong error codes are failures. Cases run in a -worker process, so a case that never returns or kills its process is reported as `hang` or `crash` -against the exact input. Promote a discovered case with the command the harness prints — never +outcome they planted, so silent acceptance and wrong error codes are failures. Promote a discovered case with the command the harness prints — never hand-copy an unshrunk input. Mutation scope: `scripts/mutation/modules.ts#KERNEL_MODULES` (weekly), `scripts/mutation/run.ts#LANE_SOURCES`