Boundary Hunter
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.
When to Use
- Reviewing package boundaries before or after a refactor
- Shrinking a package's exported API to what is actually consumed
- Preparing a package to be replaceable (rewritable from its API alone)
- Untangling tight coupling between layers or modules
- Wrapping external dependencies behind internal interfaces
- Enforcing unidirectional dependency flow between layers
Core Principles
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. (No tension with smell-hunter's primitive-obsession hunt: "not
implementation structs" and "model domain concepts as owned types" reconcile in shared domain types — a
UserID named type flowing between packages satisfies both.)
What to Hunt
1. Over-Exported API Surface
Exported symbols that serve no external consumer — internal helpers, intermediate types, or implementation utilities
that happen to start with an uppercase letter.
Signals:
- Exported function/type with zero import sites outside its own package
- Exported type that includes implementation-specific fields (internal caches, state machines)
- Exported helpers/utilities alongside domain API
- Exported constructor for a type that should be package-private
Action: Unexport the symbol. If consumed externally, evaluate whether the consumer should own the concept.
2. Missing internal/ Packages
Implementation packages that are importable by external consumers but shouldn't be.
Signals:
- Utility packages under
pkg/ that are only used by sibling packages in the same module
- Shared helper packages that are not part of the public API
- Packages containing implementation details that external consumers could accidentally depend on
- Infrastructure adapters that should not be directly imported by domain packages
Action: Move to internal/ to enforce the boundary at the compiler level.
3. Coupling Through Shared Types
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:
- Package A imports a type defined in package B that is not part of B's intended public API
- A "shared types" package that grows unboundedly, coupling all importers
- Function signature contains a parameter typed as another package's internal struct
- Domain types defined in infrastructure packages
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.
4. Deep Import Paths
Consumers importing sub-packages that should be internal to a parent package.
Signals:
- Imports like
github.com/org/repo/pkg/auth/internal/tokens from outside auth/
- Imports reaching into implementation sub-packages (
service/impl/, handler/private/)
- Multiple import paths for the same concept (re-exported inconsistently)
Action: Use internal/ to enforce boundaries. Expose needed symbols through the parent package's API.
5. Dependency Direction Violations
A lower-level package importing from a higher-level package, breaking the intended layering.
Signals:
- Domain package importing from infrastructure or transport layer
- Shared utility importing from a feature package
- A "core" package that depends on a "feature" package
- Model/entity package importing from handler/controller package
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.
6. External Type Leaks
Third-party library types appearing in exported signatures of domain or application packages.
Signals:
- Exported function parameter or return type from a third-party module in a domain package
- Package re-exports a third-party type as part of its own API
- Switching the underlying library would require changing consumer code
- Framework-specific types (e.g.,
gin.Context, echo.Context) in domain or application packages
Acceptable: 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.
7. Package Naming and Organization Issues
Packages named by what they contain rather than what they provide. Ownership: package naming and organization
is owned here as a boundary-legibility question — solid-hunter analyzes the same packages through a
responsibility/change lens (multiple actors, reasons to change), not naming.
Signals:
- Packages named
util, utils, helpers, common, misc, base, shared
- Package name that doesn't match the directory name
- Package
models that contains types for unrelated domains
- Deeply nested package paths for simple concepts
- Multiple files in a package spanning unrelated concerns
Action: Name packages after their domain concept or capability. Split mixed-concern packages. Flatten unnecessary
nesting.
Audit Workflow
Phase 1: Map Package Boundaries
Resolve audit surface. The prompt may specify the scope as:
- Diff: files changed relative to the base branch — committed, staged, unstaged, and untracked
- Path: specific files, folders, or packages
- Codebase: the entire project (the default when unspecified)
Party mode: when the orchestrator supplies a scope snapshot (a resolved file list), use it verbatim and do
not re-resolve. The resolution below applies to standalone runs only.
For diff mode, resolve fail-closed:
BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/@@')
if [ -z "$BASE" ]; then
for b in origin/main origin/master main master; do
git rev-parse -q --verify "$b" >/dev/null && BASE=$b && break
done
fi
# If BASE is still empty: STOP. Ask for an explicit base. Do not continue.
SCOPE=$( { git diff --name-only --diff-filter=d "$BASE"...HEAD;
git diff --name-only --diff-filter=d HEAD;
git ls-files --others --exclude-standard; } | sort -u )
DELETED=$( { git diff --name-only --diff-filter=D "$BASE"...HEAD;
git diff --name-only --diff-filter=D HEAD; } | sort -u )
If $SCOPE is empty, run no analysis: write the report with "Audit completed: 0 findings — empty diff scope",
listing $DELETED under "Deleted in diff" if non-empty, and stop. If the resolved surface exceeds what can be
read within the context budget, report the file count and ask to narrow or chunk.
Two surfaces. This hunter analyzes an import graph, so the analysis (import mapping, fan-in/fan-out,
dead-export consumer counting) legitimately runs project-wide — a package's consumers live outside any diff
scope. Findings are still reported only against the target scope: every finding anchors (file:line) to
in-scope packages.
Identify packages. List all Go packages and their import paths. Note internal/ packages.
Catalogue exports. For each package, list exported types, functions, and variables. Classify each as:
type, function, constant, variable.
List external dependencies. For each package, list imports from outside the module. Note which external types
appear in exported signatures.
Phase 2: Analyze Dependency Graph
- Build import map. For each package, list which other internal packages it imports from.
# List all imports per file
rg '^import' --type go -A 20 --glob '!**/vendor/**' --glob '!**/*_test.go'
# Or use go list for structured data (one line per package: import path, then its imports)
go list -f '{{.ImportPath}} {{join .Imports " "}}' ./...
- Check direction. If the project has an intended layering (domain → application → infrastructure), verify all
import arrows point in the correct direction.
- Check for
internal/ usage. Are implementation packages properly under internal/?
- Measure fan-in / fan-out. Packages with high fan-in (many importers) are stability anchors — changes are costly.
Packages with high fan-out (many imports) are fragile.
Phase 3: Audit Export Surface
For each package:
Dead exports. Is every exported symbol consumed by at least one external package?
EXCLUDE='--glob !**/vendor/** --glob !**/testdata/** --glob !**/*_test.go --glob !**/*.pb.go --glob !**/*_gen.go --glob !**/*_generated.go'
# Exported-symbol census — incomplete discovery aid: line-anchored patterns miss exported *methods*
# and members of grouped const (...)/var (...) blocks, the dominant forms of both. Enumerate the
# exported surface by reading declarations, not from this list alone.
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.
Library caveat. Zero in-module references does not make an exported symbol dead when the module is
imported by external consumers. Check whether the module is a published library (go.mod module path, known
importers) and label such candidates "unused internally", not dead. golang.org/x/tools/cmd/deadcode
gives call-graph (RTA) evidence of unreachable functions in modules with executable main packages —
useful supporting evidence, but not a symbol-level census: it says nothing about exported types, constants,
or variables, and cannot establish deadness of a library's public API.
Leaked internals. Do any exports expose implementation details?
External type leaks. Do any exports use third-party types in their signatures?
Phase 4: Audit Consumer Access Patterns
For each package's consumers:
- Deep imports. Are consumers importing sub-packages that should be internal?
- Knowledge coupling. Do consumers make decisions based on implementation details of the imported package?
Phase 5: Evaluate Replaceability
For each package, answer:
- Could this package be rewritten from its exported API alone? If a new developer needed to rewrite the package,
would the exported types + function signatures be sufficient?
- What would break if the implementation changed completely? If the answer is "only the package's tests", the
boundary is clean.
- Are there implicit contracts not captured in the API? Ordering guarantees, side effects, goroutine safety,
context cancellation behavior — anything consumers depend on that isn't in the type signature.
Output Format
Save as YYYY-MM-DD-boundary-hunter-audit-{model-name}.md — {model-name} is the executing model's short name
(e.g. fable-5) — in the project's docs folder (or project root if no docs folder exists). If the caller specifies
an output path or return mode (e.g. the party-hunter orchestrator), it overrides this default.
Severity levels, used for per-finding labels and the Recommendations grouping:
- Critical — exploitable now, causes data loss, or breaks behavior on production paths.
- High — a defect with likely user-visible, security, or reliability impact if left unaddressed.
- Medium — correctness or maintainability risk without imminent impact.
- Low — hygiene; no behavioral risk.
# Boundary Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
- {Deleted in diff: {list} — only for diff scope with deletions}
- Audit completed: {N} findings
## 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. **High**: {direction violations, external leaks in domain layer}
2. **Medium**: {dead exports, missing internal/, over-exported API}
3. **Low**: {replaceability improvements, package naming, deep imports}
Operating Constraints
- No code edits. This skill produces an audit report only. Implementation is a separate step.
- No empty finding sections. Include only categories with findings. Omit a heading, table, or list entirely when it would contain zero items — do not include empty tables, placeholder subsections, or negative statements like "no dead exports", "none found", or "no issues". Execution status is exempt: the "Audit completed: N findings" line in the Scope section is always present, even at zero findings.
- Scope: package boundaries only. Encapsulation, coupling, dependency direction, API surface. If a finding
doesn't answer "is this boundary clean?", it belongs to another hunter — do not flag it here. Named boundary:
package naming and organization is owned here (§7); solid-hunter keeps SRP as responsibility/change analysis.
- Evidence required. Every finding must cite
file/path.go:line with the exact code or import statement.
- Architecture-first. Understand the project's intended layering before flagging violations. Ask if unclear.
- Pragmatism over purism. Not every coupling is worth breaking. Small utilities shared between two closely related
packages may be fine. Flag, but don't insist on architectural astronautics.
- Measure, don't guess. Use
rg, go list, and import analysis to count actual consumers.
- Respect Go conventions. Go packages are designed to be flat and focused. Don't impose Java-style deep package
hierarchies. Go's
internal/ mechanism is the primary boundary enforcement tool.
1---2name: boundary-hunter-go3description: 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.4---56# Boundary Hunter78Audit Go code for **package boundary violations** — places where implementation details leak through exports, where9packages reach into each other's internals, or where coupling makes replacement impossible. The goal: **every package10is a black box, replaceable from its exported API alone.**1112Go enforces some boundaries at the language level (unexported identifiers, import cycle prohibition, `internal/`13packages), but many boundary violations are still possible within those constraints.1415## When to Use1617- Reviewing package boundaries before or after a refactor18- Shrinking a package's exported API to what is actually consumed19- Preparing a package to be replaceable (rewritable from its API alone)20- Untangling tight coupling between layers or modules21- Wrapping external dependencies behind internal interfaces22- Enforcing unidirectional dependency flow between layers2324## Core Principles25261. **A package is its exported API.** Exported identifiers are promises. Unexported identifiers are implementation27 details. Exports should describe _what the package does_, never _how it does it_. If a consumer must understand28 internals to use the API correctly, the boundary is broken.29302. **Minimal exported surface.** Export only what is consumed or serves as a deliberate extension point. Every exported31 symbol is a coupling point that constrains future changes. Go's convention: start unexported, export when needed.32333. **Depend on interfaces, not concretions.** Packages should depend on interfaces (defined at the consumer) — not on34 concrete types from other packages. If package A imports a concrete struct from package B, A is coupled to B's35 implementation. If A defines an interface that B happens to implement, A is coupled only to the contract.36374. **Dependency direction must follow architectural intent.** In a layered architecture, dependencies flow inward:38 infrastructure → application → domain. A domain package importing from infrastructure is a boundary violation.39405. **Wrap externals — don't let them leak.** Third-party types and APIs should not appear in domain or application41 layer exported signatures. Wrap them behind owned types so the external can be replaced without changing consumers.42 Infrastructure/adapter packages may use external types in their implementation. The rule is strict for inward43 layers, relaxed at the system edge.44456. **Use `internal/` for enforced encapsulation.** Go's `internal/` package mechanism provides compiler-enforced46 boundary protection. Packages that should not be imported by external consumers belong under `internal/`.47487. **Primitives flow; implementation types stay home.** Data that flows between packages should be expressed as49 primitive types, standard library types, or shared domain types — not as package-internal structs that force50 consumers to import from the implementation. (No tension with smell-hunter's primitive-obsession hunt: "not51 implementation structs" and "model domain concepts as owned types" reconcile in shared domain types — a52 `UserID` named type flowing between packages satisfies both.)5354## What to Hunt5556### 1. Over-Exported API Surface5758Exported symbols that serve no external consumer — internal helpers, intermediate types, or implementation utilities59that happen to start with an uppercase letter.6061**Signals:**6263- Exported function/type with zero import sites outside its own package64- Exported type that includes implementation-specific fields (internal caches, state machines)65- Exported helpers/utilities alongside domain API66- Exported constructor for a type that should be package-private6768**Action:** Unexport the symbol. If consumed externally, evaluate whether the consumer should own the concept.6970### 2. Missing `internal/` Packages7172Implementation packages that are importable by external consumers but shouldn't be.7374**Signals:**7576- Utility packages under `pkg/` that are only used by sibling packages in the same module77- Shared helper packages that are not part of the public API78- Packages containing implementation details that external consumers could accidentally depend on79- Infrastructure adapters that should not be directly imported by domain packages8081**Action:** Move to `internal/` to enforce the boundary at the compiler level.8283### 3. Coupling Through Shared Types8485Two packages that share a type where neither owns it, or where one package's internal type appears in another86package's function signatures.8788**Signals:**8990- Package A imports a type defined in package B that is not part of B's intended public API91- A "shared types" package that grows unboundedly, coupling all importers92- Function signature contains a parameter typed as another package's internal struct93- Domain types defined in infrastructure packages9495**Action:** Move shared types to a dedicated domain/contracts package owned by neither. Or define the interface in the96consumer and have the producer conform to it.9798### 4. Deep Import Paths99100Consumers importing sub-packages that should be internal to a parent package.101102**Signals:**103104- Imports like `github.com/org/repo/pkg/auth/internal/tokens` from outside `auth/`105- Imports reaching into implementation sub-packages (`service/impl/`, `handler/private/`)106- Multiple import paths for the same concept (re-exported inconsistently)107108**Action:** Use `internal/` to enforce boundaries. Expose needed symbols through the parent package's API.109110### 5. Dependency Direction Violations111112A lower-level package importing from a higher-level package, breaking the intended layering.113114**Signals:**115116- Domain package importing from infrastructure or transport layer117- Shared utility importing from a feature package118- A "core" package that depends on a "feature" package119- Model/entity package importing from handler/controller package120121**Action:** Invert the dependency. Define an interface in the lower layer; implement it in the higher layer. Wire via122dependency injection at the composition root.123124### 6. External Type Leaks125126Third-party library types appearing in exported signatures of domain or application packages.127128**Signals:**129130- Exported function parameter or return type from a third-party module in a domain package131- Package re-exports a third-party type as part of its own API132- Switching the underlying library would require changing consumer code133- Framework-specific types (e.g., `gin.Context`, `echo.Context`) in domain or application packages134135**Acceptable:** Infrastructure/adapter packages using external types in their exported API — they _are_ the wrapping136layer. Flag only when these types leak inward into domain/application consumers.137138**Action:** Define an owned interface or type that wraps the external. The wrapping package is the only place that139imports from the external. Consumers depend on the owned type.140141### 7. Package Naming and Organization Issues142143Packages named by what they contain rather than what they provide. **Ownership:** package naming and organization144is owned here as a boundary-legibility question — solid-hunter analyzes the same packages through a145responsibility/change lens (multiple actors, reasons to change), not naming.146147**Signals:**148149- Packages named `util`, `utils`, `helpers`, `common`, `misc`, `base`, `shared`150- Package name that doesn't match the directory name151- Package `models` that contains types for unrelated domains152- Deeply nested package paths for simple concepts153- Multiple files in a package spanning unrelated concerns154155**Action:** Name packages after their domain concept or capability. Split mixed-concern packages. Flatten unnecessary156nesting.157158## Audit Workflow159160### Phase 1: Map Package Boundaries1611621. **Resolve audit surface.** The prompt may specify the scope as:163 - **Diff**: files changed relative to the base branch — committed, staged, unstaged, and untracked164 - **Path**: specific files, folders, or packages165 - **Codebase**: the entire project (the default when unspecified)166167 **Party mode:** when the orchestrator supplies a scope snapshot (a resolved file list), use it verbatim and do168 not re-resolve. The resolution below applies to standalone runs only.169170 For diff mode, resolve fail-closed:171 ```bash172 BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/@@')173 if [ -z "$BASE" ]; then174 for b in origin/main origin/master main master; do175 git rev-parse -q --verify "$b" >/dev/null && BASE=$b && break176 done177 fi178 # If BASE is still empty: STOP. Ask for an explicit base. Do not continue.179180 SCOPE=$( { git diff --name-only --diff-filter=d "$BASE"...HEAD;181 git diff --name-only --diff-filter=d HEAD;182 git ls-files --others --exclude-standard; } | sort -u )183 DELETED=$( { git diff --name-only --diff-filter=D "$BASE"...HEAD;184 git diff --name-only --diff-filter=D HEAD; } | sort -u )185 ```186 If `$SCOPE` is empty, run no analysis: write the report with "Audit completed: 0 findings — empty diff scope",187 listing `$DELETED` under "Deleted in diff" if non-empty, and stop. If the resolved surface exceeds what can be188 read within the context budget, report the file count and ask to narrow or chunk.189190 **Two surfaces.** This hunter analyzes an import graph, so the *analysis* (import mapping, fan-in/fan-out,191 dead-export consumer counting) legitimately runs project-wide — a package's consumers live outside any diff192 scope. Findings are still *reported* only against the target scope: every finding anchors (file:line) to193 in-scope packages.1942. **Identify packages.** List all Go packages and their import paths. Note `internal/` packages.1953. **Catalogue exports.** For each package, list exported types, functions, and variables. Classify each as:196 type, function, constant, variable.1974. **List external dependencies.** For each package, list imports from outside the module. Note which external types198 appear in exported signatures.199200### Phase 2: Analyze Dependency Graph2012021. **Build import map.** For each package, list which other internal packages it imports from.203 ```bash204 # List all imports per file205 rg '^import' --type go -A 20 --glob '!**/vendor/**' --glob '!**/*_test.go'206207 # Or use go list for structured data (one line per package: import path, then its imports)208 go list -f '{{.ImportPath}} {{join .Imports " "}}' ./...209 ```2102. **Check direction.** If the project has an intended layering (domain → application → infrastructure), verify all211 import arrows point in the correct direction.2123. **Check for `internal/` usage.** Are implementation packages properly under `internal/`?2134. **Measure fan-in / fan-out.** Packages with high fan-in (many importers) are stability anchors — changes are costly.214 Packages with high fan-out (many imports) are fragile.215216### Phase 3: Audit Export Surface217218For each package:2192201. **Dead exports.** Is every exported symbol consumed by at least one external package?221 ```bash222 EXCLUDE='--glob !**/vendor/** --glob !**/testdata/** --glob !**/*_test.go --glob !**/*.pb.go --glob !**/*_gen.go --glob !**/*_generated.go'223224 # Exported-symbol census — incomplete discovery aid: line-anchored patterns miss exported *methods*225 # and members of grouped const (...)/var (...) blocks, the dominant forms of both. Enumerate the226 # exported surface by reading declarations, not from this list alone.227 rg 'func\s+[A-Z]|type\s+[A-Z]|var\s+[A-Z]|const\s+[A-Z]' --type go $EXCLUDE228229 # For each exported symbol, check external usage230 rg 'SymbolName' --type go $EXCLUDE231 ```232 Verify each candidate finding manually — a grep miss is not proof of zero usage.233234 **Library caveat.** Zero *in-module* references does not make an exported symbol dead when the module is235 imported by external consumers. Check whether the module is a published library (`go.mod` module path, known236 importers) and label such candidates **"unused internally"**, not dead. `golang.org/x/tools/cmd/deadcode`237 gives call-graph (RTA) evidence of unreachable *functions* in modules with executable `main` packages —238 useful supporting evidence, but not a symbol-level census: it says nothing about exported types, constants,239 or variables, and cannot establish deadness of a library's public API.2402. **Leaked internals.** Do any exports expose implementation details?2413. **External type leaks.** Do any exports use third-party types in their signatures?242243### Phase 4: Audit Consumer Access Patterns244245For each package's consumers:2462471. **Deep imports.** Are consumers importing sub-packages that should be internal?2482. **Knowledge coupling.** Do consumers make decisions based on implementation details of the imported package?249250### Phase 5: Evaluate Replaceability251252For each package, answer:2532541. **Could this package be rewritten from its exported API alone?** If a new developer needed to rewrite the package,255 would the exported types + function signatures be sufficient?2562. **What would break if the implementation changed completely?** If the answer is "only the package's tests", the257 boundary is clean.2583. **Are there implicit contracts not captured in the API?** Ordering guarantees, side effects, goroutine safety,259 context cancellation behavior — anything consumers depend on that isn't in the type signature.260261## Output Format262263Save as `YYYY-MM-DD-boundary-hunter-audit-{model-name}.md` — `{model-name}` is the executing model's short name264(e.g. `fable-5`) — in the project's docs folder (or project root if no docs folder exists). If the caller specifies265an output path or return mode (e.g. the party-hunter orchestrator), it overrides this default.266267Severity levels, used for per-finding labels and the Recommendations grouping:268269- **Critical** — exploitable now, causes data loss, or breaks behavior on production paths.270- **High** — a defect with likely user-visible, security, or reliability impact if left unaddressed.271- **Medium** — correctness or maintainability risk without imminent impact.272- **Low** — hygiene; no behavioral risk.273274```md275# Boundary Hunter Audit — {date}276277## Scope278279- Surface: {diff / path / codebase}280- Files: {count or list}281- Exclusions: {list}282- {Deleted in diff: {list} — only for diff scope with deletions}283- Audit completed: {N} findings284285## Package Map286287| Package | Exported Symbols | External Deps | Fan-In | Fan-Out |288| ------- | ---------------- | ------------- | ------ | ------- |289| domain/order | 5 types, 2 fns | 0 | 8 | 1 |290| infra/postgres | 3 fns | 1 (pgx) | 2 | 4 |291292## Dependency Graph Issues293294### Direction Violations295296- domain/X imports from infra/Y ({symbol}, {file:line})297298## Export Surface Issues299300### Over-Exported API301302| # | Package | Export | Type | External Consumers |303| - | ------- | ------ | ---- | ------------------ |304| 1 | pkg/auth | `helperFn` | function | 0 |305306### Missing internal/ Packages307308| # | Package | Reason | Action |309| - | ------- | ------ | ------ |310| 1 | pkg/crypto/impl | Only used by pkg/crypto | Move to internal/ |311312### External Type Leaks313314| # | Package | Export / Signature | External Type | Action |315| - | ------- | ------------------ | ------------- | ------ |316| 1 | domain/user | `Save(ctx context.Context, tx pgx.Tx)` | `pgx.Tx` | Wrap behind owned interface |317318## Consumer Access Violations319320### Deep Imports321322| # | Consumer | Imported Path | Should Use |323| - | -------- | ------------- | ---------- |324| 1 | file.go:line | `auth/internal/tokens` | `auth` |325326## Package Naming Issues327328| # | Package | Issue | Action |329| - | ------- | ----- | ------ |330| 1 | `utils` | Generic name, mixed concerns | Split by capability |331332## Replaceability Assessment333334### {Package Name}335336- Replaceable from API? {yes/no — why}337- Implicit contracts: {goroutine safety, ordering, side effects}338- Coupling risk: {low/med/high}339340## Recommendations (Priority Order)3413421. **High**: {direction violations, external leaks in domain layer}3432. **Medium**: {dead exports, missing internal/, over-exported API}3443. **Low**: {replaceability improvements, package naming, deep imports}345```346347## Operating Constraints348349- **No code edits.** This skill produces an audit report only. Implementation is a separate step.350- **No empty finding sections.** Include only categories with findings. Omit a heading, table, or list entirely when it would contain zero items — do not include empty tables, placeholder subsections, or negative statements like "no dead exports", "none found", or "no issues". Execution status is exempt: the "Audit completed: N findings" line in the Scope section is always present, even at zero findings.351- **Scope: package boundaries only.** Encapsulation, coupling, dependency direction, API surface. If a finding352 doesn't answer "is this boundary clean?", it belongs to another hunter — do not flag it here. Named boundary:353 package naming and organization is owned here (§7); solid-hunter keeps SRP as responsibility/change analysis.354- **Evidence required.** Every finding must cite `file/path.go:line` with the exact code or import statement.355- **Architecture-first.** Understand the project's intended layering before flagging violations. Ask if unclear.356- **Pragmatism over purism.** Not every coupling is worth breaking. Small utilities shared between two closely related357 packages may be fine. Flag, but don't insist on architectural astronautics.358- **Measure, don't guess.** Use `rg`, `go list`, and import analysis to count actual consumers.359- **Respect Go conventions.** Go packages are designed to be flat and focused. Don't impose Java-style deep package360 hierarchies. Go's `internal/` mechanism is the primary boundary enforcement tool.