v0.2.0 92ac699

README.md RawHistoryFolder

hexcheck

hexcheck is a Go architecture linter for hexagonal and clean architecture boundaries.

It is built on golang.org/x/tools/go/analysis, runs as a standalone CLI, and can be embedded in a custom golangci-lint binary through the Module Plugin System.

What it checks

  • dependency direction between configurable architecture roles
  • framework or infrastructure types leaking into core/port APIs
  • suspicious business logic in adapters and entrypoints
  • missing generated mocks for port interfaces
  • local fakes or concrete adapters in tests where generated mocks should be used

Architecture roles

hexcheck does not require folders to be named domain, usecase, or adapters. A repository maps its own paths to roles in .hexcheck.yaml.

Supported roles:

  • core — domain/core business logic
  • usecase — application use cases and orchestration
  • ports — interfaces/contracts
  • adapter — infrastructure, persistence, external services, outbound adapters
  • entrypoint — CLI, HTTP handlers, UI entrypoints, app bootstrap
  • ignore — generated code, mocks, vendored paths

Quick config

version: 1
components:
  core:
    role: core
    paths:
      - internal/domain/**
      - internal/core/**
  usecases:
    role: usecase
    paths:
      - internal/application/usecase/**
      - internal/application/usecases/**
      - internal/usecase/**
      - internal/usecases/**
  ports:
    role: ports
    paths:
      - internal/application/port/**
      - internal/domain/repository/**
  adapters:
    role: adapter
    paths:
      - internal/infrastructure/**
      - internal/adapters/**
  entrypoints:
    role: entrypoint
    paths:
      - cmd/**
  generated:
    role: ignore
    paths:
      - '**/mocks/**'
      - '**/generated/**'
      - '**/*_templ.go'
      - '**/*_gen.go'

rules:
  no-adapter-imports-in-core: error
  no-infra-imports-in-usecase: error
  no-framework-types-in-core: error
  no-infra-types-in-ports: error
  no-adapter-to-adapter-imports: warn
  suspicious-business-logic-in-adapter: warn
  no-local-fakes-for-ports: warn
  missing-generated-mock-for-port: warn
  prefer-generated-mocks: warn

heuristics:
  businessLogicThreshold: 8
  businessLogicMinStrongSignals: 2
  businessLogicMinWeakSignals: 2
  businessLogicMaxFunctionNodes: 2000
  businessLogicMaxDiagnosticsPerPackage: 10
  businessLogicMode: audit # audit|ci
  businessLogicMinConfidence: medium # low|medium|high
  excludeTestFiles: true

mocking:
  generatedMockPaths:
    - internal/mocks/**
    - internal/application/mocks/**
    - internal/application/port/mocks/**
  generatedMockNamePatterns:
    - Mock{{Interface}}
    - '{{Interface}}Mock'

A fuller example lives in examples/hexcheck.yaml.

Business-logic diagnostics include a confidence level. audit mode reports findings at or above businessLogicMinConfidence; ci mode reports only high-confidence findings.

Standalone CLI

Because hexcheck is a go/analysis analyzer, standalone flags use the analyzer prefix:

hexcheck -hexcheck.config .hexcheck.yaml -hexcheck.root . ./...

During development:

go run ./cmd/hexcheck -hexcheck.config examples/hexcheck.yaml -hexcheck.root . ./...

golangci-lint module plugin

Build a custom golangci-lint binary using the module plugin system:

golangci-lint custom -c examples/custom-gcl.yml

Example builder config:

version: v2.12.2
name: hex-golangci-lint
destination: ./bin
plugins:
  - module: github.com/bnema/hexcheck
    import: github.com/bnema/hexcheck/golangci
    version: v0.1.0

Enable it in a project:

version: "2"
linters:
  enable:
    - hexcheck
  settings:
    custom:
      hexcheck:
        type: module
        description: Checks hexagonal architecture boundaries.
        settings:
          config: .hexcheck.yaml

Agent configuration guide

SKILL.md explains how an agent should configure hexcheck for a new repository, including non-standard layouts such as core/boundaries.

Local development

make test
make check
HEXCHECK_SMOKE_REPO=/path/to/local/go/repo make smoke-local
source
1# hexcheck
2
3`hexcheck` is a Go architecture linter for hexagonal and clean architecture boundaries.
4
5It is built on `golang.org/x/tools/go/analysis`, runs as a standalone CLI, and can be embedded in a custom `golangci-lint` binary through the Module Plugin System.
6
7## What it checks
8
9- dependency direction between configurable architecture roles
10- framework or infrastructure types leaking into core/port APIs
11- suspicious business logic in adapters and entrypoints
12- missing generated mocks for port interfaces
13- local fakes or concrete adapters in tests where generated mocks should be used
14
15## Architecture roles
16
17`hexcheck` does not require folders to be named `domain`, `usecase`, or `adapters`. A repository maps its own paths to roles in `.hexcheck.yaml`.
18
19Supported roles:
20
21- `core` — domain/core business logic
22- `usecase` — application use cases and orchestration
23- `ports` — interfaces/contracts
24- `adapter` — infrastructure, persistence, external services, outbound adapters
25- `entrypoint` — CLI, HTTP handlers, UI entrypoints, app bootstrap
26- `ignore` — generated code, mocks, vendored paths
27
28## Quick config
29
30```yaml
31version: 1
32components:
33  core:
34    role: core
35    paths:
36      - internal/domain/**
37      - internal/core/**
38  usecases:
39    role: usecase
40    paths:
41      - internal/application/usecase/**
42      - internal/application/usecases/**
43      - internal/usecase/**
44      - internal/usecases/**
45  ports:
46    role: ports
47    paths:
48      - internal/application/port/**
49      - internal/domain/repository/**
50  adapters:
51    role: adapter
52    paths:
53      - internal/infrastructure/**
54      - internal/adapters/**
55  entrypoints:
56    role: entrypoint
57    paths:
58      - cmd/**
59  generated:
60    role: ignore
61    paths:
62      - '**/mocks/**'
63      - '**/generated/**'
64      - '**/*_templ.go'
65      - '**/*_gen.go'
66
67rules:
68  no-adapter-imports-in-core: error
69  no-infra-imports-in-usecase: error
70  no-framework-types-in-core: error
71  no-infra-types-in-ports: error
72  no-adapter-to-adapter-imports: warn
73  suspicious-business-logic-in-adapter: warn
74  no-local-fakes-for-ports: warn
75  missing-generated-mock-for-port: warn
76  prefer-generated-mocks: warn
77
78heuristics:
79  businessLogicThreshold: 8
80  businessLogicMinStrongSignals: 2
81  businessLogicMinWeakSignals: 2
82  businessLogicMaxFunctionNodes: 2000
83  businessLogicMaxDiagnosticsPerPackage: 10
84  businessLogicMode: audit # audit|ci
85  businessLogicMinConfidence: medium # low|medium|high
86  excludeTestFiles: true
87
88mocking:
89  generatedMockPaths:
90    - internal/mocks/**
91    - internal/application/mocks/**
92    - internal/application/port/mocks/**
93  generatedMockNamePatterns:
94    - Mock{{Interface}}
95    - '{{Interface}}Mock'
96```
97
98A fuller example lives in [`examples/hexcheck.yaml`](examples/hexcheck.yaml).
99
100Business-logic diagnostics include a confidence level. `audit` mode reports findings at or above `businessLogicMinConfidence`; `ci` mode reports only high-confidence findings.
101
102## Standalone CLI
103
104Because `hexcheck` is a `go/analysis` analyzer, standalone flags use the analyzer prefix:
105
106```bash
107hexcheck -hexcheck.config .hexcheck.yaml -hexcheck.root . ./...
108```
109
110During development:
111
112```bash
113go run ./cmd/hexcheck -hexcheck.config examples/hexcheck.yaml -hexcheck.root . ./...
114```
115
116## golangci-lint module plugin
117
118Build a custom golangci-lint binary using the module plugin system:
119
120```bash
121golangci-lint custom -c examples/custom-gcl.yml
122```
123
124Example builder config:
125
126```yaml
127version: v2.12.2
128name: hex-golangci-lint
129destination: ./bin
130plugins:
131  - module: github.com/bnema/hexcheck
132    import: github.com/bnema/hexcheck/golangci
133    version: v0.1.0
134```
135
136Enable it in a project:
137
138```yaml
139version: "2"
140linters:
141  enable:
142    - hexcheck
143  settings:
144    custom:
145      hexcheck:
146        type: module
147        description: Checks hexagonal architecture boundaries.
148        settings:
149          config: .hexcheck.yaml
150```
151
152## Agent configuration guide
153
154[`SKILL.md`](SKILL.md) explains how an agent should configure `hexcheck` for a new repository, including non-standard layouts such as `core`/`boundaries`.
155
156## Local development
157
158```bash
159make test
160make check
161HEXCHECK_SMOKE_REPO=/path/to/local/go/repo make smoke-local
162```

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 5

↑↓ moveenter openesc close