enola/Docs/Explainers

Documentation

Explainers

Explainers analyze Enola’s architecture graph and report cycles, layer violations, hotspots, coverage gaps, and other findings.

01 · Model

How a check becomes a verdict

An explainer analyzes the graph and reports individual findings. Enola compares findings before and after the change, then identifies the ones the change introduced. Your policy decides whether those new regressions pass or fail the check.

Default policy: there isn't one. Every explainer runs on every check and every finding is reported, and nothing fails until you name it in --fail-on. The Enforceable row below says whether naming an explainer is enough on its own: only findings that reach confidence 1.0 clear the default floor, and the rest need --min-confidence lowered as well.
TermMeaning
ExplainerAn analysis that checks the architecture graph.
FindingA reported condition with confidence, affected code, and supporting evidence.
RegressionA finding that is new and attributable to the proposed change.
VerdictThe policy result returned to the developer, agent, hook, or CI job.

02 · Structural checks

cycles

Runs Tarjan’s strongly connected components algorithm over resolved import edges. It returns the modules and edges that close each load-order dependency cycle.

ResultProof: a cycle either exists in the extracted graph or it does not.
ConfidenceLoad-order cycles carry 1.0. The same explainer can report highly coupled module clusters at 0.4; those are estimates, not build-breaking cycles.
EnforceableYes, with --fail-on=cycles: a load-order cycle reaches 1.0 and clears the default floor. The 0.4 coupling clusters do not, whatever you name.
BoundaryThe proof is only as complete as the resolved import graph and extractor coverage.

intent

Intent records the architectural boundaries you expect. Enola compares those declarations with the relationships found in the code and reports where they differ.

Verified differencesA relationship found across an undeclared boundary, or one using a different mechanism than declared, can reach confidence 1.0.
Estimated differencesA declared relationship that was not found remains below confidence 1.0, because it may indicate architectural drift or incomplete extraction.
EnforceableYes, with --fail-on=intent, for the differences that reach 1.0. The estimated ones need --min-confidence lowered too.
BoundaryResults depend on both the declared intent and extractor/linker coverage.

constraints

Constraints are the rules you write down: components select facts by path, and a rule states what may not reach what. Enola verdicts each rule against the measured graph rather than against file names, so a breach names the edge that caused it.

ResultProof: a breach is set membership over measured edges. Thirteen rule forms are enforceable, including forbid, the transitive forbid_reach, allow-only, protect and private.
ConfidenceA breach of a rule in enforcing mode carries 1.0. Advisory-mode breaches sit at 0.9 and a component whose selector matches nothing is reported at 0.4 — both deliberately below the floor, because they report rather than enforce.
EnforceableYes, with --fail-on=constraints.
BoundaryA rule can only be judged on facts that were extracted. A forbid_reach whose membership is too large to walk degrades to a single advisory rather than guessing.

03 · Outlier tests

Repository-relative heuristics

These explainers compare entities with the distribution inside the current repository. They flag statistical outliers rather than violations of a universal industry threshold.

god-class

Flags classes whose structural measurements sit above the repository’s normal distribution.

Method: repository-relative outlier test above mean + 2σ.

hotspots

Identifies unusually prominent or highly connected entities in the repository.

Method: repository-relative outlier test above mean + 2σ.

complexity-outliers

Flags entities whose measured complexity is unusual for this codebase.

Method: repository-relative outlier test above mean + 2σ.

These findings are estimates, remain below confidence 1.0, and are advisory by default. A uniformly complex repository may produce no complexity outlier because the comparison is against itself.

04 · Graph shape

Dependency structure and public surface

dependency-depth

Measures the longest transitive import chain and reports unusually deep dependency paths.

Evidence: the dependency path and the entities that form it.

exported-surface

Flags large modules that export nearly everything they contain.

Evidence: the module and its measured exported surface.

Both are heuristic and advisory by default. Their scores describe graph shape; they do not prove that the design is wrong.

05 · Conventions

layers

Reports imports that run against the dependency direction of a layer order — one you declared, or one Enola recognised by matching module paths against known project taxonomies. Both run; a declaration does not switch recognition off.

Declared orderA four-line layers: block in enola-intent.yaml, outermost first. Violations of it are verdicted at 1.0 — stated, not guessed.
Recognised orderA convention match against ten known taxonomies, capped at 0.80. It is a reading of your directory names, not a proof of intended design.
EvidenceThe importing module, the imported module, the layers they belong to, and the file and import that crossed.
EnforceableA declared order with --fail-on=layers alone. A recognised one needs --min-confidence=0.8 as well, since 0.80 is below the default floor.
BoundaryDeclared paths must match the modules that exist: a directory no path covers is unclassified rather than violating, so it produces no findings at all.
The finding that names which pattern was matched is a description, not a violation. It is exact, it is reported under Descriptive (never graded), and it can never fail a build — otherwise the change that first declared a layer order would fail for saying so.

06 · Reporters

Resolution, coverage, and reachability

Reporters summarise results already produced by extraction and linking. They do not run a separate structural algorithm.

crossrepo

Summarises the dependencies Enola resolved between loaded repositories.

Use it to: inspect which repositories depend on which.

coverage

Reports where extraction or linking could not follow a relationship.

Use it to: understand what the graph does not cover before trusting a result.

unused-routes

Reports routes for which no client call was resolved across the repositories currently loaded.

Boundary: “unused” means no loaded client was linked, not proof that no runtime caller exists.

messaging-coverage

Reports messaging contracts with no detected producer or consumer, and messaging code with no matching contract.

Boundary: dynamic topics, wrappers, unsupported clients, and code outside the snapshot can remain unresolved.

07 · Declarations and reachability

Framework behavior and callable code

These four explainers read framework declarations, query behavior, and call edges rather than repository-wide distributions.

domain

Reports what an application's declarations say about its data and its API: the models, the tables behind them, the routes that reach them. Heuristic, and advisory by default.

query-loops

Reports a database read or write inside a data-sized loop. For Ruby association reads, it follows the relation back to where it was built and suppresses declared preloads, unsaved records, batched paths, and test code. Dynamic receiver types remain unresolved rather than guessed.

entry-points

Marks the symbols a framework invokes directly — a routed controller action, a job a queue drains, a mailer, a scheduled task — so that reachability has roots at all. Routing is not a call, so without roots every one of them looks unreachable. It stops at marking them: reachability from the roots is not yet reported, because measuring it returns a number about extractor coverage rather than about the codebase.

dead-methods

Reports Ruby methods whose names no call edge uses, or only spec files use. It is limited to services, jobs, workers, queries, and lib, where callers are visible. Name matching can keep unrelated same-named methods alive, so every result is a candidate, never a verdict.

08 · Interpretation

Read confidence within an explainer

Confidence is comparable within one explainer, not across explainers. A coverage finding at 0.9 is not necessarily more important than a layer finding at 0.8; Enola does not present those scores as a shared severity scale.

Comparing snapshots can produce a regression, an improvement, or an incidental shift. Incidental shifts are reported but never fail the check because repository-relative thresholds can move even when the flagged entity was not touched.

For implementation-level formulas, thresholds, and exclusions, read the canonical EXPLAINERS.md and its linked architecture reference.