Repo-specific guidance for Claude Code sessions goes here, above the managed org block: build/test commands, architecture notes, gotchas, and this repo's default reviewer. Rollout: tracebloc/backend#1602.
- Branch model, for a repo on the release train:
develop → staging → main. Branch offdevelop; every PR targetsdevelop. Never open PRs tostagingormain— promotions are the train's job. - For a repo not on the train, do not infer the branch model from this file — read
repo-inventory.yml.release_train:says whether the model above applies at all, and the per-branchexempt:anchors record which branches actually exist. This bullet used to enumerate the exceptions by name and drifted from the inventory on every one of them:docswas calledmain-only while it had been on the train since 2026-08-04 (release_train: true,develop: required, staging present), andrfcswas calledmain-only while it had adeveloptaking merges (measured 2026-08-22, backend#2242 / .github#306). Restating the authority is the defect; pointing at it is the fix. - Trap, recorded in the inventory and caught by no check: a
developcreated on a non-train repo and left unprotected is invisible to the guards — that is thedevelop_unprotected_non_trainanchor, and the inventory notes "adevelopcreated and left UNPROTECTED is not flagged … no check was going to surface it." So creating one to satisfy the first bullet forks the repo silently: PRs split between the new branch and the repo's existing convention, nothing promotes between them, and the two heads diverge until someone reconciles by hand. If a repo appears to lack adevelop, that is a fact to verify in the inventory, not a gap to fill. - Before starting any task:
git fetchand branch from the current tip ofdevelop— never build on a stale checkout. A branch that lives more than a day getsdevelopmerged back in before review. We move fast; stale starts mean silent divergence and duplicated work. - One self-contained change per PR. A few hundred changed lines reviews well; at 1000+ split it. Refactors ship in separate PRs from behavior changes.
- Branches are short-lived (aim to merge within a day or two), single-author, and based on
develop— no stacked PRs on top of other open PRs. - Your branches are yours to clean up. Merged ones now delete themselves server-side, so this is about the rest: run
git reap(fromtracebloc/.github/scripts/git-reap) in your checkouts now and then. It is dry-run by default and only proposes a branch when it can prove the work landed. Nobody else can do this for you — you are the only one who knows whether an unmerged branch of yours still matters, andgit branch --mergedwill not tell you, because we squash-merge and a squashed branch is not an ancestor ofdevelop. - "Yours" is the branch you opened the PR for, never the branch whose last commit is yours. Pushing a review fixup onto someone else's branch makes you its tip-commit author and changes nothing about whose work it is — so a "my branches" list built from
%(authorname), or from the tip author in any form, aims your cleanup at other people's work. Measured: two of Shujaat'sclientbranches showed up on such a list and were one confirmation step away from--delete(backend#2365). If you are building any list that reasons about ownership, calltracebloc/.github/scripts/branch_owner.pyrather than re-deriving it; a branch it cannot attribute comes back asunattributable, which is the answer to act on, not to fill in. - Names and commits:
feat/ fix/ docs/ sec/ ci/ chore/+ issue number + short slug (fix/1234-ingest-timeout); commit subjectstype(scope): summary, referencing the ticket (backend#1234). - When you open a PR: assign yourself and request exactly one reviewer immediately — a PR without a reviewer stalls by construction. You pick the reviewer: whoever knows the code best. There is no per-repo default, and no automation assigns one — branch protection just refuses to merge without a review.
- When you are the reviewer: first response within one business day.
- Before every push: run the linter and the tests that cover your change. Never push a branch you believe is red — CI is the backstop, not the first run.
- Read the full diff before opening the PR. You own every line you ship, whoever — or whatever — wrote it.
- AI sessions end with evidence, not assertion: run the relevant check (tests, build, lint) and show the output. A change that could not be verified does not ship.
- Fix the class, not the instance. The bug you just fixed is a member of a class; check the rest of the class before you push. Two shapes, and aiming at only the first catches half of them: other call sites — grep the symbol or pattern you changed — and other inputs to the same guard — what else reaches this branch? If the class can't be cheaply enumerated, say so in the PR rather than leaving it implied that you covered it.
- After opening or pushing to a PR, stay on it: poll CI and Bugbot on the current head and triage every finding the same day — fix it, or reply on the thread saying why not. No silent dismissals. Unresolved threads block the merge and stall the release train's settle stage; cheap now beats expensive later.
- A finding that recurs across PRs becomes a rule: add it to
.cursor/BUGBOT.md, and if it is grep-expressible, to code-quality's house-rules — then stop re-arguing it in comments. - Style and naming rules live in tooling (black/ruff, eslint/prettier, house-rules), never in prose. If a rule matters, encode it; do not restate linter rules in CLAUDE.md files.
- Never commit secrets, tokens, or customer data — not in code, config, tests, issues, or commit messages. gitleaks catches secrets in code. Nothing scans PR titles, descriptions or commit messages: the public PII gate that did was retired on 2026-08-06 (backend#1409), so keeping customer names out of PR prose on public repos is on you, not on a check.
- Every ticket on the board carries a
Status— no card sits at "No Status". New tickets start inBacklog. Bugs are the exception: label themwork-type:bug(the Bug template does it) and automation moves the card straight intoReady— defects don't wait for refinement. Three repos aren't wired for the label trigger yet (.github,release-train,rfcs); move the card yourself there. - Picking up work: the team coordinates.
Readyis the refined queue — bugs excepted, per the line above — and the first choice when it's stocked; pulling fromBacklogis normal when refinement hasn't caught up — say what you're taking. - Merging to
developmoves the card toOn devautomatically; there is no dev-side review. - Functional review happens once, on staging: when it passes, comment
/fr-passon the PR or drag the card toReady for prod. Self-signoff is allowed. fr-gateis a required check on promotions. If it blocks, the board or the work isn't ready — fix that.skip-fr-gateis audited, for emergencies only.
- The release train is the only path to
staging,main, and every package registry. Never hand-cut av*tag, hand-bump a version file, or publish an artifact — every legal publish path is inventoried in release-train'sPUBLISH-PATHS.md. - Findings on a promotion PR are fixed on the source branch (
develop/staging), then the train re-prepares. Never push fixes onto a promotion PR — every push re-rolls its review.
- Internal work — planning, epics, security findings, infrastructure, anything mentioning a customer — is filed in
backend(the private catch-all), never in a public repo. When in doubt:backend. - Public repos (
cli,client,docs,data-ingestors,model-zoo,start-training,.github) only get issues a stranger could act on: about the public artifact itself, with no customer names, internal URLs, or internal paths.
- An AI session may open PRs and push its own branches. It never: merges a PR, closes another person's PR, deletes another person's branch, or force-pushes — each of those needs an explicit instruction from the human running it.
- If your change makes a statement in any CLAUDE.md, BUGBOT.md, or runbook false, update that file in the same PR.