Verdict / Observability
Routing receipts and observability
Every decision that claims Verdict authority produces a receipt: which candidates existed, which were dropped and why, what was chosen, and what happened at execution. It does this without storing prompts or credentials.
What a receipt records
Status: Shipped- Correlation: request, story, work-unit, and attempt identifiers, plus a link to any parent receipt.
- Policy: the policy version and a digest of the decision, so the record can be recomputed and checked.
- Candidates: every candidate in the pipeline, whether it was eligible, and its reason codes.
- Context: digests of the context plan and pack, not their contents.
- Execution: the selected identity, the identity the gateway reports actually served the request, and observed usage.
- Outcome: verification results and the final state.
Exact excerpt from the credential-free fixture source below. selected is the admitted model ID; exclusions come from the candidate explanation. The result stores the decision separately from receipt, whose fields are receipt_id and mode. This quickstart writes no state and does not create a durable ledger.
selected = eligible[0].model.id if eligible else None
decision = RoutingDecisionContract(
selected_route={"runtime_id": selected, "provider": "demo"},
task_spec=task_spec.to_dict(),
candidate_snapshot="fixture:issue-35",
exclusions=[row for row in explanation if row["rejected"]],
policy_floor="high",
planner_mode="deterministic_fixture",
explanation=(
"Selected the only candidate satisfying required capabilities and "
"fresh healthy availability; excluded all hard-gate failures."
),
fallback_plan=[],
policy_version="demo-policy-1",
)Durable, append-only storage
Status: ShippedReceipts persist to a local SQLite ledger in WAL mode. Each record gets a stable ID, a payload hash, and the previous record's hash, forming a chain within its scope. Existing records are never updated: lifecycle changes are new linked records, and deletion is a tombstone.
Payloads are redacted by default. Raw prompts, completions, tool arguments, and credentials are rejected at the contract boundary. Reads, exports, and replay verify the chain and fail closed if it has been tampered with.
Explaining a decision
Status: ShippedRoute responses carry an x-verdict-evidence-id header and correlation IDs. GET /v1/route/explain accepts exactly one selector and answers two kinds of question:
- What happened? Look up the immutable decision-time evidence by evidence, request, or correlation ID. This works even after caches expire or the gateway is unavailable.
- What would happen now? For a model, return its freshness record: when it was observed, when that expires, its age, source, and confidence, the eligible and excluded sets with reasons, and the cache's refresh or error state. This part requires a configured gateway.
Independent verification
Status: ShippedA verifier that imports only the Python standard library can check a serialized receipt without loading any routing, policy, or gateway code. It fails closed for tampered, malformed, incomplete, skipped, or unavailable evidence; a missing piece never becomes an implicit pass.
Observed, not assumed
Status: ExperimentalIn live paired benchmark runs, the model that served a request is bound only to identity headers reported by the gateway. The model field in the response body echoes the requested alias, so it is ignored. Cost is recorded only from observed usage headers and is never estimated from a model name. The retained evidence bundle keeps hashes of inputs and outputs, not their text.
Receipt explorer
Status: RoadmapA public UI is planned to read the receipt contract directly and to run in fixture mode without credentials, showing accepted and denied routes, reasons, freshness, provenance, and verification status. It is outside the current release boundary.
Sources
Primary material in the public Verdict repository.