docs/plan/03-testing-and-guardrails.md
Testing and guardrails
The rules are in TESTING.md. This file only lists work still to do. The lint items can land now. The browser harness lands with resume in 01-foundation.md.
Lint, landable now
All native oxlint rules in lint.config.ts. No custom plugin.
- Ceilings:
max-lines700,max-lines-per-function150 (skip blank lines and comments),complexity15,max-depth4,import/no-cycle. I measured on 2026-09-24 and the only violation at these limits isLandinginapps/web/src/landing/Landing.tsx, at 331 lines. Split it into its page sections in the same PR. Exempt migrations ande2e/scenarios/**from the function limit. - Boundaries with per-path
no-restricted-imports:packages/contracts: nosolid-js,drizzle-orm,cloudflare:*,better-auth,@distilled.cloud/polaror app paths.packages/upload-core, once it exists: nosolid-js,@uppy/*,drizzle-ormorcloudflare:*.packages/db: nosolid-js,@uppy/*or app paths.apps/api: nosolid-js,@uppy/*orapps/webpaths.apps/web: nodrizzle-orm,@tranzfer/db,better-authserver modules orapps/apipaths.- Test files: no
vitest. Use@effect/vitest.
- Runtime boundary with
no-restricted-properties: banEffect.runSync,runPromise,runFork,runPromiseExitandManagedRuntime.make. Allow them inapps/web/src/api/**,apps/web/src/uploads/uploads.ts,apps/web/src/dashboard/{deliveries.ts,Board.tsx,DeliverySheet.tsx},apps/web/src/routes/d/[token].tsx,infra/**,e2e/**and test files. Those are today's call sites. - Prove each rule with a planted violation that fails
vp check, then remove the plant.
Contract snapshot
- Add a script that emits declarations for
packages/contractsand diffs them against a committed snapshot. RPC tag names and struct fields survive as literal types, so renamingHealthor dropping a field fails. Additions pass and get recorded with--update. Run it in theverifyworkflow. - When
upload-coreexists, add the IndexedDB recovery record schema to the snapshot. Future versions of the app have to read it.
Browser harness, with resume
e2e/ already holds the fetch-based staging checks. Add to it:
playwrightand a global setup. The setup attaches toE2E_URL, or bootsvp run dev, and waits for/infra.- Port executor's
scenario()without the viewer, film splicing or test-source extraction. AddTarget,Api,Browser,NetControlandStorageas TESTING.md describes. - Before building on either of these, verify:
- Which bucket local dev presigns against, so
Storagereads the same one.
- Which bucket local dev presigns against, so
- First scenarios:
- refresh mid-upload resumes, and the ledger shows each part sent once
- offline mid-part pauses, then resumes
- lost finalize response converges to complete
- browser relaunch with a persistent profile recovers the transfer
- cancel aborts the remote upload
- Add an e2e workflow once the suite runs stable locally.
Later
Restart: restartalchemy devmid-scenario, for server-death scenarios.- A context-budget report listing the largest files, the largest functions and fan-out, once code starts pressing against the ceilings.