Skip to content

core.stores

core.stores

Zone A — scoped store access layers (BUILD-SPEC §8, CONVENTIONS).

Polyglot persistence, each store independently replaceable, with access scoped IN CODE (not by convention): a handle exposes exactly the reads/writes a role needs and nothing more. Present: the DuckDB telemetry store, the content-addressed raw store (immutable, §8), the LanceDB thought-graph vectors (derived), and the SQLite derived store for INTERPRETED artifacts (dreams + curator findings, §8) — the last writes interpreted provenance only, so the derived layer can never masquerade as authored ground truth.

authored_supersession

Owner-declared authored-historical supersession store (the-edge-model.md §4a; PD11).

A K₀↔K₀ supersession — "authored document B supersedes authored document A" — is a THIRD dispositional edge type, distinct from note-version supersedes (versions of ONE doc_id, core/stores/versions.py) and claim supersede (dialogue, core/recursion_ops.py). It connects two documents, carries no warrant, mints no derived alternative; both endpoints stay authored.

Ungated only because it is owner-declared — enforced HERE, structurally. The "no verdict gate" property rests on it being the owner's hand. A supersession between two authored notes CAN be machine-derived (Item 10's s(C,D) over authored E_geom; the curator finder), so ungated-ness follows the AUTHORITY, not the edge type. This store admits ONLY an owner-declared write: record() requires an OwnerDeclaration and verifies it at the boundary, so fail-closed survives a careless future caller (capability-dissolution, the-sacred-boundary.md §3 — the machine-write capability is removed, not guarded with a forgeable flag). A machine-inferred authored↔authored supersession is a dreamer-proposed candidate for the blessing gate (supersession-lifecycle.md §3), never a row here.

Append-only, keyed on the two authored digests; superseded() is the active-projection filter (the superseded digest leaves the active view, as ClaimOpStore.superseded did — but the write is owner-authorized). Ordering is the append at timestamp, never edge topology. Zone A, no network.

MachineAuthorityRefused

Bases: PermissionError

A write to the authored-historical store carried no valid OWNER authority — refused at the store's own boundary (fail-closed, STRUCTURAL). This is the guarantee the owner-declared-only design exists for: a machine/model/scheduler/dreamer caller is rejected HERE, so the rule holds even if a future refactor routes a machine call through what used to be an owner-only path.

OwnerDeclaration dataclass

Capability proving a supersession is OWNER-ASSERTED (a founding manifest / an owner CLI), not machine-derived. Construction-guarded: a direct OwnerDeclaration() raises, because only owner_declaration() passes the module-private token. Holding a store reference is thus not enough to write it — a caller must present owner authority the store can verify.

AuthoredSupersession dataclass

superseded instance-attribute
superseding instance-attribute
at instance-attribute
note = '' class-attribute instance-attribute

AuthoredSupersessionStore dataclass

path instance-attribute
record(superseded, superseding, *, declaration, note='')

Append an OWNER-DECLARED authored-historical supersession (superseding replaces superseded in the active projection). Idempotent on the pair (INSERT OR REPLACE).

Fail-closed, STRUCTURAL: declaration must be a valid OwnerDeclaration (owner authority), VERIFIED here at the store's own boundary — None, a forged object, or anything a machine caller could fabricate is REFUSED (MachineAuthorityRefused). The store checks; it does not rely on "no machine path calls it". A machine-inferred supersession belongs in the blessing gate as a dreamer-proposed candidate, never here.

superseded()

Every superseded digest — the active-projection filter (a consumer excludes these from the active view; the superseded note lives on in history). Same role as ClaimOpStore.superseded, but every entry here is owner-authorized by construction.

all()
count()
close()

owner_declaration()

Mint an owner-authority token. Call ONLY from an owner-operated entry point (the founding ingest / an owner CLI). Importing this into a model / scheduler / dreamer path is a boundary violation an import-lint should catch (follow-up); the store's boundary check is the primary structural defense regardless.

open_authored_supersession_store(config=None)

catalog

Vault catalog — the active/tombstone ledger for incremental ingest (vault-sync task).

The Phase-1 ingest is content-addressed: identical bytes store once (raw is sacred). That gives dedup for free but says nothing about which source files currently hold which content, which is exactly what an incremental watcher needs to answer "unchanged?", "changed?", "deleted?". This SQLite catalog is that map — source_path -> (digest, active) — and the authority for the tombstone semantics (design-notes/vault-sync-and-capture.md):

  • unchanged — the file's current digest equals the recorded one and it is active → no-op.
  • changed — a new digest → re-embed; the previous digest's derived rows are dropped iff no other active file still references them.
  • deletedtombstone() marks the row inactive; derived rows are dropped, raw is kept so a re-add dedups and nothing is lost.

It carries only local bookkeeping (paths, digests) — no note content, no network. All notes the watcher records are authored-solo (the owner's own writing) — the §1 spectrum split is now realized, so Provenance.AUTHORED_SOLO is the concrete tag (was the single authored). Dialogue capture records authored-dialogue and curated ingest records curated through the same catalog, by passing provenance= to record.

CatalogEntry dataclass

source_path instance-attribute
digest instance-attribute
title instance-attribute
active instance-attribute
provenance = Provenance.AUTHORED_SOLO.value class-attribute instance-attribute

VaultCatalog dataclass

path instance-attribute
get(source_path)
record(source_path, digest, title, *, provenance=Provenance.AUTHORED_SOLO)

Upsert a file as active at digest (re-adding a tombstoned file reactivates it).

tombstone(source_path)

Mark a file inactive (deleted from the vault). Returns the digest it held, or None if it was unknown. The raw blob is intentionally NOT touched — raw is sacred; true deletion is the separate, owner-gated purge (core/ingest/purge.py).

active_refs(digest)

How many ACTIVE files currently hold this content. Derived rows for a digest may be dropped only when this is 0 (so dedup-shared content isn't pulled out from under a still-present file).

active_paths()
active_entries()
relabel_provenance(old, new)

Rewrite every entry's provenance from old to new. Returns rows changed.

The catalog-side half of the §1 spectrum-split migration (relabel legacy 'authored''authored-solo'). Same-trust-tier relabel, idempotent (a second run matches no old rows).

remove(source_path)

Delete the catalog row entirely (used by the gated purge after raw removal).

paths_for_digest(digest)
remove_digest(digest)

Delete every catalog row for a digest (the gated purge removes only tombstoned content — callers must verify active_refs(digest) == 0 first). Returns rows removed.

close()

derived

Derived-artifact store for the INTERPRETED layer (BUILD-SPEC §8).

The interpreted layer is what the system inferred — dreams (thematic synthesis) and curator findings (near-duplicate / prune / contradiction candidates). Per §8 it is kept SEPARATE and PROVENANCE-MARKED from the owner's authored ground truth: this store holds INTERPRETED only and exposes NO way to write any other provenance — so the derived layer can never masquerade as authored ground truth. That is the structural form of "explicit vs interpreted — separate, provenance-marked layers"; it is not an honor-system check.

Everything here is regenerable: reset() drops it and a fresh dreaming/curation run rebuilds it from the (immutable) corpus. Artifact ids are content-derived, so re-running a cron pass is idempotent (INSERT OR REPLACE) rather than accumulating duplicates.

DREAM = 'dream' module-attribute

FINDING = 'finding' module-attribute

DREAM_LOG = 'dream_log' module-attribute

DERIVES = 'derives' module-attribute

DerivationCycleError

Bases: ValueError

Inserting an artifact would create a cycle in the derivation DAG (Invariant 10).

Confidence decay c ≤ γ^d·g is only well-defined on an ACYCLIC graph with authored leaves — a chain that closes on itself has unbounded (or undefined) depth, the formal shape of the rumination loop the recursion bound exists to tame. So a cycle is refused at insert time, structurally, rather than detected later.

Artifact dataclass

id instance-attribute
kind instance-attribute
subkind instance-attribute
provenance instance-attribute
summary instance-attribute
subjects instance-attribute
data instance-attribute
created_at instance-attribute
derived_from = () class-attribute instance-attribute
attestation_id = None class-attribute instance-attribute

Hyperedge dataclass

One derives B-arc of the derivation hypergraph ℋ (companion III §1.3): the tail set supp(κ) jointly entails the single head κ. The typed, normalized form of an artifact's derived_from — named for what it is. Today every head-set has size 1.

edge_id instance-attribute
head instance-attribute
tails instance-attribute
rel_type = DERIVES class-attribute instance-attribute

DerivedStore dataclass

path instance-attribute
add(*, kind, summary, subjects, data=None, subkind=None, derived_from=None, attestation_id=None)

Store one INTERPRETED artifact. There is deliberately NO provenance parameter: the derived store writes INTERPRETED and nothing else (§8 firewall, structural).

derived_from records the refs this artifact was built from (gap G2): authored note digests (leaves) and/or other artifact ids. The edge set is checked for acyclicity BEFORE insert — a cycle is refused (DerivationCycleError), so the derivation DAG is always acyclic and depth d(κ) is computable (Invariant 10).

attestation_id links this record to the signed attestation that produced it (the runtime proof layer, attestation-layer.md §5); None when written without an attestor.

is_artifact(ref)

True if ref is an interpreted (DERIVED) artifact id; False if it is an authored leaf digest (external, depth 0). The public authored-vs-derived predicate for a grounding decision — e.g. a supersede may ground a revision on an authored C (bedrock, g=1) but never on a derived C (which decays / is superseded without a verdict); see core.recursion_ops.apply_operations.

depth(artifact_id)

Derivation depth d(κ): 0 for an authored leaf; for an interpreted artifact, 1 + the max depth of its interpreted parents (authored-leaf parents count 0). An artifact with no recorded scaffolding is depth 1 (interpreted, minimally one step from ground). Well-defined because the graph is acyclic by construction.

leaf_refs(artifact_id)

The support closure's LEAVES — every ref reachable from artifact_id that is not itself an artifact (i.e. authored note digests). A caller checks these are authored (Invariant 10: 'every leaf of the support-closure is authored').

hyperedges()

The derivation hypergraph ℋ as typed B-arcs (the normalized form of every artifact's derived_from). What the reasoning complex (family 5, core/complex/) consumes.

tails_of(head)

The tail set supp(κ) of artifact head read from the junction — equals, as a set, its derived_from (the invariant _write_hyperedge maintains).

all(*, kind=None, subkind=None)
count(*, kind=None)
reset()

Drop all derived artifacts and their hyperedges. Interpreted data is regenerable (§8): a fresh dreaming/curation run rebuilds it from the immutable corpus.

close()

artifact_id(kind, subkind, subjects)

Public, stable artifact id for a (kind, subkind, subjects) triple — same value add() will assign. An emitter precomputes it so an attestation can record this record as its output BEFORE the record is written, then add(..., attestation_id=...) links back.

open_derived_store(config=None)

edges

The typed/signed edge store — the fiber ε(e) = (t, w, s, τ) (companion III §1.2; Prompt H1).

A binary typed edge carries a relation type, a strength w ≥ 0, and a polarity s ∈ {+1, −1} (EdgeSign — the R1 enum). This is the persistent home for edges that are not recomputable from embeddings (BUILD §1.1): explicit contradictions/links a detector or the owner asserts. Similarity edges are recomputed each pass from the embeddings and are NOT stored here (that would duplicate a regenerable signal); this table stores the polarity/relations the cosine graph cannot carry — chiefly contradiction, the input to balance.py.

Derivation (a B-arc, tail set → head) is a hyperedge, stored in the DerivedStore junction, not here; the two are deliberately distinct structures (companion III §1.3). Zone A, no network.

SIMILAR = 'similar' module-attribute

SUPPORTS = 'supports' module-attribute

CONTRADICTS = 'contradicts' module-attribute

CONTEXTUALIZES = 'contextualizes' module-attribute

Edge dataclass

One typed/signed binary edge — the fiber (w, s, rel_type, τ) over the pair (u, v).

edge_id instance-attribute
u instance-attribute
v instance-attribute
w instance-attribute
sign instance-attribute
rel_type instance-attribute
created_at instance-attribute
provenance instance-attribute

EdgeStore dataclass

path instance-attribute
add(u, v, *, sign, rel_type, w=1.0, provenance='interpreted', created_at=None)

Assert a typed/signed edge. sign is the EdgeSign enum (±1); w ≥ 0 is the strength. Idempotent on (u, v, rel_type) — re-asserting replaces (INSERT OR REPLACE).

all(*, rel_type=None)
count()
reset()

Drop all edges. Similarity edges are regenerable; asserted edges are re-derivable by the detector/owner that created them.

delete_rel_type(rel_type)

Delete every edge of a given rel_type; returns rows removed. The migration for build plan Item 6: retire any misplaced supersedes rows a prior build wrote here, now that note-version history lives in the dedicated VersionStore the balance math cannot read.

close()

open_edge_store(config=None)

rawstore

Content-addressed immutable raw store (BUILD-SPEC §8: "raw is sacred").

Every ingested input is stored verbatim under the SHA-256 of its bytes. The hash is the identity: identical content stores once (dedup for free), and an object is never rewritten or summarized away — derived representations (embeddings, summaries, links) are regenerated from here if a model or strategy changes. This verbatim original is the source of truth the frozen anchors protect.

RawStore dataclass

root instance-attribute
add(data)

Store bytes; return (digest, is_new). Idempotent: re-adding identical content is a no-op and reports is_new=False — that boolean is the dedup signal.

add_text(text)
get(digest)
exists(digest)
delete(digest)

Remove a raw blob — the ONE deliberate exception to "raw is sacred".

Never called by the ingest or watcher path (a vault delete only TOMBSTONES — derived rows dropped, raw kept). This is the owner-gated true-deletion primitive used solely by the purge action (core/ingest/purge.py). Returns whether a blob was actually removed.

sourceset

The source-set: "a source object IS the set of its idea-vectors", as a type (BUILD-SPEC §8).

Embedding is single-scale at chunk grain: index_records writes one vector row per chunk, and nothing stores a note-level vector. The membership relation "these chunk vectors belong to that source" is therefore already present in the rows (they carry digest, the raw-store identity), but only as an implicit group-by re-derived at each call site (note_centroids, note_snippets). This module promotes that relation to a type, the way MirrorView promoted the provenance projection to a type: a source object is SourceId (its content digest at a provenance stratum) together with members, the idea-vectors that constitute it.

Three things it deliberately is NOT:

  • It is not a new vector or embedding pass. vectors() returns the raw member vectors, never a mean. A coarse/note-level vector (centroid or medoids for cheap routing at scale) would be a separately-gated DERIVED performance cache carrying its own DERIVED provenance — never a source's identity — and is out of scope here.
  • It is not a bespoke writer. Grouping is provenance-parametric: an authored-solo note today and a curated-external item at another stratum tomorrow use the SAME machinery with a different label (mirroring ingest_note's one-pipeline shape, §1). The stratum is a first-class field on SourceId, not a convention.
  • It is not the hypergraph. A source-set is formally a hyperedge over the idea-vector vertices (the derivation hypergraph ℋ, Family 5 / companion III); that wiring is a separate track — named here, not built.

Flat retrieval is unchanged and remains the default; source-grained retrieval is an explicit opt-in (grouped_semantic_search), so the flat path is provably byte-identical (the recursive- strata I3 floor-zero posture: the added mode recovers the old behavior exactly when unused).

RowSource

Bases: Protocol

Anything that yields provenance-filtered chunk rows — the VectorStore, or a test fake. Same shape as core.mirror.RowSource; kept local so the store layer doesn't depend on the mirror view.

all_rows(*, provenances=None)

MixedProvenanceError

Bases: ValueError

A single source digest carried rows of more than one provenance. A source object lives at exactly one stratum, so this is a data-integrity error, not a mergeable state — surfaced rather than silently picking a label (the firewall discipline: fail closed on ambiguity).

SourceId dataclass

The typed identity of a source object: its raw-store content digest (an element of Σ, core/stores/rawstore.py) at a provenance provenance (its stratum/label).

Both fields are first-class. Identity is not merely "a digest" but "a digest at a stratum", which is what lets the same membership machinery serve every provenance class with no bespoke path — a different label through the same type (the provenance-parametric shape of ingest_note). digest is the identity; provenance is the stratum it lives in.

digest instance-attribute
provenance instance-attribute

SourceSet dataclass

A source object as the set of its member idea-vectors (chunk rows).

id is the typed identity (digest + stratum); members are the source's chunk rows in chunk_index order — the idea-vectors that constitute the source. This is a view over existing rows: no vector is produced. vectors() returns the raw member vectors, never an aggregate.

Whether members is the source's complete chunk set or a matched subset is a property of the constructor, not of the type: source_set / source_sets build complete sets (a full scan grouped by digest); grouped_semantic_search builds the matched subset (the search projection). The type only ever claims "these member idea-vectors of this source".

id instance-attribute
title instance-attribute
members instance-attribute
digest property
provenance property
vectors()

The member idea-vectors — raw, one per chunk, never aggregated. A source's coarse vector is intentionally not derivable here (see module docstring).

best_distance()

The nearest member's cosine distance if these rows came from a search (_distance present), else None. A read-through over existing row data — no score is stored.

group_sources(rows)

Group flat chunk rows into source objects by digest — the membership relation made first-class. This is the one grouping path; callers scope by provenance upstream (or not).

Ordering is deterministic: source order follows first appearance in rows (so a distance- ranked search result stays ranked by each source's best hit), and members within a source are ordered by chunk_index (so a complete set reconstructs the note in reading order).

Every row of a source must share one provenance — a source lives at one stratum — so a digest spanning provenances raises MixedProvenanceError rather than merging strata.

source_sets(store, *, provenances=None)

Every source object in the store, optionally scoped to provenance classes.

Default is all strata: this is a structural grouping utility, not a mirror read, so it does not default to MIRROR_READABLE. Pass provenances=MIRROR_READABLE for the introspective firewall, exactly as with VectorStore.all_rows. A non-authored stratum (e.g. {CURATED}) works through this same call with no bespoke path.

source_set(store, digest, *, provenances=None)

The one source object identified by digest, or None if absent. Scans then filters (single-user corpus scale, the same posture as VectorStore.all_rows).

telemetry

DuckDB telemetry store (BUILD-SPEC §8).

Quantitative time-series — system vitals only at launch (the system is itself a sensor source). Access is scoped in code (CONVENTIONS): a TelemetryWriter has no read methods and a TelemetryReader has no write methods, so the wrong access is impossible, not merely discouraged. The dormant sensor_readings table is the body/health adapter target, built now so a wearable can later emit into the same store without rework (§8, §20.6) — no adapter writes to it yet.

SCHEMA_VERSION = 2 module-attribute

TelemetryWriter dataclass

Write-only handle. No read methods exist on this object BY DESIGN (scoped access).

record_vital(metric, value, *, unit=None, source='core', labels=None)
record_vitals(readings, *, source='core')

Bulk-write objects exposing .metric/.value/.unit/.labels (e.g. vitals.Reading).

record_context_usage(agent, report, *, job_id=None, tier=None)

Record a context-budget outcome (BUILD-SPEC §13). report is duck-typed (a scheduler.budget.BudgetReport) so telemetry stays independent of the scheduler.

TelemetryReader dataclass

Read-only handle. No write methods exist on this object BY DESIGN (scoped access).

latest(metric)
count(metric=None)
window(metric, seconds)
context_usage_count(agent=None)

TelemetryStore dataclass

path instance-attribute
writer()
reader()
close()

open_store(config=None)

vectorstore

LanceDB thought-graph vector store (BUILD-SPEC §7, §8).

Embedded, no daemon. Every chunk row carries its provenance so retrieval can be filtered by provenance class — this is what makes the mirror=AUTHORED-only firewall (design-notes/observed-data-and-the-assistant-tier.md) structural, not advisory: a mirror query passes provenances={AUTHORED} and cannot surface observed exhaust.

Vectors are a derived, regenerable layer; the raw store remains the source of truth.

TABLE = 'chunks' module-attribute

VectorStore dataclass

path instance-attribute
dim instance-attribute
add(rows)
count()
reset()

Drop the vector table. Vectors are derived (§8): a full re-ingest rebuilds them from the raw corpus, so this is the idempotent path for re-indexing.

delete(*, digest)

Drop all derived rows for a source note (by its raw-store digest). Idempotent.

The incremental watcher uses this to retire stale embeddings when a note changes or is deleted — derived layer only; the raw blob is untouched (§8). digest is a hex SHA-256 (no quoting hazard).

rows_for_source(source_path)

Every stored chunk row for one source document (by source_path) — the note's current projection. The amendment path (ingest-identity §4) reads this to REUSE unchanged chunks' vectors instead of re-embedding. Python-side filter (single-user scale; avoids a quoting hazard on arbitrary source paths, matching all_rows).

delete_source(source_path)

Drop every derived row for one source document, by source_path (the stable doc identity an amendment replaces a projection under — §4). Idempotent. Deletes by the rows' own ids with single-quotes escaped, so an arbitrary source path never breaks the predicate.

relabel_provenance(old, new)

Rewrite every row's provenance from old to new. Returns rows relabeled.

Used by the §1 spectrum-split migration to relabel the legacy 'authored' rows to 'authored-solo'. This is a SAME-TRUST-TIER relabel (both are mirror-readable), not a promotion across the §8 firewall — so it is a safe, deterministic data migration, not a gated provenance change. Idempotent by construction: a second run finds no old rows and is a no-op. Implemented as delete-then-re-add (the store's existing re-index idiom) so it stays portable — no dependency on a LanceDB in-place update.

all_rows(*, provenances=None)

Full scan, optionally restricted to provenance classes — the read the dreaming agent clusters over. The clustering itself is deterministic and model-free (§9), so the mirror passes provenances={AUTHORED} and observed exhaust never seeds a dream. Single-user corpus scale; filtered in Python after the Arrow scan for portability.

search(vector, *, k=5, provenances=None)

Nearest-neighbour search, optionally restricted to provenance classes. provenances=None searches everything; the mirror passes {AUTHORED}.

open_vector_store(config=None)

verdicts

Append-only signed verdict store (design-notes/verdict-authority.md; live-adoption §3, L2).

Persists owner verdicts as the labeled ground truth the longitudinal apparatus is missing, with the sacred-boundary upgrade the plain L2 claim_verdicts schema lacked: each row carries the owner's Ed25519 signature (VERIFIED before it is stored) and a monotonic sequence number. A compromised transport (a tampered Ambassador) can DROP or REORDER — both refused or made visible here — but can never FORGE a verdict, because the store holds only the owner PUBLIC key (verdict-authority.md §4: the Ambassador degrades to transport).

APPEND-ONLY IS STRUCTURAL, like the attestation store (core/attestation/store.py): this class exposes append + reads and NO update/delete. Corrections are new verdicts at a higher seq (supersession by sequence), never in-place edits — the same discipline the ingestion boundary uses. Verdicts are operational ground truth, NOT mirror content (they label interpreted-tier output; they never enter MIRROR_READABLE). Zone A, no network.

The APPLY half — what promote / supersede DO to the graph — is deliberately NOT here: it depends on the promotion mechanism (recursive-strata I1), which is parked. This store is the buildable, lower-blast-radius half of Item 4b; apply lands when the promotion mechanism does.

VerdictSignatureError

Bases: ValueError

A verdict was offered whose signature did not verify under the owner public key — the illegal state the boundary deletes (fail closed): an unsigned or forged verdict is never stored, so no compromised transport can inject an owner authorization.

VerdictSequenceError

Bases: ValueError

A verdict's seq did not strictly exceed the stored maximum — a replay, a reorder, or a reused number. Refused so the sequence is monotone; a genuine DROP shows up as a forward gap (detectable via gaps()), never as a silently-accepted lower number.

VerdictCategoryError

Bases: ValueError

A verdict category outside the configured ratified taxonomy (when one is set). Absent a ratified set (allowed_verdicts=None) any category is accepted — the taxonomy is an owner decision (build plan R3), and this store must not hard-code it.

VerdictRecord dataclass

One stored, signature-verified owner verdict.

seq instance-attribute
subject_id instance-attribute
verdict instance-attribute
timestamp instance-attribute
signature instance-attribute
signer instance-attribute
recorded_at instance-attribute
as_signed()

Reconstruct the SignedVerdict for re-verification (tamper-evidence over the store).

VerdictStore dataclass

path instance-attribute
allowed_verdicts = None class-attribute instance-attribute
append(signed, *, public_b64)

Verify + persist one owner verdict. Fail closed, in order:

  1. category ∈ the ratified taxonomy (when configured), else VerdictCategoryError;
  2. the signature verifies under public_b64, else VerdictSignatureError;
  3. seq strictly exceeds the stored maximum, else VerdictSequenceError.

Only if all three hold is the row appended. The whole check+insert is under the lock, so a concurrent append cannot race the monotonic-seq guard.

latest_seq()

The highest stored seq, or None if empty — what the next verdict must exceed.

all()
get(seq)
gaps()

The missing sequence numbers between the smallest and largest stored seq — the censorship signal (verdict-authority.md §4: a dropped verdict is a visible gap). Empty when the stored sequence is contiguous.

verify_all(public_b64)

Re-verify every stored verdict's signature under public_b64 — tamper-evidence over the store as a whole (a mutated row fails). True iff all rows verify (or the store is empty).

count()
close()

open_verdict_store(config=None, allowed_verdicts=None)

Wire a VerdictStore beside the other core stores. allowed_verdicts is the ratified taxonomy once the owner decides it (build plan R3); None keeps the honest accept-any default.

versions

Append-only note-version history (ingest-identity-and-amendment.md §4A; build plan Item 6).

A note edited over time is a sequence of VERSIONS, and "v2 supersedes v1" is a PRIMARY provenance fact — distinct from the semantic support/contradiction edges the balance math consumes. It lives HERE, not in the EdgeStore, for two reasons the shipped implementation got wrong (§4A C1–C2):

  • Keyed on version identity, not content digest. Endpoints are (doc_id, version_seq), so a revert (v1 → v2 → back to v1's exact bytes) is v3 at seq 3 — distinct from v1 even though the digest repeats. The chain stays linear; NO cycle-guard is wanted (rejecting the revert would refuse truthful history and break append-only). Content-hash stays the key for the vector projection; version-seq is the key here — two layers, two identities.
  • The balance math cannot read it. A version relation must never enter the signed-edge graph (a placeholder sign corrupts λ_min / frustration). build_complex takes an EdgeStore and has no handle to this store, so the exclusion is structural — not a rel-type-filter discipline every consumer must remember (the prior design was excluded only accidentally; see Q8).

Append-only: each version is one row, version_seq monotonic per doc_id. The current version is max(version_seq); supersession is the consecutive-seq relation — both DERIVED from the ordered sequence, never from edge topology (§4A Ordering authority). Zone A, no network.

Version dataclass

doc_id instance-attribute
version_seq instance-attribute
digest instance-attribute
at instance-attribute

VersionStore dataclass

path instance-attribute
current(doc_id)

The current (highest-seq) version of a document, or None if never recorded.

record(doc_id, digest)

Append the next version of doc_id at digest (version_seq = current + 1, or 1). A revert to an earlier version's bytes is a NEW version at a higher seq — never a cycle, never a merge (§4A C1). Append-only: no prior row is mutated.

history(doc_id)

Every version of a document in version-seq order (the append-only chain).

supersessions(doc_id)

The (superseded_seq, superseding_seq) pairs — consecutive versions, DERIVED from the ordered sequence (never from edge topology, §4A Ordering authority).

count()
close()

open_version_store(config=None)