mathema badges¶
Three orthogonal measures of a codebase, each 0-100, and one picture.
mathema badges [targets] # print the triangle + numbers
mathema badges [targets] --out .mathema/badges/ # also write artifacts
Omit targets for the rootwide analogue of verify (every function the
store knows under --root).
The three badges¶
- implementation (code layer): the raw line ratio from
mathema coverage, how many statements a test, a probe, or a derive proof exercises (their union). A physical ratio, so it is neither averaged nor weighted. - intent (spec layer):
docsync, how much of what each function is meant to do is explicitly specified and up to date, so intent that lives only in someone's head shows up as a gap. - clarity (behaviour layer): how much is KNOWN about a function's behaviour, scored from its VERIFIED claims. See below.
The two per-function-quality scores, intent and clarity, roll up to the repo by a centrality-weighted mean: a function the rest of the codebase depends on (by PageRank over the call graph) counts for more than a leaf helper.
The test report behind implementation¶
The test source of implementation comes from a coverage report that
already exists at the root, coverage.json first, then a native
.coverage. mathema reads it and never runs your tests, unless you ask:
mathema coverage [targets] --run-tests # re-run the tests under coverage first
mathema coverage --stamp # stamp a report produced elsewhere
A report only counts while it still describes the code. Its lines are
keyed by line number, so once a source file changes they can point at
the wrong statements. Those lines are then left out of the score and
reported as reclaimable by a re-run. Freshness is judged per source file,
in one of two ways, and mathema coverage prints which one it used:
- by content hash, when the report is stamped.
coverage.sources.jsonsits beside the report and holds the sha256 of every file it measured, plus the report's own hash. A file's lines count exactly while its content still matches. This holds anywhere the report travels: a fresh checkout, a CI artifact, a cache. A stamp written for a different report is ignored. - by file modification time, when there is no stamp. A file is stale when it was modified after the report was written. That is only reliable on the machine that ran the tests, because a checkout resets file times.
--run-tests stamps the report it produces. A report made by another
tool, for example pytest --cov in CI, needs mathema coverage --stamp
run once afterwards, in the same tree. Stamping costs a hash of each
measured file, milliseconds even for a large package.
When the tests run in parallel mode, or measure subprocesses, coverage
writes one data file per process (.coverage.<suffix>). --run-tests
combines them before exporting, so lines executed in a subprocess, such as
a test that invokes the CLI, count. A test run that fails still produces
the report: the lines every other test executed are kept.
mathema's own functions get no probe source when mathema measures itself. Checking one of them runs mathema's machinery, which may call that same function, so a probe's lines cannot be told apart from the machinery's. Test and derive lines still count.
How clarity is scored¶
Clarity asks a single question: of everything knowable about this
function's behaviour, how much have your verified claims actually pinned
down? It is an information measure, not a pass-rate. Formally it is the
fraction of the function's behavioural uncertainty that the evidence has
removed (1 - remaining / total); informally, how sharply the function
is characterised.
The uncertainty is split into five dimensions, each a different kind of thing you can know:
| dimension | the question it answers | a claim that settles it |
|---|---|---|
| what it computes | which function is this, exactly? | f(x) == 2*x, an equivalence, a closed form |
| what it accepts | which inputs are in and out of bounds? | a domain, a marker, a raises on bad input |
| its bounds & shape | what is the output's range and form? | a bound, monotonicity, symmetry (a proof of what it computes settles these too) |
| how safely it runs | does it run cleanly and deterministically? | the safety families (state, determinism, numerical stability, ...) |
| where it can go wrong | how and where does it fail? | a raises contract, is_compendium_safe |
A few consequences worth knowing:
- A dimension that cannot apply is already fully known. A pure, total function with nothing that can fail has no where it can go wrong to characterise, so that dimension is satisfied for free rather than counted against it.
- Stronger evidence counts for more. A proof settles a dimension in
full; a
holdscounts for less, and aholdsfrom a structured probe (critical-point or exhaustive sampling) counts for more than one from plain random sampling, because it leaves less of the input surface unexplored. A witnessed falsification still counts as knowledge (you now know where it fails); correctness is reported separately. - A
[float]companion that holds counts as numerical stability. A claim the derive route proves spawns a<name>[float]companion, the relation executed against the code in float, in theis_numerically_stablefamily. A companion that holds or is proven credits that family for its function, as a verifiedis_numerically_stableclaim with the same verdict and route would. A falsified companion credits nothing: it records one relation the float code breaks, not the function's stability. - A black-box dependency lowers clarity, and caps it. When a function
calls a library that has a compendium, mathema
has a model of where that call can fail, and
is_compendium_safecan clear it. When it calls a library with NO compendium, there is no model of where it fails, and no claim you can write will settle it. That uncertainty is irreducible, so such a function cannot reach 100 until a compendium covers the library. Writing a compendium stub is the way to lift the ceiling. - No claims is a low floor, not always zero. A function nobody has verified is scored only on what its code visibly shows: a plainly pure, total, hazard-free helper reads low but not zero (it is nearly transparent), while a branchy, impure, or black-box-calling function floors much closer to zero. Declaring and verifying claims is what raises the score.
The scoring algorithm is versioned (entropy-dimensions@1.1) and recorded
beside the scores, so a number is only ever compared against one computed
the same way; a change to the algorithm reads as an algorithm change, not
a regression. @1.1 made two changes, and both raise clarity and overall
against @1. It added the [float] companion credit above. It also reads
the relation of a verified claim the way the claim grammar does, so a
stored identity (the store writes f(x) = 2*x, with a single =), or an
approximate one (~=), now counts toward what it computes. Under @1
it counted only toward bounds & shape.
The triangle¶
The three scores are the corners of a triangle: CLARITY at the top,
IMPL and INTENT the two bottom nodes. The sharp dotted outline is
the full 100/100/100 frame; inside it, each score is a dot on its spoke
from the base (all-zero) out to its corner, and the triangle those three
dots span is filled, so the filled area is the current state and the gap
to the frame is the room to grow. The fraction of the frame that area
shades is the overall number, so half the triangle shaded reads 50:
with clarity the apex height and implementation and intent the base, it
is clarity * (implementation + intent) / 2 on the fractions. It is 100
when all three are 100, and 0 whenever clarity is 0 (a flat shape) or
both base scores are, so comparing two commits' areas shows the change in
overall characterisation. A dimension under 5% is degenerate and the
shape is not drawn, only the numbers.
The standard location, and embedding in a README¶
mathema badges --out # writes to .mathema/badges/ under --root
mathema badges --out path/ # or an explicit directory
Bare --out writes to .mathema/badges/, the documented home under
the tracked .mathema/. Committing it means a README can embed the SVG by
its in-repo path, no external hosting:

The shields.io JSON files serve the same badges through shields' endpoint renderer, referenced by their raw URL, when separate pills are wanted instead of the one triangle.
Artifacts (--out [DIR])¶
mathema OWNS the badge directory. Every run rewrites its artifacts and
DELETES any badge-shaped file (.json, .svg, .txt, .md) it did
not write, naming each removal. That is what keeps a renamed or retired
badge from being served forever from a stale file: a diff-based CI
guard cannot notice a file nothing writes any more, but it does notice
the deletion. Other extensions, and anything in a subdirectory, are
left alone. readme-snippet.md is a paste-ready block for your own
README: the three shields, the triangle, and one line on what each
score means.
triangle.txt, the ASCII triangle: the git-diffable canonical artifact.implementation.json/intent.json/clarity.json, shields.io endpoint badges in the Tetrion Labs colours (an ink label, a deep green value) a README references by raw URL.triangle.svg, a dark-card twin of the ASCII triangle with the same layout: the dashed100/100/100frame, each score a vertex on its spoke, the triangle they span filled in the accent green, and the overall number in the top right. It carries its own background and needs no external fonts, so it reads the same on a light or a dark README.snapshot.json, the numbers (implementation, intent, clarity, overall, plusclarity_algoand the per-function rows), for CI to compute and comment the area delta between commits.