> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hr-easy.nlead.ch/llms.txt
> Use this file to discover all available pages before exploring further.

# Contributing

> Branching, commit messages, the self-review checklist, and the automated review loop.

## Branching

The integration branch is **`develop`**, 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.

| Rule                                                                     |
| ------------------------------------------------------------------------ |
| Every feature, fix and refactor PR targets **`base: develop`**.          |
| Never merge or push to `main` directly.                                  |
| Never re-target an open PR from `develop` to `main` without instruction. |

Verify before opening a PR:

```bash theme={null}
git log --oneline origin/main..origin/develop | head -3
```

`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:

| Prefix              | For                     |
| ------------------- | ----------------------- |
| `feat(module):`     | New feature             |
| `fix(module):`      | Bug fix                 |
| `refactor(module):` | Restructuring           |
| `test(module):`     | Adding or fixing tests  |
| `docs:`             | Documentation only      |
| `chore:`            | Build, CI, dependencies |
| `security(module):` | Security fix            |

## Working attitude

Ship work that would clear the bar of a careful senior reviewer.

<AccordionGroup>
  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

## 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.
* `timingSafeEqual` for token comparison.
* Security fixes carry the attack, `401` and `403` tests.

**Sibling-path completeness** — the most repeated finding

* `grep` for every sibling call site of what you changed and fix each.
* Pin structural twins with a same-shape test.

**Code quality**

* No `console.*` in `src/`; 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.

**Database**

* Schema change has exactly one matching migration; no duplicate.
* Conditional `$transaction` uses the interactive callback form.
* Every `JSON.parse()` is wrapped.

**Tests and i18n** — as above.

## The PR test plan

The `## Test plan` section lists **only the checks you ran locally**, with their
results.

<Warning>
  Do not list `CI Quality Gate` or `Bot review on this PR` as test-plan items. Both run
  automatically and are reported elsewhere; an unchecked checkbox for them is noise. The test plan
  ends at the last local check.
</Warning>

## 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 in `docs/`, 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](/operations/docs-site) for how to run and deploy it.
