Measures¶
Tip
Every program evaluated by Topos is measured along four independent Quality Pillars. These pillars are the generators for the Quality Medals you can earn. Topos never collapses these into a single number — you always see which pillar is the problem.
1. The SIMPLE Pillar (Code Complexity)¶
Evaluates the internal quality of the code by analyzing the Control Flow Graph (CFG) and Abstract Syntax Tree (AST). The SIMPLE pillar always runs and maps to the SIMPLE badge outcome.
Cyclomatic Complexity (
cfg.cyclomatic) Measures the number of linearly independent paths through the code. Branches, loops, and conditionals increase complexity. Higher values negatively impact the SIMPLE score.Essential Complexity (
cfg.essential) Counts “structured” vs. unstructured control flow. Complex nested conditions reduce this metric.Nesting Depth (
cfg.nesting_depth) Maximum nesting level of control structures. Deeper nesting is harder to reason about.Longest Path (
cfg.longest_path) Longest acyclic execution path through the CFG. Long paths correlate with high cognitive load.Entropy (
ast.entropy) A Kolmogorov-complexity proxy using compression ratios. It measures how predictable the code is. Very low entropy suggests excessive boilerplate; very high entropy signals chaotic or highly unusual structure (often seen in hallucinated code). The healthy range sits around 0.5.
2. The COMPOSABLE Pillar (Outward Dependency Burden)¶
Evaluates how much external behavior a file coordinates by analyzing the dependency graph. (Requires GitNexus) The COMPOSABLE pillar maps to the COMPOSABLE badge outcome.
Coupling (
mdg.coupling) The total number of afferent (incoming) and efferent (outgoing) dependencies. High total coupling negatively impacts the COMPOSABLE score.Instability (
mdg.instability) Calculated asEfferent / (Afferent + Efferent).Near 0: The module is a rigid dependency for many others and is hard to change safely.
Near 1: The module is highly unstable because it depends on many other parts of the system.
A balanced range (0.3 – 0.7) helps achieve a higher COMPOSABLE score.
Fan-out (
mdg.fan_out) Counts distinct external symbols called by the file. This is the v0.5 file-level gate:fan_out <= 10.Fan-in (
mdg.fan_in) Counts incoming call edges. It remains scored and actionable as a responsibility/change-impact signal, but is advisory because a stable interface or shared utility can legitimately have many callers.Dependency Depth (
mdg.dep_depth) The longest dependency chain from this module. Shallow chains are easier to understand and refactor.
3. The SECURE Pillar (Vulnerability Analysis)¶
Evaluates whether the code flow can reach dangerous operations or untrusted data. Computed from the Code Property Graph (CPG) — derived intrinsically from the UAST, no external tooling required. The SECURE pillar maps to the SECURE badge outcome.
Dangerous Calls (
cpg.dangerous_calls) Count of reachable call sites matching a per-language registry of dangerous APIs (Python:eval,exec,pickle.loads, …; C++:gets,strcpy, …). Lower counts improve the SECURE score.Taint Flows (
cpg.taint_flows) Source→sink data-flow paths along the CPG’s data-dependence edges, from untrusted sources (e.g.input,request.args) to dangerous sinks. Longer taint chains increase risk.
Note
The embedded Sighthound SAST
engine supplies supplementary security_findings detail (per-finding
callee, line, taint source/sink) for Python/JavaScript/TypeScript/Go —
but cpg.dangerous_calls/cpg.taint_flows above, and therefore the
SECURE score itself, always come from the native CPG probes. Sighthound
never feeds SECURE.
Scoring and Manager Priorities¶
Topos produces a continuous normalized score [0.0, 1.0] for each pillar.
A pillar is achieved when its independent raw gate or gates pass. Score
floors are used by aggregate morphism paths and continuous scores preserve
advisory detail; they are not the live file-level verdict rule.
Pillar |
Threshold |
Raw Requirement (Policy Φᵢ) |
|---|---|---|
SIMPLE |
|
|
COMPOSABLE |
|
|
SECURE |
|
Zero |
NAVIGABLE |
|
|
Scores are reported as percentages (0–100%) in all CLI and MCP output. Note that while the thresholds are used for score-floor aggregation, the authoritative achievement of a pillar is determined by the independent AND of the raw metric requirements defined in each generator’s policy.
The weights (w_*) for each pillar’s internal components are controlled by the Priority (part of the Preference Ranking):
Priority |
|
|
|
Effect |
|---|---|---|---|---|
|
0.7 |
0.15 |
0.15 |
Upweights SIMPLE; rewards low-complexity code |
|
0.15 |
0.7 |
0.15 |
Upweights COMPOSABLE; rewards tightly-bounded modules |
|
0.15 |
0.15 |
0.7 |
Upweights SECURE; rewards low-risk data flows |
Changing the priority does not change what is measured — it changes the weights within each generator’s scoring function.
Calibration against real corpora¶
The thresholds above are not arbitrary. They are tuned so that the medal tiers
track how mature, widely-trusted Python libraries actually score. Below, three
reference codebases — requests, numpy, and pandas — measured pillar
by pillar and by the resulting medal mix.
Verdicts¶
The per-pillar scores map to a 16-valued Heyting algebra (free lattice on 4 generators), representing the Quality Medals:
The medal tier is the count of pillars achieved; which ones you
achieved is named by the lattice_element and matters for diagnosis:
Pillars |
Medal |
Verdicts |
|---|---|---|
4 of 4 |
🏆 |
|
3 of 4 |
🥇 |
|
2 of 4 |
🥈 |
|
1 of 4 |
🥉 |
|
0 of 4 |
❌ none |
|
Changed in version 0.5.0: IDEAL now requires all four pillars and awards PLATINUM. The
verdict formerly called IDEAL — the top of the three-generator
algebra — is now SIMPLE_COMPOSABLE_SECURE and bands as GOLD.
The four pillars SIMPLE, COMPOSABLE, SECURE, and NAVIGABLE
are pairwise incomparable — a file can achieve any subset of them
independently. The overall lattice_element is determined by which
combination of pillars passed their calibrated gates:
SIMPLE COMPOSABLE SECURE NAVIGABLE → verdict
1 1 1 1 → IDEAL 🏆
1 1 1 0 → SIMPLE_COMPOSABLE_SECURE 🥇
1 1 0 1 → SIMPLE_COMPOSABLE_NAVIGABLE 🥇
1 1 0 0 → SIMPLE_COMPOSABLE 🥈
1 0 0 1 → SIMPLE_NAVIGABLE 🥈
1 0 0 0 → SIMPLE 🥉
0 0 0 0 → SLOP ❌
COMPOSABLE requires a GitNexus dependency graph and SECURE requires
a CPG; either is reported as not measured rather than failed when its
input is unavailable. SIMPLE and NAVIGABLE need only the file
itself, so they are always evaluated.
Comparing Programs (Profunctors)¶
While the four quality pillars define a program’s absolute placement on the evaluation lattice (the characteristic morphism), Topos also provides relational tools to measure the “distance” or “overlap” between two programs. In our category-theoretic model, these are Profunctors.
Note
Important: Profunctors are comparative metrics. They are highly useful for agent workflows (e.g., “did this refactor actually change the structure?”) but they do not influence the Quality Badges or the evaluation lattice.
Topos supports several relational metrics across its different graph representations:
CFG Comparison: Measures changes in cyclomatic complexity and edge distribution. (e.g., detecting if an agent added a new conditional branch).
CPG Comparison: Measures changes in dangerous API usage and taint flows, as well as general node-type overlap (Jaccard similarity).
MDG Comparison: Measures changes in coupling, fan-in/fan-out, and dependency depth.
PDG Comparison: Computes the Jaccard similarity of control and data dependencies between two versions of a function.
AST Edit Distance: Measures the topological drift between two programs using UAST edit distance.
Refactor Suite (also not scored)¶
Beyond profunctor comparisons, the topos_refactor MCP tool surfaces
ranked structural hotspots from three more engines — CFG cycle basis and
MDG/process-graph curvature. Like the
profunctors above, none of these feed the evaluation lattice; they’re
refactoring guidance layered on top. See For Agents and the repository’s
docs/decisions/refactor-suite.md for the full design.
Structural Test Coverage¶
Topos uses Declaration-level Bipartite Coverage to estimate how much of a program-under-test (PUT) appears in a test suite at the level of normalized UAST structure.
Unlike line or branch coverage, this method does not require code execution. It answers: does the test code contain similar structural shapes (kinds, control-flow nodes, kind paths) as the declarations in the PUT?
The CLI command is:
topos coverage --tests tests/test_mod.py src/mod.py
How it works
Extraction: Every
FunctionDeclandMethodDeclis extracted from both the PUT and the test suite.Fingerprinting: Each declaration is fingerprinted by the multiset of UAST kinds (excluding the root declaration kind itself) in its body.
Bipartite Matching: Each PUT declaration is matched against the best-matching declaration in the test suite using multiset recall.
Scoring:
Mean Declaration Coverage: The average best-match recall across all PUT declarations.
F2 Score: A harmonic mean that combines declaration recall with test precision, biased heavily toward recall (F2). This penalizes bloated test suites that contain large amounts of code unrelated to the PUT.
Uncovered Declarations: The tool identifies specific locations in the source code that lack corresponding structural representation in the tests.
Interpretation
Higher mean coverage indicates more of the PUT’s structural declarations have matches in the test suite.
An F2 score significantly lower than mean coverage indicates a bloated test suite.
A low score suggests tests may be missing classes of syntax present in the PUT.