Run and read

Read a Scorecard

What the result actually means.

The scorecard.json is the primary artifact: verdict, ci_should_fail, per-metric values and status counts, authority, baseline state, and diagnostics. The Markdown and terminal views are renderings of this typed data — never the source of authority. The friendly tour lives on the marketing Scorecards page; this is the precise reader.

Read the Scorecard in one direction: verdict first, then why. The verdict tells you what CI should do; authority tells you whether any gate was even active; metric status counts tell you what was actually measured. The most common misreadings — informational as pass, a missing measurement as 0.0 — are exactly what these fields prevent.

The artifact

This is the quickstart’s Scorecard, trimmed to one metric for clarity:

json

{
  "verdict": {
    "verdict": "informational",
    "ci_should_fail": false,
    "passing_gates": [], "failing_gates": [], "blocked_gates": [],
    "informational_metrics": ["exact_match", "structural_shape", "field_presence", "answer_nonempty"],
    "reasons": {}
  },
  "metrics": [
    { "metric": "exact_match", "value": 1.0, "included_count": 3,
      "aggregation": "mean", "status_counts": { "scored": 3, "non_evaluable": 2 } }
  ],
  "authority": {
    "exact_match": { "can_gate": false, "blocked": false, "level": "informational",
      "reasons": ["metric_status=informational", "threshold_proposed", "dataset_proposed"] }
  },
  "baseline_state": "comparison_not_requested",
  "diagnostics": []
}

Field by field

FieldHow to read itWhat it cannot prove
verdict.verdictThe run outcome: informational / pass / fail / blocked.informational is not pass — it means no gate ran.
verdict.ci_should_failThe single CI signal. true only for fail/blocked.false does not mean “quality checked” — an informational run is also false.
metrics[].valueThe aggregated value over scored + valid examples.A high value on a informational metric still gates nothing.
metrics[].included_countHow many examples entered the math.If it is below your dataset size, some examples were excluded — read status_counts.
metrics[].status_countsEvery status, counted — including non_evaluable, blocked, error.Excluded states are never folded into the value as 0.0.
authority[m]Whether metric m may gate, with typed reasons.The report renders this; it can never grant authority the records don’t.
baseline_stateWhether a regression claim is supported this run.comparison_not_requested / not_comparable means no delta is claimed.
diagnosticsStructured reasons for non-perfect states.A warning here does not by itself fail CI.

Same data, three views

The JSON above is authoritative. report.md and the terminal summary render it; with --format ci the same verdict becomes GitHub annotations. None of them recompute the verdict — they read verdict.verdict and ci_should_fail as-is.

Read authority before celebrating a value A metric can show value: 1.0 and still be informational. The value says “measured well”; authority says “allowed to gate.” Both must hold before a number enforces anything.

Coming to the scorecard

Two additions are built in the framework but not yet in the version the plugin installs — badged here, and never shown executing until they ship. Status lives on the ahead-of-code register.

AdditionStatusWhat it is
HTML report (report.html)nextA fourth rendering of the same typed source: the verdict, per-metric values with their intervals, a “what this run does not claim” panel, and the delta from your previous run. Still a rendering — scorecard.json stays authoritative.
Confidence intervals & lower-bound gatingexperimentalA score can carry a confidence interval and an effective sample size, and a gate can decide on the lower bound — so a perfect score over too few examples does not gate. Being hardened; not core-reliable.

Diagnostic clusters

A metric value says that something is wrong, not what. The additive Scorecard.clusters field groups a run’s failing and non-scored items by their shared Diagnostic.code, per metric — so “faithfulness = 0.82” becomes “the 18% that failed are all missing-citation cases.” It is a lens on the same typed source: clusters summarize failures, they never gate and never change a score. Built in the framework; not yet in the version the plugin installs.

What it guaranteesWhy it holds
Grouping is by explicit cause, not list orderThe pure cluster() function is order-invariant — any input order yields an equal, canonically-sorted list.
You cannot inject a cluster the data does not supportA fabricated or hand-edited cluster fails _verify_consistency, which recomputes the clusters from the persisted scores and rejects a mismatch.
A non-scored item is grouped, not zeroedA blocked / non_evaluable / error item is counted by its cause and never coerced to 0.0.
The same typed source, one more renderingClusters render in both report.md and report.html from the scorecard.json field — no re-grouping, no second verdict path.
Clusters are a different axis from the metrics explorer Diagnostic clusters group failures by Diagnostic.code and are next. The call-identity “metrics explorer” — mapping a score to its source call site — is unbuilt, and the per-source-function view below is a deliberate non-goal, never “planned.” The Diagnostic Clusters reference has the typed field.

From per-metric to per-call

By default /evalglass view reports per metric. With --by-call it groups the same scores by their subject identity — the example_id/unit_id each score now carries (framework slice F1). Grouping is by explicit identity, never by list order; a score with no identity (an older artifact) is reported as such, never guessed.

json

{
  "metric": "set_overlap",
  "value": 0.4,
  "status": "scored",
  "validity": "valid",
  "evaluator_version": "set_overlap@1",
  "example_id": "datasets/grounded.jsonl#1",
  "unit_id": "datasets/grounded.jsonl#1"
}

This is a single Score from runrecord.json (the complete record; the Scorecard above is the compact aggregate). The identity is additive provenance: it changes no score meaning, no aggregation, and nothing in the Verdict Engine — and the engine stamps it, so evaluators never have to. Old artifacts without the fields still parse.

Three granularities

GranularityGroups byStatus
per-metricmetric nameshipped — view
per-callexplicit subject identity (example_id/unit_id)shipped — view --by-call
per-source-functionthe discovered source call sitenon-goal
Per-source-function is not built Mapping a score back to the source function that produced the call needs a trace↔call-site correlation that does not exist yet; inventing one (by name-matching or list order) would be exactly the false confidence the project forbids. Per-call grouping by explicit identity is the honest ceiling today.

Next steps