123 lines
5.6 KiB
Markdown
123 lines
5.6 KiB
Markdown
# CLAUDE.md — Agent Instructions
|
|
|
|
This file is read automatically by Claude Code at the start of every session.
|
|
Keep it concise — the agent needs signal, not essays.
|
|
|
|
---
|
|
|
|
## Project overview
|
|
|
|
Go 1.25 template for PoC, hobby projects, and small publishable packages.
|
|
Demonstrates: structured logging (slog), config (flag), consumer-defined interfaces + manual fakes,
|
|
result type (happy-path error handling), linting (golangci-lint), security scanning (gosec, govulncheck),
|
|
git hooks, devcontainer, VSCode tasks.
|
|
|
|
Key constraint: `go.mod` stays free of dev tool deps (tools are pinned in `tools.versions` and run via
|
|
`go run tool@version`) so packages published from this repo have a clean module graph for consumers.
|
|
|
|
Module: `gitea.djmil.dev/go/template` — update this when you fork.
|
|
|
|
---
|
|
|
|
## Project structure
|
|
|
|
```
|
|
cmd/app/main.go composition root — wires deps, no logic here
|
|
internal/config/ flag-based config loader (config.Load)
|
|
internal/logger/ slog wrapper with WithField / WithFields
|
|
internal/greeter/ Example domain package (delete or repurpose)
|
|
pkg/result/ Example publishable package (Result/Expect types)
|
|
tools.versions Pinned tool versions (sourced by Makefile and pre-push hook)
|
|
.golangci.yml Linter rules
|
|
.githooks/pre-push Runs gofmt + go vet + golangci-lint + gosec before push
|
|
```
|
|
|
|
---
|
|
|
|
## Project rules
|
|
|
|
- **Module imports** — always use the full module path `gitea.djmil.dev/go/template/...`
|
|
- **Packages** — keep `cmd/` thin (wiring only); business logic belongs in `internal/`
|
|
- **Types** — expose concrete types from constructors (`New(...) *Type`); never wrap in an interface at the implementation site. Consumers define their own interfaces if they need one (Go's implicit satisfaction makes this free)
|
|
- **Errors** — wrap with `fmt.Errorf("context: %w", err)`; never swallow errors silently
|
|
- **Logging** — use `log.WithField("key", val)` for structured context; never `fmt.Sprintf` in log messages; `log/slog` is the backend
|
|
- **Config** — all configuration through `internal/config` (flag-parsed); no hard-coded values in logic packages
|
|
|
|
---
|
|
|
|
## Code style
|
|
|
|
- Follow `gofmt` + `goimports` formatting (enforced by linter and git hook)
|
|
- Imports: stdlib → blank line → external → blank line → internal (goimports handles this)
|
|
- Error variables: `err` for local, `ErrFoo` for package-level sentinels
|
|
- Constructors: `New(deps...) *Type` pattern
|
|
- Comment every exported symbol (golangci-lint will warn if missing)
|
|
- Max line length: 120 chars (configured in `.golangci.yml`)
|
|
- Prefer explicit over clever; PoC code should be readable first
|
|
|
|
---
|
|
|
|
## Testing rules
|
|
|
|
- Tests use only the standard `testing` package — no third-party assertion libraries
|
|
- Test files: `package foo_test` (black-box) unless white-box access is needed
|
|
- Fake dependencies with **manual fakes** (implement the interface inline in `_test.go`)
|
|
- Use `logger.NewNop()` when the test doesn't care about log output
|
|
- Table-driven tests with `t.Run("description", ...)` for multiple cases
|
|
- The race detector is enabled in CI (`make test-race`); don't introduce data races
|
|
- Never use `time.Sleep` in tests; use channels or `t.Cleanup`
|
|
|
|
---
|
|
|
|
## Development commands
|
|
|
|
```bash
|
|
make init # first-time setup: fetch deps, configure git hooks
|
|
make tools # install tool binaries to GOPATH/bin (versions from tools.versions)
|
|
make build # compile to ./bin/app
|
|
make run # go run with default flags
|
|
make test # run all tests
|
|
make test-race # tests + race detector
|
|
make lint # go vet + golangci-lint
|
|
make lint-fix # go fix + golangci-lint auto-fix
|
|
make security # gosec + govulncheck
|
|
make release # list releases, or tag+push after full checks (make release VERSION=v0.1.0)
|
|
make clean # remove bin/
|
|
```
|
|
|
|
VSCode: `Ctrl+Shift+B` = build, `Ctrl+Shift+T` = test.
|
|
Debug: use launch config "Debug: app" (F5).
|
|
|
|
---
|
|
|
|
## Adding new features (checklist)
|
|
|
|
1. Write the implementation in `internal/<domain>/` — return a concrete `*Type`, no interface at the implementation site
|
|
2. In the *consumer* package (or `_test.go`), declare a minimal interface covering only the methods you call
|
|
3. Write unit tests using a manual fake that satisfies that interface
|
|
4. Wire the concrete type in `cmd/app/main.go`
|
|
5. Run `make lint test` before committing
|
|
|
|
---
|
|
|
|
## Known pitfalls
|
|
|
|
- `govulncheck` makes network calls; excluded from pre-push hook (run manually)
|
|
- Tool versions live in `tools.versions` — edit that file to upgrade, both Makefile and hook pick it up
|
|
- `go run tool@version` is used in lint/security targets; Go caches downloads so subsequent runs are fast
|
|
- `make tools` installs binaries to `GOPATH/bin` for IDE integration (e.g. dlv for the debugger)
|
|
- `go fix` rewrites source files; run `make lint-fix` before committing after a Go version bump
|
|
|
|
---
|
|
|
|
## Recent work
|
|
<!-- Agent: append a dated bullet when completing a significant chunk of work.
|
|
Keep this section to ~10 entries; remove stale items.
|
|
Format: YYYY-MM-DD — what was done and why. -->
|
|
|
|
- 2026-03-29 — Stripped to stdlib-only: removed testify/mockery→manual fakes, zap→slog, viper→flag.
|
|
- 2026-03-29 — Pre-commit hook moved to pre-push; go vet + go fix added to lint pipeline.
|
|
- 2026-03-29 — Fixed stale docs and .golangci.yml local-prefixes; .vscode launch configs use CLI flags.
|
|
- 2026-04-01 — Replaced tools.go/go.mod pinning with tools.versions + go run tool@version; go.mod is now free of dev tool deps.
|
|
- 2026-04-01 — Added make release: lists tags with no args; validates semver, runs test-race+lint+security, then tags+pushes.
|