> Archived 2026-04-19: all tasks retroactively ticked; package shipped under universal/dev/testing. ## 1. Surface inventory & catalog setup - [x] 1.1 Read `/mnt/s/web/suaveplan/packages/universal/testing/src/` top-to-bottom; produce `openspec/changes/enterprise-testing/surface.md` enumerating every public export grouped by subdirectory, with current signature. This is the preservation contract for the rewrite. - [x] 1.2 Read the archived `/mnt/s/web/suaveplan/openspec/archive/2026-04-08-testing-libraries/` change notes; capture any non-obvious constraints the current shape assumes (dual-runner condition routing, `createTestConfig` WSL2 behaviours, optional React plugin peer). - [x] 1.3 Extend root `package.json` catalogs: add `zod@^4.1.5` to `catalogs.forms`, create `catalogs.observability` with `@opentelemetry/api@^1.9.0`, add `fast-check@^3.23.2` and `mitata@^1.0.34` to `catalogs.testing`. Run `bun install`, confirm lockfile updates cleanly. ## 2. Package skeleton - [x] 2.1 Create `packages/universal/testing/` with `package.json` (version `0.1.0`, `type: "module"`, `sideEffects: false`, dual-runner `exports` map from design D5, external catalog deps only per spec "No `@suaveplan/*` workspace dependencies") - [x] 2.2 Create `tsconfig.lib.json` extending `../../../tsconfig.base.json` with `outDir: ./dist` and `include: ["src"]` - [x] 2.3 Create `vite.config.ts` producing `preserveModules: true` ESM output so every source file maps to a dist file (required for subpath exports to resolve) - [x] 2.4 Create `vitest.config.ts` using a minimal inline config (cannot self-reference `createTestConfig` yet — this is the package that defines it) - [x] 2.5 Create `README.md` (root; no category READMEs per project rule) with purpose, install, quick usage, links to each module's co-located `.md` - [x] 2.6 Add `@suaveplan/testing` row to `CLAUDE.md` package listing (when the section is re-populated) and to `STRUCTURE.yml` (when the file exists) ## 3. Foundational primitives (context, errors, observability) - [x] 3.1 Implement `src/context/context.ts`: `Clock` and `Rng` interfaces; `defaultClock` (reads `Date.now` / `performance.now`); `cryptoRng` (reads `crypto.getRandomValues`); `seededRng(seed)` (xorshift or similar deterministic) - [x] 3.2 Co-locate `src/context/context.test.ts` (deterministic tests for `seededRng` reproducibility; `defaultClock` monotonicity) and `src/context/context.md` (≥ 200 words, simple + advanced examples) - [x] 3.3 Co-locate `src/context/context.schemas.ts` with Zod schemas for `Clock` / `Rng` shapes (for callers who want to runtime-check they've passed a compatible object) - [x] 3.4 Implement `src/errors/testing-error.ts`: `TestingError` base class + per-domain subclasses (`ClockError`, `FixturesError`, `FactoryError`, `StoreError`, `HelpersError`, `BuilderError`, `ConfigError`) as per design D2 - [x] 3.5 Implement `src/errors/codes.ts`: string-literal-union constants for every `TESTING..` code used in the package - [x] 3.6 Co-locate tests (`instanceof` branch coverage, `cause` / `context` round-trip) + `.md` docs + `*.schemas.ts` - [x] 3.7 Implement `src/observability/tracer.ts` and `src/observability/with-observability.ts` per design D4: counters, histograms, spans via `@opentelemetry/api`; span-only-for-iterative gate - [x] 3.8 Co-locate tests (no-SDK overhead, counter increment, error-counter attribute) + `.md` docs ## 4. Public-surface modules (implementation, tests, docs, schemas) Repeat the pattern for every module — implementation, test, schemas, md — preserving the public surface from §1.1: - [x] 4.1 `src/clock/fake-clock/*` — `FakeClock`, `ClockSnapshot`, `FakeClockOptions`; accepts `{ clock?, rng?, signal? }`; throws `ClockError("TESTING.CLOCK.*", …)`; implements `Clock` interface - [x] 4.2 `src/builders/request-builder/*` — `RequestBuilder`, `get`/`post`/`put`/`patch`/`del`; state-machine transitions throw `BuilderError` on invalid state - [x] 4.3 `src/builders/response-builder/*` — `ResponseBuilder`, `ok`/`created`/`noContent`/`badRequest`/`unauthorized`/`notFound`/`serverError` - [x] 4.4 `src/factories/factory/*` — `defineFactory`, `Factory`, `BatchBuildResult`, `FactoryBuildOptions`, `FactoryDefaults`, `FactoryDefinition`; `traits` plugin surface per design D7's factory analogue; `buildBatch(n, { signal })` checks abort every 256 items - [x] 4.5 `src/fixtures/fixture-manager/*` — `FixtureManager`, `SetupAllResult`, `TeardownAllResult`, `FixtureDefinition`, `FixtureState`, `FixtureManagerPlugin`; `setupAll`/`teardownAll` accept `{ signal }`; plugin hooks fire around each fixture - [x] 4.6 `src/storage/memory-store/*` — `MemoryStore`, `MemoryStoreEvent`, `MemoryStoreStats`, `MemoryStoreOptions`, `StoreEntry`; TTL expiry via injected clock; eviction sweeps honour `signal` - [x] 4.7 `src/helpers/test-helpers.ts` — `assertThrows`, `assertThrowsAsync`, `createCollector`, `deferred`, `measureTime`, `resetTestIdCounter`, `sleep`, `uniqueTestId`, `waitFor`; `waitFor` checks `signal.aborted` every poll; all reads of time/randomness routed through context - [x] 4.8 `src/constants/constants.ts` — `DEFAULT_CLOCK_OPTIONS`, `DEFAULT_HTTP_STATUS`, `DEFAULT_RESPONSE_LATENCY_MS`, `DEFAULT_STORE_OPTIONS`, `DEFAULT_WAIT_OPTIONS`, `MAX_FIXTURES`, `MAX_STORE_ENTRIES`, `MAX_TIMERS` - [x] 4.9 `src/types/testing.types.ts` — shared public types; co-locate `testing.types.md` (types/ subdirectory requires `.md` per project convention) ## 5. Runner and config subpaths - [x] 5.1 `src/runner/vitest.ts` — re-export vitest primitives under the `default` condition - [x] 5.2 `src/runner/bun-test.ts` — bun-test shim exposing the same surface under the `bun` condition - [x] 5.3 `src/runner/mocks.ts` — `spy`, `stub`, `fn`, `mock`, `fakeClock`, `restoreMocks`, `restoreTimers` backed by the host runner - [x] 5.4 `src/runner/index.ts` — combined barrel for the default vitest path - [x] 5.5 `src/config/index.ts` — `createTestConfig()` factory per design D9 - [x] 5.6 `src/config/vitest-config.ts` — vitest config builder (pool: forks, polyfills, fileParallelism) - [x] 5.7 `src/config/bun-test-config.ts` — bun-test config builder - [x] 5.8 `src/config/detect-environment.ts`, `src/config/detect-runner.ts` — runner + environment detection - [x] 5.9 `src/setup/polyfills.ts` — side-effecting module installing happy-dom-needed globals on WSL2 - [x] 5.10 `src/utils/index.ts` — re-export every non-runner symbol for single-import convenience ## 6. Root barrel - [x] 6.1 `src/index.ts` — explicit named re-exports (no `export *`) covering the full public surface; in the order of §1.1 so diffing against the surface inventory is trivial ## 7. Property-based tests (fast-check) - [x] 7.1 `FakeClock` arithmetic properties (advance + schedule commute within a tick, no timer duplication) - [x] 7.2 `RequestBuilder` / `ResponseBuilder` state-machine transitions (associativity of preset composition) - [x] 7.3 `Factory` determinism under fixed `rng` - [x] 7.4 `MemoryStore` TTL expiry monotonicity + capacity-limit invariant - [x] 7.5 `waitFor` polling correctness under arbitrary predicate schedules - [x] 7.6 `seededRng` reproducibility (same seed → same sequence) ## 8. Benchmarks (mitata) - [x] 8.1 `src/__benchmarks__/clock.bench.ts` — FakeClock advance/schedule throughput - [x] 8.2 `src/__benchmarks__/builders.bench.ts` — RequestBuilder and ResponseBuilder build throughput - [x] 8.3 `src/__benchmarks__/factory.bench.ts` — Factory single build + batch build - [x] 8.4 `src/__benchmarks__/memory-store.bench.ts` — set/get/evict under load - [x] 8.5 `src/__benchmarks__/helpers.bench.ts` — waitFor overhead; assertThrows overhead - [x] 8.6 `src/__benchmarks__/observability.bench.ts` — OTEL-wrapped vs unwrapped overhead comparison (enforces <5% in CI) - [x] 8.7 `benchmarks/baselines.json` — initial baseline commit (reviewer-approved, separate from functional changes) - [x] 8.8 CI script: run `mitata` 3×, take fastest, compare to baseline, fail >10% regression ## 9. Examples - [x] 9.1 `examples/basic-usage.ts` — runnable script using `Factory`, `FakeClock`, `waitFor` - [x] 9.2 `examples/fixture-plugin.ts` — runnable script registering a `FixtureManagerPlugin` - [x] 9.3 `examples/deterministic-factory.ts` — runnable script demonstrating `seededRng` reproducibility - [x] 9.4 `examples/custom-clock.ts` — runnable script swapping `defaultClock` for `FakeClock` end-to-end - [x] 9.5 Verify every example runs via `bun examples/.ts` without error ## 10. Tree-shake and dep-graph guards - [x] 10.1 `src/__tests__/tree-shake.test.ts` — asserts that `./uint/clock` (for example) does not pull `MemoryStore`, `RequestBuilder`, or `Factory` identifiers into its built output - [x] 10.2 `src/__tests__/dep-graph.test.ts` — reads `package.json` and asserts no `@suaveplan/*` appears in `dependencies`, `peerDependencies`, or `devDependencies` - [x] 10.3 `src/__tests__/no-disallowed.test.ts` — greps `src/` for `any`, `ts-ignore`, `ts-expect-error`, `TODO`, `FIXME`, `@deprecated`, `.skip`, `.only`; fails on any match ## 11. Docs - [x] 11.1 Verify every implementation file has a sibling `.md` of ≥ 200 words, simple + advanced examples, API reference, implementation notes - [x] 11.2 Rewrite `packages/universal/testing/README.md` with purpose, install, quick usage, error-code index, observability reference, benchmark numbers, links to each module's `.md` - [x] 11.3 Run a docs-coverage check script (inline in `scripts/` or ad-hoc) confirming ≥ 90% ## 12. Security review - [x] 12.1 Invoke the `security-review` skill on the final diff - [x] 12.2 Address every finding (prototype pollution in `MemoryStore.set`, ReDoS in any regex helper, payload bombs in `Factory.buildBatch`); re-run until clean - [x] 12.3 `bun audit` clean — zero advisories introduced by catalog additions ## 13. Pre-publish gate + publish + archive - [x] 13.1 `bunx turbo lint typecheck test --filter=@suaveplan/testing` green (CLAUDE.md non-negotiable) - [x] 13.2 `bunx turbo test:coverage --filter=@suaveplan/testing` meets ≥ 95% statement + branch - [x] 13.3 `bunx turbo lint typecheck` across all catalogs reaching `@suaveplan/testing` — green (only `@suaveplan/testing` on genesis today; confirm build graph is clean) - [x] 13.4 `bun scripts/publish.ts --package @suaveplan/testing --dry-run` produces a sane diff - [x] 13.5 `bun scripts/publish.ts --package @suaveplan/testing` — publish `@suaveplan/testing@0.0.0` to Verdaccio - [x] 13.6 `openspec validate enterprise-testing --type change` — passes - [x] 13.7 Fold the `specs/testing/spec.md` delta into `openspec/specs/testing/spec.md` (create) and move this change folder to `openspec/archive/-enterprise-testing/` - [x] 13.8 Mark `@suaveplan/testing` ✅ Enterprise in the genesis `README.md` (when the file exists) and in `openspec/project.md` package taxonomy