Project conventions for any AI coding agent (Claude Code, Aider, Copilot Coding Agent, Cursor, future tools) operating on this repository. Cross-tool consistency surface; the standing rules in agent/memory/ remain authoritative.
CAIA is a private monorepo housing the multi-agent AI software-development platform. Hono microservices behind apps; reusable agent + utility code in packages/ under the @chiefaia/* scope. Strictly TypeScript, ESM, Node ≥20, pnpm workspaces. NEVER published to public npm — see "Option E shape" below.
Sites (pokerzeno, roulette-community, stolution, etc.) live in their own repos and historically consumed @chiefaia/* from npm; new agent packages stay private workspace packages per the 2026-05-06 standing rule.
Rule. Before writing any code — UI primitive, backend helper, type, or new dependency — you MUST search the workspace for an existing reusable package and either use it or extend it. Writing one-off inline code when a reusable package exists (or should exist) is institutional debt.
4-step check (run before the first line of code):
- UI primitives — does
@caia/uiexport the component you need? If yes, import. If no but it's a UI primitive: add it to@caia/uifirst (its own PR), then consume. Raw@radix-ui/*,@/components/ui/*, or inline-Tailwind component code outsidepackages/ui/**is a Semgrep ERROR (caia-no-raw-shadcn-import-outside-ui-package,caia-no-raw-radix-outside-ui-package). - Backend helpers — grep
packages/and the installed@chiefaia/*set. The canonical wrappers are:@chiefaia/http-client(no raw axios/fetch — Semgrep ERROR),@chiefaia/persistence-sqliteand@chiefaia/persistence-postgres(no rawbetter-sqlite3/pg.Pool— Semgrep ERROR),@caia/architect-kit,@caia/state-machine,@chiefaia/claude-spawner,@chiefaia/events,@chiefaia/critic,@chiefaia/ticket-template,@chiefaia/logger,@chiefaia/errors,@chiefaia/config,@chiefaia/metrics,@chiefaia/tracing. - Dependencies — before
pnpm add <thing>, check whether a workspace package already wraps it. If yes, add the workspace package instead. - New code — treat every helper, component, and type as a future-package candidate. Land it in a package, not inline in an app.
Mechanical enforcement (defence-in-depth — see caia-ea/decisions/ADR-065):
- L1 — this section. Doctrine.
- L2 — EA gate. Plans submitted through
@caia/reuse-check-gatemust include areuseSearchResultsfield naming the@caia/*/@chiefaia/*packages considered + why each was selected or rejected. Plans of typeimplementationwith emptyreuseSearchResultsare refused at the gate. - L3 — dispatch-time reuse search. Orchestrators call
searchReuseCandidates(brief)from@caia/reuse-searcher(also available as thecaia-reuse-search-mcptool) before spawning any code task. The ranked candidate list is injected into the spawned agent's system prompt. - L4 — CI gate.
reuse-advisory-blockingis a required status check ondevelop+main. It refuses PRs that add raw shadcn/Radix/Tailwind imports outsidepackages/ui/**, rawaxios/node-fetchoutsidepackages/http-client/**, rawbetter-sqlite3outsidepackages/persistence-*/**. The legacy non-blockingreuse-advisoryjob stays as a comment-only second opinion. - L5 — Semgrep. The rules in
.semgrep/caia-rules.ymlnamedcaia-no-raw-shadcn-import-outside-ui-package,caia-no-raw-radix-outside-ui-package,caia-no-inline-tailwind-in-customer-facing-app,caia-no-raw-axios-outside-http-client,caia-no-raw-better-sqlite3-outside-persistenceblock the same patterns at PR-diff time.
Consequence when violated. PR fails the reuse-advisory-blocking and semgrep required checks. The escape hatch is the reuse-advisory-escape label, which the EA-reviewer applies after recording the one-off rationale on the PR. Operator does NOT need to repeat the rule in agent context — the gates above carry it.
pnpm install # install workspace deps (pnpm@9, lockfile is committed)
pnpm build # turbo build across all workspaces
pnpm typecheck # turbo typecheck (tsc --noEmit per workspace)
pnpm test # turbo test (vitest in most packages, jest in some)
pnpm lint # turbo lint (eslint per workspace)
# Single-package targeting:
pnpm --filter @chiefaia/<pkg-name> build
pnpm --filter @chiefaia/<pkg-name> test
pnpm --filter @chiefaia/<pkg-name> typecheck
# Evidence Gate aggregates (run before opening a PR):
pnpm evidence:tsc # whole-repo typecheck
pnpm test:regression # regression suiteTest runners: vitest is the default for new packages; a few legacy ones still use jest (@caia-app/core regression suite). Playwright lives in packages/playwright-config and is consumed by E2E suites in apps.
- TypeScript
strict: true,exactOptionalPropertyTypes: true,noUncheckedIndexedAccess: true,noImplicitOverride: true. No relaxing these flags. - No
any. No@ts-ignore. Useunknown+ narrow, or fix the type. - ESM only (
"type": "module",module: "NodeNext"). Use.jsimport suffixes in source. - Functional patterns preferred. Functions <50 lines. Early returns. No nesting >4 levels.
- Comments explain WHY (constraint, invariant, workaround). Don't comment WHAT — names should carry it.
- File layout:
src/for code,tests/for tests,dist/for build output (gitignored).
develop is the canonical integration branch. main is a fast-forwarded mirror of develop.
- ALL new feature branches MUST be cut from
origin/develop(never fromorigin/main). - ALL new PRs MUST target
develop.gh pr createrequires--base developuntil the operator flips the GitHub UI default-branch setting (operator TODO #2). mainis updated only by the auto-sync workflow.github/workflows/sync-main.yml, which fast-forwardsmaintodevelop's tip on every push todevelop. Never push directly tomain.- The old "release/ PR to main → merge → tag" cycle is retired. No more periodic develop→main back-merges; the auto-sync workflow makes them redundant.
- Background and full rationale: standing rule
~/Documents/projects/agent-memory/standing_rule_develop_canonical_2026-05-25.md; one-time reconcile auditreports/main_develop_reconcile_audit_2026-05-25.md.
feature/<id>-<slug> → PR to develop → squash-merge → branch deleted
develop → auto-sync workflow fast-forwards main → no manual release PRs
main ← auto-sync only; never manually pushed
backup/<reason> ← preservation only, never merged
- NEVER push directly to
mainordevelop. Husky hooks + branch protection + the requiredgitflow-conformanceCI check block this. - One PR per logical unit. Open the PR as soon as you have a first commit; don't accumulate work on a branch without a PR.
- "Done" = merged into
develop(the auto-sync workflow handles main mirroring), branch + worktree gone. - Quickstart wrapper:
pnpm flow start <id>-<slug>/pnpm flow ready/pnpm flow ship. Thepnpm flow releasestep is no longer required (auto-sync replaces it). Full runbook:docs/git-flow.md.
Six required CI contexts must be green before a PR can merge:
Build·Test·Lint·Typecheck— turbo aggregategitflow-conformance— no unwanted merge commits, correct base branchtypecheck— whole-repo strict tscsemgrep—.semgrep/caia-rules.yml+ auto rule setgitleaks— secret scanbundle-size— size-limit per package
Three warn-only: lighthouse, axe, visual baselines.
- NEVER hard-code secrets. Resolve at runtime via
scripts/get-vault-secret.shor the orchestrator's vault adapter. - NEVER paste raw vault key names alongside literal values in fixtures (semgrep rule
caia-no-hardcoded-vault-keyswill reject the PR). - Irreversible operations (gh push, deploy, delete, billing) MUST go through
@chiefaia/capability-broker. Never call them directly from agent code. - MCP server invocations MUST go through
@chiefaia/mcp-allowlist-proxy. The allowlist lives in the proxy's config. - Tool output that re-enters an LLM prompt MUST go through
@chiefaia/tool-output-sanitizer(defends against prompt injection). - LLM spend MUST go through
@chiefaia/spend-guard. Subscription bucket only — seefeedback_no_api_key_billing.md. - The
feedback_pat_topic.mdrule is settled: plaintext tokens in operational locations (~/.bashrc, .env, plist, docker config) are intentional post-rotation copies; do NOT re-flag them as findings.
Every CAIA agent built from this point forward follows the CAIA-Bonded Skeleton shape. See agent/memory/agent_architecture_shape_2026-05-06.md.
The five mechanical gates (Evidence Gate semgrep enforces them):
- Private package:
package.jsonhas"private": trueand scope@chiefaia/<name>. Never published to public npm. - Parameterised public API: every CAIA-specific path/topic/registry/integration is a constructor parameter with a CAIA default. NO hard-coded literals like
~/Documents/projects/caia/agent/memory. UsecorpusRoot: string = '~/Documents/projects/caia/agent/memory'instead. - Fixture-corpus tests: tests inject fake corpora; production injects CAIA defaults. If a test cannot be written without live CAIA paths, parameterisation is broken.
- Pre-spawn injection consumed: agent reads task prompts AFTER Mentor + Librarian retrieval has prepended relevant lessons + precedent. Don't roll your own context layer.
- No second-customer abstraction: configuration matrix is exactly one (CAIA). NO config files. NO multi-tenant API. NO OSS-style docs for unknown contributors.
Open-sourcing is NOT a priority (operator-explicit 2026-05-06). Configuration matrix stays one. Re-evaluation triggers documented in the standing-rule memory file.
The pipeline ships with deterministic detection → idempotent rollback → bounded retry → escalation. When extending it, mirror this shape.
| Area | Mechanism | Reference |
|---|---|---|
| Failure recovery | WorkerCrashRecovery rolls back assignedWorkerId/codingSessionId/phase2Status in a transaction; escalates after maxCodingAttempts (default 3) |
PR #159 |
| Cost tracking | pipeline_run_costs + PipelineCostTracker; alert when single run breaches CAIA_PIPELINE_COST_ALERT_USD |
PR #162 |
| Resource cleanup | WorktreeReaper 5-min sweep of orphan worktrees; opt-in via CAIA_WORKTREE_REAPER_ENABLED=1 |
PR #166 |
| LLM resilience | @chiefaia/local-llm-router wraps dispatch in breaker.exec(withRetry(withTimeout(...))); 60s timeout, 3 attempts |
PR #169 |
| Observability | /api/pipelines/:promptId/trace returns prompt + stages + events + summary |
PR #171 |
| Logging | @chiefaia/logger ships DEFAULT_REDACT_PATHS (32 patterns) |
PR #175 |
Worktree cap = 8 concurrent. Going above triggers a Steward alarm. See feedback_operational_discipline.md.
-
Drizzle multi-statement migrations need explicit
--> statement-breakpointmarkers. A migration that bundlesALTER TABLE+CREATE INDEXin one SQL string will execute as a single statement and fail mid-way without rollback. Always split with breakpoints. Bit us in PR #287/#285. -
NEVER use
gh pr update-branch. It creates merge commits that violategitflow-conformance. To bring a feature branch up to date, rebase locally ondevelop(or usegit merge --ff-only developfrom local, then push). The CAIA flow assumes linear feature-branch history. -
NEVER use
gh pr merge --admin— with one carve-out below. By default,--adminbypasses required status checks; semgrep rulecaia-no-admin-mergeblocks it.TRUE-ZERO EXCEPTION (build phase only). During the CAIA build phase, the operator's True-Zero standing rule (
~/Documents/projects/agent-memory/standing_rule_true_zero_before_new_pr_2026-05-13.md) permits admin-squash-merge when, and only when, ALL of:- The build-phase marker file
.caia/build-phase-activeexists at the repo root (orchestrator-maintained; absence ⇒ exception does not apply). - The standard
pnpm flow shippath is unavailable because either (a) the PAT in scope cannot bypass required status checks at org-admin level, or (b) pre-existing dev-broken checks unrelated to the PR's diff are wedging the merge. - Evidence Gate locally passed on the PR's own contents (
pnpm typecheck && pnpm test && pnpm lint && semgrep ci --config=auto --config=.semgrep/caia-rules.yml) —--adminis a CI-bypass, not a correctness-bypass.
Procedure (same pattern as PRs #574 / #575 / #580):
# 1. Temporarily flip enforce_admins=false on develop's branch protection gh api -X PUT "/repos/prakashgbid/caia/branches/develop/protection/enforce_admins" -f enabled=false # 2. Admin-squash-merge the PR gh pr merge <PR> --squash --admin --delete-branch # TRUE-ZERO-MERGE # 3. Immediately restore enforce_admins=true (DO NOT skip — leaving it off is a security regression) gh api -X PUT "/repos/prakashgbid/caia/branches/develop/protection/enforce_admins" -f enabled=true
Commit-message tag. The squash-merge commit MUST include
[True-Zero admin-merge]in the title or trailer so the audit trail is greppable. Example title:chore(governance): document True-Zero admin-merge exception [True-Zero admin-merge] (#NNN)Outside the build phase (when
.caia/build-phase-activeis absent), this exception does not apply and--adminis forbidden. The marker file is removed by the orchestrator at build-phase exit. - The build-phase marker file
-
NEVER
git push --forceoutsidebackup/*. Semgrep rulecaia-no-force-push-non-backupblocks this. -
Custom semgrep rules live in
.semgrep/caia-rules.yml. Run them locally withsemgrep ci --config=auto --config=.semgrep/caia-rules.yml. The Evidence Gate runs the same set. -
required_linear_historyis OFF ondevelop(since 2026-05-03) to allow classic back-merges from release branches.gitflow-conformancestill rejects unwanted merge commits inside feature PRs. -
Memory directory is operator-controlled and lives OUTSIDE the repo at
~/Library/Application Support/Claude/local-agent-mode-sessions/<session>/agent/memory/. Some legacy paths referencecaia/agent/memory— those are stale. New code parameterisescorpusRootand reads it from constructor args. -
Sites (pokerzeno, stolution, etc.) live in their own repos. Do not vendor them in. Do not edit them from this repo.
-
Apps in
apps/*are runtime services consuming agent packages. Don't put reusable agent logic there — push it intopackages/<agent-name>/. -
Templates in
templates/site/andtemplates/utility/contain{{PLACEHOLDER}}syntax. They are NOT workspace members; they are scaffolds. Don'ttscthem directly.
- Read first. Check
agent/memory/MEMORY.mdfor relevant standing rules. Checkdocs/for the affected area's runbook (e.g.docs/git-flow.md,docs/evidence-gate.md,docs/capability-broker.md). - Cut a feature branch.
pnpm flow start <id>-<slug>from a cleandevelop. - Work in small commits with conventional-commit messages (
feat(scope): subject/fix(scope): subject/ etc.). - Run the full local check before opening the PR.
pnpm typecheck && pnpm test && pnpm lint && semgrep ci --config=auto --config=.semgrep/caia-rules.yml. - Open the PR to
develop.pnpm flow ready— opens a draft PR if not yet ready, marks ready when CI is green. - Wait for Evidence Gate. All six required contexts must be green. If a context fails, fix root cause; never bypass with
--admin. - Squash-merge.
pnpm flow ship— merges + deletes branch. - End-of-day release.
pnpm flow release --auto— opens release PRdevelop → main, merges when CI green. - Confirm "Done" per
feedback_definition_of_done.md— branch merged into BOTH develop AND main, branch + worktree gone, regression suite green.
agent/memory/MEMORY.md— standing rules index (always-loaded)agent/memory/agent_architecture_shape_2026-05-06.md— Option E standing ruleagent/memory/feedback_git_flow_enforced.md— git flow ruleagent/memory/evidence_gate_2026-04-29.md— evidence gate ruleagent/memory/feedback_definition_of_done.md— DoD checklistdocs/git-flow.md— operator runbook for git flowdocs/evidence-gate.md— evidence-gate runbookdocs/capability-broker.md— irreversible-action gatingdocs/mcp-security.md— MCP allowlist runbookdocs/prompt-injection-defense.md— sanitizer runbookdocs/spend-guard.md— LLM spend gating.semgrep/caia-rules.yml— custom semgrep rules