Guidance for coding agents working in this repository.
- Runtime: Node.js 20
- Package manager: Bun
- Language: TypeScript
- Test runner: Jest with
ts-jest - Linting: ESLint
- Primary workflow entrypoint:
make
Prefer these commands:
make devto start the local Docker-backed development stackmake dev-allto start the broader local development workflowmake populate-local-devto seed the local stack with test data./scripts/login.ts adminto automate logging into the seeded admin accountmake testto run all testsnpx jest path/to/test.test.tsto run a single test filemake lintto run ESLintmake typecheckto run type checking without emitting filesmake watch-typecheckto run type checking in watch modemake unused-exportsto detect unused exportsmake auditto run the Bun security auditmake smoketestto run smoke testsmake checkto run the standard verification suite: test, lint, typecheck, unused-exports, auditmake fixto apply automatic ESLint fixes
Use make start only inside the devcontainer. It is not the normal local app entrypoint.
Local app stack endpoints:
- App:
http://localhost:8080 - Mailcatcher:
http://localhost:1080
Seeded local users created by make populate-local-dev:
foo@example.comfor a regular memberadmin@example.comfor a super user
This app is built around event sourcing.
- Commands live in
src/commands/ - Read models live in
src/read-models/ - Queries live in
src/queries/ - Authentication flows live in
src/authentication/ - Background sync code lives in
src/sync-worker/ - HTTP handlers live in
src/http/ - Top-level routes are wired in
src/routes.ts - Event store code lives in
src/init-dependencies/event-store/
Important boundaries:
- Commands validate authorization and business rules, then emit domain events.
- Commands should only use the events passed to them. They should not query read models directly.
- Read models project events into queryable state.
- Queries read from read models and render HTTP responses.
- Read models are eventually consistent, so do not assume command results are immediately visible in read-side code.
- The sync worker ingests external state and turns it into events instead of bypassing the event model.
- Communication across the app should happen via events, not by coupling command code to read-model code.
Important domain notes:
- Events are immutable facts. Prefer adding a new event over mutating prior state assumptions.
- The event store uses optimistic concurrency and resource versioning.
- Authentication is passwordless and handled by magic link flows under
src/authentication/. - Member numbers are linked to email addresses through domain events.
- Actor types include
system,token, anduser. - Be careful with authorization, session, and member-linking changes.
External integrations:
- Recurly provides subscription-related data.
- Google Sheets provides training and trouble-ticket data.
- Paxton data is read from an external service.
- Sync-related tests commonly use fixtures under
tests/data/.
- Prefer existing fp-ts patterns such as
TaskEither,Option, andpipe. - Use existing io-ts types and decoders where applicable.
- Use
deps.loggerfor logging instead ofconsole.log. - Follow the repo's current style rather than introducing a new abstraction or library unless necessary.
- Keep changes small and targeted.
- Preserve the existing command, read-model, query, and sync-worker boundaries instead of shortcutting across layers.
- When wiring new behavior, prefer extending the matching layer and handler path rather than adding ad hoc logic to routes or HTTP glue.
Tests live under tests/ and usually mirror the source layout.
- Use
.test.tsfiles. - Command tests should assert on resulting events.
- Read-model tests should feed events into the read model and assert on query results.
- It is acceptable in read-model tests to use
command.process()only to generate events for setup. - Avoid calling read models from command tests.
- Tests should follow a behaviour-driven style.
Directory mapping notes:
- Most source paths map directly into
tests/ - Before creating a new test file, inspect nearby tests and follow the local naming convention already in use.
Helpful test utilities already used in the repo:
fakerfor test datajest-date-mockfor date-sensitive teststests/helpers.tsfor shared test helperstests/data/for external-data fixtures
- Prefer
rgfor searching the codebase. - Before broader verification, run the smallest relevant check for the files you changed when practical.
- If you change behavior, add or update tests in the matching area under
tests/. - Preserve the command/read-model separation when adding features.
- Avoid speculative refactors unless they are necessary for the task.
- Do not overwrite unrelated local changes.
- Check
Makefile, nearby tests, and existing module patterns before introducing a new command or workflow.
README.mdfor local setup, login flow, and operational contextCLAUDE.mdfor deeper repository architecture guidanceMakefilefor the canonical development, verification, and helper commands