hunter-party-go/type-hunter-go/SKILL.md
Audit Go type definitions for design debt — duplicated struct shapes, misused generics, under-constrained type parameters, embedding antipatterns, poor enum patterns, and disorganized type architecture. Type structure and maintainability. Use when: reviewing type definitions for maintainability, reducing type duplication, simplifying over-engineered generics, or reorganizing type architecture after growth.
npx skillsauth add skyosev/agent-skills type-hunter-goInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Audit Go type definitions for type design debt — places where types are duplicated instead of composed, generics are more complex than they need to be, embedding leaks implementation, or type organization has drifted. The goal: types are composed from single sources of truth, use the simplest constructs that work, and are easy to find and maintain.
Compose, don't duplicate. When two types share structure, consider composition: explicit shared fields via a common struct, or delegation through a helper function. Prefer explicit field composition over embedding when only partial reuse is needed — embedding promotes the full method set, which can leak unintended API surface. Parallel struct definitions that duplicate fields are a maintenance trap — a change to one must be replicated in every copy.
Simplest construct wins. If a concrete type works, don't add a generic. If a struct works, don't use an interface. Reach for generics only when the same logic genuinely operates on multiple types. Type-level code must be readable and maintainable.
Constraints document intent. A type parameter [T any] accepts anything — it communicates nothing. [T comparable] or [T io.Reader] tells the reader and the compiler what T must be. Every generic should have the
tightest constraint that works.
Generics must vary. A type parameter that is always instantiated with the same concrete type is indirection, not
abstraction. If Cache[T] is always Cache[User], remove T and use User directly. Introduce generics when
there are 2+ distinct instantiations.
Embedding is composition, not inheritance. Embedding a struct promotes all its methods and fields. If the outer struct only uses 2 of 10 promoted methods, the embedding leaks unnecessary API surface. Embed intentionally; prefer explicit field + delegation when only partial access is needed.
Types have a place. Shared domain types belong in a dedicated package. Implementation-local types belong in their package. A 500-line file mixing domain types with internal helpers is disorganized. A type defined in one package but used by five others may be misplaced.
Two or more struct types that represent the same domain concept with the same or near-identical field set.
Signals:
const blocks or string sets representing the same valuesAction: Identify the canonical source type. Derive variants by embedding, composition, or separate request types that explicitly reference the canonical type. Delete the duplicates.
Struct embedding that leaks implementation details or promotes unintended API surface.
Signals:
sync.Mutex) in an exported struct, promoting Lock()/Unlock() to the APIAction: Replace embedding with an explicit unexported field and delegate only the needed methods. Embed sync.Mutex
only in unexported structs, or use an unexported field.
Generic types or functions that are more complex than the problem requires, or generics applied where concrete types would be simpler.
Signals:
any when a narrower constraint would workAction: Remove the generic and use the concrete type. Tighten constraints. Reserve generics for genuinely polymorphic data structures and algorithms.
Constant groups that lack type safety, have gaps in iota sequences, or mix concerns.
Signals:
iota constants without a named type (bare const ints)iota with gaps or manual assignments that make the sequence fragileString() method for enum typesUnknown = 0) that are never checkedAction: Define a named type. Use iota consistently. Add a String() method and a validation function for
external input. Consider using go generate with stringer.
Generic type parameters with no meaningful constraint.
Signals:
[T any] where T is always used in a context that assumes comparable or a specific interface[T any] when only int and string are passedAction: Add the tightest constraint that matches actual usage. If the generic accepts only one type, remove it.
Misuse of type aliases (=) vs named types, or missing named types where they'd add clarity.
Signals:
string or int used for domain identifiers (UserID, OrderID) where a named type would prevent mixingtype X = Y alias that serves no purpose (not for gradual migration)Action: Use named types for domain identifiers to prevent mixing. Use type aliases only for gradual migration or compatibility layers. Remove aliases that add no value.
Type definitions that have drifted into the wrong locations or accumulated into unwieldy files.
Signals:
Do not flag:
Action: Collocate implementation-local types with their code. Centralize shared domain types in a domain package. Split large type files by domain concept. Ensure one canonical import path per type.
main/master)BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main)
SCOPE=$(git diff --name-only $(git merge-base HEAD $BASE)...HEAD)
Constrain all subsequent scans to the resolved surface.EXCLUDE='--glob !**/*_test.go --glob !**/vendor/** --glob !**/testdata/**'
# Struct definitions
rg 'type\s+\w+\s+struct' --type go $EXCLUDE
# Interface definitions
rg 'type\s+\w+\s+interface' --type go $EXCLUDE
# Generic type parameters
rg '\[\w+\s+(any|comparable|\w+\.\w+)' --type go $EXCLUDE
# Type aliases
rg 'type\s+\w+\s*=' --type go $EXCLUDE
# Named types (non-struct, non-interface)
rg 'type\s+\w+\s+(string|int|int64|float64|uint)' --type go $EXCLUDE
# iota enums
rg 'iota' --type go $EXCLUDE
# Embedding
rg '^\s+\*?\w+$' --type go $EXCLUDE
# Large type files
rg -c 'type\s+\w+\s+' --type go $EXCLUDE --sort path
# sync.Mutex embedding
rg 'sync\.(Mutex|RWMutex)' --type go $EXCLUDE
For each generic type: Is the constraint tight? Does the parameter vary? Is a concrete type simpler? For each embedding: Is the full promoted surface intentional? Would an explicit field be cleaner? For each enum pattern: Is the type safe? Is there validation?
Save as YYYY-MM-DD-type-hunter-audit-{$LLM-name}.md in the project's docs folder (or project root if no docs folder exists).
# Type Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
## Findings
### Type Duplication
| # | Types | Locations | Overlap | Action |
| - | ----- | --------- | ------- | ------ |
| 1 | `User`, `UserDTO` | file:line, file:line | 8/10 fields identical | Compose DTO from embedded User |
### Embedding Antipatterns
| # | Struct | Location | Embedded Type | Issue | Action |
| - | ------ | -------- | ------------- | ----- | ------ |
| 1 | `Server` | file:line | `sync.Mutex` | Promotes Lock/Unlock to API | Use unexported field |
### Generic Overuse
| # | Type/Function | Location | Parameter | Instantiations | Action |
| - | ------------- | -------- | --------- | -------------- | ------ |
| 1 | `Cache[T]` | file:line | `T any` | Always `User` | Remove generic, use `User` |
### Poor Enum Patterns
| # | Type | Location | Issue | Action |
| - | ---- | -------- | ----- | ------ |
| 1 | bare `const` ints | file:line | No named type, no validation | Define named type with iota |
### Under-Constrained Generics
| # | Type/Function | Location | Parameter | Action |
| - | ------------- | -------- | --------- | ------ |
| 1 | `process[T any]()` | file:line | `T` always comparable | Add `comparable` constraint |
### Type Alias Misuse
| # | Type | Location | Issue | Action |
| - | ---- | -------- | ----- | ------ |
| 1 | `type UserID = string` | file:line | Alias doesn't prevent mixing with OrderID | Use named type |
### Type Organization
| # | File | Location | Issue | Action |
| - | ---- | -------- | ----- | ------ |
| 1 | `types.go` | file:line | 400 lines, mixes domain + internal types | Split by domain concept |
## Recommendations (Priority Order)
1. **Must-fix**: {type duplication with drift risk, embedding leaking sensitive API surface}
2. **Should-fix**: {generic overuse, poor enum patterns, under-constrained generics}
3. **Consider**: {type organization, alias cleanup, named type introduction}
file/path.go:line with the exact type definition.development
Transforms vague feature ideas into precise, codebase-grounded technical requirements. Use when requirements are ambiguous/incomplete, the user struggles to describe behavior, terminology is unclear, or multiple concepts are mixed. Output is a requirements spec—NOT an implementation plan.
tools
Audit TypeScript type definitions for design debt — duplicated shapes, missing derivations, over-engineered generics, under-constrained type parameters, reinvented utility types, and disorganized type architecture. Type structure and maintainability, not type enforcement. Use when: reviewing type definitions for maintainability, reducing type duplication, simplifying over-engineered type-level logic, or reorganizing type architecture after growth.
development
Audit TypeScript test code for quality gaps — missing coverage on critical paths, brittle tests coupled to implementation, over-mocking, assertion-free tests, missing edge cases, and duplicated test setup. Focuses on test effectiveness, not production code structure. Use when: reviewing TypeScript test suites for reliability, reducing false-positive test failures, improving coverage of critical business logic, or cleaning up test debt.
tools
Audit TypeScript class and interface design for SOLID violations — god classes, rigid extension points, broken substitutability, fat interfaces, and concrete dependency chains. Focuses on responsibility assignment and abstraction fitness. Use when: reviewing class hierarchies, preparing for extension with new variants, reducing coupling between services, or improving testability of class-heavy code.