Derived-evidence lineage
Termivar records immutable [Evidence]. Some records are derived: computed from
other evidence records rather than observed first-hand. This page defines how a
derived record identifies the exact source record(s) that justify it.
Direct vs. derived
- Direct evidence — a first-hand observation. Its origin is
EvidenceOrigin::Direct. This is the default and the historical meaning of every record. - Derived evidence — a record computed from one or more exact parent
records. Its origin is
EvidenceOrigin::Derived(EvidenceDerivation).
A record with no parents is direct. There is no "zero-parent derivation": an empty parent set is rejected at construction.
Producer provenance is not derivation lineage
These are distinct and must not be conflated:
| Concept | Where it lives | Meaning |
|---|---|---|
| Producer provenance | EvidenceSource { component, method } |
Who produced the record and how |
| Case correlation | EvidenceSource.correlation_id |
Which verification case / execution turn emitted it |
| Derivation lineage | EvidenceOrigin::Derived |
Which exact records were transformed into this one |
| Reasoning support | Fact.evidence_ids, EvidenceContribution |
Which records support a fact or hypothesis |
| Entity relation | KnowledgeRelation |
A semantic edge between knowledge entities |
Sharing a correlation_id means two records came from the same turn — not
that one was derived from the other. Lineage is exact parent EvidenceIds.
Contract
- Exact parents.
EvidenceDerivationholds one or more parentEvidenceIds, canonicalized (sorted, de-duplicated). Acceptance never depends on input order, and an equivalent lineage is a single stable value. - Algorithm identity. A stable, bounded
DerivationAlgorithm { name, version }lets a consumer know exactly which transformation produced a child, not merely that some transformation did. - Atomic insertion. The knowledge store validates lineage before any write: self-reference, parent existence (over the committed store plus the same batch), subject agreement, and cycle freedom. Any violation rejects the whole batch — no orphan child, no orphan lineage, no partial index. A parent may appear after its child within a batch.
- Boundedness. Parents per record are bounded by
MAX_DERIVATION_PARENTS; the algorithm name byMAX_DERIVATION_ALGORITHM_BYTES. Cycle detection is an explicit-stack (never recursive) traversal scoped to a batch's new records, bounded by batch size times the parent bound. - Identity. Origin participates in structural equality, so reusing an evidence ID as direct-vs-derived, or with a different parent set, is an identity conflict.
What lineage does not do
- It does not change claim semantics. Reasoning still evaluates predicates
and values exactly as before; an
EvidenceSelectorcontributes the derived record, not automatically its ancestors. Parent reliability and contribution are never auto-propagated into hypotheses. - It does not assert completeness. A derived record reflects a bounded observation, never a complete inventory.
- It does not create findings or confirm hypotheses.
Compatibility
Lineage is runtime truth held in the live knowledge store. It is
deliberately excluded from the serialized Evidence wire (origin is
#[serde(skip)]): the serialized form of every record — direct or derived — is
byte-identical to the historical contract, and historical/current direct wire
loads unchanged. This avoids encoding a strippable derived/direct discriminator
on a wire that has no durable archive today. Durable lineage export is a future,
explicitly versioned surface (see the profiled API-comparison envelope for the
pattern), not this record's wire.
First consumer
http.response.form-control-names is derived from the exact
http.response.response-body-sample record it parsed, using the
http.form-control-names v1 algorithm. The body sample is the sole
transformation input; the response media type and truncation flag gate or
contextualize extraction but are not cited as lineage.