]> git.giacomolli.uk - onechat/blob - .agents/skills/go-code/SKILL.md
Log image and message sends
[onechat] / .agents / skills / go-code / SKILL.md
1 ---
2 name: go-code
3 description: Go style rules. Use before you write or review Go code.
4 ---
5
6 # Go code
7
8 ## Packages and files
9
10 - Keep Go code in one root `main` package. Do not add `internal/`,
11 `cmd/`, or one-file packages until the user asks for them.
12 - Keep `main.go` limited to the entry point. Name other files for their
13 function. Do not create `main_test.go`.
14 - A file carries its own context. Keep each struct and all of its
15 methods in one file. Give a separate responsibility its own type.
16 - When code in one file gets a user in another file for an unrelated
17 purpose, move it to a file named for its function in the same change.
18 - Start each package-level name that belongs to one feature or
19 integration with the name of that feature. Start the names of its
20 files with the same name. Keep a name generic only when more than one
21 feature uses it.
22 - Write type names with a capital letter, so that a variable can have
23 the name of its type. Write all other package-level names in lower
24 case.
25 - Write all the letters of an initialism in one case. `revive` checks
26 common initialisms.
27 - Put entry points first, then each struct's methods as a group, then
28 standalone utility functions. Do not put a utility function between
29 the methods of one type.
30 - In each group, order functions by first use in the file.
31
32 ## Entry point and context
33
34 - `main` calls `run()` and handles its returned error. Do not use
35 `log.Fatal`.
36 - `run()` creates the SIGINT/SIGTERM signal context and defers its
37 cancellation. Pass that context through operations and let cleanup
38 finish before `main` exits.
39 - Use `ctx` for a context and `cancel` for its cancellation function.
40 Call `defer cancel()` immediately after you create the context.
41 - Reuse `ctx` when a child context replaces its parent. Use a different
42 name only when both contexts stay in use. Return `ctx.Err()` directly.
43 - Derive each context from the context of the caller. When work must
44 finish even after the caller is cancelled, such as a final write, use
45 `context.WithoutCancel` of the caller's context. Use
46 `context.Background()` only where no caller context exists: in `run`,
47 and in methods that follow `io.Closer`.
48 - Name a duration that limits one operation after that operation, and
49 end the name with `Timeout`, so that the name says what it limits
50 without its comment. Define it in the file that applies it, not in the
51 file of another type.
52
53 ## Errors
54
55 - Handle every returned error.
56 - Reuse an existing `err` for later operations in the same scope. Use a
57 different variable only to keep or join errors.
58 - Use `errors.Is` and `errors.As` to examine errors. Do not compare an
59 error directly with a sentinel, because errors can be wrapped.
60 `errorlint` checks comparisons and type assertions on errors.
61 - When a function has more than one error path, wrap each returned error
62 with the context of its operation. A function with one error path can
63 return the error directly. `wrapcheck` checks package and interface
64 calls, with the exceptions in `.golangci.yml`. Review local calls and
65 the number of error paths by hand.
66 - When deferred cleanup can fail, use a named `err` result and join the
67 cleanup error with `errors.Join`.
68
69 ## Logs
70
71 - Log with the `*slog.Logger` of the part (`daemonLogger`). The handler
72 formats the line; do not format values into the message.
73 - Write the message as a short event in plain words, such as "receive
74 failed" or "subagent done". Do not explain what it means or what the
75 user must do.
76 - Put names, IDs, counts, and durations in attributes, with short keys
77 (`name`, `id`, `after`, `retry`). Put the cause in `err`; the handler
78 writes it last.
79 - Use info for normal events, warn for a failure that is retried or
80 loses nothing, and error for a failure that loses data or stops a
81 part.
82 - Never log secrets, tokens, message text, or phone numbers. Redact IDs
83 as the existing code does.
84
85 ## Style
86
87 - Keep function signatures on one line. Write function bodies on more
88 than one line, also for anonymous functions, deferred cleanup, and
89 test callbacks.
90 - Do not use short `if` initializers.
91 - Use a one-letter receiver name for each method.
92 - Use field names in struct literals. Put each field on its own line
93 when you set more than one field. This includes the cases of a
94 table-driven test. A small type of two fields that acts as one value,
95 such as a key or a coordinate pair, can use a positional literal on
96 one line.
97 - Put an empty line between logical blocks, also after multiline
98 declarations, assignments, literals, and calls. Keep an operation with
99 its immediate error or result check. Keep a short declaration with its
100 immediate use. `wsl_v5` and `nlreturn` check part of this rule. Review
101 the remaining logical blocks and immediate-use groups by hand.
102 - Use GNU-style command flags: two dashes for long flags, such as
103 `--cwd`, and one dash only for one-letter flags, such as `-h`.
104 - Prefer the Go standard library. Prefer Go over cgo.
105 - Do not add compile-time interface assertions. Prove compatibility
106 through actual assignments and calls.
107 - Use `// nolint: name` for lint directives.
108
109 ## Dependencies
110
111 - After a change to `go.mod` or `go.sum`, update `vendorHash` in
112 `nix/package.nix`. While the module has no dependencies, `vendorHash`
113 is `null`.
114
115 ## Tests
116
117 - Add tests for meaningful behavior. Do not add tests that only repeat
118 implementation details.
119 - Use `testify/require` for assertions. `testifylint` checks the
120 assertion forms.
121 - Name a table-driven case slice `testCases` and its range variable
122 `tc`. A subtest name describes the behavior, not a generic outcome or
123 input.
124 - In a test file, put constants immediately after the imports, then the
125 tests, then supporting types, methods, and helpers.
126 - Keep a helper that only one test file uses in that file. Put a helper
127 that more than one test file uses in a file without tests. Name that
128 file for what it holds, and end the name with `_helper_test.go`.
129 - Supply valid required dependencies in tests. Do not make a dependency
130 optional only to make a test simpler.