Skip to main content

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-types only. 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.ts or *.constants.ts.
  • Prefer interface contracts for services and ports; avoid ad-hoc inline service object types.
  • Service-thrown errors must come from feature-local *.errors.ts files.

Composition and Startup

  • Treat src/init/*.init.ts as composition roots.
  • Keep src/server.ts as a thin launcher (initApi() + start() + signal teardown).
  • Bootstrappers are wiring-only and receive fully composed BootstrapDeps.
  • Do not duplicate env/config objects when ProjectConfig already carries them.

Tests

  • New or changed behavior requires at least unit tests in the same folder as source.
  • Use *.spec.ts or *.integration.spec.ts next 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.ts helpers 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.ts files.

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 other gh commands that need to see the repo, set GH_TOKEN to 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 ~/.zshenv in the same shell), then run GH_TOKEN="$PERSONAL_GITHUB_TOKEN" gh .... Do not read or log token values; reference only variable names.
  • If the user has a helper like ghp that does GH_TOKEN="$PERSONAL_GITHUB_TOKEN" gh "$@", running ghp pr create ... is equivalent.

Cursor Cloud specific instructions

Services overview

ServiceHow to startPortNotes
MongoDB + Valkeypnpm dev:infra27017, 6379Docker required; must be running before pnpm dev
API + AdminJSMONGO_URL=mongodb://localhost:27017/ledgerline ADMIN_AUTH_USE_LOCAL=true pnpm dev3000Hot-reloads via tsx watch

Running the dev server

  1. Start infra: pnpm dev:infra (Docker must be running; use sudo dockerd & if needed)
  2. Start app: MONGO_URL=mongodb://localhost:27017/ledgerline ADMIN_AUTH_USE_LOCAL=true pnpm dev
  3. Health check: curl http://localhost:3000/health should return ok
  4. AdminJS panel: http://localhost:3000/admin (redirects to auth entrypoint)

Authentication in dev

  • NODE_ENV defaults to development, which enables local auth with username admin / password password.
  • Login via API: POST /api/auth/local/login with JSON {"username":"admin","password":"password","tenantId":"default"}. The response sets an HttpOnly signed cookie (ledgerline_auth).
  • Set ADMIN_AUTH_USE_LOCAL=true so the admin panel's auth entrypoint uses the local provider instead of Discord OAuth.

Important gotchas

  • NODE_ENV=production is set by default in the Cloud VM environment. You must unset NODE_ENV (or set it to development) before pnpm install, otherwise devDependencies are skipped and husky/tsx/vitest will not be available.
  • The pre-commit hook requires semgrep (installed via pip install semgrep). The binary is at ~/.local/bin/semgrep; ensure ~/.local/bin is on PATH.
  • pnpm dev:infra requires Docker. In the Cloud VM, Docker must be started with sudo 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:

  1. Always analyze the changelog before running tests — don't just wait for failures.
  2. Audit every usage of the upgraded package across the codebase.
  3. Use scripts/parse-renovate-pr.mjs to extract package names and version ranges from the PR.
  4. The CI workflow at .github/workflows/renovate-validate.yml handles auto-merge for passing PRs.
  5. For major version bumps, the skill includes codebase-specific guidance for common packages (Mongoose, Express, AdminJS, Zod, etc.).