# Working in this repository For anyone opening a pull request against crowdsec: human, or LLM. `CLAUDE.md` is a symlink to this file. PRs that ignore the rules below are unlikely to be reviewed. ## Keep the change small - One concern per PR. A bug fix and a refactor are two PRs. - No drive-by reformatting, renaming, or restructuring. - Don't bump dependencies. Dependabot owns `go.mod` and `go.sum`. - If the diff is over ~400 lines, either split it, or say in the first line of the PR body why it can't be split. ## A human must have tested and reviewed this Before you open the PR, a human must have: - read the diff line by line, and - run the change, or supervised and independently verified the testing an agent did. Say which, in the PR body. ### Automated checks - While iterating: `go test ./pkg//...` - Full suite: `make test`. Needs `gotestsum` installed, and `make localstack` running in another shell, or set `TEST_LOCAL_ONLY=1` to skip the tests that need containers. - If you touched `pkg/exprhelpers`, use `make test`. - Lint while iterating: `golangci-lint run ./pkg//...` - Before pushing: `make lint`. It lints linux, windows and freebsd, same as CI — so expect findings outside your change if it's platform-specific. - Use golangci-lint v2.13, the version CI pins. Other versions report different things. - Don't restructure working code to satisfy a linter that's switched off. Check the `disable:` list in `.golangci.yml` before "fixing" something it never complained about. ### Running it for real This is how you can spin a simple test instance: ``` make build # crowdsec, cscli and the notification plugins scripts/test_env.sh -d ./tests # working config tree in ./tests cd ./tests $EDITOR config/acquis.yaml # point it at a log file you can write to ./crowdsec -c dev.yaml ``` `scripts/test_env.sh` copies the binaries out of the source tree, so `make build` has to run first. Look up the crowdSec-skill to deploy, configure and troubleshoot CrowdSec instances: https://www.skills.sh/crowdsecurity/crowdsec-skill/crowdsec ## Tests - New behaviour needs a test. - A test must fail when the behaviour it covers breaks. No tautological tests: don't check that a mock returns what you told it to, that a setter sets, or that the code does what it says. More test lines is not better; a large suite that misses the obvious case is worse than a small one. - Test the cases the change exists for, not just the happy path. Don't skip the hard one with a comment: a skipped test for the scenario that matters is a missing test. - Refactoring or replacing an existing component: first cover its current user-visible behaviour with tests, and make them pass unchanged before and after. List every intended behaviour change in the PR body. - `require` over `assert` — a failed `assert` keeps going and buries the real error. - Table-driven, with `t.Run` subtests. - Unit tests live next to the code. Functional tests are BATS under `test/bats/`; see `test/README.md`. ## Be concise Humans read your output, keep verbosity to minimum. **PR and issue bodies** — what changed, why, and how it was tested. Nothing else. No prose restatement of the diff. Any issue or PR text should be under 5k characters. Instead of: > ## Summary > This PR introduces a comprehensive enhancement to the syslog acquisition module... > ## Motivation > Currently, the existing implementation suffers from a limitation whereby... > ## Implementation Details > - ✅ Added a new `timeout` field to the configuration struct > - ✅ Updated the parsing logic to respect the new field > ## Testing Strategy > Comprehensive unit tests have been added to ensure correctness... Write: > syslog acquisition hung forever when the remote stopped sending. Adds a configurable > `timeout` (default 30s) and closes the connection when it expires. > > Tested: `go test ./pkg/acquisition/modules/syslog/...`, plus a local run against a syslog > source that goes silent — connection now drops after 30s instead of hanging. **Code comments** — explain why, not what. If the comment restates the line under it, delete it. Keep them short and to the point, not prose: a comment should rarely be longer than the code it describes. A 20-line comment over a 5-line function is noise. **Review replies** — answer the question that was asked. Don't re-explain the PR, and don't paste your agent's output into the thread. Read it, then reply in your own words, in a few lines. ## Don't break these Each one is a one-way door — users are already depending on it. - **LAPI/CAPI payloads** — change `pkg/models/localapi_swagger.yaml` or `pkg/modelscapi/centralapi_swagger.yaml` and regenerate. Never edit the generated Go by hand. - **Database schema** — your migration lands on existing user databases. - **Config keys** in `pkg/csconfig` — renaming or removing one breaks upgrades. - **`cscli -o json` and `-o raw`** — bouncers and scripts parse these. `-o human` is the only output you can freely change. If you have to break one of these, say so explicitly at the top of the PR body. ## Commits and PRs - Base branch is `master`. - Commit titles are loose: `area: what changed` or `type(scope): what changed`, both fine. - Never write `(#1234)` in a commit title. Squash-merge adds it. Yours will be wrong. - One `kind/*` label and one `area/*` label.