Hexagon Audit
Audit Ports & Adapters (Hexagonal Architecture, Cockburn 2005) compliance in a
monorepo that separates interface packages from provider implementations —
typically a top-level packages/ (ports, shared kernel, inner-core code) and
adapters/ (provider implementations). This is a read-only audit unless the
user explicitly asks for fixes.
Invariants
- Inward dependency flow. Files under
packages/must not import fromadapters/, either by relative path or by a workspace package whosepackage.jsonlives underadapters/. - No peer-adapter imports. An adapter may self-import its own package name,
but it must not import another package whose
package.jsonlives underadapters/. - Vendor I/O is classified. Port/interface packages should not import
vendor SDKs (for example
@modelcontextprotocol/sdk,@anthropic-ai/sdk,@google-cloud/*,bun:sqlite,postgres,pg,kubernetes-client). Treat these as findings to classify, not automatic violations. - One adapter, one transport. Do not bundle two backends in one adapter package.
Process
1. Run the deterministic scan
Run the bundled scanner from the repo root. The script path depends on how the
hexagon-audit skill is available:
# installed via `npx skills add` (project-local skills dir)
bun .agents/skills/hexagon-audit/scripts/audit-hexagon.ts
# inside the abpai/skills checkout itself
bun hexagon-audit/skills/hexagon-audit/scripts/audit-hexagon.ts
# loaded as a Claude Code plugin (runtime cache)
bun "${CLAUDE_PLUGIN_ROOT}/skills/hexagon-audit/scripts/audit-hexagon.ts"
Pick whichever resolves; if none do, the scanner isn't on disk yet — re-install
or re-add the hexagon-audit skill before continuing.
The script discovers workspace package names and dependency edges from
packages/*/package.json and adapters/*/package.json, then reports:
packages/source or package manifests that import/depend on adapters.adapters/source or package manifests that import/depend on peer adapters.- vendor SDK imports and dependency declarations found inside
packages/.
If the script reports package-to-adapter or adapter-to-peer-adapter imports,
surface them as hard violations. Vendor SDK hits need human classification in
step 3. If the repo does not have packages/ and adapters/
top-level directories, this skill does not apply — say so instead of guessing.
This step is complete when the scanner command, package counts, dependency edge counts, and any hard-violation candidates are recorded.
2. Run focused source checks
Use rg to inspect the code around any script findings. These are useful spot
checks, not the source of truth:
rg -n "from ['\"](\.\./)*adapters/|/adapters/" packages adapters
rg -n "@modelcontextprotocol/sdk|@anthropic-ai/sdk|bun:sqlite|@google-cloud/|^import .*postgres|from ['\"]pg['\"]|kubernetes-client" packages
If the script reports an unexpected dependency edge, inspect the corresponding
package.json and the source import that uses it.
This step is complete when every deterministic scan hit has either a
file:line source citation or an explicit "manifest-only" note.
3. Audit by domain group
If subagents are available and the user has explicitly allowed delegation, split
related package/adapter clusters across explorer subagents. Otherwise audit the
groups locally. Group packages with their matching adapters (e.g. a session
port with session-postgres / session-sqlite adapters).
For each group, answer:
- Does any package import an adapter package or path?
- Does any adapter import a peer adapter package or path?
- Are vendor SDKs confined to adapters, or is a
packages/*module acting as runnable inner-hexagon code or shared infrastructure? - Does each adapter represent one transport/provider?
Adapters may import their port package, shared kernel/domain packages, inner-core packages, and vendor SDKs. They may not import peer adapters.
This step is complete when every package/adapter group has been classified as hard violation, soft smell, or clean with cited evidence.
4. Classify findings
Sort each finding into one bucket:
- Hard violation:
packages/*imports an adapter, an adapter imports a peer adapter, or concrete vendor I/O implementation lives in a package that is meant to be a pure port. - Soft smell: a package mixes interface and reference implementation, an
infra package lives under
packages/without a clear boundary, or a transport name obscures ownership. - Clean: the package is interface-only, shared kernel/domain code, or runnable inner-hexagon core with I/O behind abstract ports.
Report Format
Produce a single markdown report with these sections:
- Headline Result: does the hexagon hold? Include the deterministic scan counts and name any hard violations.
- Hard Violations: cite
file:linefor every hard violation. - Soft Smells: cite
file:lineand explain why it is cleanup rather than a blocker. - Clean Groups: summarize the groups that preserve the boundary.
- Recommendations: split into hard-violation fixes and soft-smell cleanup. Be concrete: name the package, adapter directory, or file to move.
- Verification: include the script command and focused
rgchecks so the audit can be rerun later.
Layout Notes
- Storage/provider-shaped code under
packages/deserves extra scrutiny: it can look adapter-shaped even when it is consumed as shared infrastructure. - Companion repos outside the monorepo are out of scope unless the user includes them.