AGENTS
Agent Operating Rules
Ports vs Adapters (Ports-and-Adapters)
- Ports (
src/ports): Define what the app needs. Own lifecycle, config, context wiring. MUST NOT directly depend on third-party libraries. MAY import from@app-types,@adapters,@utils. - Adapters (
src/adapters): Describe how it’s done. Concrete implementations (Pino, Sentry, Mongo, etc.). All adapter files are named*.adapter.ts. MAY import from@app-typesonly. MUST NOT import from ports, bootstrap, app code, or utils. - Types (
src/types): Pure contracts only. No runtime logic, no side effects. MUST NOT import from ports, adapters, or app code. - App code (
src/data,src/endpoints): MAY import ports and types only. MUST NOT import adapters directly; bootstrap wires adapters into ports.
ESLint enforces these boundaries. See ARCHITECTURE.md for the full table.
How to Touch Files
- Prefer small, isolated changes per commit.
- Use feature folders under
src/data/<feature>for all new logic. - Keep code ASCII unless the file already uses Unicode.
- Add JSDoc for exported/shared classes, functions, and methods.
- Add explicit inline comments for non-obvious branching/logic decisions.
- Keep modules/functions single-purpose; split when there are multiple reasons to change.
- Keep code DRY; extract repeating helpers into
src/utils/*when reuse is clear. - Eliminate magic strings for domain concepts; hoist shared values as typed constants (
as const) in*.types.tsor*.constants.ts. - Prefer
interfacecontracts for services and ports; avoid ad-hoc inline service object types. - Service-thrown errors must come from feature-local
*.errors.tsfiles.
Composition and Startup
- Treat
src/init/*.init.tsas composition roots. - Keep
src/server.tsas a thin launcher (initApi()+start()+ signal teardown). - Bootstrappers are wiring-only and receive fully composed
BootstrapDeps. - Do not duplicate env/config objects when
ProjectConfigalready carries them.
Tests
- New or changed behavior requires at least unit tests in the same folder as source.
- Use
*.spec.tsor*.integration.spec.tsnext to the file under test. - For bug fixes, add a test that fails before and passes after the fix.
- Do not change public APIs without updating tests.
- Use colocated faker-backed
*.factory.tshelpers for model fixtures. - In tests, do not construct DB model records inline; use factories with override support.
Safe Refactoring
- Keep refactors within a single layer per commit.
- Ports are types only; adapters do the framework work.
- If unsure about impact, add tests first.
- For orchestration services, move dependency graph assembly into dedicated
*.factory.tsfiles.
Permissions (Do Not Do Without Asking)
- Do not add new dependencies without explicit approval.
- Do not refactor across module boundaries without tests in place.
- Do not change public API contracts without updating integration tests.
Negative Capability Rules
- No direct DB access outside repository adapters.
- No cross-layer imports (respect ports/adapters/types boundaries).
- No framework imports inside ports or services.
- Adapters must not import from ports or app code.
Checklist (Agents Must Satisfy)
- No new logic without tests.
- No cross-layer imports; respect ports/adapters/types rules.
- Types in
src/types, not inline (unless file-local). - Validators in
src/data/*/*.validators.ts. - Endpoints are thin (validate/map/dispatch only).
- New infrastructure implementations go in
src/adapters/*.adapter.ts, not in ports. - Complex dependency construction belongs in service factories at wiring boundaries, not request handlers.
GitHub CLI and pull requests
The host may define multiple GitHub tokens (e.g. in ~/.zshenv). The default GH_TOKEN might not have access to the repo (e.g. org vs personal), which can cause gh pr create or gh api to fail with "Could not resolve to a Repository".
- Use the personal token for PR operations. Before running
gh pr create,gh pr view, or otherghcommands that need to see the repo, setGH_TOKENto the personal token variable (e.g.PERSONAL_GITHUB_TOKEN). - From a shell that has sourced the env file:
GH_TOKEN="$PERSONAL_GITHUB_TOKEN" gh pr create --base main --head <branch> --title "..." --body-file docs/pr-body.md - From an agent or script: Source the user's env file (e.g.
source ~/.zshenvin the same shell), then runGH_TOKEN="$PERSONAL_GITHUB_TOKEN" gh .... Do not read or log token values; reference only variable names. - If the user has a helper like
ghpthat doesGH_TOKEN="$PERSONAL_GITHUB_TOKEN" gh "$@", runningghp pr create ...is equivalent.
Cursor Cloud specific instructions
Services overview
| Service | How to start | Port | Notes |
|---|---|---|---|
| MongoDB + Valkey | pnpm dev:infra | 27017, 6379 | Docker required; must be running before pnpm dev |
| API + AdminJS | MONGO_URL=mongodb://localhost:27017/ledgerline ADMIN_AUTH_USE_LOCAL=true pnpm dev | 3000 | Hot-reloads via tsx watch |
Running the dev server
- Start infra:
pnpm dev:infra(Docker must be running; usesudo dockerd &if needed) - Start app:
MONGO_URL=mongodb://localhost:27017/ledgerline ADMIN_AUTH_USE_LOCAL=true pnpm dev - Health check:
curl http://localhost:3000/healthshould returnok - AdminJS panel:
http://localhost:3000/admin(redirects to auth entrypoint)
Authentication in dev
NODE_ENVdefaults todevelopment, which enables local auth with usernameadmin/ passwordpassword.- Login via API:
POST /api/auth/local/loginwith JSON{"username":"admin","password":"password","tenantId":"default"}. The response sets an HttpOnly signed cookie (ledgerline_auth). - Set
ADMIN_AUTH_USE_LOCAL=trueso the admin panel's auth entrypoint uses the local provider instead of Discord OAuth.
Important gotchas
NODE_ENV=productionis set by default in the Cloud VM environment. You mustunset NODE_ENV(or set it todevelopment) beforepnpm install, otherwise devDependencies are skipped andhusky/tsx/vitestwill not be available.- The pre-commit hook requires
semgrep(installed viapip install semgrep). The binary is at~/.local/bin/semgrep; ensure~/.local/binis onPATH. pnpm dev:infrarequires Docker. In the Cloud VM, Docker must be started withsudo dockerd &and the socket made accessible (sudo chmod 666 /var/run/docker.sock).
Quality checks (see also docs/CONTRIBUTING.md)
Standard order: pnpm typecheck then pnpm lint then pnpm test then pnpm check:cycles.
Renovate dependency upgrade validation
When working on a Renovate PR branch, follow the skill at .cursor/skills/renovate-upgrade-validation.md. Key points:
- Always analyze the changelog before running tests — don't just wait for failures.
- Audit every usage of the upgraded package across the codebase.
- Use
scripts/parse-renovate-pr.mjsto extract package names and version ranges from the PR. - The CI workflow at
.github/workflows/renovate-validate.ymlhandles auto-merge for passing PRs. - For major version bumps, the skill includes codebase-specific guidance for common packages (Mongoose, Express, AdminJS, Zod, etc.).