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/org-config/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/org-config/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).(scope)is the component —mint-scope,kanban— never the ticket number. A number in a PR title (sec(2157): …) is read byclosing-refas a reference the body must make good, in one of two forms:Closes <owner>/<repo>#Nwhen this PR really finishes the ticket, orPart of <owner>/<repo>#Nwhen it does not. Both satisfy the check; onlyClosescloses the ticket and moves its card, so never write it for partial work — and a bareCloses #Nresolves against the repo you are in, which for atracebloc/backendticket links the wrong issue. Keeping the number in the title is right either way: naming the parent is traceability, not a promise to close it (backend#2616). - 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. This holds in every repo, and the exception that used to be written here is gone rather than kept accurate by hand: the labels exist fleet-wide, andtriage-labels.ymlasserts daily that every label the templates apply and the caller fires on exists in every repo declaring that caller. A hand-written exception list drifts on every entry — it named two repos while the inventory said three — so the fix was to empty it (backend#2598). - 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 or publish an artifact — every legal publish path is inventoried in release-train'sPUBLISH-PATHS.md. (Thehand-bump a version fileclause was removed on 2026-09-08: it contradicted a REQUIRED check, and the meta-rule above says an enforced rule leaves this file.version-bump-gate / version-checkfails a PR that touches a published path while the version file still reads an already-released version — "Bump package.json in this PR. The release train reads that file and cuts the tag from it — it never bumps for you." So the bump a feature PR ships is the train's INPUT, not a bypass of it. Read literally, the old clause forbade what the gate demands: it blocked two component PRs on design-system-v2 until someone put the two side by side, and a reviewer there opened and then retracted a change-request over the same collision.) - 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.