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
componentfield 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 checkrunsfakes-check, which rejectsfake/stub/spy/dummy/mocktype 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, packageportsmocks, typeMockX. - Adapter-internal seams (e.g. a kernel ioctl) stay unexported in the adapter. Mocks:
<name>_mock_test.goin the same package.
- Cross-layer dependencies go in
- Add a
.mockery.ymlentry for each new interface, runmake mocksand commit the generated files.make mocks-check(CI) fails if they are stale or missing. - Use mocks through
.EXPECT(). Record calls withRunAndReturnwhen a test inspects arguments. - Channel ports (sealed message interfaces in
ports/messages.go) are tested by sending messages, not with mocks. - Not doubles, so allowed:
- real protocol clients in wayland tests (
wlturboproxies); - real in-memory implementations of std interfaces (e.g. a locked
bytes.Bufferasio.Writer); - production constructor parameters such as
NewRenderer func(w, h) (ports.Renderer, error), where tests return a generated mock; - channels passed to
app.runto inject input or observe scenes (channel ports).
- real protocol clients in wayland tests (
Checks before merge
make check, make race, make mocks-check and staticcheck ./... must pass.