# Repository rules - Use Go 1.27 only; normal builds and tests use CGO_ENABLED=0. - Keep a lightweight hexagonal architecture: core imports only stdlib and ports. - Owner goroutines own window state; no mutex on window state. - Use zerowrap logging with a `component` field on every entry. - Run tests with `-race`. - Use signed conventional commits: `type(scope): description`. ## Mocks (hard rule) Test doubles are always generated by Mockery v3 (`testify` template). Never handwrite them. - **No handwritten fakes, stubs or spies.** This covers any test type standing in for a project interface, and function fields swapped by tests (`x.ioctl = func...`). `make check` runs `fakes-check`, which rejects `fake`/`stub`/`spy`/`dummy`/`mock` type names in tests. - **Every seam is an interface.** When a test needs to replace a dependency, add an interface and generate its mock: - Cross-layer dependencies go in `internal/ports`. Mocks: `internal/mocks/ports`, package `portsmocks`, type `MockX`. - Adapter-internal seams (e.g. a kernel ioctl) stay unexported in the adapter. Mocks: `_mock_test.go` in the same package. - **Add a `.mockery.yml` entry** for each new interface, run `make mocks` and commit the generated files. `make mocks-check` (CI) fails if they are stale or missing. - Use mocks through `.EXPECT()`. Record calls with `RunAndReturn` when a test inspects arguments. - Channel ports (sealed message interfaces such as `ports.ClientEvent` or `ports.OutputEvent`) are tested by sending messages, not with mocks. - Not doubles, so allowed: - real protocol clients in wayland tests (`wlturbo` proxies); - real in-memory implementations of std interfaces (e.g. a locked `bytes.Buffer` as `io.Writer`); - production constructor parameters such as `NewRenderer func(w, h) (ports.Renderer, error)`, where tests return a generated mock; - channels passed to `app.run` to inject input or observe scenes (channel ports). ## End-to-end tests (headless backend) Unit tests are not enough for user-facing behavior (binds, gestures, window placement, config keys). Test it end to end on the headless backend before declaring it done; see [docs/dev/headless.md](docs/dev/headless.md). - Build the branch binary (`CGO_ENABLED=0 go build -o /neferwl ./cmd/neferwl`) and run it with a throwaway config: `/neferwl --backend=headless --config /config --no-xwayland --screenshot /shots --input