mixle.substrate.trust module¶
verify_lineage() – check that an item’s provenance chain is intact (trust / provenance, N1).
Every substrate item can point at the items it genuinely derives from – derived_from is the typed
provenance/ancestry edge list (parent documents, the model that produced an artifact, the trace it came
from), kept separate from links, the generic, untyped KG-relation surface (kg_rag
and multihop’s “related to” edges) that is NOT ancestry (MXR-080-0261; see
SubstrateItem). A citation or a merge is only trustworthy if that ancestry
chain actually resolves: a link to an item that no longer exists is a dangling provenance edge, and any
claim resting on it is unverifiable.
verify_lineage() walks an item’s ancestry through derived_from alone and returns a three-state
LineageState verdict, never a bare boolean (MXR-080-0260/0261):
INTACT– the full chain was traversed: every ancestor authorized, resolved, cycle-free, complete.BROKEN– a confirmed defect within the verified region: a dangling edge, or a cycle (an ancestor that is, directly or transitively, its own ancestor).UNVERIFIED– the chain could not be fully checked:max_depthwas reached with further, unexplored edges beyond it (MXR-080-0260 – an unvisited tail is never silently certified), or an ancestor lies outside the caller’s authorizedscopeand could be neither confirmed present nor absent (MXR-080-0261).
Scope enforcement (MXR-080-0261) follows the same convention belief uses for the
identical problem (see assimilate/_resolve/_launders): scope doubles as the authorization
principal, and every ancestor at every hop is resolved through an AccessPolicy
– never a raw, scope-blind lookup. An ancestor id that does not exist at all and one that exists but is
private to a scope the caller cannot read are deliberately indistinguishable in the report (both land in
unverified): telling them apart would let a caller use their own scoped audits as an oracle for
“does something exist in a scope I can’t read,” the same side channel MXR-080-0237 closed for semantic
search and MXR-080-0244 closed for belief evidence. A scoped audit can no longer be validated – silently
promoted to INTACT – using content it has no authorized view of.
audit_substrate() runs it over the whole store – a knowledge-integrity sweep, the same “trust is
re-derivable, not asserted” discipline the factuality receipts apply to answers, applied here to the
knowledge itself.
- class LineageState(*values)[source]
Bases:
StrEnumThe three-state verdict of a lineage verification (MXR-080-0260/0261) – a closed vocabulary, the same discipline
MergeStrategyapplies to merge strategies. Never a bare boolean: a bareintactcannot distinguish “checked everything, it’s fine” from “stopped looking partway and don’t actually know” – exactly the overclaim these findings fix.- INTACT: the full chain was traversed – every ancestor authorized and resolved, no cycle, no
depth-cap truncation, no dangling edge. The only state that certifies anything.
- BROKEN: a confirmed structural defect within the verified region – a dangling edge (an ancestor
id that resolves to nothing, where the caller had legitimate visibility to know that for a fact) or a cycle (an ancestor that is, directly or transitively, its own ancestor).
- UNVERIFIED: the chain could not be fully checked –
max_depthwas reached with further, unexplored edges beyond it, or an ancestor lies outside the caller’s authorized scope and could be neither confirmed present nor absent. Never silently promoted to INTACT.
- class LineageReport(item_id, state, n_links, dangling=<factory>, cycles=<factory>, unverified=<factory>, truncated=<factory>, depth=0, visited=0)[source]
Bases:
objectWhether an item’s provenance chain resolves end to end – and exactly why, if it doesn’t.
stateis the headline three-state verdict (seeLineageState); every other field names precisely which ancestor ids drove it, so a caller can always see WHY, not just THAT.- Parameters:
- property intact: bool
TrueiffstateisLineageState.INTACT.Never itself the source of truth –
stateis (MXR-080-0260/0261): a bare boolean cannot distinguish a confirmed break from an open question, which is exactly what these findings fix. Kept only so a caller that wants a single coarse check still gets a CORRECT one: bothBROKENandUNVERIFIEDnow readFalsehere, where the pre-fix code could readTruefor a truncated or cross-scope chain it had never actually verified.- Type:
Read-only convenience view
- verify_lineage(substrate, item_id, *, max_depth=20, scope=None, policy=None)[source]
Walk
item_id’s ancestry viaderived_fromONLY, returning a three-state verdict (cycle-safe).Traverses exclusively
derived_from(MXR-080-0261) – never the generic, untypedlinksKG edges (seeSubstrateItem) – so a merely-related entity can no longer be certified as a derivation ancestor just by existing.scope, when given, doubles as the authorization principal (the same conventionbeliefuses for the identical problem): every ancestor, at every hop, is resolved throughpolicy(a fresh, home-scope-plus-PUBLIC-onlyAccessPolicywhen omitted). An ancestor outside that authorized view can neither ground anINTACTverdict nor be silently skipped as though absent-therefore-fine – it is named inLineageReport.unverified. An ancestor id that does not exist at all and one that exists but is private to a scopescopecannot read are DELIBERATELY indistinguishable here: both land inunverified, neverdangling– telling them apart would let a caller use their own scoped audits as an existence oracle for another scope’s content, the same side channel MXR-080-0237 closed for semantic search and MXR-080-0244 closed for belief evidence.scope=None(the default) is the one deliberate exception, matchingSubstrate.all()/Substrate.search()’s own convention: an unrestricted caller has no boundary to hide behind, so a missing ancestor is unambiguously reportable asLineageState.BROKEN(dangling) – the legacy behavior.max_depthbounds pathological chains, but reaching it is no longer silent (MXR-080-0260): a node at the cap that still has its OWN unexploredderived_fromentries is named inLineageReport.truncatedand the verdict isLineageState.UNVERIFIED, neverINTACT– an unvisited tail is never certified. A node at the cap with no further edges (a genuine leaf) is not truncated.A back-edge into an ancestor already on the CURRENT root-to-node path is a genuine cycle (an item cannot legitimately derive, even transitively, from itself) and is named in
LineageReport.cycles, forcingBROKEN. Reaching an already-fully-resolved ancestor via a DIFFERENT branch (a shared ancestor / diamond) is ordinary DAG structure, not a cycle, and is not re-walked.Precedence when a walk has more than one kind of finding:
BROKEN(a confirmed defect) outranksUNVERIFIED(an open question) outranksINTACT– the legacy “one break anywhere fails the whole chain” discipline, extended to the two new states. A missing or unauthorized ROOT item is handled the same way:BROKENwhen unrestricted (scope=None),UNVERIFIEDwhen scoped.
- audit_substrate(substrate, *, scope=None, policy=None)[source]
A knowledge-integrity sweep: how many items are intact/broken/unverified, every one named.
MXR-080-0261:
scope/policyare threaded into everyverify_lineage()call, so a scoped audit (scopenotNone) can only ever certify a chain using ancestors that SAME scope is authorized to read. Previously this filtered its own item list byscopebut then handed each id to an unscopedverify_lineage, which walked every scope regardless – a scoped audit could be, and was, validated by (or leak the internal integrity of) items it had no business seeing at all.Returns
{n_items, n_intact, n_broken, n_unverified, broken: [...], unverified: [...]}.brokennames every item with a confirmed structural defect (dangling edge and/or cycle) within its authorized visibility;unverifiednames every item whose chain could not be fully checked (depth-cap truncation and/or an ancestor outsidescope’s authorized visibility) – kept separate frombrokenbecause “could not verify” is never the same claim as “confirmed defective.”