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 apply declared rules to components selected from measured facts. A breach names the edge that violated the rule.
| Result | Proof: a breach is set membership over measured edges. Twenty-one rule forms are enforceable, including forbid, the transitive forbid_reach, allow-only, protect and private. |
|---|---|
| Confidence | An enforcing breach carries 1.0. An advisory breach carries 0.9; an empty component carries 0.4. Both remain below the default gate floor. |
| Enforceable | Yes, with --fail-on=constraints. |
| Boundary | A rule can use only extracted facts. If a forbid_reach component is too large to traverse, Enola emits one advisory. |
03 · Outlier tests
These explainers flag entities outside the current repository’s statistical distribution. They do not use a universal 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.
Measures what import yourpackage actually executes, including the __init__.py files no import statement names.
Evidence: the initialisations responsible for most of the closure, which is the part a split would give back.
All three are heuristic and advisory by default. Their scores describe graph shape and import cost; 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 fourteen known taxonomies. Its violations carry 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 existing modules. Modules outside all declared paths are unclassified and produce no layer findings. |
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.
Names directories that look like in-tree copies of another project: a licence file of their own, under a parent conventionally used for dependencies.
Boundary: it reports and hands you the ignore: globs. It never excludes anything itself, because a heuristic wired to an irreversible action has no way to be wrong safely.
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 traces where the relation was built and excludes declared preloads, unsaved records, batched paths, and test code. Dynamic receiver types remain unresolved.
Marks symbols invoked directly by a framework, including routed controller actions, queued jobs, mailers, and scheduled tasks. These symbols become roots for reachability analysis. The explainer does not report reachability from those roots.
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 · Whole-repository analysis
These three analyze the full repository. Each has an MCP tool that returns the complete result. Snapshot findings are capped at fifty entries plus a summary.
Arithmetic over edges the graph already holds: afferent and efferent coupling, instability, abstractness, and distance from Martin's main sequence. The numbers are exact. Only the claim that a large distance is a problem is an opinion, which is why the findings stay advisory. Tool: package_metrics.
Reports unreferenced facts in three confidence tiers: functions high, types medium, and other facts low. It excludes methods already reported by dead-methods and ranks vendored-code candidates last. Dynamic dispatch and unresolved type use can hide references, so results require review. Tool: find_orphans.
Estimates where cost grows with the input, from loop-nesting and call-in-loop properties the parsers record. The Big-O labels are a structural worst case, not a measurement, and nothing here knows whether a loop is hot. It files no call-in-loop finding against a symbol query-loops already reports, and each language has its own gate for which calls count as expensive. Tool: analyze_performance.
09 · 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.