diff --git a/.claude/agents/auto-review-commit.md b/.claude/agents/auto-review-commit.md new file mode 100644 index 0000000..1850fa8 --- /dev/null +++ b/.claude/agents/auto-review-commit.md @@ -0,0 +1,8 @@ +--- +description: Auto-trigger review and commit when feature implementation is complete +alwaysApply: true +--- + +# Auto-Trigger Review & Commit + +When a feature implementation is complete, automatically execute the `/review-and-commit` workflow without waiting for the user to manually request it. Do not automatically push and watch. diff --git a/.claude/agents/go-conventions.md b/.claude/agents/go-conventions.md new file mode 100644 index 0000000..450455b --- /dev/null +++ b/.claude/agents/go-conventions.md @@ -0,0 +1,40 @@ +--- +description: Go conventions and patterns for this project +globs: "**/*.go" +alwaysApply: false +--- + +# Go Conventions + +## Context + +Every exported function that does I/O takes `context.Context` as its first argument. + +## Errors + +- Define package-level sentinel errors for expected conditions: + +```go +var ErrNotFound = fmt.Errorf("not found") +``` + +- Wrap unexpected errors with `fmt.Errorf("doing X: %w", err)` to preserve the chain. +- Callers check expected errors with `errors.Is(err, store.ErrNotFound)`. + +## UUIDs + +- Use `github.com/google/uuid` for all UUID types. +- Model structs use `uuid.UUID`, not `string`, for ID fields. + +## Database + +- Use `?` parameter placeholders (MySQL style), never string interpolation. +- Use `gen_random_uuid()` for server-generated UUIDs (Postgres built-in). +- **Queries**: use [sqlc](https://sqlc.dev/) to generate type-safe Go from SQL. + Write annotated SQL in `internal/store/queries/*.sql`; run `task sqlc` to + regenerate. Never hand-write query code in Go — edit the `.sql` source instead. + +## Testing + +- Use the standard `testing` package. No external assertion libraries. +- Test functions follow `TestTypeName_MethodName` naming diff --git a/.claude/agents/go-dev.md b/.claude/agents/go-dev.md new file mode 100644 index 0000000..2d879bd --- /dev/null +++ b/.claude/agents/go-dev.md @@ -0,0 +1,66 @@ +--- +description: Standard Go development commands for this project +globs: "**/*.go" +alwaysApply: false +--- + +# Go Development Commands + +## Testing + +```bash +# Run all tests +go test ./... + +# Run tests in a specific package +go test ./internal/checkin/... + +# Run a single test by name +go test ./internal/checkin/... -run TestCheckOut + +# Run tests with verbose output +go test -v ./... + +# Run tests with race detector +go test -race ./... +``` + +## Formatting and Linting + +```bash +# Format all Go files +gofmt -w . + +# Organize imports (group stdlib, external, internal) +goimports -w . + +# Vet for common mistakes +go vet ./... +``` + +## Code Generation + +```bash +# Regenerate sqlc query code after editing internal/store/queries/*.sql +task sqlc + +# Regenerate proto/gRPC code after editing .proto files +task generate +``` + +## Building + +```bash +# Compile all packages (catches errors without producing binaries) +go build ./... + +# Tidy module dependencies after adding/removing imports +go mod tidy +``` + +## After any code change + +1. `gofmt -w .` +2. `goimports -w .` (if imports changed) +3. `go vet ./...` +4. `go test ./...` diff --git a/.claude/agents/minimal-changes.md b/.claude/agents/minimal-changes.md new file mode 100644 index 0000000..8d13ca1 --- /dev/null +++ b/.claude/agents/minimal-changes.md @@ -0,0 +1,12 @@ +--- +description: Make only the changes the user asked for +alwaysApply: true +--- + +# Minimal Changes + +When implementing a requested change, make **only** the changes the user asked for. + +- Do not make additional "cleanup" or "consistency" changes alongside the requested work (e.g. removing annotations you consider redundant, restructuring related code, adding backward-compatibility guards). +- If you notice something that *should* be changed but wasn't requested, mention it after completing the requested work — don't silently include it. +- When presenting multiple implementation paths, wait for the user to choose before writing any code. diff --git a/.claude/agents/taskfile-not-make.md b/.claude/agents/taskfile-not-make.md new file mode 100644 index 0000000..ca73ba3 --- /dev/null +++ b/.claude/agents/taskfile-not-make.md @@ -0,0 +1,13 @@ +--- +description: Use go-task (Taskfile) instead of Make for task automation +alwaysApply: true +--- + +# Use Taskfile, not Make + +This project uses [go-task](https://taskfile.dev/) (`Taskfile.yaml`) for task automation. **Do not** use Makefiles. + +- Task definitions go in `Taskfile.yaml` at the repo root. +- Use `task ` to run tasks (e.g. `task test`, `task db`). +- Use colon-separated namespacing for related tasks (e.g. `task db:stop`). +- When adding new automation, add a task to `Taskfile.yaml` rather than creating a Makefile or shell script. diff --git a/.claude/agents/test-isolation.md b/.claude/agents/test-isolation.md new file mode 100644 index 0000000..6b58260 --- /dev/null +++ b/.claude/agents/test-isolation.md @@ -0,0 +1,14 @@ +--- +description: Tests must be self-contained; mock external service dependencies +globs: "**/*_test.go" +alwaysApply: false +--- + +# Test Isolation + +Tests must be self-contained. The only shared infrastructure allowed is the database. + +- **Database**: tests may depend on a real database connection — this is expected and acceptable. +- **External services** (gRPC clients, HTTP APIs, third-party SDKs, etc.): must be mocked or stubbed. Tests must never make real calls to external services. +- Use interfaces for service dependencies so they are straightforward to mock in tests. +- Keep mocks minimal — only stub the methods the test actually exercises. diff --git a/.claude/agents/testing-mocks.md b/.claude/agents/testing-mocks.md new file mode 100644 index 0000000..3b3d18e --- /dev/null +++ b/.claude/agents/testing-mocks.md @@ -0,0 +1,11 @@ +--- +description: Preferences for testing and mocks +globs: ["**/*_test.go", "e2e/**/*.go"] +--- + +# Testing Mocks + +When writing or updating tests: +- Always prefer real code, a local version of a server (like `httptest`), etc., to static mocks. +- Prefer dependency injection if useful for testing. +- A mock implementation should be the absolute last resort. diff --git a/.claude/skills/review-and-commit/SKILL.md b/.claude/skills/review-and-commit/SKILL.md new file mode 100644 index 0000000..4f4d5f3 --- /dev/null +++ b/.claude/skills/review-and-commit/SKILL.md @@ -0,0 +1,93 @@ +--- +name: review-and-commit +description: Review and Commit +disable-model-invocation: true +--- + +# Review and Commit + +Perform the following steps in order. Stop and report if any step fails. + +## 1. Run Code Checks + +Run these in parallel where possible: + +- `task lint` (buf format + buf lint + gofmt + goimports + go vet) +- `task generate` then verify no diff in generated files (`git diff --exit-code gen/ api/openapi.yaml`) + +Fix any issues found. Re-run checks after fixes. + +## 2. Run Tests with Coverage + +Run tests uncached with verbose output and coverage: + +``` +go test -count=1 -v -coverprofile=coverage.out ./... +``` + +Then analyze coverage: + +``` +go tool cover -func=coverage.out +``` + +### Coverage analysis + +1. **Baseline**: before the review, capture coverage on the base branch (or use the most recent known coverage if available). If no baseline exists, use the current run as the initial baseline. +2. **Compare**: check total coverage and per-package coverage for packages that contain changed files. +3. **Thresholds**: + - If total coverage drops by **2 or more percentage points**, suggest adding tests and ask for confirmation before committing. Do not block the commit. + - If a changed package has **0% coverage**, mention it in the report unless the package is purely generated code or contains only types/constants. +4. **Report**: include a short coverage summary in the findings (total %, per-changed-package %, and delta if baseline is available). + +Clean up `coverage.out` after analysis. + +## 3. Review Changed Go Files + +For each `.go` file in the diff (excluding `gen/`), review against: + +### Go Code Review Comments (go.dev/wiki/CodeReviewComments) + +- Comment sentences: doc comments are full sentences starting with the name, ending with a period. +- Contexts: `context.Context` as first param, never stored in structs. +- Error strings: lowercase, no trailing punctuation. +- Handle errors: no discarded errors with `_`. +- Imports: grouped (stdlib, external, internal), no unnecessary renames. +- Indent error flow: normal path at minimal indentation, error handling first. +- Initialisms: `ID`, `URL`, `HTTP` — consistent casing. +- Interfaces: defined where used, not where implemented. +- Named result parameters: only when they clarify meaning. +- Receiver names: short, consistent, never `this`/`self`. +- Variable names: short for narrow scope, descriptive for wide scope. + +### Effective Go (go.dev/doc/effective_go) + +- Zero value useful: structs should be usable without explicit init. +- Don't panic: use error returns, not panic, for normal error handling. +- Goroutine lifetimes: clear when/whether goroutines exit. +- Embedding: prefer embedding over forwarding methods when appropriate. +- Concurrency: share memory by communicating, not vice versa. + +### Go Proverbs (go-proverbs.github.io) + +- The bigger the interface, the weaker the abstraction. +- A little copying is better than a little dependency. +- Clear is better than clever. +- Errors are values — handle them, don't just check them. +- Don't panic. +- Make the zero value useful. +- Design the architecture, name the components, document the details. + +## 4. Report Findings + +Present findings grouped by severity: + +- **Must fix**: violations that would break conventions, correctness, or backwards compatibility. +- **Should fix**: style, clarity, or maintainability issues. +- **Nit**: minor suggestions, optional. + +Apply must-fix and should-fix changes (ask if unsure about any). Re-run checks. + +## 5. Commit + +After all checks pass and review issues are resolved, create a commit following the repository's commit conventions. Do not push unless asked.