audit-style
The native simplify has good taste, but it does not know your rules. It
cannot tell that a custom exception must earn its existence through a gate,
that an expected failure should be a typed result rather than a throw, or
that abstraction is a cost you pay only at the third occurrence. And because
it optimizes for less code, it can introduce the very over-abstraction your
rules forbid. audit-style is the reader that holds the rules in one hand and
the diff in the other.
What it solves
Octopus already guards code quality on two axes — but neither is the house design rules:
- the syntactic block (the
guardrailsbundle) stops unformatted, untyped, or secret-bearing code — but it judges syntax, not whether a design decision honors a rule the team wrote down; - the semantic grounding signal (
audit-grounding) flags invented conventions and unsupported domain facts — but it judges the diff against the domain, not against the coding rules.
That leaves the third axis: does this code honor the opinionated design
rules, and is it over-engineered? A formatter will never say “this custom
exception has one throw site and zero catch sites — throw the stdlib type
instead.” The generic simplify will never say “you extracted this at the
second occurrence; the rule says wait for the third.” audit-style is the
side that reads the rules and reports where the diff diverges.
The three findings
rule-violation— a construct the diff introduces that contradicts a stated rule: a custom exception that fails the gate, a throw where a typed result is called for, a boolean parameter, a magic number, a missing guard clause, business logic leaking into a repository, a swallowed exception.over-engineering— abstraction the rules explicitly call a cost: a premature abstraction, a speculative subtype hierarchy “for the future”, DRY applied before three occurrences, an indirection layer with no present caller. This is the dimension the genericsimplifystructurally cannot produce —audit-styleis the reader that knows when not to simplify.rigidity— the opposite failure: a concept spread across several files, so every extension has to edit all of them. Unlike the other two, this one is never a judgment call. It is emitted only on measured evidence.
One ruler, two directions
Flagging over-abstraction and flagging under-abstraction in the same review is a good way to sound incoherent — unless both readings come from the same measurement. They do:
Abstraction without co-change is premature abstraction. Co-change without abstraction is rigidity.
The measurement is octopus git-signals: a deterministic pass over your git
history that finds co-change clusters — files that keep changing in the
same commit, which is what it looks like when one concept lives in three
places. It is git and awk, nothing else: no language parser, no coverage
tool, no CI, no baseline. It runs in any repo, in any language, and costs no
model tokens.
A cluster on its own is not a finding about your PR, though. What earns a finding is whether this diff enlarges the cluster — touching two or more members and pulling in a path that was not one. Passing through a cluster someone else created is information; adding a fourth file that will now pay the same toll forever is something you did.
Who measures, who decides
audit-style runs on the cheapest model tier and stays signal-only, so it
reports the evidence and stops there — deliberately naming no principle and
no pattern. The architect role picks the finding up on the frontier tier and
makes the call:
| Evidence | Verdict |
|---|---|
| the diff enlarges the cluster | BLOCKING |
| the diff touches it without enlarging | ADVISORY |
| evidence unavailable (shallow clone, no history) | QUESTION |
architect is also where the vocabulary comes in: the SOLID principle at
stake, and the design pattern that would create the seam. One rule binds it —
a pattern is never named without the evidence line that justifies it.
“Consider a Strategy here” is cargo-cult. “Each new provider edits these three
files, eight times in ninety days” is a claim you can argue with.
Nothing is ever reported as a clean zero when it was not measured: a shallow clone or a repo without enough history returns unavailable, never “no rigidity found”.
The source of truth
It loads the rules the repo already ships, in order, and degrades gracefully
when one is absent: rules/common/exceptions.md (the custom-exception gate),
rules/common/patterns.md (Result pattern, repository/service separation,
guard clauses), rules/common/coding-style.md (naming, structure, the
anti-pattern catalogue), and the active stack rules that match the languages
the diff touches. A missing rules file becomes an info note so you know the
audit was partial — it never invents a rule that is not written down.
Why signal-only, never block
A design verdict is a judgment call. Blocking a merge on a probabilistic
read of “is this over-engineered?” would be worse than the problem it solves.
The syntactic gate already blocks at commit; audit-style surfaces the
design-rules gap as warn / info and leaves the call to you. Recurring
findings feed the existing knowledge loop, so a violation the team keeps
making gets promoted into a rule rather than re-flagged forever.
How it runs
Unlike audit-grounding and audit-verification, audit-style has no
Stop hook — there is no per-task LLM cost. It runs only when a review flow
invokes it: the codereview and pr-review self-reviews, or the implement
simplify pass. It registers in the quality bundle:
bundles: - quality - guardrailshooks: trueaudit-style complements the native simplify rather than replacing it: the
generic pass applies taste and edits the code, audit-style reads the house
rules and signals — including the over-engineering the generic pass can miss.
Rigidity thresholds live under git_signals.cochange in .octopus.yml and
need no configuration to work. The one worth knowing is max_findings, which
defaults to 1: a mature repo has many co-change clusters, and reporting all
of them is how a review becomes a wall of ambient debt nobody reads.