Overview
Introduced in v5.2.0. The
DiagnosticResult model is an additive contract — no existing engine return types are changed. Engines migrate incrementally.Dict[str, Any] results with no consistent structure. Three incompatible VerificationResult dataclasses existed. Some engines returned (bool, str) tuples. Verification diagnostics were not separable by audience — agents saw internal detection patterns, developers couldn’t reliably find expected vs actual values, and auditors had no proof artifact references.
v5.2.0 introduces a unified DiagnosticResult type with three disclosure layers, each targeted at a specific audience.
The 3-layer model
Layer 1 — Agent-Safe Diagnostics
Field:agent_message: str
Agent/model-facing summary. Allows agents to correct failures without exposing verification internals.
Allowed:
- “Missing required field: customer_id”
- “Verification failed — claim not supported”
- “Could not deterministically verify”
- Detection signatures
- Rule IDs
- Internal regex patterns
- Prompt injection indicators
- Security bypass guidance
- Verification implementation details
Layer 2 — Developer Diagnostics
Field:developer_fields: dict
Application-developer-facing structured evidence. Includes constraint_id, expected/actual values, advisory_checks, methods_used, and engine-specific evidence.
Layer 3 — Proof Diagnostics
Field:proof_ref: Optional[str]
Cryptographic hash (sha256:...) of retained proof artifact. Present only when status == VERIFIED and proof was established. None for UNVERIFIABLE / BLOCKED.
This is the authority bit. Downstream gates use a mechanical rule:
Status taxonomy
Three states only — no proliferation.
Richer distinctions (ambiguity, insufficient evidence, non-convergence, provider drift) live in
developer_fields.constraint_id, not in status values. This keeps the taxonomy small while preserving diagnostic richness.
Key invariants
VERIFIED requires proof
__post_init__ raises ValueError if status == VERIFIED and proof_ref is None or empty. “VERIFIED without proof” is impossible to construct — not a caller convention, a type-level invariant.
Non-VERIFIED rejects proof
The inverse is also enforced —UNVERIFIABLE and BLOCKED must have proof_ref = None. A non-pass state with a proof hash is a contract violation.
Frozen dataclasses
BothDiagnosticResult and AdvisoryCheck are frozen=True. Post-construction mutation of proof_ref or status is blocked — prevents bypassing the authority contract.
Advisory checks never influence verdicts
AdvisoryCheck represents non-proof-bearing analysis (LLM fallback, NLI entailment, VLM interpretation, heuristic consistency checks). It populates developer_fields.advisory_checks with advisory_only=True enforced via __post_init__.
Advisory checks never set status or proof_ref. This structurally enforces the constraint: diagnostics must never originate from model reasoning, confidence, or self-assessment.
Usage
Constructing results
Advisory checks
Downstream gate
Serialization
Migrating legacy engines
from_legacy_dict() converts ad-hoc engine dicts to DiagnosticResult for fail-closed states:
API reference
DiagnosticStatus
DiagnosticResult
AdvisoryCheck
compute_proof_ref(evidence)
ValueError (fail-closed) — callers must pre-convert.
Constraints
Migration path
TheDiagnosticResult contract is additive. Existing engines continue to work with their ad-hoc return types. Migration is incremental:
- v5.2.0 (this release) — contract established, 83 tests
- Engine conformance — each engine adopts
DiagnosticResultin a separate PR - Full migration — ad-hoc dicts and
VerificationResultdataclasses replaced
Engines being migrated
Related
Attestations
Cryptographic proof artifacts and JWT signing for verification results.
Architecture overview
High-level QWED architecture and verification lifecycle.
Determinism guarantee
How QWED enforces deterministic verification outcomes.
Compliance
Audit trails, SOC 2, and GDPR compliance documentation.