diff --git a/.agents/skills/ub-go/SKILL.md b/.agents/skills/ub-go/SKILL.md new file mode 100644 index 0000000..b6e9a9c --- /dev/null +++ b/.agents/skills/ub-go/SKILL.md @@ -0,0 +1,356 @@ +--- +name: ub-go +description: Use this skill for Go code, modules, tests, and tooling. Apply it when the task involves Go files, go.mod or go.work, goroutines and channels, context propagation, error wrapping, interfaces or generics, golangci-lint, go vet, table-driven tests, or Go application and repository logic. +--- + +# UB Go + +## Overview + +Use this skill to enforce latest stable Go modern defaults: module-faithful +builds, small consumer-side interfaces, wrapped errors, context-first +concurrency, and repository-truth validation. + +Generate modern patterns for new code and refactor legacy patterns +incrementally when touching existing code. + +Write against the module's declared Go language version, but structure code so +it can move cleanly toward newer stable releases instead of accumulating +compatibility layers. + +## When Not To Use + +- Do not use this skill for workflow intake, sprint planning, or resumable + initiative orchestration; defer that to `ub-workflow`. +- Do not use this skill as the primary surface when the main problem is owned + by a non-Go workflow, documentation, or framework skill. +- Do not use this skill as a generic documentation-normalization layer when the + main task is Markdown structure or documentation quality rather than Go + implementation. + +## Bundled Assets + +This skill ships a reusable golangci-lint starter under `assets/` and a +deterministic helper under `scripts/`. + +Use them when a repository wants the same lint baseline but does not yet have +an active golangci-lint config of its own. + +## Load References On Demand + +- Read `../ub-authoring/references/authoring-conventions.md` when adjusting + shared routing guidance, output structure, or cross-skill authoring + conventions. +- Load and apply `references/go-standards.md` for canonical Go rules and + toolchain policy. +- Load and apply `references/repository-go-workflows.md` for resolving the + target repository's actual Go validation and build-tooling baseline. +- Load `references/golangci-config-resolution.md` when golangci-lint config + discovery, adaptation points, or scaffolding behavior matters. +- Use `scripts/scaffold_golangci.py` with `assets/golangci-template/` when a + target repository needs a deterministic golangci-lint starter instead of ad + hoc config rewriting. + +## Skill Coordination + +- Co-load this skill with `ub-quality` for cross-language structure, + readability, and decision-analysis baselines. +- Defer planning artifacts and resumable delivery to `ub-workflow` when the Go + change itself needs them. + +## Forward-Compatibility Contract + +- Implement for the module's actual declared Go language version and library + floor. +- Bias design toward the next stable upgrade path rather than preserving + older-version behavior by default. +- Do not emit syntax or standard library APIs that the module's declared Go + version cannot compile. +- Add build-tag guarded fallbacks or compatibility shims only when an explicit + support contract or staged migration plan requires them. + +## Core Workflow + +1. Detect runtime and tooling truth from project files (`go.mod`, `go.work`, + CI, task wrappers, existing lint configs). +2. Confirm the Go version contract (`go` directive, optional `toolchain` + directive) before proposing implementation. +3. Compare official guidance, repo truth, and observed code reality for + non-trivial or version-sensitive recommendations. +4. Surface `OFFICIAL_CONFLICT` when authoritative sources, repo truth, or live + code reality materially disagree on a non-trivial recommendation. +5. Surface `UNVERIFIED` when a non-trivial claim could not be confirmed in + official sources after targeted research. +6. Propose at least two viable implementation paths for non-trivial Go work, + with concise pros/cons. +7. Choose the simplest path that satisfies correctness, maintainability, and + project constraints. +8. Write or adjust tests first for behavior changes. +9. Implement minimal changes to pass tests, then refactor while preserving + behavior and boundaries. +10. Run repository-configured validation after modifying Go code, starting + with the smallest relevant scope. +11. Report outcomes, bounded exceptions, any conflict or uncertainty + disclosures, and missing tooling gaps that materially reduce confidence. +12. If a repository wants this lint baseline but has no active golangci-lint + config yet, scaffold the bundled starter and explain the required + repo-local adaptations instead of embedding policy in prose. + +## Version & Research Policy + +- Target the latest stable release of Go for guidance, but implement against + the module's actual declared version contract. +- Detect the project's actual Go version from `go.mod`, `go.work`, CI + configuration, and container or CI images. +- Use web search to verify current best practices, API availability, and + migration guidance against official Go documentation and release notes. +- Treat repo truth as the gold implementation standard when deciding what can + actually ship without breaking the current project. +- Treat official Go docs as the preferred guidance baseline for + forward-looking design and migration-ready patterns. +- If official guidance and repo truth diverge materially on a non-trivial + recommendation, surface `OFFICIAL_CONFLICT`, implement the repo-safe path, + and explain the forward migration path. +- If a non-trivial claim cannot be confirmed in official sources after + targeted research, mark it `UNVERIFIED` or avoid presenting it as settled + guidance. +- Keep conflict and uncertainty disclosure scoped to non-trivial, + version-sensitive, or contested guidance rather than trivial edits. +- Do not generate syntax or standard library behavior only available in + unreleased Go versions unless the user explicitly requests it. +- When the module's declared version is behind latest stable, note the version + gap, recommend an upgrade path, and prefer patterns that will survive that + upgrade cleanly. +- Inspect the host repository's `AGENTS.md` or equivalent instructions when + present for project-specific version policy and tooling; do not assume it + contains this catalog's defaults. +- Do not hardcode version numbers in generated guidance; keep recommendations + evergreen. + +## Freshness Review + +- Volatility: high +- Review recommendation: review on touch and during periodic maintenance, + targeting a quarterly rhythm when practical. +- Trigger signals: Go release changes, standard library additions that replace + common third-party helpers, lint tooling major versions, or repository + validation changes that alter the real enforced baseline. +- Enforcement: advisory only; freshness should highlight review targets + without pretending missing lint or test wiring is automatically a blocker in + every adopting repository. +- Stable core: module-faithful builds, wrapped errors, context-first + concurrency, table-driven tests, and repository-truth validation remain the + durable baseline even when tooling preferences evolve. + +## Implementation Rules + +### Modules and Builds + +- Treat `go.mod` as the source of truth for the module path, Go language + version, and dependency floor. +- Use `go.work` workspaces for multi-module local development instead of + `replace` directives left in committed `go.mod` files. +- Keep `go mod tidy` clean; do not commit unused requirements. +- Keep generated code marked and reproducible via `go generate` with pinned + tool versions. + +### Typing and API Design + +- Define interfaces where they are consumed, not where types are implemented; + keep them small. +- Accept interfaces and return concrete types in exported APIs when practical. +- Use `any` instead of `interface{}` in new code. +- Use generics to remove real duplication in data structures and algorithms; + do not use them to abstract speculatively. +- Prefer standard library generic helpers (`slices`, `maps`, `cmp`) over + hand-rolled loops or third-party equivalents when the module floor provides + them. +- Keep zero values useful; design structs so their zero value is valid or + provide a constructor. + +### Error Handling + +- Wrap errors with `fmt.Errorf("...: %w", err)` and inspect with `errors.Is` + and `errors.As`; aggregate with `errors.Join` when multiple failures matter. +- Choose sentinel errors or typed errors deliberately and keep them part of + the documented API contract. +- Reserve `panic` for programmer errors and unrecoverable invariants; never + use it for expected failure paths in libraries. +- Return early on errors; keep the happy path at minimal indentation. + +### Concurrency + +- Pass `context.Context` as the first parameter of request-scoped and + long-running functions; propagate cancellation and deadlines. +- Prefer structured concurrency helpers such as `errgroup` over hand-managed + `sync.WaitGroup` plus error plumbing. +- Make goroutine lifetimes explicit; every goroutine needs a clear owner and a + clear exit condition to avoid leaks. +- Choose channels for ownership transfer and signaling, mutexes for simple + shared state; do not mix both around the same state. +- Run tests with the race detector when concurrency is touched. + +### Observability and Logging + +- Prefer `log/slog` structured logging with explicit levels and key-value + context in new code. +- Log decisions and failures with actionable context, not noise. + +### Testing + +- Use table-driven tests with subtests (`t.Run`) as the default shape. +- Use `t.Parallel()` where tests are independent, and testing helpers such as + `t.Setenv`, `t.TempDir`, and `t.Cleanup` instead of manual setup/teardown. +- Add fuzz targets for parser-like and boundary-decoding functions when the + repository already embraces fuzzing. +- Keep unit tests fast and hermetic; push external systems behind interfaces. + +### Post-Edit Validation Contract + +- After generating or modifying Go code, run the repository-configured checks + that are relevant to the touched scope. +- Minimum expectation: `gofmt` (or the repo formatter), `go vet`, and targeted + `go test` coverage when tests exist or behavior changed; run golangci-lint + when configured. +- Start narrow (touched packages, targeted tests) and widen only when failures + indicate broader impact. +- Fix newly introduced lint/vet/test failures before finalizing when they are + within the task scope. +- If a configured check cannot be run, state exactly why it was skipped or + blocked. + +Repository truth note: + +- Detect the host repository's actual format, lint, and test baseline before + presenting a command or gate as established truth. +- golangci-lint and `govulncheck` are often strong recommendations for + Go-heavy repos, but present them as active gates only when the host + repository has actually wired them. +- If lint or test expectations are weak or absent, report that gap explicitly + instead of pretending those checks are already enforced. + +### Tooling Gap Warning Policy + +- If repository Go tooling for linting, vulnerability scanning, or testing is + missing, weakly configured, or clearly not wired into normal workflows, warn + the user explicitly. +- Recommend concrete additions when relevant: golangci-lint for lint + aggregation, `govulncheck` for known-vulnerability scanning, race-enabled + test runs in CI, and pre-commit or CI wiring for automatic enforcement. +- Treat missing quality gates as a reported risk, not as silent permission to + skip validation. + +## Toolchain Strategy (Primary + Fallback) + +- Treat repository-configured tooling as the source of truth. If golangci-lint, + staticcheck, or CI checks are configured, generate code that satisfies their + active rules instead of relying on generic defaults. +- Primary stack: `go` toolchain (`build`, `vet`, `test`), `gofmt`, + `golangci-lint`, `govulncheck`. +- Fallback stack when constraints require it: + - lint: `staticcheck` standalone when golangci-lint is not adopted + - formatting: `gofumpt` when the repo opts into stricter formatting + - test running: plain `go test ./...` with `-race` and `-count=1` as needed +- Keep one-command check workflows discoverable in project docs/automation + when possible. + +## Config Resolution And Scaffolding + +Treat real tool config as the source of truth: + +1. inspect `.golangci.yml`, `.golangci.yaml`, `.golangci.toml`, or + `.golangci.json` in the repo root first when they exist +2. inspect task-runner, CI, and Makefile entrypoints before assuming a command + shape +3. use the bundled golangci-lint scaffold only when config is absent and the + adopting repo wants this house style +4. treat the scaffold as a starter that still needs local adaptation for the + module's Go version, enabled linters, and exclusion paths +5. do not silently install dependencies or mutate CI as part of scaffolding + +## Tradeoff Handling + +- Use the shared `ub-quality` decision-analysis baseline for major design or + tooling choices. +- When Go-specific tradeoffs matter, call out compatibility, concurrency + safety, delivery, and modernization cost explicitly in the option pros/cons. +- Default to the safest modern option unless compatibility, policy, or + delivery constraints justify an alternative. +- Document any intentionally deferred cleanup or technical debt retained for + compatibility. + +## Legacy-Avoidance Guardrails + +- Do not use `interface{}` in new code when `any` is available. +- Do not use deprecated `io/ioutil` functions in new code; use the `io` and + `os` equivalents. +- Do not use deprecated global random seeding patterns in new code; prefer the + current standard library random APIs. +- Do not add loop-variable copy workarounds when the module's Go version + already scopes loop variables per iteration. +- Do not import experimental package variants when the standard library + provides the same API at the module's floor. +- Do not use dot imports outside of narrowly scoped test helpers. +- Do not add new `GOPATH`-dependent layouts or tooling assumptions; module + mode is the baseline. +- Do not swallow errors with `_` when the error is meaningful to the caller. + +Migration-aware exception policy: + +- Allow temporary legacy retention only for explicit compatibility constraints + in existing code. +- Document exactly what is retained, why it is retained, and the next + modernization step. + +## Output Requirements + +Treat this section as the stable output expectation for non-trivial Go work in +this catalog. + +When generating or reviewing Go code, include: + +1. Environment note: detected module layout, Go version contract, and + relevant tooling context. +2. Source truth note: repo version/tooling reality and any material gap versus + latest stable guidance. +3. Decision note: chosen approach and at least one alternative. +4. Tradeoff note: concise pros/cons for chosen and rejected paths. +5. Legacy note: removed legacy patterns or bounded exceptions with rationale. +6. Validation note: checks run (format/vet/lint/test) and outcomes. +7. Tooling gap note: missing or weak lint/vulnerability/test enforcement that + the user should consider adding or wiring in. +8. Conflict note when relevant: `OFFICIAL_CONFLICT` or `UNVERIFIED` with a + concise explanation and the implementation consequence. + +When this skill is used to scaffold golangci-lint into another repository, +also include: + +1. which files were created or skipped +2. which repo-local lint settings still need adaptation +3. the exact lint command the target repo should run next + +## References + +- Core rules and source-backed policy: `references/go-standards.md` +- Repository-specific workflow and validation baseline: + `references/repository-go-workflows.md` +- Lint config discovery and starter behavior: + `references/golangci-config-resolution.md` + +## Completion Checklist + +- Latest stable Go baseline is enforced intentionally against the module's + declared version contract. +- Interfaces stay small and consumer-defined; generics are used only where + they remove real duplication. +- Errors are wrapped, inspectable, and part of the documented contract. +- Concurrency has explicit ownership, cancellation, and leak-free lifetimes. +- Repository-configured format/vet/lint/test checks were run after edits, or + an explicit reason was reported. +- No new legacy output is introduced. +- Missing lint/vulnerability/test enforcement was reported when relevant. +- Any retained legacy behavior is documented with a migration path. +- Any material official-source conflict or unverified non-trivial guidance is + disclosed explicitly when relevant. +- Any scaffolded golangci-lint starter was reported as a starter profile + rather than silent repo policy. diff --git a/.agents/skills/ub-go/assets/golangci-template/.golangci.yml b/.agents/skills/ub-go/assets/golangci-template/.golangci.yml new file mode 100644 index 0000000..75c5fac --- /dev/null +++ b/.agents/skills/ub-go/assets/golangci-template/.golangci.yml @@ -0,0 +1,34 @@ +# UB Go starter profile for golangci-lint. +# Starter only: adapt enabled linters, exclusions, and settings to repo truth. +# Requires a golangci-lint major version that uses this config schema +# (declared by the `version` key below); see +# references/golangci-config-resolution.md in the ub-go skill. +version: "2" + +linters: + # `standard` enables the curated default set (errcheck, govet, ineffassign, + # staticcheck, unused). Additions below are high-signal, low-noise picks. + default: standard + enable: + - misspell + - nolintlint + - revive + - unconvert + - unparam + settings: + nolintlint: + # Inline suppressions must say which linter and why. + require-explanation: true + require-specific: true + exclusions: + # Keep lint noise out of generated code; extend paths to repo truth. + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ + +formatters: + enable: + - gofmt + - goimports diff --git a/.agents/skills/ub-go/references/go-standards.md b/.agents/skills/ub-go/references/go-standards.md new file mode 100644 index 0000000..a21a09d --- /dev/null +++ b/.agents/skills/ub-go/references/go-standards.md @@ -0,0 +1,260 @@ +# Go Standards (Portable, Non-Formatting, Latest Stable) + +> Verify these standards against the latest official Go documentation and +> release notes via web search. + +This reference codifies Go standards for the target repository or project +after local module and tooling truth has been inspected. +It is aligned to official Go and tooling guidance without assuming one +specific host repository. + +## 1. Platform and Environment + +- Runtime baseline: latest stable Go (detect from `go.mod` and verify via web + search). +- Module mode is the baseline; treat `go.mod` as the source of truth for the + module path, language version, and dependency floor. +- Use `go.work` for multi-module local development instead of committing + `replace` directives meant only for local work. +- Pin developer tools with `tool` directives in `go.mod` and run them via + `go tool` when the module floor supports it; treat the older `tools.go` + blank-import pattern as migration-only. +- Keep builds reproducible: pinned tool versions, tidy module files, and no + machine-specific paths. + +## 2. Implementation Strategy + +- Prefer clear, boring Go over clever abstractions; readability is a hard + constraint. +- Keep packages small and purpose-named; avoid `util` and `common` grab bags. +- Implement against the module's declared Go version, but bias design toward + the next stable upgrade path instead of preserving older-version behavior by + default. +- Use streaming (`io.Reader`/`io.Writer`) over whole-buffer processing for + large data. +- For non-trivial work, explicitly compare at least two approaches with + pros/cons. + +## 2A. Source Truth And Disclosure + +- Treat target-project truth as the shipping constraint: detect the active Go + version, build tags, and tooling before recommending implementation details. +- Treat official Go docs and release notes as the preferred guidance baseline + for forward-looking design and migration-ready patterns. +- If official docs, target-project truth, or live code reality materially + disagree on a non-trivial recommendation, surface `OFFICIAL_CONFLICT`, + implement the project-safe path, and explain the forward migration path. +- If a non-trivial claim cannot be confirmed in official sources after + targeted research, mark it `UNVERIFIED` or avoid presenting it as settled + guidance. +- Keep these disclosures scoped to non-trivial, version-sensitive, or + contested guidance rather than every trivial edit. + +## 3. Interfaces, Generics, and API Design + +- Define interfaces at the consumer, keep them small, and let concrete types + satisfy them implicitly. +- Accept interfaces, return concrete types in exported APIs when practical. +- Use `any` in new code instead of `interface{}`. +- Use generics for real duplication in containers, algorithms, and typed + helpers; avoid speculative type parameters on APIs with one concrete use. +- Newer floors extend generics with generic type aliases and generic + methods; use them where they simplify real APIs, and remember interfaces + still cannot declare generic methods. +- Prefer standard library generic helpers (`slices`, `maps`, `cmp`) when the + module floor provides them, instead of experimental package variants or + hand-rolled loops. +- Design useful zero values; when a zero value cannot be valid, provide a + constructor and document it. +- Keep exported surfaces minimal; start unexported and promote deliberately. + +## 4. Error Handling + +- Wrap with `fmt.Errorf("context: %w", err)`; inspect with `errors.Is` and + `errors.As`; aggregate with `errors.Join` when several failures matter. +- Choose the error contract deliberately: + - sentinel errors for simple, stable conditions callers match on + - typed errors for structured data callers need + - opaque wrapped errors when callers only need the message chain +- Do not panic for expected failures; reserve `panic` for programmer errors + and unrecoverable invariants, and never let it cross package boundaries in + libraries. +- Return early on failure and keep the happy path at minimal indentation. +- Do not discard meaningful errors with `_`; if an error is intentionally + ignored, make that visible and justified. + +## 5. Concurrency + +- `context.Context` is the first parameter of request-scoped and long-running + functions; propagate cancellation and deadlines instead of inventing custom + stop channels. +- Prefer structured concurrency (`errgroup` or equivalent) over hand-managed + `sync.WaitGroup` plus error plumbing. +- Every goroutine needs an owner and an exit condition; unbounded goroutine + spawning and fire-and-forget goroutines are defects. +- Channels transfer ownership and signal; mutexes guard simple shared state. + Do not mix both around the same state. +- Use the race detector (`go test -race`) whenever concurrency is touched. +- For CPU-bound parallelism, size worker pools from workload and available + cores instead of unbounded fan-out. + +## 6. Modern Standard Library Preferences + +- `log/slog` for structured logging with levels and key-value context. +- `slices`, `maps`, and `cmp` for comparisons, sorting, and collection + helpers. +- Builtin `min`, `max`, and `clear` where they improve clarity. +- Current standard library random APIs instead of deprecated global seeding + patterns. +- Range-over-integer and iterator-based range loops (`iter.Seq`) where the + module floor supports them and they improve clarity. +- `errors.Join`, `context.WithTimeoutCause`, and cause-aware cancellation + where failure attribution matters. +- `os.Root` for filesystem access rooted in untrusted or user-supplied + directories, so path traversal and symlink escapes fail by construction. +- `unique.Handle` for interning comparable values in canonicalization-heavy + or cache-heavy code paths. +- The current `encoding/json` implementation and its options; check the + module floor before relying on v2-specific behavior such as strict UTF-8 + rejection. + +Notes: + +- Loop variables are scoped per iteration in current Go; do not add copy + workarounds (`v := v`) in code targeting a floor that already has this + behavior. +- Check the module's `go` directive before using newer standard library + packages; recommend the upgrade rather than silently emitting incompatible + code. + +## 7. Imports and Dependency Hygiene + +- Group imports as stdlib, third-party, local; keep grouping consistent with + the repo formatter (`gofmt`/`goimports`/`gofumpt`). +- Prefer the standard library first; add dependencies only when they provide + clear correctness, maintainability, or DX value. +- Keep `go mod tidy` clean and review indirect dependency growth. +- No dot imports outside narrowly scoped test helpers. +- Pin tool dependencies with `go.mod` `tool` directives (or the legacy + `tools.go` pattern on older floors) so generated code is reproducible. + +## 8. Toolchain Policy + +### Baseline + +- `gofmt` formatting is non-negotiable; `gofumpt` is an optional stricter + layer a repo may adopt. +- `go vet` runs on touched packages as a minimum static check. +- golangci-lint aggregates linters when the repo adopts it; treat its active + config as the lint policy source of truth. +- `govulncheck` scans for known vulnerabilities reachable from the code path; + recommend it for services and released tooling. + +### Tooling Stance (Primary + Fallback) + +Primary stack: + +- Build/test: `go build`, `go test` (with `-race` where concurrency matters) +- Vet: `go vet` +- Lint aggregation: `golangci-lint` +- Vulnerability scanning: `govulncheck` + +Fallback options when constraints require them: + +- `staticcheck` standalone when golangci-lint is not adopted +- plain `go test ./...` workflows when no task runner exists + +Operational guidance: + +- Treat actual lint config files as the policy source of truth when they + exist; use starter scaffolds only when a repo wants this house style and + does not already have active config. +- After generating or modifying Go code, run the configured checks that apply + to the touched scope instead of assuming code generation is sufficient. +- Start with touched packages or targeted tests, then widen only when + failures indicate broader impact. +- If configured tooling cannot be run, report the exact blocker. +- If linting, vulnerability scanning, or testing is missing or not wired into + routine workflows, warn the user and recommend concrete additions rather + than silently accepting the gap. + +## 9. Function and API Design + +- Keep functions focused, composable, and testable. +- Use narrow signatures; avoid boolean parameter flags when separate functions + or an options struct is clearer. +- Use functional options or config structs for exported constructors with many + optional settings; pick one style per package and stay consistent. +- Validate boundary data near entry points (HTTP handlers, CLI parsing, queue + consumers), not deep in domain logic. +- Document exported identifiers with standard doc comments that start with the + identifier name. + +## 10. Architecture and Boundaries + +- Preserve layered flow: transport -> service -> repository -> data. +- Keep domain language consistent between implementation and tests. +- Avoid leaking storage or transport details into domain logic. +- Prefer composition over embedding-driven pseudo-inheritance when both are + viable. +- Keep `internal/` packages for code that must not become public API. + +## 11. Testing Practice + +- Table-driven tests with subtests (`t.Run`) are the default shape. +- Use `t.Parallel()` for independent tests and `t.Setenv`, `t.TempDir`, + `t.Cleanup` instead of manual setup/teardown. +- Keep unit tests hermetic; put external systems behind interfaces and use + small hand-written fakes before reaching for mock frameworks. +- Add fuzz targets for parser-like and untrusted-input functions when the + repository embraces fuzzing. +- Use `testing/synctest` for deterministic tests of concurrent, + time-dependent code where the module floor provides it, instead of sleeps + and flaky timing assumptions. +- Use benchmarks for performance-sensitive paths and keep them runnable in CI + or on demand. +- Treat missing tests or missing race-enabled runs on concurrent code as + explicit quality risks worth reporting. + +## 12. Legacy Patterns to Avoid (Migration-Only Exceptions) + +- `interface{}` in new code where `any` is available. +- Deprecated `io/ioutil` functions in new code. +- Deprecated global random seeding patterns in new code. +- Loop-variable copy workarounds on floors with per-iteration scoping. +- Experimental package imports (`golang.org/x/exp/...`) for APIs the standard + library already provides at the module floor. +- `GOPATH`-dependent layouts or tooling assumptions. +- Panic-driven control flow for expected errors. +- Dot imports outside narrowly scoped test helpers. + +Exception policy: + +- Temporary retention is allowed only for explicit compatibility constraints. +- Document what is retained, why it remains, and the follow-up modernization + step. + +## 13. Source Map (Official References) + +- Go release notes index: +- Go modules reference (`tool` directives): +- Workspaces (`go.work`): +- Traversal-resistant file APIs (`os.Root`): +- Deterministic concurrency tests: +- Value interning (`unique`): +- Effective Go: +- Go Code Review Comments: +- Generics tutorial: +- `log/slog` docs: +- `slices` docs: +- `maps` docs: +- `cmp` docs: +- `errors` docs (`Is`, `As`, `Join`): +- `context` docs: +- `errgroup` docs: +- Data race detector: +- Fuzzing: +- Vulnerability management (`govulncheck`): +- golangci-lint docs: +- Staticcheck docs: +- gofumpt: diff --git a/.agents/skills/ub-go/references/golangci-config-resolution.md b/.agents/skills/ub-go/references/golangci-config-resolution.md new file mode 100644 index 0000000..2762d10 --- /dev/null +++ b/.agents/skills/ub-go/references/golangci-config-resolution.md @@ -0,0 +1,54 @@ +# golangci-lint Config Resolution + +Use this reference when deciding what lint policy actually applies in a target +repository and whether the bundled starter should be scaffolded. + +## Resolution Order + +1. An explicit config path passed on the command line or in CI flags wins. +2. Otherwise golangci-lint searches from the working directory upward for + `.golangci.yml`, `.golangci.yaml`, `.golangci.toml`, or `.golangci.json`. +3. If no config exists, golangci-lint runs with its built-in defaults. + +Treat the first config found through that order as the repository's real lint +policy. Do not present the bundled starter as active policy when a local +config already exists. + +## Config Format Awareness + +- Current golangci-lint major versions declare a `version` key at the top of + the config file and split linters and formatters into separate sections. +- Older configs without a `version` key may target a previous major version; + check the installed golangci-lint version in CI or tool manifests before + editing rules, and prefer the project's official migration command over + hand-porting settings. +- Do not mix config schema generations in one file. + +## What Counts As Real Policy + +1. the discovered config file and its enabled/disabled linters +2. CI flags that override or scope lint runs +3. inline `//nolint:` directives with reasons, as bounded local exceptions + +## Scaffolding Behavior + +Use `scripts/scaffold_golangci.py` with `assets/golangci-template/` only when: + +1. no golangci-lint config exists anywhere in the repository, and +2. the adopting repository wants this catalog's starter baseline + +The scaffold copies files only when they are missing and reports write/skip +results deterministically. It never overwrites existing config, installs +binaries, or mutates CI. + +## Adaptation Points After Scaffolding + +1. align the lint run with the module's Go version (golangci-lint reads it + from `go.mod`; pin explicitly only when needed) +2. review the enabled linter set against repo noise tolerance +3. add exclusion paths for generated code, vendored code, or migration + directories +4. wire the lint command into the repo's task runner or CI so the policy is + actually enforced +5. declare how golangci-lint is installed (CI action, tool dependency, or + documented local install) instead of assuming a global binary diff --git a/.agents/skills/ub-go/references/repository-go-workflows.md b/.agents/skills/ub-go/references/repository-go-workflows.md new file mode 100644 index 0000000..78563df --- /dev/null +++ b/.agents/skills/ub-go/references/repository-go-workflows.md @@ -0,0 +1,99 @@ +# Repository Go Workflows + +Use this reference to resolve a repository's actual Go module, build, and +validation truth before recommending commands or tooling. + +It should help the agent inspect local reality, not hardcode one repository's +workflow into a distributable skill. + +## Module and Version Baseline + +Inspect the target repository for: + +1. the `go` directive (language floor) and optional `toolchain` directive in + `go.mod` +2. `go.work` workspaces and their member modules for multi-module repos +3. whether the repo is an application, a library, a CLI tool, or a mixed + workspace +4. the active golangci-lint config location, if any +5. build tags, cgo usage, and platform constraints that change what compiles +6. `go generate` usage and how generator tools are pinned + +## Enforced Validation Baseline + +Treat the repository's wired checks as the real baseline. +Inspect task runners, Makefiles, CI, and pre-commit hooks for the actual +Go-oriented gates. + +Common examples: + +1. `gofmt` or `gofumpt` formatting checks +2. `go vet` or golangci-lint runs +3. targeted or full `go test` commands, with or without `-race` +4. `govulncheck` or dependency scanning +5. build matrix checks across platforms or Go versions + +## Not Yet Repository-Wired + +Recommend extra tools only as explicit gaps when they are not already wired +into the repository. + +Typical examples: + +1. `golangci-lint` +2. `govulncheck` +3. race-enabled test runs in CI + +When advising users inside any repository: + +1. treat the detected `go` directive as the shipping constraint even when + official docs describe newer features +2. recommend missing tools as improvements, not as if they were already part + of the enforced baseline +3. keep guidance consistent with the repo's actual workload instead of + assuming a service-backend shape +4. do not recommend parser-incompatible newer syntax or standard library APIs + as if they were currently shippable below the module floor + +## Practical Command Patterns + +Use the command shape that matches the target repository's tooling. + +Recommended resolution order: + +1. a repo wrapper such as `task`, `make`, or another documented helper + entrypoint +2. direct `go` toolchain commands (`go build ./...`, `go vet ./...`, + `go test ./...`) scoped to touched packages first +3. lint and scan commands (`golangci-lint run`, `govulncheck ./...`) only + after confirming they are installed or wired locally + +Scoping guidance: + +1. start with the touched packages (`go test ./pkg/...`) before running the + full module +2. add `-race` when concurrency was touched +3. use `-run` and `-count=1` for targeted, cache-bypassing test iterations + +## Starter Scaffolding + +This skill ships a reusable golangci-lint starter profile and helper: + +1. `assets/golangci-template/.golangci.yml` +2. `scripts/scaffold_golangci.py` + +Use them only when a target repository wants this lint baseline and does not +already have active golangci-lint config. Adapt the copied file to local repo +truth after scaffolding. + +## Packaging and Release Guidance + +Because adopting repositories range from libraries to services to CLI tools: + +1. keep `main` packages thin; put logic in importable packages so it stays + testable +2. use `internal/` to keep implementation details out of the public API +3. for released tools, prefer reproducible builds and version stamping via + `-ldflags` or build info rather than hand-edited version constants +4. align generated Go with the active lint rule set, including error checking + and modernization rules diff --git a/.agents/skills/ub-go/scripts/scaffold_golangci.py b/.agents/skills/ub-go/scripts/scaffold_golangci.py new file mode 100644 index 0000000..6e13b95 --- /dev/null +++ b/.agents/skills/ub-go/scripts/scaffold_golangci.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Scaffold the UB Go golangci-lint starter into a target repository.""" + +from __future__ import annotations + +import argparse +from pathlib import Path + +EXISTING_CONFIG_NAMES = ( + ".golangci.yml", + ".golangci.yaml", + ".golangci.toml", + ".golangci.json", +) + + +def copy_if_missing(source: Path, target: Path) -> str: + if target.exists(): + return f"skip {target}" + target.write_text(source.read_text(encoding="utf-8"), encoding="utf-8") + return f"write {target}" + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Copy the UB Go golangci-lint starter into a target repository." + ) + parser.add_argument( + "target_root", + help="Target repository root that should receive the golangci-lint starter.", + ) + args = parser.parse_args() + + script_path = Path(__file__).resolve() + template_root = script_path.parent.parent / "assets" / "golangci-template" + target_root = Path(args.target_root).resolve() + + if not target_root.exists() or not target_root.is_dir(): + raise SystemExit( + f"Target root does not exist or is not a directory: {target_root}" + ) + + existing = [ + target_root / name + for name in EXISTING_CONFIG_NAMES + if (target_root / name).exists() + ] + if existing: + for path in existing: + print(f"skip {path} (existing golangci-lint config is repo truth)") + print() + print("No files written: adapt the existing config instead of scaffolding.") + return 0 + + results = [ + copy_if_missing( + template_root / ".golangci.yml", target_root / ".golangci.yml" + ) + ] + + for line in results: + print(line) + + print() + print("Next steps:") + print("1. Review enabled linters and exclusion paths against repo noise tolerance.") + print("2. Ensure golangci-lint is installed or wired in CI for the target repo.") + print("3. Run: golangci-lint run ./...") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index addf814..14a4806 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -12,7 +12,7 @@ "plugins": [ { "name": "uncle-bob", - "description": "11 domain skills (workflow, quality, authoring, governance, customizations, CSS, Tailwind, TypeScript, Python, Vue, Nuxt) and 1 custom agent (teacher).", + "description": "12 domain skills (workflow, quality, authoring, governance, customizations, CSS, Tailwind, TypeScript, Python, Vue, Nuxt, Go) and 1 custom agent (teacher).", "version": "1.0.0", "author": { "name": "robert-hoffmann", diff --git a/AGENTS.md b/AGENTS.md index b39e181..a4140d6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -164,6 +164,7 @@ trigger rules inside the skill before producing work that depends on them. | ub-quality | Cross-language code quality: design patterns, formatting, documentation, structure, refactoring. | `.agents/skills/ub-quality/SKILL.md` | | ub-authoring | Shared authoring conventions for routing quality, non-use boundaries, naming, progressive disclosure, and reusable skill guidance. | `.agents/skills/ub-authoring/SKILL.md` | | ub-css | Plain CSS & Vue/Nuxt style blocks: design tokens, cascade layers, native nesting, container queries, progressive enhancement. | `.agents/skills/ub-css/SKILL.md` | +| ub-go | Go (latest stable): module-faithful builds, small interfaces and generics, wrapped errors, context-first concurrency, golangci-lint workflows. | `.agents/skills/ub-go/SKILL.md` | | ub-nuxt | Nuxt (latest stable): typed composables, SSR/SSG/hybrid rendering, runtime config, Nitro/server routes, app-directory semantics. | `.agents/skills/ub-nuxt/SKILL.md` | | ub-python | Python (latest stable): typed patterns, boundary validation, structured error handling, pytest/ruff/mypy workflows. | `.agents/skills/ub-python/SKILL.md` | | ub-tailwind | Tailwind CSS (latest stable): setup, migration, debugging across standalone HTML, Vue + Vite, and Nuxt projects. | `.agents/skills/ub-tailwind/SKILL.md` | diff --git a/README.md b/README.md index 055a0ac..f1a0433 100644 --- a/README.md +++ b/README.md @@ -281,6 +281,7 @@ core stack has already shaped the work. | ub-vuejs | Vue workflow for modern SFC authoring, strict TypeScript, current reactivity patterns, and hydration-aware component design. | | ub-nuxt | Nuxt workflow for app-structure detection, runtime/rendering choices, modern framework-native patterns, and migration away from legacy layouts. | | ub-tailwind | Tailwind workflow for environment detection, CSS-first setup, modern directives, upgrade-safe migration, and framework-specific integration. | +| ub-go | Go workflow for module-truth detection, small consumer-side interfaces, wrapped errors, structured concurrency, and lint-baseline validation. | | Agent | Description | | ----------------- | ------------------------------------------------------------------------------------ | diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index ba26aff..42d243c 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -138,6 +138,10 @@ export default withMermaid({ text : "UB Tailwind", link : "/skills/ub-tailwind", }, + { + text : "UB Go", + link : "/skills/ub-go", + }, ], }, { diff --git a/docs/skills/index.md b/docs/skills/index.md index ceaf30d..5121860 100644 --- a/docs/skills/index.md +++ b/docs/skills/index.md @@ -41,6 +41,8 @@ on. container queries, accessibility, and progressive enhancement. - [UB Tailwind](./ub-tailwind): Tailwind setup, CSS-first configuration, framework recipes, tokens, and migration guardrails. +- [UB Go](./ub-go): modern Go, module truth, small interfaces, generics + discipline, wrapped errors, structured concurrency, and lint baselines. ## References And Progressive Disclosure diff --git a/docs/skills/ub-go.md b/docs/skills/ub-go.md new file mode 100644 index 0000000..dc1ea4f --- /dev/null +++ b/docs/skills/ub-go.md @@ -0,0 +1,100 @@ +# UB Go + +Source: `.agents/skills/ub-go/SKILL.md` + +`ub-go` guides Go implementation, tests, module hygiene, concurrency, and +repository logic. It favors modern Go patterns while still implementing +against the target module's real declared Go version and tooling. It treats +repository truth as the shipping boundary for every recommendation. + +## Core Principles + +- Detect repository Go truth first: `go.mod` language version, workspaces, + build tags, and wired validation gates before choosing commands or syntax. +- Write modern Go against the module floor: current syntax where allowed and + no unreleased features presented as shippable. +- Keep interfaces small and consumer-defined; accept interfaces, return + concrete types; use generics only where they remove real duplication. +- Wrap errors with context and keep them inspectable; sentinel or typed + errors are deliberate API contract decisions, never accidents. +- Make concurrency structured: context-first cancellation, owned goroutine + lifetimes, and race-detector runs when concurrency is touched. +- Prefer modern standard library capabilities over third-party or + experimental equivalents the module floor already covers. +- Test behavior with table-driven subtests and hermetic helpers; report + missing race or fuzz coverage as explicit risk. +- Modernize incrementally instead of adding compatibility layers or + retaining legacy Go patterns by habit. + +## Behavior In Practice + +- Starts by reading `go.mod`, `go.work`, CI, Makefiles or task wrappers, and + lint configs before saying which Go command or syntax is safe. +- Compares repo truth with official Go documentation and release notes. If + they disagree materially, it implements the repo-safe path and reports the + migration path instead of pretending there is no conflict. +- Designs APIs around Go idioms: useful zero values, small consumer-side + interfaces, `any` instead of `interface{}`, and standard library generic + helpers where the module floor provides them. +- Treats error flow as part of the API: `%w` wrapping, `errors.Is`/`As` + inspection, joined errors where multiple failures matter, and early + returns that keep the happy path flat. +- Makes goroutine ownership explicit: context propagation, structured groups + over hand-managed wait groups, and clear exit conditions so nothing leaks. +- Prefers structured logging with levels and key-value context in new code. +- Shapes tests as table-driven subtests with parallel-safe helpers, and adds + fuzz targets for untrusted-input functions where the repo embraces fuzzing. +- Treats `gofmt`, `go vet`, golangci-lint, and `govulncheck` as detected repo + truth. Missing lint, vulnerability, or test wiring is reported as a tooling + gap, not silently ignored. +- Avoids new legacy output: no fresh `interface{}`, deprecated `io/ioutil` + helpers, deprecated random seeding, loop-variable copy workarounds on + modern floors, or panic-driven control flow for expected errors. + +## Reference Highlights + +- `.agents/skills/ub-go/references/go-standards.md`: encodes the modern Go + baseline: module-faithful builds, consumer-side interfaces, generics + discipline, wrapped errors, structured concurrency, modern standard + library preferences, testing practice, and legacy-avoidance rules with an + official source map. +- `.agents/skills/ub-go/references/repository-go-workflows.md`: how to + discover the target repository's actual Go workflow, module and version + contract, active lint and test commands, build tags, and CI truth before + presenting any command as required. +- `.agents/skills/ub-go/references/golangci-config-resolution.md`: + golangci-lint config discovery order, config format awareness, what counts + as real lint policy, when the bundled starter can be scaffolded, and which + settings remain repo-local decisions. +- `.agents/skills/ub-go/assets/golangci-template/.golangci.yml`: starter + golangci-lint profile used only when a repository explicitly wants the + house baseline and lacks an active local config. + +## Progressive Disclosure + +The main skill sets the Go posture. Deeper references load when the task +needs specifics: Go standards for implementation, workflow discovery for repo +validation, and golangci-lint resolution plus the bundled scaffold helper +only when lint setup is actually in scope. + +## Common Invocation Examples + +- "Use `ub-go` to add tests for this handler." +- "Review this package for error wrapping and goroutine leaks." +- "Refactor this service without changing its public API." +- "Scaffold the golangci-lint baseline for a repo that has no lint config + yet." + +## Boundaries + +Do not use it as a general documentation or workflow-planning skill. Pair it +with `ub-workflow` when the Go change itself needs planning artifacts. + +## Tradeoffs + +Strength: makes Go changes safer by aligning modern language practice with +the repository's actual validation surface and concurrency risks. + +Cost: strict interface and error-contract work can reveal follow-up +modernization outside the initial task, especially in older or lint-free +projects.