00 · Environment setup
One-shot bring-up that every other playbook in this directory assumes. Run this once per machine and per branch checkout. If a playbook says "open mailpit" or "log in as admin@spade.local" it expects everything below to be in place.
0. Prerequisites
- Node.js ≥ 22 (use
nvm useif.nvmrcis present) - pnpm ≥ 10
- Docker + Docker Compose
1. Bring up infra + dependencies
pnpm install
docker compose up -d # postgres:17, minio, mailpit
cp .env.example .env # keep dev defaults unless noted
pnpm db:generate
pnpm db:migrate:deploy # apply committed migrations (idempotent)
pnpm db:migrate:status # expect "Database schema is up to date"
pnpm db:seed # creates ACME, GLOBEX, ZENITH
pnpm dev # api:3001, web:3000, worker (tsx)The seed script logs every page URL, magic-link URL, batch ID, statement ID, and contact email at the end. Copy them from there rather than guessing — they change on every reseed.
2. Verification checklist
| Service | URL | Expectation |
|---|---|---|
| API liveness | http://localhost:3001/health/live | { status: "ok" } |
| API readiness | http://localhost:3001/health/ready | DB + S3 + SMTP all green |
| Swagger | http://localhost:3001/docs | OpenAPI UI loads, both /ops and /ops/bookkeeping route groups visible |
| Web | http://localhost:3000 | redirects to /login |
| Mailpit | http://localhost:8025 | empty mailbox |
| MinIO console | http://localhost:9001 | login minioadmin / minioadmin |
| Prisma Studio | pnpm db:studio | browse tables during the flow |
3. Seeded staff accounts
Password is read from SEED_ADMIN_PASSWORD; if unset and NODE_ENV is not production, the dev fallback is DevAdmin-Password-1!.
| Role | Use for | |
|---|---|---|
admin@spade.local | PLATFORM_ADMIN | Client config, ingestion channels, staff-user management, both products |
lead@spade.local | PAYROLL_LEAD | Payroll override approvals, template management |
executive@spade.local | PAYROLL_EXECUTIVE | Payroll cycle ops, exports, output uploads |
No bookkeeping-specific staff user is seeded by default. Either log in as
admin@spade.local(PLATFORM_ADMIN has the bookkeeping action set) or create aBOOKKEEPER/SENIOR_ACCOUNTANTuser via/dashboard/staff-users/newbefore running the bookkeeping flows.
4. Seeded clients
| Client | Code | Product(s) | Notable state |
|---|---|---|---|
| ACME Pte Ltd | ACME | PAYROLL | Open 2026-04 cycle in INTAKE_IN_PROGRESS. Closed 2026-03 prior cycle (powers baseline-roster dropdown + OCR prefill demo). |
| GLOBEX Pte Ltd | GLOBEX | PAYROLL | Open 2026-04 cycle in CLIENT_REVIEW, approval round dispatched to bob@globex.sg. |
| Zenith Trading Pte Ltd | ZENITH | BOOKKEEPING | Xero, SGD, materiality SGD 50. Draft 2026-04 batch with 4 entries (1 APPROVED, 2 FLAGGED, 1 DRAFT). UOB statement with 6 transactions and a reconciliation run in AWAITING_REVIEW. |
The seed script also writes magic-link tokens for each contact and logs them — copy from the seed output.
5. Sample fixtures
Sample files used by the upload-driven flows live in scripts/fixtures/:
2026-04-ACME-attendance.xlsx
offer-letter-{marcus-lim,sarah-tan,wei-lin}.pdf
nric-{marcus-lim,sarah-tan}.pdf
fin-wei-lin.pdf
bank-details-{marcus-lim,sarah-tan,wei-lin}.pdf
change-letter-emp-001-increment.pdf
resignation-letter-rachel-goh.pdfFor bookkeeping uploads the seed already provisions sample documents in MinIO (ZENITH/bookkeeping/2026-04/*.pdf) — re-run pnpm db:seed to repopulate them. If you need a fresh PDF for ad-hoc tests, any small (< 5 MB) PDF works because the dev OcrAdapter is the mock by default.
6. Reset commands
When a flow leaves the system in a broken state:
# Wipe everything and reseed
docker compose down -v && docker compose up -d
pnpm db:migrate:deploy && pnpm db:seed
# Re-seed without dropping the DB (cleans fixtures only)
pnpm db:seed
# Tail worker job activity
pnpm --filter @breezycorp/worker dev
# Browse / edit DB directly
pnpm db:studio7. Environment variables you may need to flip
| Variable | Effect on flows |
|---|---|
OCR_PROVIDER=mock | Default. Documents extract instantly with hand-picked sample fields. Set to vision+claude to exercise the real pipeline (requires ANTHROPIC_API_KEY + GOOGLE_VISION_CREDENTIALS_JSON). |
SEED_ADMIN_PASSWORD | Set before pnpm db:seed to control the staff-user password. |
SMTP_HOST / SMTP_PORT | Default to mailpit on 1025. Point at a closed port to verify pg-boss retry behaviour during email-failure flows. |
STAFF_APP_URL / PORTAL_BASE_URL | Default to http://localhost:3000. Change before re-seeding to test cross-domain magic-link delivery. |
8. Where to ask
If anything in §1–§4 fails, fix it before opening a flow file. The flows assume green infra. Common failures:
pnpm db:migrate:deployreports drift → usepnpm db:migrate:resolve --applied <name>only after verifying the DB matches.- Mailpit empty after a flow that should have sent mail → check
outbox_events.dispatched_atin Prisma Studio; the worker poller runs every 10 s. - Worker silent → kill
pnpm devand runpnpm --filter @breezycorp/worker devin its own terminal so you can read the handler logs.