First Path-B task. Path A landed daily-driver features without a
test runner; Path B is "harden so the next batch of changes can't
silently regress what just shipped." Step 1 is making `pnpm test`
real and gating CI on it.
Test runner:
* Install vitest + @vitest/coverage-v8 at the workspace root.
* Add vitest.config.ts (environment: "node", no JSDOM) + test /
test:watch scripts to packages/shared, packages/database,
packages/ai. Wire `test` into turbo.json with dependsOn: ^build
for future-proofing; add `pnpm test` to root package.json.
Three real tests (no snapshot theater — verified by mutation):
* packages/shared/src/utils/id.test.ts: asserts generateId() matches
the RFC 4122 v4 regex and produces 1000 distinct values. Mutating
generateId() to a constant fails both assertions.
* packages/shared/src/types/objects.test.ts: pins objectTypes and
objectStatuses arrays. These back the zod enum on objects.create
and the "open tasks" count on the workspace-home dashboard; a
silent reorder/rename would otherwise corrupt the dashboard math.
* packages/database/src/markdown-backlog/parse.test.ts: covers
parseBacklogMarkdown across three shapes (well-formed Task,
no-frontmatter Plan with path inference, malformed YAML that
must NOT throw — the importer runs in a file watcher). Plus
hashFileContents determinism.
* packages/ai/src/actions/index.test.ts: five tests across the
prompt builders (summarize/expand/rewrite × 3 tones / translate /
generateFromPrompt). Pure functions; no model mocking needed.
CI:
* .github/workflows/ci.yml runs on pull_request and push to main.
Node 20, pnpm 9 pinned explicitly (per AGENTS.md). Uses
setup-node's built-in pnpm cache. Steps: install --frozen-lockfile,
lint, type-check, test. Concurrency group cancels superseded runs
on non-main branches.
Docs:
* AGENTS.md: drop the "no test runner configured" disclaimer.
Document pnpm test / test:watch. Update the PR-readiness rule
from `pnpm lint && pnpm type-check` to
`pnpm lint && pnpm type-check && pnpm test`.
All 14 tests pass; lint + type-check still green across all 6
packages. The CI workflow's first run is gated on the operator
pushing this branch — that's the only acceptance criterion left
unverified in this commit.
Closes plans/Plan-multitenant-saas-hardening/Epic-test-foundation/
Task-bootstrap-vitest-and-ci.md.
Co-authored-by: Cursor <cursoragent@cursor.com>