Skip to content
digline
digline explain

digline explain — the run, read back at length

digline compare gives you a sentence and at most twenty lines. digline report gives a customer a document with an answer in a box. Both are compressions: the report's whole craft is deciding what fits on the first screen.

digline explain is the other direction. Same facts, same numbers, expanded — what ran, what moved and by how much, inside or outside which measured interval, what was set aside, what could not be judged, and which of the three configurations differed underneath it all.

It states, and never advises. There is no "you should re-run this", no "this looks promotable". That boundary is deliberate and is the reason the command has a decision record of its own, ADR 0012: whether a red run is a regression or a wobble, and which run deserves to be the reference, is judgment digline leaves to a person. Where you want the counsel it is in AGENTS.md, written for the purpose and marked as judgment.

Using it

One run key, and the presence of a baseline decides the rest:

$ digline explain --suite suite.py --run latest
What ran
5 cases ran.
10 checks ran.
Every case could be judged.
No case is suspended.
The suite is unchanged from the reference.

What differed underneath
prompts/system.txt changed: +1 −0 lines.

What moved
gift-wrap · llm_rubric got worse: 1.000000 to 0.700000, a drop of 0.300000. That is outside the interval its reference measured, 1.000000–1.000000 across 3 samples.
opening-hours · levenshtein got worse: 1.000000 to 0.636364, a drop of 0.363636.

A comparison states no threshold, and the report is why: its case tables are about movement against a reference and print no bars, so a reading of one prints none either. The number is on the fact and in --json, and the run document shows score / threshold on every row. That rule has a name — every value this command prints appears in the same run's report — and it is a test, not a convention.

With no baseline it reads the run alone, and says so by what it does not say — no "reference", no "got worse", no comparison of any kind:

$ digline explain --suite suite.py --run latest
What ran
5 cases ran.
10 checks ran.
Every case could be judged.
No case is suspended.

How it was set up
prompts/system.txt was under test.
The system under test answered with model claude-haiku-4-5.

What it found
opening-hours · levenshtein is under its bar at 0.636364. The bar is 0.750000.

There is no mode flag, and there is not going to be one. Making you name the scope means making you know, before you type, which of two documents you are entitled to — and the first person to hit that is always somebody on their first run, who has no baseline and does not yet know what one is.

The exit code

explain gates, exactly as report does: 0 fine, 1 got worse, 2 could not be judged. It is reading the same comparison, against the same approved reference, that compare gates on.

With no baseline it can never exit 1 — "worse" is a relation and there is nothing here to be worse than — but 2 survives, because a case the suite could not judge is a fact about the harness rather than about a reference.

This is the opposite of digline diff, which always exits 0, and the difference is the reference: a verdict exists only against one that a person approved.

--json: the facts, not the prose

$ digline explain --suite suite.py --run latest --json

emits the fact list the prose is rendered from — typed facts with case and assertion references — and no sentences at all:

{
  "output_version": 1,
  "scope": "comparison",
  "exit_code": 1,
  "facts": [
    {"about": "run", "kind": "cases", "count": 5, "state": null},
    {"about": "setting", "kind": "artifact", "name": "prompts/system.txt",
     "outcome": "changed", "before": null, "after": null,
     "withheld": false, "added": 1, "removed": 0},
    {"about": "check", "kind": "regressed", "scope": "case",
     "case_id": "gift-wrap", "assertion": "llm_rubric",
     "assertion_id": "llm_rubric:…", "before": 1.0, "after": 0.7,
     "delta": -0.3, "threshold": 0.7,
     "noise_min": 1.0, "noise_max": 1.0, "noise_samples": 3}
  ]
}

Three shapes, and you discriminate on about first and then on kind: two of them have a kind called within_noise and they mean different things — one check that moved inside its interval, and the count of every check that did.

scope is "comparison" or "run" and is stated rather than left to be inferred: a run whose comparison found nothing and a run with no reference at all produce different readings, and the difference must not be something you deduce from an absence.

exit_code is the number the process exits with, carried as a field for the same reason it is on compare --json.

What it will not tell you

The judge's words. No fact carries a reason — the field does not exist on the types, which is what makes the payload boundary something no later edit can open by accident. For the judge's words, digline report is one command away, and it renders them in a Reason column.

The same rule takes the stated reason a case was suspended: that a case was set aside travels, because it is a fact about coverage; why somebody set it aside does not, because a developer writes things like "fails on the Rossi account".

Anything about a second run. A reading is of one run and its reference, so it will never tell you that a drop "did not repeat" or that a check is "drifting". Those need a cycle of runs, and the thing that runs cycles is the operator loop — whose alert keeps its own layers for exactly that, and marks the one a model wrote as an opinion rather than as digline's verdict.

--locale

en or it, defaulting to en. Terminal output is for you, so it follows the runtime rule; a document — which is what digline report --locale writes — has a recipient who did not choose English, and takes the flag as mandatory.

There is no --out, and its absence is what keeps that distinction honest rather than an oversight: the moment a reading is written to a file it has a recipient, and the locale would have to become mandatory too.