Imported from DavidNix/safeagent (
go/AGENTS.md). Install upstream withnpx skills add DavidNix/safeagent --skill go. Copyright stays with the author.
SafeAgent Go Workspace
Module: github.com/DavidNix/safeagent. Go 1.26+.
Verification
Run from the go/ directory or make -C go from project root.
make vet # autoformat and linters
make test # run all tests
Hard Constraints
- Bug fixes require TDD RED/GREEN: write a failing test first, confirm it fails, then write the fix and confirm tests pass.
- Feature work with new behavior requires tests and a verification pass that proves the tests fail without the implementation.
Code Standards
- Complete implementations without placeholders or TODOs.
- Minimal comments unless code is complex.
context.Contextas first parameter, variable namectx.- Wrap errors with context:
fmt.Errorf("<context>: %w", err). - Use package-level
slog.Info,slog.Error,slog.Warn,slog.Debuginstead ofslog.Default(). - Use
anyinstead ofinterface{}. - Never use naked returns.
- Use
switchwith no condition instead ofif/elsechains; avoidelse. - For non-cryptographic randomness, use
math/rand/v2. - Bubble up errors. Never swallow or ignore them. If continuing is safe and intentional, log the error with structured context before continuing.
- Add short doc comments to exported methods and functions except constructors and initializers.
- Define interfaces at the call site that needs them; keep shared packages focused on concrete types and structs.
- Assume constructors and initializers fully initialize their values. Avoid nil-guarding those fields unless a caller can omit them.
- Never use
http.DefaultClientin production code. It's unsafe. - Use
time.Now().UTC()for wall-clock timestamps. Use plaintime.Now()only for monotonic duration/deadline measurement. - Log messages: proper casing, no interpolation. Use structured fields for dynamic values, for example
slog.Error("Failed to capture pageview", "error", err)instead ofslog.Error(fmt.Sprintf("failed: %v", err)). - Public methods and functions should never accept private types or interfaces.
- Go 1.26+ allows
new(<literal>)to get a pointer from a literal, for examplenew(42). - Use
cmp.Or(value, fallback)for zero-value fallback selection instead of assigning and then checking the value with anif. - Nest the error path, not the happy path. Avoid
err == nil; prefererr != nil.
Test Standards
- Always use testify's
require; never useassertort.Error. - Always use
t.Parallel()in top-level tests, never in subtests. - Naming:
Test<FunctionName>for functions,Test<Type>_<FunctionName>for methods. - Always use subtests with "happy path" and error condition cases under the public function or method. Do not create new top-level tests.
- Use
t.Context(); never usecontext.Background()orcontext.TODO(). - Use
synctest, channels, orerrgroup.Groupfor concurrency; never usetime.Sleepunless in asynctestbubble. - Prefer
require.EqualErroroverrequire.Containsfor error assertions. - Never test unexported functions; test through public interface only.
- Ignore return values when testing error cases:
_, err := FunctionToTest(). - Do not test
Newconstructor or initializer functions.
Test Helper Pattern
When exporting test helpers from a package, define a TestingT interface within that package rather than relying on a shared testutil package or importing testing.T directly. This keeps the interface minimal and co-located with the code that uses it.