Evidence reference¶
The evidence interface records why every selected focus node passed or failed, linked to both the authored SHACL statement and its normalized algebra.
Frontends |
Python |
Stability |
Stable |
Related |
Explain why a node passed or failed, Why evidence, Shape map reference |
Explain why a node passed or failed shows how to use it; Why evidence explains why it is shaped this way.
Structure of a run¶
EvidenceRun
└── StatementEvaluation one per included authored statement
├── selected_foci = [] target selected nothing
└── FocusEvaluation one per selected focus node
├── status = "pass"
│ └── Satisfaction why the constraint holds
└── status = "fail"
└── Failure why the constraint does not hold
The interface is statement-oriented. It preserves statements whose targets
select nothing, then partitions every selected (statement, focus) pair into
exactly one polarity. Three states are therefore observably different:
unselected — no focus row exists for that node;
selected and passing — the row carries satisfaction evidence;
selected and failing — the row carries failure evidence.
EvidenceSession¶
shifty.EvidenceSession(shacl_graph, data_graph=None, *,
infer=True, graph_mode="union", base=None)
Parsing, lowering, optional inference, dataset indexing, and SPARQL preparation happen in the constructor and are retained, so reuse the session rather than rebuilding it.
Argument |
Meaning |
|---|---|
|
Run SHACL-AF rules to a fixed point first. Default |
|
|
|
Base IRI used while parsing relative IRIs. |
Methods¶
session.validate(*, shape_names=None, minimum_severity="info",
sort_results=True) -> EvidenceRun
shape_namesRestrict top-level validation to named shapes. Statements excluded by this filter do not appear in the run at all — which is different from an included statement whose selector produced
selected_foci=[].minimum_severityWhich failures make
run.conformsfalse. Evidence is still materialized for pairs below the threshold, so a run can conform while containing afailrow.sort_resultsWhen true (the default), statements are ordered by source statement id and foci by lexical N-Triples form.
session.evidence_for(focus, constraint_id) -> EvidenceNode
Evidence for one focus against one normalized constraint id — any
constraint in the run’s catalog, not only a statement’s top shape. The returned
node exposes status, evidence_kind, kind, constraint_id,
to_dict(), and to_json(). No target selection is involved, so a focus
that no statement selects still yields well-defined evidence. This is the
drill-down for children a canonical failure elided.
Objects and fields¶
Object |
Fields |
|---|---|
|
|
|
|
|
|
|
the evidence tree, its projections, serialization, and |
The polarity-specific properties make type-directed code straightforward:
if focus.status == "pass":
assert focus.satisfaction is focus.evidence
assert focus.failure is None
else:
assert focus.failure is focus.evidence
assert focus.satisfaction is None
Source and normalized identity¶
Normalization simplifies, compacts, and deduplicates the authored algebra, so two identities are kept.
source_statement_id/source_constraint_idIdentify the statement and constraint in the schema produced directly from the authored SHACL graph. Use these to correlate evidence with author intent and with source-level progress.
normalized_statement_id/normalized_constraint_idIdentify the executable normalized form. Several source statements may share one normalized identity after common-subexpression elimination.
constraint_kind/constraintThe stable semantic category and the structured normalized operator. Branch on
constraint_kind; do not parseexplain().
A serialized run carries both source and normalized constraint catalogs, so child constraint ids stay resolvable without the live session.
Canonical evidence and progress¶
Canonical evidence is decisive: a failed conjunction retains the children that establish the failure and does not carry successful siblings. It is a proof, and it is the same structured witness the repair layer consumes. Satisfaction is its logical complement, recording the branches and values that establish a pass.
FocusEvaluation.progress is the source-oriented view for the immediate
authored children, including ones the canonical tree elided:
for node in focus.evidence.walk():
print("canonical", node.status, node.kind, node.constraint_id)
if focus.progress is not None:
for child in focus.progress.evaluated_children:
print("authored child",
child.source_constraint_ref,
child.normalized_constraint_ref,
child.constraint_kind,
child.status)
Canonical evidence answers why did this result hold?; progress answers what happened to the immediate authored children along the way?
Projections¶
Satisfaction and Failure share these. They traverse deterministically
and deduplicate by first occurrence.
walk()Typed pre-order traversal. Each node has
status,kind,constraint_id, and JSON-compatible detail.supporting_triples()RDF triples used by positive path certificates, in N-Triples form.
path_supports()Structured path certificates. A certificate is one concrete successful route — not an enumeration of every route, and not necessarily a deletion cut.
matched_values()Values that qualified or were checked successfully along count and all-values constraints.
missing_obligations()Cardinality deficits, each with
constraint_id,observed_count,required_count, andmissing.offending_values()Values implicated in atomic, closed, relational, and excessive-count failures.
source_constraints()Source constraint ids associated with this evidence object.
summary()A flat list of
WitnessAtom(onFailure) orSatAtom(onSatisfaction) — the leaf facts, without the tree.explain()Human-readable rendering. Not a parsing API.
Serialization¶
payload = run.to_dict()
encoded = run.to_json()
assert payload == json.loads(encoded)
evidence = run.statements[0].selected_foci[0].evidence
evidence.to_dict()
evidence.to_json()
The JSON uses explicit status and variant tags. Treat the tags, ids, and structured fields as the interchange format.
Compact encoding¶
wire = run.to_compact_json(include_catalog=False)
catalog = run.to_dict()["constraints"]
restored = shifty.expand_evidence(wire, catalog)
assert restored == run.to_dict()
Each distinct evidence node and RDF term is stored once and referenced by index.
Lossless, and shifty.expand_evidence(compact, catalog=None, *, as_dict=True)
restores the run exactly. The catalog is fixed per snapshot regardless of how
many findings there are, so omitting it matters most on small runs — see
Evidence performance for measured sizes.
Rust API¶
use shifty_engine::{
Evidence, PreparedEvidenceValidator, ValidationGraphMode, ValidationOptions,
};
let shapes = shifty_parse::load_turtle(shapes_bytes, None)?;
let parsed = shifty_parse::parse_loaded(&shapes);
let data = shifty_parse::load_turtle(data_bytes, None)?;
let prepared = PreparedEvidenceValidator::with_graphs(
&data.graph,
&shapes.graph,
&parsed.schema,
ValidationGraphMode::Union,
)?;
let run = prepared.validate(&ValidationOptions::default());
for statement in &run.statements {
for focus in &statement.selected_foci {
match &focus.evidence {
Evidence::Satisfaction(trace) => println!("{} passes", focus.focus),
Evidence::Failure(failure) => println!("{} fails", focus.focus),
}
}
}
PreparedEvidenceValidator also has partial entry points. Conformance-only
scans take ConformanceOptions rather than the broader ValidationOptions
because severity filtering requires failure evidence:
Method |
Cost |
|---|---|
|
Counts only, no evidence. The baseline. |
|
Counts plus which pairs failed. |
|
Evidence for one already-selected pair. |
|
Evidence for one focus against one constraint id. |
|
The constraint catalog, fixed per snapshot. |
|
Evidence for every selected pair. |
For one-shot calls there are validate_with_evidence,
validate_with_context_and_evidence, and validate_graphs_with_evidence,
with variants taking a graph mode and ValidationOptions.
Guarantees and limits¶
Evidence uses the logical validation evaluator as its oracle. It reuses normalized schemas, indexed datasets, and SPARQL preparation, but is not yet fused with every physical-plan operator.
Positive recursion follows the greatest-fixed-point validation semantics. A recursive success back-edge becomes a finite
coinductivesatisfaction leaf.Canonical failure evidence is pruned to decisive failures; immediate authored siblings remain available through
progress.Statement ordering, focus ordering, traversal, and projections are deterministic under the default sorting option.
A
PathSupportis a positive reachability certificate. For an alternative path, Shifty retains the first successful syntactic alternative.
Opaque and blocked evidence¶
Validation status stays exact even where Shifty cannot construct actionable provenance. These cases are explicit in the tree rather than silent:
a failing SPARQL constraint is
opaque, carrying its query diagnostic;a passing SPARQL constraint is
blockedwithopaque_sparql, because an arbitrary query cannot generally be falsified by a sound data deletion;SHACL-AF expression failures are opaque, and passing expressions are blocked where expression-level provenance is unavailable;
passing closed and relational constraints are blocked only in the deletive repair direction — their validation result is still valid;
coinductive satisfaction records a greatest-fixed-point back-edge, not a finite set of supporting triples.