127 lines
7.2 KiB
Markdown
127 lines
7.2 KiB
Markdown
# Development and testing
|
|
|
|
## Prerequisites
|
|
|
|
- Node.js compatible with the lockfile/native `better-sqlite3` build.
|
|
- 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
|
|
|
|
```bash
|
|
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
|
|
|
|
1. Read `agents.md`, `docs/README.md`, the routed topic docs, owning source, and focused tests.
|
|
2. Inspect `git status` before editing. Preserve unrelated user changes and do not broadly stage a dirty worktree.
|
|
3. Establish a baseline appropriate to the change. Code-changing tasks follow the repository test-first instructions; docs-only changes use documentation/link checks.
|
|
4. Identify the source-of-truth boundary: config schema, migration, service, shared transport contract, route, or UI feature.
|
|
5. Implement the smallest coherent behavior using dependency injection and single-purpose modules.
|
|
6. Add behavior-focused tests for observable inputs, outputs, state transitions, and side effects.
|
|
7. Update every affected document and regenerate API docs when the catalog changes.
|
|
8. Run focused tests while iterating, then full required checks before publishing.
|
|
9. Review the exact staged diff, commit only the coherent set, push, and verify branch/remote sync.
|
|
|
|
## Engineering practices
|
|
|
|
### Types and validation
|
|
|
|
- No production `any`; use `unknown`, 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.
|