Skip to main content

Contributing

Conventions

  • Use Conventional Commits (enforced by commitlint).
  • Keep changes small and scoped to a single capability.
  • Maintain tenant scoping on every data access.
  • Keep modules and symbols aligned to single responsibility.
  • Keep code DRY; extract repeated logic into focused helpers (typically src/utils/*).
  • Prefer Zod schemas in src/data/*/*.validators.ts for inputs.
  • Ports live in src/data/*/*.repository.ts and contain no framework imports.
  • Adapters live in src/data/*/*.repository.<impl>.ts and may use mongoose/express.
  • Treat src/init/*.init.ts as composition roots and keep src/server.ts launcher-only.
  • Add JSDoc for exported/shared classes, functions, and methods.
  • Add explicit inline comments for non-obvious logic paths.

Tooling

  • Format on demand: pnpm format
  • Lint: pnpm lint
  • Typecheck: pnpm typecheck
  • Cycle detection: pnpm check:cycles (no circular deps under src/; run before pushing).
  • E2E: pnpm run e2e
  • Pre-commit: doc automation runs first (pnpm docs:pre-commit-update), then lint-staged, typecheck, test, contract coverage, semgrep, and e2e.

Recommended local order: pnpm typecheckpnpm lintpnpm testpnpm run e2epnpm check:cycles.

Pre-commit documentation automation

When you commit changes to source files, the pre-commit hook may require documentation updates. The hook invokes scripts/docs/doc-update-cmd.sh which runs Claude headlessly to update the affected docs.

Required environment variable

VariablePurpose
ANTHROPIC_API_KEYAnthropic API key used by the claude CLI to update docs. Get one at console.anthropic.com.

Export it in your shell profile (~/.zshenv, ~/.bashrc, etc.):

export ANTHROPIC_API_KEY=sk-ant-...

Install the claude CLI

npm i -g @anthropic-ai/claude-code

Verify it works:

claude --version

Optional environment variables

VariableDefaultPurpose
CLAUDE_BINclaudePath to the claude binary if it is not on PATH.
MAX_PARALLEL_DOC_AGENTS4Maximum number of doc-type agents to run concurrently.
DOC_UPDATE_LOG_DIRscripts/docs/.doc-update-logs/Directory where per-doc-type agent logs are written.

Verified happy path

With ANTHROPIC_API_KEY exported and claude on PATH, a normal commit proceeds without any bypass:

# One-time setup (already done if ANTHROPIC_API_KEY is in your shell profile)
export ANTHROPIC_API_KEY=sk-ant-...
export DOC_UPDATE_CMD="bash scripts/docs/doc-update-cmd.sh"

# Stage your changes and commit normally — no --no-verify needed
git add src/...
git commit -m "feat(order): add new pricing logic"

What happens during the commit:

  1. scripts/docs/pre-commit-doc-update.mjs examines the staged diff against scripts/docs/documentation-types.rules.json.
  2. For each affected doc type (e.g. service-guide, api), doc-update-cmd.sh spawns a Claude agent (up to MAX_PARALLEL_DOC_AGENTS in parallel) with a focused prompt.
  3. Each agent writes docs to docs/ and the hook stages them automatically.
  4. If all agents succeed the hook exits 0 and the commit proceeds.
  5. Agent logs land in scripts/docs/.doc-update-logs/<doctype>.log for post-commit inspection.

If the hook exits non-zero, check the log for the failing doc type. Common causes: API key invalid, claude CLI version mismatch, or a doc type whose docPaths directory does not exist yet.

Emergency bypass

If you need to commit without doc automation (e.g., ANTHROPIC_API_KEY is temporarily unavailable):

export DOC_UPDATE_CMD='echo skipped'
git commit ...
unset DOC_UPDATE_CMD

This bypasses the AI doc update entirely. Use only in emergencies — the docs will be stale until you update them manually or via a follow-up commit.

Release process

  • Canonical release source is conventional commits on main.
  • semantic-release computes semver, updates docs/releases/CHANGELOG.md, tags, and publishes GitHub Releases.
  • Validate release impact without publishing: pnpm release:dry.

Reviews

  • New/changed behavior must include at least unit tests.
  • Tests are co-located with source (*.spec.ts / *.integration.spec.ts).
  • Bug fixes include a test that reproduces the issue.
  • No cross-layer imports.
  • No public API changes without updating docs/tests.