Guidance for Claude Code when working in this repository.
When asserting something about this codebase, cite the file and line you read it from. If you have not verified something, say so rather than inferring it.
packages/web/— React + TypeScript frontend. Conventions in.claude/rules/frontend.md. That rule loads automatically when you read a frontend file; read it explicitly before writing a new component from scratch.service/— Django REST API + PostgreSQL. Conventions in.claude/rules/backend/(hub:index.md).packages/design-playground/— prototyping app with its ownCLAUDE.mdthat overrides this one.
docker compose up # frontend :5173, API :8000, DB :5432.claude/hooks/session-start.sh provisions everything (venv, PostgreSQL, npm install, Railway CLI) and is idempotent.
- Always use
service/.venv/bin/python— systempython3is 3.11, Django 6 requires 3.12+. The hook prepends the venv toPATH. - SQLite is not viable — some migrations use Postgres-only SQL. Use the native cluster.
DJANGO_DEBUG=Truealone is sufficient to start the service against the local DB.- Without credentials, Firebase push (
FIREBASE_PROJECT_ID) and Google OAuth (GOOGLE_CLIENT_ID) are disabled. railwaycommands and/prod-queryrequire Railway hosts allowlisted. In Claude Code on the web: set Network access to Custom, add*.railway.comand*.railway.app, and tick "Also include default list of common package managers".
docker compose up codegen # regenerates all four generated artefactsTwo OpenAPI schemas are produced from the same serializers, because the API has two wire representations:
| Artefact | Casing | Consumer |
|---|---|---|
service/openapi-schema.yaml |
camelCase | HTTP clients — drives src/api/generated/ via orval |
service/openapi-schema.internal.yaml |
snake_case | in-process clients — drives service/harness/generated/api.py |
camelCase is applied by CamelCaseJSONRenderer at render time, so response.data (what agent/api_client.py reads via DRF's APIClient) is snake_case. The internal schema is generated by passing --custom-settings project.openapi.INTERNAL_SCHEMA_SETTINGS, which drops the camelize postprocessing hook for that invocation only.
Never hand-edit service/harness/generated/api.py or packages/web/src/api/generated/ — both are regenerated.
After codegen, run npx tsc -b --noEmit in packages/web. See .claude/rules/backend/codegen.md for the environment config required to reproduce committed output byte-for-byte.
python -m pytest <file> -v # single file — preferred while iterating
python -m pytest -n auto --reuse-db # full suitepython manage.py run_evals --limit 1 # quick smoke runPermission policy: running a single epoch (the default — one pass over the dataset) is fine without asking. Do not run multiple epochs unless the user explicitly asks — that multiplies model calls and cost. For multiple epochs, invoke inspect_ai.eval(..., epochs=N) directly; the command exposes no --epochs flag.
dump_phase turns a live game phase into eval raw material:
python manage.py dump_phase --game <id> --out phase_dumps
cd packages/web
npx vite-node scripts/render-phase.mjs ../../service/phase_dumps/<prefix>_render.json /tmp/board.pngpackages/design-playground/ prototypes screens before we build them, deploys independently, and ships to no users. Its own CLAUDE.md overrides this file — read it before working there. Two rules bind from this side:
packages/webmust never import from the playground. The dependency direction is one-way; prototype code is rewritten into the app, not promoted out of it.- Design gallery screens belong in the playground, not the app. Do not add fixture-driven component galleries to
packages/web.
- Follow existing patterns. New code should be indistinguishable from existing code in style and structure. Raise deviations as a discussion; do not silently deviate.
- Do not add comments or docstrings, including in tests. Do not annotate assertions to explain their values. The only exception is DRF view docstrings, which are extracted for OpenAPI.
- Never suppress lint or type violations — no
eslint-disable,@ts-ignore,# noqa,pytest.mark.skip. The only exception is the mutation-in-useEffectpattern documented in.claude/rules/frontend.md. - Write tests alongside features, not as an afterthought.
- Absent beats dormant scaffolding. Do not leave unused domain, apps, or wire fields “for later” — remove them and re-add when the feature is real. Backend detail in
.claude/rules/backend/.
Soft WIP limits: 5 open PRs, 10 open issues. A bot warns when exceeded — check the count before opening a new PR.
Issues have three sections (enforced by the create-issue skill): Goal (always), Context (when useful), Approach (when discussed). No acceptance criteria or implementation checklists.
PR screenshots are a completion criterion, not optional polish. If a PR changes anything visible in the web app — new screens, layout, styling, copy, empty or error states — you MUST embed screenshots of the changed component in the PR description, so the reviewer sees what changed without pulling the branch. Use the screenshots skill for the workflow.
Never commit screenshots. Write them outside the working tree (/tmp/shots/) and upload them as GitHub attachments — do not add image files to the repo or reference them via raw.githubusercontent.com. screenshots/ and shots/ are gitignored; if you find committed screenshots, remove them.
When you establish a convention or make an architectural decision, update this file or the relevant file in .claude/rules/ in the same session — do not park lasting guidance only in a walkthrough ledger.
Guidance states rules, never inventories. Do not write out a list or table of what exists in the codebase — classes, fixtures, endpoints, components, settings. Claude reads the code; a copy in a guidance document only goes stale and is wrong the moment someone adds a class without updating it. Say which file to read instead. Illustrative examples of a pattern are fine; an enumeration meant to be exhaustive is not.