hunter-party-go/boundary-hunter-go/SKILL.md
Audit Go packages for boundary violations — leaked internals via exports, coupling through shared types, import cycles, missing internal/ packages, over-exported APIs, and dependency direction violations. Use when: reviewing package structure, shrinking public API surface, enforcing encapsulation, preparing packages for replacement, or untangling tight coupling between layers.
npx skillsauth add skyosev/agent-skills boundary-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 code for package boundary violations — places where implementation details leak through exports, where packages reach into each other's internals, or where coupling makes replacement impossible. The goal: every package is a black box, replaceable from its exported API alone.
Go enforces some boundaries at the language level (unexported identifiers, import cycle prohibition, internal/
packages), but many boundary violations are still possible within those constraints.
A package is its exported API. Exported identifiers are promises. Unexported identifiers are implementation details. Exports should describe what the package does, never how it does it. If a consumer must understand internals to use the API correctly, the boundary is broken.
Minimal exported surface. Export only what is consumed or serves as a deliberate extension point. Every exported symbol is a coupling point that constrains future changes. Go's convention: start unexported, export when needed.
Depend on interfaces, not concretions. Packages should depend on interfaces (defined at the consumer) — not on concrete types from other packages. If package A imports a concrete struct from package B, A is coupled to B's implementation. If A defines an interface that B happens to implement, A is coupled only to the contract.
Dependency direction must follow architectural intent. In a layered architecture, dependencies flow inward: infrastructure → application → domain. A domain package importing from infrastructure is a boundary violation.
Wrap externals — don't let them leak. Third-party types and APIs should not appear in domain or application layer exported signatures. Wrap them behind owned types so the external can be replaced without changing consumers. Infrastructure/adapter packages may use external types in their implementation. The rule is strict for inward layers, relaxed at the system edge.
Use internal/ for enforced encapsulation. Go's internal/ package mechanism provides compiler-enforced
boundary protection. Packages that should not be imported by external consumers belong under internal/.
Primitives flow; implementation types stay home. Data that flows between packages should be expressed as primitive types, standard library types, or shared domain types — not as package-internal structs that force consumers to import from the implementation.
Exported symbols that serve no external consumer — internal helpers, intermediate types, or implementation utilities that happen to start with an uppercase letter.
Signals:
Action: Unexport the symbol. If consumed externally, evaluate whether the consumer should own the concept.
internal/ PackagesImplementation packages that are importable by external consumers but shouldn't be.
Signals:
pkg/ that are only used by sibling packages in the same moduleAction: Move to internal/ to enforce the boundary at the compiler level.
Two packages that share a type where neither owns it, or where one package's internal type appears in another package's function signatures.
Signals:
Action: Move shared types to a dedicated domain/contracts package owned by neither. Or define the interface in the consumer and have the producer conform to it.
Consumers importing sub-packages that should be internal to a parent package.
Signals:
github.com/org/repo/pkg/auth/internal/tokens from outside auth/service/impl/, handler/private/)Action: Use internal/ to enforce boundaries. Expose needed symbols through the parent package's API.
A lower-level package importing from a higher-level package, breaking the intended layering.
Signals:
Action: Invert the dependency. Define an interface in the lower layer; implement it in the higher layer. Wire via dependency injection at the composition root.
Third-party library types appearing in exported signatures of domain or application packages.
Signals:
gin.Context, echo.Context) in domain or application packagesAcceptable: Infrastructure/adapter packages using external types in their exported API — they are the wrapping layer. Flag only when these types leak inward into domain/application consumers.
Action: Define an owned interface or type that wraps the external. The wrapping package is the only place that imports from the external. Consumers depend on the owned type.
Packages named by what they contain rather than what they provide.
Signals:
util, utils, helpers, common, misc, base, sharedmodels that contains types for unrelated domainsAction: Name packages after their domain concept or capability. Split mixed-concern packages. Flatten unnecessary nesting.
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.internal/ packages.# List all imports per file
rg '^import' --type go -A 20 --glob '!**/vendor/**' --glob '!**/*_test.go'
# Or use go list for structured data
go list -json ./... 2>/dev/null | rg '"(Imports|ImportPath)"'
internal/ usage. Are implementation packages properly under internal/?For each package:
EXCLUDE='--glob !**/vendor/** --glob !**/*_test.go'
# Find all exported symbols
rg 'func\s+[A-Z]|type\s+[A-Z]|var\s+[A-Z]|const\s+[A-Z]' --type go $EXCLUDE
# For each exported symbol, check external usage
rg 'SymbolName' --type go $EXCLUDE
Verify each candidate finding manually — a grep miss is not proof of zero usage.For each package's consumers:
For each package, answer:
Save as YYYY-MM-DD-boundary-hunter-audit-{$LLM-name}.md in the project's docs folder (or project root if no docs folder exists).
# Boundary Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
## Package Map
| Package | Exported Symbols | External Deps | Fan-In | Fan-Out |
| ------- | ---------------- | ------------- | ------ | ------- |
| domain/order | 5 types, 2 fns | 0 | 8 | 1 |
| infra/postgres | 3 fns | 1 (pgx) | 2 | 4 |
## Dependency Graph Issues
### Direction Violations
- domain/X imports from infra/Y ({symbol}, {file:line})
## Export Surface Issues
### Over-Exported API
| # | Package | Export | Type | External Consumers |
| - | ------- | ------ | ---- | ------------------ |
| 1 | pkg/auth | `helperFn` | function | 0 |
### Missing internal/ Packages
| # | Package | Reason | Action |
| - | ------- | ------ | ------ |
| 1 | pkg/crypto/impl | Only used by pkg/crypto | Move to internal/ |
### External Type Leaks
| # | Package | Export / Signature | External Type | Action |
| - | ------- | ------------------ | ------------- | ------ |
| 1 | domain/user | `Save(ctx context.Context, tx pgx.Tx)` | `pgx.Tx` | Wrap behind owned interface |
## Consumer Access Violations
### Deep Imports
| # | Consumer | Imported Path | Should Use |
| - | -------- | ------------- | ---------- |
| 1 | file.go:line | `auth/internal/tokens` | `auth` |
## Package Naming Issues
| # | Package | Issue | Action |
| - | ------- | ----- | ------ |
| 1 | `utils` | Generic name, mixed concerns | Split by capability |
## Replaceability Assessment
### {Package Name}
- Replaceable from API? {yes/no — why}
- Implicit contracts: {goroutine safety, ordering, side effects}
- Coupling risk: {low/med/high}
## Recommendations (Priority Order)
1. **Must-fix**: {direction violations, external leaks in domain layer}
2. **Should-fix**: {dead exports, missing internal/, over-exported API}
3. **Consider**: {replaceability improvements, package naming, deep imports}
file/path.go:line with the exact code or import statement.rg, go list, and import analysis to count actual consumers.internal/ mechanism is the primary boundary enforcement tool.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.