+---
+name: go-code
+description: Go style rules. Use before you write or review Go code.
+---
+
+# Go code
+
+## Packages and files
+
+- Put commands in `cmd/` and shared packages at the repository root
+ beside `cmd/`. Do not add an `internal` package.
+- Give each package one clear responsibility and a small public API.
+ Keep implementation types and helpers private.
+- Keep composition and dispatch in the main service type. Put distinct
+ work in focused private types or standalone functions with explicit
+ inputs. Do not spread the methods of the service type across files to
+ make a file shorter.
+- Keep `main.go` limited to the entry point. Name other files for their
+ function. Do not create `main_test.go`.
+- A file carries its own context. Keep each struct and all of its
+ methods in the file that declares the struct. Give a separate
+ responsibility its own type.
+- Name a file that holds a struct after that struct, in snake_case. Use
+ the platform suffix for a platform-specific file. When platforms share
+ the struct, put the platform details in standalone helpers.
+- Write all the letters of an initialism in one case. `revive` checks
+ common initialisms.
+- Put entry points first, then constructors with a `new` or `New`
+ prefix, then each struct's methods as a group, then standalone utility
+ functions. Do not put a utility function between the methods of one
+ type.
+- In each group, order functions by first use in the file.
+- Group related constants in one `const (...)` block and related
+ variables in one `var (...)` block. Put an empty line between entries
+ that have comments.
+
+## Entry point and context
+
+- `main` creates the SIGINT/SIGTERM signal context and defers its
+ cancellation. Pass that context through operations and let cleanup
+ finish before `main` exits. Do not use `log.Fatal`.
+- Use `ctx` for a context and `cancel` for its cancellation function.
+ Call `defer cancel()` immediately after you create the context.
+- Reuse `ctx` when a child context replaces its parent. Use a different
+ name only when both contexts stay in use. Return `ctx.Err()` directly.
+- Derive each context from the context of the caller. When work must
+ finish even after the caller is cancelled, such as a final write, use
+ `context.WithoutCancel` of the caller's context. Use
+ `context.Background()` only where no caller context exists: in `main`,
+ and in methods that follow `io.Closer`.
+- Name a duration that limits one operation after that operation, and
+ end the name with `Timeout`, so that the name says what it limits
+ without its comment. Define it in the file that applies it, not in the
+ file of another type.
+
+## Errors
+
+- Handle every returned error. Do not discard an error with the blank
+ identifier. `errcheck` checks this rule.
+- Reuse an existing `err` for later operations in the same scope. Use a
+ different variable only to keep or join errors.
+- Use `errors.Is` and `errors.As` to examine errors. Do not compare an
+ error directly with a sentinel, because errors can be wrapped.
+ `errorlint` checks comparisons and type assertions on errors.
+- Handle a documented sentinel error explicitly when it does not show a
+ failure.
+- When a function has more than one error path, wrap each returned error
+ with the context of its operation. A function with one error path can
+ return the error directly. `wrapcheck` checks package and interface
+ calls, with the exceptions in `.golangci.yml`. Review local calls and
+ the number of error paths by hand.
+- When deferred cleanup can fail, use a named `err` result and join the
+ cleanup error with `errors.Join`.
+
+## Logs and cancellation
+
+- Log with `log/slog`. Write the message as a short event in plain
+ words, such as "receive failed". Put names, IDs, counts, and durations
+ in attributes with short keys. Put the cause in `err`. Do not format
+ values into the message.
+- Use info for normal events, warn for a failure that is retried or
+ loses nothing, and error for a failure that loses data or stops a
+ part.
+- Cancellation of a service or request context is an expected stop, not
+ a failure. Do not log it as an error, and do not hide all errors only
+ because the context is canceled. A joined error with an independent
+ failure stays a failure.
+- Use the worker or request context for this check. A device request
+ that reaches its own timeout while its worker is active stays a
+ failure.
+- A client that cancels an HTTP request does not get a server-error
+ response for that cancellation.
+- Never log secrets or tokens.
+
+## Style
+
+- Keep function signatures on one line. Write function bodies on more
+ than one line, also for anonymous functions, deferred cleanup, and
+ test callbacks.
+- Do not use short `if` initializers.
+- Use a one-letter receiver name for each method.
+- Use field names in struct literals. Put each field on its own line
+ when you set more than one field. This includes the cases of a
+ table-driven test.
+- Put an empty line between logical blocks, such as two independent `if`
+ statements. Put an empty line before and after loops, switches,
+ selects, standalone blocks, and multiline declarations, assignments,
+ literals, and calls, when there is an adjacent statement. These
+ boundaries also apply when a loop immediately uses the declaration
+ before it.
+- Keep an operation with its immediate error or result check. Keep a
+ short declaration with its immediate use, except at the boundaries
+ above. `wsl_v5` and `nlreturn` check part of these rules. Review the
+ remaining logical blocks and immediate-use groups by hand.
+- Group imports in this order: standard library, external dependencies,
+ project imports. `gci` checks the groups.
+- Use GNU-style command flags: two dashes for long flags, such as
+ `--cwd`, and one dash only for one-letter flags, such as `-h`.
+- Prefer the Go standard library. Prefer Go over cgo.
+- Do not add compile-time interface assertions. Prove compatibility
+ through actual assignments and calls.
+- Use `// nolint: name` for lint directives.
+
+## Dependencies
+
+- After a change to `go.mod` or `go.sum`, update `vendorHash` in the Nix
+ package.
+
+## Tests
+
+- Add tests for behavior changes. Do not add tests that only repeat
+ implementation details, and do not add a test file without behavior
+ worth a test.
+- Put tests in a file that matches the tested source file.
+- Use `testify/require` for assertions. `testifylint` checks the
+ assertion forms.
+- Name a table-driven case slice `testCases` and its range variable
+ `tc`. A subtest name describes the behavior, not a generic outcome or
+ input.
+- In a test file, put constants immediately after the imports, then the
+ tests, then supporting types, methods, and helpers. Keep each
+ supporting struct with its methods.
+- Keep a helper that only one test file uses in that file. Put a helper
+ that more than one test file uses in a file without tests. Name that
+ file for what it holds, and end the name with `_helper_test.go`.
+- Supply valid required dependencies in tests. Do not make a dependency
+ optional or add a nil fallback only to make a test simpler.