7.2 KiB
Development and testing
Prerequisites
- Node.js compatible with the lockfile/native
better-sqlite3build. - npm.
- Playwright system/browser dependencies for browser-backed tests or runtime work.
- No live account is required for unit, contract, route, or dashboard tests.
NodeNext imports use .js suffixes even when the source file is .ts. Vitest globals are disabled, so tests import describe, it, expect, and hooks explicitly.
Safe setup
npm ci
npm run build
npm test
Do not start npm run dev merely to verify a build. The repository .env and persisted database can make the process live-capable. Starting the backend, importing sessions, or toggling the bot to running requires explicit authorization and a confirmed dry-run/live intent.
If native SQLite bindings fail after a Node upgrade, distinguish pure TypeScript/unit results from database verification. Rebuild/install a compatible better-sqlite3; --ignore-scripts is not a valid database test environment.
Commands
| Command | Purpose |
|---|---|
npm run build |
Compile server and production dashboard. |
npm run build:server |
Run backend TypeScript compiler. |
npm run dashboard:typecheck |
Type-check the React application. |
npm run dashboard:build |
Build dashboard assets. |
npm test |
Run the complete Vitest suite once. |
npm run test:watch |
Watch tests during local development. |
npm run test:coverage |
Run with coverage. |
npm run docs:api |
Regenerate docs/api.md. |
npm run docs:api:check |
Fail if generated API documentation is stale. |
npm run mcp |
Start the stdio MCP adapter; requires an authorized running API. |
npm run dev |
Start the live-capable backend watcher. |
npm run dashboard:dev |
Start Vite, proxying API requests to port 3000. |
Change workflow
- Read
agents.md,docs/README.md, the routed topic docs, owning source, and focused tests. - Inspect
git statusbefore editing. Preserve unrelated user changes and do not broadly stage a dirty worktree. - Establish a baseline appropriate to the change. Code-changing tasks follow the repository test-first instructions; docs-only changes use documentation/link checks.
- Identify the source-of-truth boundary: config schema, migration, service, shared transport contract, route, or UI feature.
- Implement the smallest coherent behavior using dependency injection and single-purpose modules.
- Add behavior-focused tests for observable inputs, outputs, state transitions, and side effects.
- Update every affected document and regenerate API docs when the catalog changes.
- Run focused tests while iterating, then full required checks before publishing.
- Review the exact staged diff, commit only the coherent set, push, and verify branch/remote sync.
Engineering practices
Types and validation
- No production
any; useunknown, Zod, discriminated unions, or type guards. - Keep database/service-private types separate from transport contracts.
- Parse at boundaries and pass trusted domain values inward.
- Return explicit terminal/retryable/human-required results where callers must decide policy.
Service design
- Constructor-inject external dependencies, clocks, fetchers, schedulers, and persistence seams.
- Keep process-global coordination separate from account-scoped mutable state.
- Prefer a small policy/service over another conditional in a composition root or route.
- Preserve abort signals through queues, sleeps, and remote operations.
Logging
Use the injected Winston logger. Include stage, account context (through child loggers), post/conversation IDs, and safe rejection/retry reasons. Never serialize raw Axios/Playwright errors that may include cookies or authorization headers; extract a safe message/status. Avoid high-frequency unchanged status logs.
Remote effects
- Check dry-run at the final side-effect boundary.
- Make retries idempotent and distinguish remote acceptance from local persistence.
- Reserve recipients/claims before sending and release only on safe failure paths.
- Treat 403, 404, and 429 according to their typed domain meaning rather than one generic retry.
Test layout and patterns
Tests live in tests/; dashboard tests are under tests/dashboard/, route/event tests under tests/web/, and shared contract tests under packages/api-contracts/tests/.
Preferred patterns:
- in-memory SQLite with real migrations/repositories;
- fake clock/sleep/fetch functions injected into services;
- small service fakes implementing narrow interfaces;
- Testing Library for user-observable React behavior;
- representative payload parsing for strict Zod contracts;
- lifecycle/restart tests for queues, claims, and account runtimes.
Avoid deleting coverage to make a suite pass, assertions that pin private call order without behavioral value, ESM namespace mocking when injection is available, or tests that read the real .env/production data.
Verification matrix
| Change | Minimum focused verification | Final verification |
|---|---|---|
| Pure backend/service | owning Vitest files | npm test, npm run build:server |
| Database/migration | migration + repository tests | full tests and server build |
| API/contract | route + contract + auth tests; regenerate docs | full tests, build, docs:api:check |
| Dashboard | feature tests + typecheck | full tests and full build |
| Docker/deployment | compose/Docker tests + image build as appropriate | health, API, container, logs, and persistence after authorized deploy |
| Documentation only | generated-doc check + internal link scan | staged diff review |
If the worktree has pre-existing failures or concurrent changes, record the exact baseline and do not attribute them to your change. Never hide a failed required check behind a passing focused test.
Common implementation recipes
Add an endpoint
Create or extend a focused router, validate input, add shared output contracts, register metadata in api-catalog.ts, add authorization/behavior tests, consume via dashboard query helpers if needed, regenerate docs/api.md, and update the relevant narrative doc.
Add persisted state
Append a migration, add a narrow repository, wire it through the composition root/runtime, test fresh and restart/upgrade cases, and update database.md.
Change candidate or reply behavior
Start with the durable state transition and idempotency rule. Test rejection/retry/success, process interruption, account ownership, and operator-visible reason counts. Update product, flow, service, and data docs as applicable.
Add dashboard state
Define the shared schema first, add a typed query/mutation, keep route/filter state in the URL where useful, invalidate snapshots from relevant SSE events, and verify loading/error/stale/accessibility states.
npm run learning:eval -- --db <sqlite-path> --policy <version>runs the offline learning hard gates against eligible redacted decisions and persists a versioned evaluation result. Use a database backup or non-production copy when evaluating production-derived data. AI request logs contain prompt length and SHA-256 only. Do not restore full prompt logging: conversation prompts can contain private messages and retrieved learning context.