Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .claude/rules/go-idioms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
globs:
- "**/*.go"
---

# Go 1.26 Idioms and Modernizers

Write modern Go — never generate pre-1.24 patterns when the project's go.mod allows it.

## Language (1.26)

```go
type yearsSince int
age := new(yearsSince(born)) // *yearsSince — allocates and initializes from expression

type Adder[A Adder[A]] interface { // self-referential generic constraints
Add(A) A
}
```

`new(expr)` is available in Go 1.26. The return type is `*T` where `T` is the type of the expression. Use it when it improves clarity for optional scalar pointer values. Do not force it where `&T{...}` or a plain local variable is clearer.

## Iterators (1.23+)

Use `iter.Seq`/`iter.Seq2` and range-over-func. Prefer stdlib iterator APIs:
- `slices.Collect`, `slices.Sorted`, `slices.SortedFunc`, `slices.Concat`
- `maps.Keys`, `maps.Values`, `maps.Collect`, `maps.Insert`
- `bytes.Lines`, `bytes.SplitSeq`, `strings.Lines`, `strings.SplitSeq`

## Struct Tags (1.24+)

- `omitzero` for struct-typed fields and types with `IsZero()` (e.g., `time.Time`)
- `omitempty` for slices, maps, strings, and other empty-value cases
- Use both when the wire format should omit either: `json:",omitzero,omitempty"`
- JSON tag changes are behavior changes — review carefully
- Generic type aliases are fully supported

## go fix Modernizers (1.26)

`go fix` applies modernizations in-place. Always review the git diff before committing — some rewrites change observable behavior.

Useful analyzers:
- `rangeint` — 3-clause `for` → `for range`
- `minmax` — if/else clamp → `min`/`max`
- `slicessort` — `sort.Slice` → `slices.Sort` for basic ordered types
- `any` — `interface{}` → `any`
- `fmtappendf` — `[]byte(fmt.Sprintf(...))` → `fmt.Appendf`
- `testingcontext` — simple cancellable test context setup → `t.Context()`
- `omitzero` — suggests `omitzero` for struct fields where `omitempty` has no effect
- `mapsloop` — map update loops → `maps.Copy`/`maps.Insert`/`maps.Clone`/`maps.Collect`
- `newexpr` — wrappers returning `&x` or call sites → `new(expr)`; result type is `*T` matching the expression's type
- `stringsseq` / `stditerators` — loops over eager APIs → iterator-based forms
- `waitgroup` — `wg.Add(1)`/`go`/`wg.Done()` → `wg.Go` (stdlib `sync.WaitGroup`); prefer `errgroup.Group.Go` from `golang.org/x/sync/errgroup` when error propagation is needed
- `//go:fix inline` — source-level inliner for API migrations
58 changes: 58 additions & 0 deletions .claude/rules/go-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
globs:
- "**/*.go"
---

# Go Patterns and Style Reference

Detailed conventions that complement `CLAUDE.md`. The go-rig skill owns process discipline (ATDD/TDD, DI, review workflow). This file owns language-specific patterns and API conventions.

## Style

- Package names: short, lowercase, no `util`/`common`/`helpers`
- Initialisms: `ID`, `URL`, `HTTP`, `JSON`, `API`, `SQL`
- No stutter: `orders.Service` not `orders.OrderService`
- Accept interfaces, return structs
- Constructors enforce invariants or own resources — not mandatory for every type
- Options struct when params > 3
- Prefer domain-specific named types and typed constants over `string`/`int` aliases
- Avoid `map[string]any` / `any` in core domain code except at loose boundaries
- Do not hardcode environment-specific values, collaborator selection, or operational limits

## API Design

- Make the zero value useful when practical
- Prefer nil slices by default; do not distinguish nil from empty unless the API requires it
- Pointer receivers when mutating or copying is non-trivial; keep receiver choice consistent
- Avoid copying types with mutex-like fields
- Keep exported APIs stable; treat JSON shape and error behavior changes as compatibility work
- Explicit types for IDs, states, units — prevent invalid mixing at compile time

## Documentation

- Every exported name and package should have a doc comment
- Doc comments start with the declared name and describe caller-visible behavior
- Update docs when exported behavior, config, or wire format changes
- Comments explain intent, invariants, ownership — not restate the code
- TODOs must include a reason and ticket reference when one exists

## Testing Patterns

- Subtest names should be readable; failure messages make `got` vs `want` obvious
- Prefer `for b.Loop() { ... }` for new benchmarks (1.24+)
- `t.Context()` for test-lifetime context (no independent cancel needed)
- `t.ArtifactDir()` for test output files (1.26)
- `testing/synctest` for isolated concurrent code with virtual time (1.25+)
- Prefer `cmp.Equal`/`cmp.Diff` for complex structure comparisons
- Avoid brittle assertions on exact JSON formatting or error text unless that output is part of the contract
- Acceptance tests verify user-visible behavior; unit tests keep domain rules fast and precise
- Add fuzz tests for parsers and input-heavy packages when the project uses fuzzing

## Common Patterns

- **SQLite**: prefer `modernc.org/sqlite` (pure Go, no CGO). Parameterized queries only. Explicit transactions.
- **CLI**: match existing framework (cobra, kong, or plain). Check `cmd/` and `internal/` layout before adding commands.
- **HTTP / DB**: set client/server/query timeouts deliberately. Always close rows/bodies and check terminal errors (`rows.Err`, close failures).
- **Modules**: avoid new dependencies without justification. Keep `replace` directives intentional and short-lived.
- **Linting**: `.golangci.yml` v2 per project. Do not modify without request. Fix all lint findings.
- **Static analysis**: prefer repo gates (`make lint`, `make ci`) and include `go vet ./...`, `staticcheck`, and other configured analyzers
98 changes: 47 additions & 51 deletions .claude/skills/go-rig/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: go-rig
description: Use this skill when building, reviewing, or refactoring Go code that must follow strict design discipline — ATDD/TDD workflow, explicit dependency injection, package-boundary discipline, and structured code review. Complements CLAUDE.md by focusing on process and design judgment rather than version-specific Go features.
metadata:
short-description: Go design, workflow, and review discipline
slash-command: enabled
---

# Go Rig
Expand All @@ -12,10 +13,12 @@ Strict design and testing discipline for Go projects.
This skill **complements** `CLAUDE.md`.

`CLAUDE.md` owns:
- Go version and toolchain guidance
- stdlib and language idioms
- testing APIs and tooling commands
- modernization guidance such as `go fix`
- Go version, toolchain, and commands
- Key style, error, context, and concurrency rules

`.claude/rules/` owns:
- Go 1.26 idioms and go fix modernizer catalog (`go-idioms.md`)
- Detailed style, API, documentation, and testing patterns (`go-patterns.md`)

This skill adds:
- ATDD/TDD workflow
Expand All @@ -28,6 +31,16 @@ This skill adds:

Do not restate or override version-specific guidance from `CLAUDE.md`. If `CLAUDE.md` is stricter on a shared point, follow `CLAUDE.md`.

## When to Use

Use this skill when:

- implementing a new feature or behavior increment
- refactoring Go code for clearer ownership or testability
- reviewing package boundaries or dependency flow
- replacing hidden collaborator construction with explicit injection
- tightening tests around user-visible or integration behavior

## ATDD/TDD Workflow

Test-first is a design tool, not an afterthought.
Expand Down Expand Up @@ -81,7 +94,6 @@ When applying SRP/DRY/OCP in Go, prefer deleting duplication caused by mixed res
- If an abstraction adds files, wiring, and names but no clear testability or ownership win, do not add it

Avoid:
- interface-per-struct
- repositories or services that only forward calls
- configuration objects passed everywhere to avoid choosing explicit parameters
- “future-proofing” abstractions without a concrete second implementation or consumer
Expand All @@ -90,9 +102,7 @@ Avoid:

- A function should usually do one thing: validate, transform, orchestrate, persist, or render
- If a function mixes business rules with transport, storage, or logging details, split it
- Prefer early returns over nested condition pyramids
- Keep parameter lists explicit and intention-revealing; if many values travel together for one reason, introduce a small typed struct
- Use whitespace to separate logical phases so the control flow reads top to bottom

Refactor when a function:
- needs comments to explain the control flow
Expand All @@ -105,26 +115,8 @@ Refactor when a function:
- **Constructors** for types that must enforce invariants or own long-lived collaborators
- **Function parameters** for short-lived collaborators and pure logic
- Never construct DB clients, HTTP clients, loggers, or repositories inside domain methods
- No DI frameworks — explicit wiring only
- No hidden globals or singletons
- Prefer passing dependencies from the composition root (`main`, wiring package, or test setup) instead of looking them up deep inside the call stack
- Inject seams for time, randomness, process execution, filesystem, and external I/O when behavior depends on them
- Do not hide dependencies behind package-level variables except in rare compatibility shims

```go
// constructor injection for long-lived deps
func NewOrderService(store OrderStore, clock Clock) *OrderService {
return &OrderService{store: store, clock: clock}
}

// function parameter for short-lived/pure logic
func ValidateOrder(order Order, now time.Time) error {
if order.ExpiresAt.Before(now) {
return fmt.Errorf("order %s expired: %w", order.ID, ErrExpired)
}
return nil
}
```

## Package Design

Expand All @@ -136,11 +128,9 @@ Organize by domain, not by technical layer.
- Split packages only when coupling pressure is real, not speculative

Avoid:
- interface-per-struct without a consumer need
- deep layering in small services
- `internal/platform/` catch-all layers — keep cross-cutting concerns in focused packages (`internal/config/`, `internal/db/`)
- `internal/platform/` catch-all layers — keep cross-cutting concerns in focused packages
- packages that combine unrelated domains because they share a datastore or transport
- "shared" packages that centralize unrelated helpers and create import gravity

## Hardcoding And Configuration

Expand Down Expand Up @@ -204,32 +194,38 @@ Treat linting and static analysis as design feedback, not cosmetic cleanup.
Before finishing any change, verify:

- [ ] Package boundaries are coherent — no cross-domain leaks
- [ ] No premature abstractions — interfaces have real consumers
- [ ] Dependencies injected explicitly — no hidden construction
- [ ] No hardcoded runtime values (URLs, ports, credentials, timeouts)
- [ ] Types are explicit where they protect domain correctness
- [ ] Functions are readable in one pass
- [ ] Functions do not mix unrelated responsibilities
- [ ] Repeated logic is unified only when it shares the same reason to change
- [ ] Errors wrapped with useful context (`%w`)
- [ ] Functions are readable in one pass and do not mix unrelated responsibilities
- [ ] Tests cover acceptance behavior and unit behavior
- [ ] TDD/ATDD flow was followed as closely as the repo constraints allowed
- [ ] Behavioral compatibility checked where public APIs, JSON, or persistence shape changed
- [ ] Nil vs empty behavior is intentional for slices, maps, pointers, and JSON fields
- [ ] Concurrency changes have a shutdown path and observable ownership
- [ ] Exported docs and package docs were updated when public behavior changed
- [ ] Tests are robust against irrelevant formatting churn
- [ ] Version/tooling guidance from `CLAUDE.md` has been followed
- [ ] Lint and test gates expected by the repo have been run or consciously deferred

## Reject These Patterns

- Interface-per-struct without consumer need
- Giant functions mixing validation, orchestration, and persistence
- Hardcoded configuration or collaborator selection
- Weakly typed domain data kept as raw maps or generic blobs without need
- Comments that restate code
- Brittle mock-only tests — prefer fakes with real behavior
- Transport concerns embedded in core domain logic
- Production design distorted to satisfy a mocking framework
- Refactors that add indirection without improving correctness, ownership, or testability
- [ ] Root-package wrappers added/updated for any new `internal/` exports
- [ ] Lint and test gates (`make check`) have been run or consciously deferred

## Project-Specific: Root-Package Facade

This project uses a facade pattern where the root `dashboard` package re-exports `internal/` APIs via thin wrappers and type aliases.

When adding a new feature:
1. Implement logic in `internal/app<domain>/` with exported names
2. Add type aliases (`type X = appfoo.X`) and wrapper functions in the root package
3. Write tests at both the internal package level (unit) and root level (integration)

When modifying an existing internal API:
- If the signature changes, update the corresponding root-level wrapper
- Root-level wrappers must remain zero-logic forwarding — no business logic in the facade

Legacy note: `CollectTokenUsage` and `CollectTokenUsageWithCache` have 15+ map parameters. New code should use options structs per CLAUDE.md convention. These functions are candidates for future refactoring but are not blocking.

## Success Criteria

This skill is being followed correctly when:

- changes are small, test-backed, and easy to review
- dependency flow is explicit from the composition root
- package responsibilities are cleaner after the change, not blurrier
- the implementation follows the Go standards in `CLAUDE.md`
- tests speak in behavior terms, not implementation vocabulary
- the resulting code reads clearly without comments explaining the control flow
35 changes: 35 additions & 0 deletions .codex/skills/frontend-dashboard/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: frontend-dashboard
description: Use this skill when editing the embedded dashboard frontend in this repository. It focuses on preserving the single-file embedded SPA model, keeping the UI lightweight, and avoiding unnecessary frontend tooling or dependencies.
---

# Frontend Dashboard

This is the native Codex frontend skill for the embedded dashboard UI in this repository.

Use it when:
- editing `web/index.html`
- changing dashboard layout, styling, or interaction behavior
- improving usability of the embedded SPA

## Repository Constraints

- The frontend is embedded into the Go binary.
- Keep the frontend simple and self-contained.
- Avoid introducing new frontend build systems, frameworks, or third-party packages unless explicitly requested.
- Preserve compatibility with the Go server contract and embedded asset flow.

## UI Direction

- Prefer clear information hierarchy over visual noise.
- Keep interactions fast and obvious.
- Improve readability of metrics, status, and operational data first.
- Use intentional spacing, typography, and contrast.
- Favor small, maintainable JS and CSS over abstraction-heavy patterns.

## Change Discipline

- Do not turn the SPA into a toolchain-driven frontend without explicit approval.
- Keep API assumptions explicit.
- Treat data-shape changes between backend and frontend as contract changes.
- When in doubt, optimize for operability and clarity instead of novelty.
40 changes: 40 additions & 0 deletions .codex/skills/go-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: go-review
description: Use this skill when the task is to review Go code in this repository. Focus on bugs, regressions, API compatibility, test gaps, concurrency risks, and violations of the zero-dependency and root-facade constraints.
---

# Go Review

This is the native Codex review skill for Go work in this repository.

Use it when:
- the user asks for a review
- the task is to validate a Go change
- the task is to assess risks before or after a refactor

## Review Priorities

Report findings first.
Prioritize:
- correctness bugs
- behavioral regressions
- API and JSON compatibility changes
- missing or weak tests
- concurrency and shutdown risks
- dependency policy violations
- facade leakage from root package into domain logic

## Repository-Specific Checks

- Root package wrappers must stay zero-logic.
- New behavior belongs in `internal/app<domain>/`.
- Do not assume third-party helpers are acceptable in this zero-dependency repo.
- Treat `json:",omitempty"` and `json:",omitzero"` changes as API behavior changes.
- Check that goroutines have shutdown and ownership semantics.

## Review Output

- Lead with concrete findings.
- Use file references.
- Keep summaries brief.
- If there are no findings, say that explicitly and mention residual risk or testing gaps.
Loading
Loading