Branching
The integration branch isdevelop, which is also the repository’s default branch.
main is the release branch — only develop → main integration PRs land there, and a
human performs that step.
Verify before opening a PR:
develop being ahead of main is the expected state.
Because develop is the default branch, scheduled workflows run the develop copy of
their files. For the same reason, the PR watchdog never auto-merges a PR that touches
.github/workflows/ — a human clicks merge on those.
Commit messages
Conventional commits:Working attitude
Ship work that would clear the bar of a careful senior reviewer.Refactors preserve behaviour
Refactors preserve behaviour
When you split a file, every input, mutation, side effect and accessibility hook that existed
before must exist after — even the ones that were already broken. If you find a pre-existing bug
while refactoring, fix it in the same PR and say so in the description. Never silently
propagate it.
Read your own diff like a hostile reviewer
Read your own diff like a hostile reviewer
Walk every JSX block, form schema and payload serialiser side by side with the pre-change
version. For every field in the schema ask: is it rendered? is it serialised? is it seeded?
Missing one is the silent-data-loss bug.
Every fix gets a regression test
Every fix gets a regression test
A fix without a test is a fix that reverts on the next refactor. Keep the test on the failure
mode, not the implementation.
Round-N review findings mean round N−1 was incomplete
Round-N review findings mean round N−1 was incomplete
Treat each new finding as a signal that the original work missed a checkpoint, not as a fresh
task.
Self-review checklist
Before opening or updating a PR, walk your own diff: Security- Auth first, then permission, in every handler.
- Ownership verified on every HTTP verb in the file.
- No unvalidated redirect target; no token in a
Location:header or query string. - No interpolation into GraphQL, shell commands or raw SQL.
timingSafeEqualfor token comparison.- Security fixes carry the attack,
401and403tests.
grepfor every sibling call site of what you changed and fix each.- Pin structural twins with a same-shape test.
- No
console.*insrc/; no empty catch blocks. - No
any,@ts-ignore, or silencing casts. - All user-facing strings in de + en + fr.
- No magic numbers, dead code, duplicate JSDoc or stale comments.
- Functions under 80 lines.
- Schema change has exactly one matching migration; no duplicate.
- Conditional
$transactionuses the interactive callback form. - Every
JSON.parse()is wrapped.
The PR test plan
The## Test plan section lists only the checks you ran locally, with their
results.
The automated review loop
After every push to an open PR, a bot review posts a severity table (🔴 Must Fix / 🟡 Should Fix / 🟢 Nit). When the summary reports non-zero 🔴 or 🟡, an auto-fix job rebases, applies fixes, runs the full gate, and pushes — which re-triggers the review. The loop is capped at three rounds; when the cap is hit it posts a stuck summary and a human takes over. Integration PRs (develop → main) are excluded — every commit in them was already
reviewed on its feature PR.
What you still own
- Round-1 quality. The auto-fix loop is a safety net for what the checklist missed, not a substitute for it.
- 🟢 Nits get fixed too. “Non-blocking” describes the merge gate, not your obligation. The only acceptable way to skip a nit is a reply explaining why it is factually wrong or would make the code worse.
- Stuck PRs. A round-3 finding usually signals an architectural call the auto-fixer cannot make. Fix it by hand and the loop resumes, or escalate.
- Tests for every fix, including the ones the auto-fixer wrote.
Documentation
Documentation lives indocs/, and this site is docs/site/. Update the relevant
operations playbook in the same PR as any production change that affects operations.
See The docs site for how to run and deploy it.