god-class
Flags classes whose structural measurements sit above the repository’s normal distribution.
Method: repository-relative outlier test above mean + 2σ.
01 · Model
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.
| Term | Meaning |
|---|---|
| Explainer | An analysis that checks the architecture graph. |
| Finding | A reported condition with confidence, affected code, and supporting evidence. |
| Regression | A finding that is new and attributable to the proposed change. |
| Verdict | The policy result returned to the developer, agent, hook, or CI job. |
02 · Structural checks
Runs Tarjan’s strongly connected components algorithm over resolved import edges. It returns the modules and edges that close each load-order dependency cycle.
| Result | Proof: a cycle either exists in the extracted graph or it does not. |
|---|---|
| Confidence | Load-order cycles carry 1.0. The same explainer can report highly coupled module clusters at 0.4; those are estimates, not build-breaking cycles. |
| Enforceable | Yes, 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. |
| Boundary | The proof is only as complete as the resolved import graph and extractor coverage. |
Intent records the architectural boundaries you expect. Enola compares those declarations with the relationships found in the code and reports where they differ.
| Verified differences | A relationship found across an undeclared boundary, or one using a different mechanism than declared, can reach confidence 1.0. |
|---|---|
| Estimated differences | A declared relationship that was not found remains below confidence 1.0, because it may indicate architectural drift or incomplete extraction. |
| Enforceable | Yes, with --fail-on=intent, for the differences that reach 1.0. The estimated ones need --min-confidence lowered too. |
| Boundary | Results depend on both the declared intent and extractor/linker coverage. |
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.
| Result | Proof: a breach is set membership over measured edges. Thirteen rule forms are enforceable, including forbid, the transitive forbid_reach, allow-only, protect and private. |
|---|---|
| Confidence | A 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. |
| Enforceable | Yes, with --fail-on=constraints. |
| Boundary | A 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
These explainers compare entities with the distribution inside the current repository. They flag statistical outliers rather than violations of a universal industry threshold.
Flags classes whose structural measurements sit above the repository’s normal distribution.
Method: repository-relative outlier test above mean + 2σ.
Identifies unusually prominent or highly connected entities in the repository.
Method: repository-relative outlier test above mean + 2σ.
Flags entities whose measured complexity is unusual for this codebase.
Method: repository-relative outlier test above mean + 2σ.
04 · Graph shape
Measures the longest transitive import chain and reports unusually deep dependency paths.
Evidence: the dependency path and the entities that form it.
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
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 order | A four-line layers: block in enola-intent.yaml, outermost first. Violations of it are verdicted at 1.0 — stated, not guessed. |
|---|---|
| Recognised order | A convention match against ten known taxonomies, capped at 0.80. It is a reading of your directory names, not a proof of intended design. |
| Evidence | The importing module, the imported module, the layers they belong to, and the file and import that crossed. |
| Enforceable | A 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. |
| Boundary | Declared paths must match the modules that exist: a directory no path covers is unclassified rather than violating, so it produces no findings at all. |
06 · Reporters
Reporters summarise results already produced by extraction and linking. They do not run a separate structural algorithm.
Summarises the dependencies Enola resolved between loaded repositories.
Use it to: inspect which repositories depend on which.
Reports where extraction or linking could not follow a relationship.
Use it to: understand what the graph does not cover before trusting a result.
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.
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
These four explainers read framework declarations, query behavior, and call edges rather than repository-wide distributions.
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.
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.
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.
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
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.