15 KiB
Developer Runbook
You just cloned this repo. This is the only doc you need to read end-to-end. Everything else is reference.
Prerequisites
| Tool | Version | Why |
|---|---|---|
| Node.js | 22+ | Runtime for all apps + scripts |
| pnpm | 9+ | Package manager (workspace-aware) |
| Docker | 24+ | Local Postgres + sandcastle sandboxes |
| Git | 2.40+ | Version control + worktrees |
Recommended editor: VS Code or Cursor with the official TypeScript, ESLint, and Prettier extensions.
First-time setup
# 1. Clone + install
git clone <repo-url> template-vertical
cd template-vertical
pnpm install
# 2. Start Postgres (background)
docker compose up -d
# 3. Copy env template and fill in secrets
cp .env.example .env
# Edit .env (see "Environment variables" section below for what each one does)
# 4. Verify the gate stack is green
pnpm typecheck
pnpm test
pnpm lint
pnpm conformance
pnpm fallow
pnpm turbo boundaries
All six should exit 0. If any fails on a fresh clone, file an issue — the main branch is supposed to stay green.
# 5. Start the dev servers
pnpm dev
This runs Next.js (3000), Payload CMS (3001), TanStack Start (3002), and Storybook (6006) in parallel. The bindAll() dispatcher in each app picks the dev-seed binders by default (mock repositories, no Payload connection needed beyond Postgres).
Daily commands
# Development
pnpm dev # all dev servers
pnpm dev --filter @repo/web-next # one app
# Tests
pnpm test # everything
pnpm test --filter @repo/auth # one package
pnpm test:e2e # Playwright e2e
pnpm test:stories # Storybook smoke tests
pnpm test:visual # visual regression (Playwright screenshots)
# Linting + type checking
pnpm typecheck # tsc across all packages
pnpm lint # ESLint across all packages
pnpm format # Prettier write
pnpm format:check # Prettier check (CI mode)
# Conformance gates
pnpm conformance # cross-feature event closure
pnpm fallow # whole-codebase: dead exports, dupes, complexity
pnpm fallow:audit # AI-change audit (run before commits)
# Boundary validation
pnpm turbo boundaries # workspace dependency graph
# Work system
pnpm work status # tree of epics + stories
pnpm work next # next ready story
pnpm work ready # all ready stories
pnpm work blocked # blocked stories + what they wait on
pnpm work rebuild-state # regenerate docs/work/_state.json
pnpm work dispatch # print next dispatch plan
pnpm work dispatch --execute # invoke sandcastle (requires ANTHROPIC_API_KEY)
Environment variables
Copy .env.example to .env and fill what you need. NOT every variable is required for pnpm dev — defaults are dev-friendly.
Required for pnpm dev
| Var | Example | Why |
|---|---|---|
DATABASE_URL |
postgresql://postgres:postgres@localhost:5433/template |
Postgres connection (docker compose default) |
PAYLOAD_SECRET |
your-secret-here |
Payload CMS encryption key (any random 32+ char string in dev) |
Optional — app URLs (defaults work in dev)
| Var | Default | Why |
|---|---|---|
NEXT_PUBLIC_APP_URL |
http://localhost:3000 |
Public-facing web-next URL |
CMS_URL |
http://localhost:3001 |
Payload CMS URL |
USE_DEV_SEED |
true in dev |
Force dev-seed binders (mock repos) instead of Payload |
NODE_ENV |
inherited | production flips bind dispatcher to real Payload |
Optional — Sentry observability (no DSN = no-op tracer/logger)
| Var | Why |
|---|---|
WEB_NEXT_SENTRY_DSN |
Server-side OTel + Sentry for web-next |
NEXT_PUBLIC_WEB_NEXT_SENTRY_DSN |
Browser Sentry for web-next |
CMS_SENTRY_DSN |
Server-side for Payload CMS |
WEB_TANSTACK_SENTRY_DSN |
Server-side for TanStack Start |
VITE_WEB_TANSTACK_SENTRY_DSN |
Browser-side for TanStack Start |
SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT_* |
Source-map upload at build time |
SENTRY_TRACES_SAMPLE_RATE |
OTel trace sample rate (0.1 recommended in dev) |
SENTRY_ENVIRONMENT |
development / staging / production |
Optional — Git commit SHA for releases
| Var | Why |
|---|---|
VERCEL_GIT_COMMIT_SHA / VITE_GIT_COMMIT_SHA / NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA |
Surfaces commit SHA in Sentry releases + UI footers |
Optional — core-audit (only when gen core-package audit is scaffolded)
| Var | Why |
|---|---|
AUDIT_PSEUDONYM_SALT |
Salt for the audit log's GDPR-erasure pseudonymisation (production only — must be a stable secret) |
Optional — sandcastle dispatch (only when running pnpm work dispatch --execute)
| Var | Why |
|---|---|
ANTHROPIC_API_KEY |
Claude API key (sandcastle's default agent) |
OPENAI_API_KEY |
OpenAI/Codex alternative |
GITHUB_TOKEN |
GitHub access for PR creation by the orchestrator |
SANDCASTLE_PROVIDER |
docker (default) / podman / vercel |
The agent-first workflow
This template enforces a manifest-first, generator-driven, gate-protected workflow.
When adding a new feature
pnpm turbo gen feature <name>
This emits:
packages/<name>/src/feature.manifest.ts— the conformance manifest (use cases, audits, publishes, consumes)packages/<name>/src/di/bind-production.tswithassertFeatureConformance(...)at the tail (refuses to boot on drift)- Mock repository, factory, seed, entity, use-case, controller, tests — full Lazar-conformant shape
packages/<name>/src/index.tsexports
After scaffolding, the four-step ordering for any new use case:
- Manifest entry — declare the use case in
feature.manifest.ts - Contracts — export
xInputSchema,xOutputSchema,IXUseCase(factory body throwsnot implemented) - Tests (red) — write the failing test
- Implementation (green) — fill the factory body
The five conformance gates catch drift at every step. See docs/guides/conformance-quickref.md for the manifest field reference.
When adding cross-feature primitives
pnpm turbo gen event # event contract or handler (needs gen core-package events)
pnpm turbo gen job # background job
pnpm turbo gen realtime # realtime channel or handler (needs gen core-package realtime)
pnpm turbo gen core-package <x> # optional core package (events/realtime/trpc/ui/audit)
pnpm turbo gen core-ui-component <x> # atomic-design component (needs gen core-package ui)
Always prefer generators over hand-rolling. The generators emit the canonical shape; hand-rolled code drifts from generator output and breaks the CI scaffold-drift check.
Tracking work
The repo uses docs/work/ for epic/story/task tracking:
docs/work/
├── README.md
├── _state.json # derived, regenerated by pre-commit hook
├── prds/ # PRDs go here
├── _templates/ # markdown templates
└── <epic-slug>/
├── _epic.md
└── <story-slug>/
└── _story.md # contains the Tasks checklist
Use pnpm work next to see what's ready. Use pnpm work dispatch to plan the next sandcastle dispatch.
The five conformance gates
| Gate | Latency | What it catches | Runs when |
|---|---|---|---|
| TypeScript brands | 0s | forgotten withSpan / withCapture / withAudit; manifest ↔ binding-slot type mismatch |
on save (IDE) |
| ESLint (8 conformance/* rules) | <1s | manifest ↔ code drift; missing sibling test; missing manifest; atomic-tier import direction | on save / pnpm lint |
| Boot assertion | ~3s | runtime binding without required brand; manifest edited without rebinder | pnpm dev startup |
pnpm conformance |
~120s | orphan event consumers across features | CI |
pnpm fallow |
~30–60s | dead exports / unused files; duplicate code; circular deps; complexity hotspots; AI-change audit | CI |
For the full design see docs/architecture/agent-first-workflow-and-conformance.md. For the daily reference see docs/guides/conformance-quickref.md.
Troubleshooting
pnpm dev refuses to boot with ConformanceError
— A feature's binding lost a required brand. The error message tells you which use case + which brand. Re-bind through withSpan / withCapture / withAudit as needed.
pnpm lint errors with conformance/feature-must-have-manifest
— You created a feature with use cases but no feature.manifest.ts. Run pnpm turbo gen feature <name> to scaffold the canonical shape, or hand-write the manifest at packages/<feature>/src/feature.manifest.ts.
pnpm conformance says "orphan consumer"
— A feature declares consumes: ["X"] but no feature publishes X. Either add the publish to the producing feature's manifest + factory, or remove the consumer.
pnpm fallow reports new dead exports or dupes
— Your change added unused exports or duplicated logic. Either remove the dead code or accept with pnpm fallow:audit --gate all (the audit considers the baseline; only NEW findings fail).
Pre-commit hook refuses to commit with "state-sync-guard"
— You staged docs/work/_state.json but it's not byte-identical to pnpm work rebuild-state output. Run pnpm work rebuild-state && git add docs/work/_state.json and try again.
Tests fail in @repo/turbo-generators with Vitest worker timeouts
— Known flaky on slow machines. Re-run; if persistent, increase the turbo-generators package's vitest testTimeout.
pnpm work dispatch --execute errors with "ANTHROPIC_API_KEY required"
— You're trying to run the sandcastle orchestrator without an agent API key. Set the env var, or run pnpm work dispatch (no flag) to just print the plan.
Where to read next
Once you've got pnpm dev running:
AGENTS.md— package map, boundary rules, per-package conventionsCLAUDE.md— full convention reference (manifest-first ordering, factory patterns, instrumentation rules)docs/guides/conformance-quickref.md— daily manifest + gates referencedocs/guides/tdd-workflow.md— red-green-refactor with the gate stackdocs/guides/scaffolding-a-feature.md—pnpm turbo gen featurereferencedocs/guides/adding-a-feature.md— end-to-end walkthroughdocs/architecture/agent-first-workflow-and-conformance.md— the full designdocs/architecture/feature-conformance-explainer.html— interactive explainer (open in browser)
For deeper topics:
docs/guides/events-and-jobs.md— cross-feature events (requiresgen core-package events)docs/guides/realtime.md— Socket.IO channels (requiresgen core-package realtime)docs/guides/audit-and-compliance.md— DPA-compliant audit logging (requiresgen core-package audit)docs/guides/frontend-work-shape.md— atomic design + Storybook conventionsdocs/guides/infrastructure-work-shape.md— ADR-first flow for new infrastructure
Common pitfalls
- Skipping the generator. Always run
pnpm turbo gen <kind>before hand-rolling. Generators emit the canonical shape; the CI scaffold-drift check will fail on hand-rolled features. - Forgetting
pnpm work rebuild-stateafter editingdocs/work/markdown. The pre-commit hook handles this automatically when you stage markdown; only matters if you push without committing. - Bypassing
--no-verifyon commits. The pre-commit hook catches drift early. If it's blocking a legitimate change, fix the underlying issue, not the hook. - Hand-editing
_state.json. Don't. The state-sync-guard refuses commits that drift from rebuild output. Edit the markdown; let the rebuild script propagate. - Committing
.env. It's gitignored. Use.env.examplefor new vars.
For deeper philosophy: this template is built around the assumption that AI agents will author most feature work. The conformance system is designed as an agent feedback loop. Latency-layered gates compound: 0s + <1s + 3s + 120s + 60s. The faster the inner loop, the more iterations agents can make per task.
If you're a human contributor, the same workflow applies — the gates aren't punitive, they're navigational aids.