Skip to content

core.stores

core.stores

Outer-ring residue of the store layer (dn-inner-outer-core §2.7, K1 / bp-090).

The two file-backed content stores (rawstore — the content-addressed immutable archive — and sourceset, + the package's inner init text) moved to core/kernel/stores/. What remains here is the outer half: the sqlite/duckdb/lancedb/pyarrow-backed stores — the austere persistence plumbing the owner's v2 ruling (§2.1) placed in the outer ring beside the machinery that operates it (chatlog, derived, edges, runledger, catalog, causal_edges, chat_events, agent_observations, authored_supersession, code_observations, observation_history, reference_edges, versions, curated_store, telemetry, vectorstore, verdicts, staging, claim_ops). This init is stdlib-import-free so it stays inner by construction (a pure package marker); the residue submodules beside it are the outer machinery.

agent_observations

OBSERVED-only store for agent (self-sensing) observations (ratified self-sensing.md B-b).

One row = one fact reading of one commit: the agent's own operation is the THIRD stream through the sensing seam (after the biometric stream and the code stream, bp-012), and the self sensor (ops/self_sensor.py) is the sole interpreter φ_self (§2.2 — deterministic, transform-attributed, sole path in, stateless). Observations enter through the AgentSensingHandoff seam (core/sensing.py, the sensing-seam's third sibling) and land here wearing observed — there is deliberately NO provenance parameter on any API surface, so a caller physically cannot launder an agent reading into an authored (or any other) class: the same structural move as DerivedStore.add, SensedObservation.to_row, and CodeObservation.to_row.

This plan (B-b) licenses exactly ONE stream: stream == 'cost' (build-plan cost: frontmatter blocks — estimate at the plan's landing commit, actual at its seal commit). Additional streams re-enter per-stream with their own small plans (note PD-a) — this store's schema is stream-generic (the stream column), but nothing in THIS plan projects anything else.

Mirror-opacity (§2.6): observed is not in MIRROR_READABLE, so a MirrorView refuses these rows by construction and the self-model never reads them. The only typed container is ObservedView (all_rows returns view-compatible dict rows).

HONESTY NOTE (finding-0020 class, code-store precedent): no consumer reads these rows yet (bp-019 non-goal §9 — "any consumer (nothing reads ObservedView here)"). This store is write-side only; the live first projection over history is bp-020's, deliberately deferred.

Engine: SQLite (plan Q6) — an identity-keyed append-style ledger, the runs/versions/ snapshots convention, not the DuckDB telemetry lane. Reset semantics (plan §6(h)): this store is CORPUS-side (the observed stratum) and joins reset_targets() — wiped with the corpus, unlike the snapshot LEDGER pattern. That corpus-side call covers current READINGS only, which rebuild by re-projection from git; the worldview HISTORY — generations superseded when a bumped interpreter re-projects — lives in the ledger-class, reset-guarded sidecar (core/stores/observation_history.py, discriminator store='agent'; dn-self-sensing §2.5 ruling).

MissingHistoryError

Bases: RuntimeError

A superseding write arrived with history=None — refusing to silently drop a worldview generation (the bp-018 §6(c) archive-then-replace discipline, verbatim).

AgentObservation dataclass

One fact reading of one commit (note §2.3, verbatim columns).

Deliberately has NO provenance field: like CodeObservation/SensedObservation, the class label is minted at to_row() with no parameter — the wire payload (to_dict) carries nothing a caller could forge a class with.

commit_sha instance-attribute
stream instance-attribute
subject_id instance-attribute
key instance-attribute
payload instance-attribute
from_dict(d) classmethod

Parse one handoff wire payload (the seam's inbound half, the sibling shape).

to_dict()

The handoff wire payload — schema fields only, NO provenance (nothing to forge).

to_row()

The observed-tier row. Provenance is HARDCODED — there is no parameter, so no caller can launder an agent reading into another class (the CodeObservation.to_row move, verbatim). ObservedView admits these rows; MirrorView refuses them (§2.6).

AgentObservationStore dataclass

The observed stratum's agent-observation table. Writes observed UNCONDITIONALLY — no method on this class accepts a provenance value (Item 5 falsifier, ruled out by construction and pinned by test).

path instance-attribute
add_batch(observations, *, interpreter, history=None)

Land one projection batch under a declared interpreter version. Returns (new rows, superseded rows). Three cases per identity key (§6(b)):

  • no existing row → INSERT (a new reading);
  • existing row, SAME interpreter → no-op (idempotence — the B-b falsifier, inverted);
  • existing row, DIFFERENT interpreter → archive the existing generation to history (store='agent'), then replace: versioned supersession (§2.2), and the main table stays exactly latest-per-identity by construction. A superseding write with history=None raises MissingHistoryError — a generation is never silently dropped.
all_rows(*, provenances=None)

Full scan, optionally restricted to provenance classes (the RowSource shape). Every stored row is observed, so a filter containing OBSERVED sees ALL rows and any filter excluding it sees NONE — there is no third case.

rows_for(commit_sha)
count()
is_projected(commit_sha, interpreter=None)

Was commit_sha projected under interpreter? With interpreter=None: under ANY interpreter (the any-generation read, kept for parity with the code store's finding-0047 shape). The sensor always passes its version.

mark_projected(commit_sha, content_hash, interpreter)

Record that φ_self-at-interpreter projected commit_sha. INSERT OR IGNORE on (commit_sha, interpreter): first mark per worldview wins, and a NEW interpreter's mark is a NEW row — the versioned supersession §2.2 promises.

chain_for(commit_sha, stream, subject_id, key, history)

The queryable worldview chain at one identity key (§2.4): archived generations + the current row, oldest → current. Each element carries its own interpreter — the second orthogonal history (across interpreter at fixed identity), readable without touching default reads.

close()

batch_content_hash(observations)

Content hash of a projection batch — sha256 over the canonical (sorted-key, sorted-row) JSON of the wire payloads. Deterministic (§2.2): re-running φ_self over the same commit yields the same hash, so the project_agent_observations attestation is content-addressed. Sort key: (commit_sha, stream, subject_id, key) — the identity key, module-local (this store's own copy; §11 parked decision — a fourth stream re-decides the shared-helper question).

open_agent_observation_store(config=None)

The open_* helper: data/agent_observations.sqlite (plan §6(b) — the sibling-store convention beside code_observations, no dedicated cfg path; registered in reset_targets() as a corpus-side wipe target, plan §6(h)).

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.

verify_owner_declaration(declaration)

Raise MachineAuthorityRefused unless declaration is a valid owner-authority token.

The ONE owner-capability check, factored out of record() so a second boundary — the owner-gated doc_id re-key primitives (versions.migrate_rekey_doc_id, catalog.migrate_rekey_doc_id; bp-034, §11 ruling 2026-07-14) — reuses this exact structural verification instead of minting a second owner token. There is ONE owner-capability system-wide and it lives here. Verifies not just the type (isinstance) but the guarded token identity — getattr(..., "_token", None) defends against a bypass-constructed object.__new__(OwnerDeclaration) — so a machine/model/scheduler/dreamer caller is refused at the store boundary regardless of how the value was produced (fail-closed).

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, doc_id=None)

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

doc_id binds the note's stable identity (bp-031 Item 2): pass an explicit id (an existing id::, or a renamed predecessor's carried id) to bind it; omit it and a NEW row defaults doc_id := source_path (identity == path) while a re-record PRESERVES the stored doc_id. An explicit doc_id DOES overwrite on conflict — but sync only passes one at first bind, never to switch a historied note's identity (the re-key is owner-run bp-034).

doc_id_for(source_path)

The stable doc_id bound to this source_path — the identity the version store keys on. Equals source_path (identity == path) until a mechanism (bp-031 Item 2) diverges it. An unknown path resolves to itself, so a first-ingest resolve is well-defined even before the catalog row exists (the caller records the row, then resolves).

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).

migrate_rekey_doc_id(source_path, new_doc_id, *, declaration)

Owner-gated identity migration (the catalog twin of versions.migrate_rekey_doc_id; bp-034, §11 ruling 2026-07-14): rebind a note's resolved doc_id to new_doc_id, keyed by the UNCHANGED PK source_path (the in-place id:: mint never moves the path). Same owner- authority gate — this marks the write as a deliberate migration, not the runtime record path, even though the catalog is not append-only.

Fail-closed. doc_id carries NO unique index, so this method is the ONLY guard against a resolution-level lineage merge: it REFUSES if any OTHER row already resolves to new_doc_id (guardrail 5). Idempotent — a row already at new_doc_id (a re-run) is a no-op; an unknown source_path matches 0 rows and is a silent no-op.

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()

causal_edges

The C-fiber causal-edge store — the integrator's proven output (bp-071 Item 1).

One row = one proven cross-strata edge: a DIALOGUE L1 action → the endpoint it produced. Two species (finding-0111): a commit event resolves (by abbreviated-sha prefix match) to a ledger commit — dst_type='commit', pair_cut_sha=the full sha (the (digest, sha) consistent cut); a file_edit/build_plan/finding/design_note event mints its endpoint directly (the Write tool record is the proof) — dst_type='file'|'doc', pair_cut_sha='' (a working-tree write has no commit anchor). The endpoints are NOT fanned out from a commit's file set: the commit ledger stores the full tree, not the diff, so fanning would be an inferred edge (the falsifier). Composing action→commit with commit→file is Δ's ComposedGraph job (C≠D composition).

Sibling-store convention (chat_events.py/reference_edges.py): SQLite, no dedicated cfg path — data/causal_edges.sqlite beside the L1 store. A corpus-side derived layer (a pure function of retained raw + the ledger), so it joins reset_targets() and rebuilds by re-integration.

EDGE_KINDS = frozenset({'C', 'F'}) module-attribute

DST_TYPES = frozenset({'commit', 'file', 'doc'}) module-attribute

CausalEdge dataclass

One proven C-fiber edge: a DIALOGUE action → its produced endpoint. dst is STRUCTURAL (sha | path | artifact-id). The witness is (witness_digest, witness_turn) plus the L1 event's own (kind, ref); the pair-cut is (witness_digest, pair_cut_sha) — a full sha for a commit edge, '' for a working-tree write (no cross-clock cut).

edge_id instance-attribute
session_id instance-attribute
event_order instance-attribute
kind instance-attribute
dst_type instance-attribute
dst instance-attribute
witness_digest instance-attribute
witness_turn instance-attribute
pair_cut_sha = '' class-attribute instance-attribute
mint(*, session_id, event_order, kind, dst_type, dst, witness_digest, witness_turn, pair_cut_sha='') classmethod

Construct with the content-derived identity; validates the closed vocabularies at the boundary (a typo'd fiber/dst_type is unrepresentable in the store).

CausalEdgeStore dataclass

The C-fiber edge table. replace_session is the only mutator — it wipes and rewrites one session's edges atomically (a grown session re-integrates cleanly) and records the L1 digest they were minted from, so digest_for drives incremental re-integration (the landed L1 pattern). The sole writer is the model-free integrator (core/integrator.py).

path instance-attribute
replace_session(session_id, edges, transcript_digest)

Replace one session's edges wholesale (the resolver is deterministic, so a re-run over a grown transcript rewrites the full set) and record the digest they came from. Returns the number of edges written. Every edge must belong to session_id — a mismatch would orphan a row past the keyed DELETE, so it fails loudly rather than silently.

digest_for(session_id)

The L1 digest this session's edges were last minted from — None if never integrated. The incrementality signal: unchanged digest ⇒ skip re-integration (no churn).

edges_for(session_id)

One session's edges, in event order.

all_edges()
count()
sessions_with_edges()
close()

open_causal_edge_store(config=None)

data/causal_edges.sqlite beside the L1 store (the sibling-store convention; no dedicated cfg path). Registered in reset_targets() as a corpus-side wipe target — rebuilt by re-integration from the immutable rawstore-backed L1 + the commit ledger.

chat_events

The L1 action-log store for the dialogue sensor (bp-069 Item 3).

One row = one typed ACTION in one Claude Code session, at (session_id, ord) grain: the actor (owner|agent), the kind (prompt|response|commit|file_edit|build_plan|finding|design_note|ratify| tool_use), a STRUCTURAL ref (sha|path|artifact-id|turn_index — never verbatim content), and the turn_index backpointer into the L0 chatlog (the projection fiber). The projector (core/chat_events.py) is the sole writer; it reads the session's OWN raw transcript via the chatlog's transcript_digest and re-extracts iff that digest changed (replace_session).

Sibling-store convention (code_observations.py/chatlog.py): SQLite, no dedicated cfg path — data/chat_events.sqlite beside the chatlog. A corpus-side derived layer, so it joins reset_targets() (launcher) as a wipe target, rebuilt by re-projection from the immutable rawstore.

ChatEventStore dataclass

The dialogue stratum's L1 action-log table. replace_session is the only mutator — it wipes and rewrites one session's log atomically (a grown session re-extracts cleanly), and records the transcript_digest it was extracted from so digest_for drives incremental re-projection.

path instance-attribute
replace_session(session_id, events, transcript_digest)

Replace one session's action log wholesale (the extractor is deterministic, so a re-run over a grown transcript rewrites the full ordered log) and record the digest it came from. Returns the number of events written.

digest_for(session_id)

The transcript digest this session's log was last extracted from — None if never projected. The incrementality signal: unchanged digest ⇒ skip re-extraction (no churn).

events_for(session_id)

One session's action log, in order (ord).

count()
sessions()
close()

open_chat_event_store(config=None)

data/chat_events.sqlite beside the chatlog (the sibling-store convention; no dedicated cfg path). Registered in reset_targets() as a corpus-side wipe target — rebuilt by re-projection from the immutable rawstore (the orchestrator's post-merge step).

chatlog

OBSERVED-only store for chat utterances (ratified dn-chat-sensor CS-2/CS-3).

One row = one utterance-grain reading of one Claude Code session transcript: the owner's and the agent's natural-language prose, extracted at (session_id, turn_index, speaker, text) grain with tool exhaust structurally stripped (the sensor ops/chat_sensor.py is the sole interpreter φ_chat — deterministic, model-free, sole path in). Every row lands wearing observed — there is deliberately NO provenance parameter on any API surface, so a caller physically cannot launder a chat reading into an authored (or any other) class: the SAME structural move as core/stores/code_observations.py (CodeObservation.to_row), DerivedStore.add, and core/sensing.py's SensedObservation.to_row. In particular provenance is NEVER derived from speaker: owner utterances and agent utterances both land observed (CS-2 — decided against auto-classing owner CLI prose as AUTHORED_DIALOGUE; CLI sessions mix registers, and machine authorship-inference is exactly what the taxonomy forbids, core/provenance.py:74-77). /capture remains the one working promotion path; the typed promote(x: Derived[T], cap: OwnerVerdict) -> Authored[T] seam (core/provenance.py:145) is registered here as a consumer and depended on by nothing.

Mirror-opacity (CS-2): observedMIRROR_READABLE (core/provenance.py:78-80), so a MirrorView (core/mirror.py:66) refuses these rows by construction — the self-model never reads them, and the proof extends to chat rows without touching the view. The only typed read container is ObservedView (core/sensing.py:190); all_rows returns view-compatible dict rows, so the ratified cross-strata correlator (CS-5, the sole future reader — its own scoped grant) inherits "I read exhaust, never ground truth".

CS-1 (verbatim-first): the sensor stores each closed transcript byte-verbatim in the immutable rawstore (core/stores/rawstore.py) BEFORE any extraction; every row carries the transcript_digest it is recoverable from. This store is the DERIVED, regenerable layer over those bytes — the same two-layer shape as ingest (raw → derived), no new pattern.

Engine: SQLite — an identity-keyed append-style ledger, the sibling-store convention (code_observations.py), not the DuckDB telemetry lane. Reset semantics (Q6): this store is CORPUS-side (the observed stratum) and joins reset_targets() (ops/lifecycle/ launcher.py) as a wipe target — wiped with the corpus, rebuilt by re-ingest from the IMMUTABLE rawstore (which is NOT a reset target — raw is sacred). [cross-ref: extension, dn-chat-sensor; the launcher registration is the orchestrator's post-merge step.]

Spine-invisible in v1 (dn-chat-sensor §3): this plan writes NO chain and registers NO stratum. bp-064 (clock wiring, CS-4) joins the store to the spine as a g1-chained store (chain-key = session_id, position = turn_index) with session-close cut certificates. ts_bookmark is METADATA ONLY — order is turn index, never wall time (Law C4; CS-4).

INTERPRETER_VERSION = '1.0.0' module-attribute

SPEAKERS = ('owner', 'agent') module-attribute

ChatUtterance dataclass

One utterance-grain reading of one session transcript (CS-3 grain, verbatim columns).

Deliberately has NO provenance field: like CodeObservation/SensedObservation, the class label is minted at to_row() with no parameter — the wire payload carries nothing a caller could forge a class with, and speaker is metadata, never a provenance input.

session_id instance-attribute
turn_index instance-attribute
speaker instance-attribute
text instance-attribute
transcript_digest instance-attribute
ts_bookmark = '' class-attribute instance-attribute
to_dict()

The wire payload — schema fields only, NO provenance (nothing to forge).

to_row()

The observed-tier row. Provenance is HARDCODED — there is no parameter, so no caller can launder a chat reading into another class, and speaker is NEVER read to decide it (CS-2 — the never-automatic rule). ObservedView admits these rows; MirrorView refuses them (mirror-opacity).

ChatlogStore dataclass

The observed stratum's chat-utterance table. Writes observed UNCONDITIONALLY — no method on this class accepts a provenance value (the item-1 falsifier, ruled out by construction and pinned by test), and none derives provenance from speaker. Identity key (session_id, turn_index): a re-ingest of a frozen session is idempotent.

path instance-attribute
add_batch(utterances)

Land utterances, returning the count of NEW rows. Idempotent by the identity key (session_id, turn_index): a re-add of an already-stored utterance is a no-op (an already-frozen session re-ingested writes 0). INSERT OR IGNORE — first write per identity wins; a grown open session is out of v1 (Q4 — a session is frozen once ingested).

all_rows(*, provenances=None)

Full scan, optionally restricted to provenance classes (the RowSource shape). Every stored row is observed, so a filter containing OBSERVED sees ALL rows and any filter excluding it sees NONE — there is no third case. Ordered by the chain grain (session_id, turn_index) for a stable read.

rows_for(session_id)

One session's utterances, in chain order (turn_index).

count()
sessions()

The distinct session ids present — the sensor's idempotency read (a session already ingested is frozen, Q4).

close()

open_chatlog_store(config=None)

The open_* helper: data/chatlog.sqlite (the sibling-store convention beside code_observations, no dedicated cfg path; registered in reset_targets() as a corpus-side wipe target — the orchestrator's post-merge step, Q6).

claim_ops

The claim-operations persistence — ClaimOpStore + apply_operations (bp-089, S1′).

The inner-ring promotion (dn-inner-outer-core §2.6b) moves the sqlite-backed ClaimOpStore and the DerivedStore-consuming application logic (apply_operations, stale_closure) OFF core/recursion_ops.py — so that module keeps only the pure dialogue-operations vocabulary and becomes inner. Item 3's DRY audit (finding-0144) confirmed NO existing core/stores/* covers claim_ops: authored_supersession is a distinct owner-declared K₀↔K₀ edge type; versions is note-version supersession. A new store is genuinely needed — this one.

Claim-supersede is a DISTINCT relation in a DISTINCT store from version-supersedes (§4A C3): warrant-bearing reasoning, never a note edit and never a semantic ± edge. Byte-identical behavior: the store DDL, the record/query methods, and the apply_operations/stale_closure bodies are relocated verbatim from recursion_ops.py; the pure vocabulary (OpKind, ClaimOp, Supersede, …, _op_id, _utcnow) is imported from its inner home. Zone A: reads/writes the claim-ops sqlite + reads/writes the DerivedStore; no model, no network.

ClaimOpStore dataclass

Append-only store of dialogue operations over claims. A DISTINCT structure from the version store and the balance-fed edge store (§4A C3): claim-supersede is warrant-bearing reasoning, not a note edit and not a semantic ± edge, so it shares no rel-type or store with either.

path instance-attribute
record(kind, claim_id, *, related_id='', text='')
superseded()

Claim ids with a SUPERSEDE op — the active-projection filter (a consumer excludes these, exactly as DispositionStore.retracted does for verdicts; the superseded claim lives on in history).

defeaters(claim_id)
all()
count()
close()

stale_closure(derived, claim)

Stale(C) = { x : C is reachable from x along grounding fibers } — C's grounding-descendant closure (supersession-lifecycle.md §5). When C is superseded, every x that transitively grounds on C routes its support through a dead node, so its grounding ratio g will fall; this names them at the moment of supersession — the PROACTIVE complement to the detective grounding gauge.

These are flagged for re-examination, not resolved: whether a derived claim survives its parent's revision is a semantic judgment the Dreamer proposes later (terminating in proposals, never silent edits). Read-only; walks the derived_from DAG (x → … → C). Note the Item 9 grounding correction keeps a revision chain from self-generating this set — C′ grounds on warrant anchors, not on its predecessor, so the closure holds only genuine dependents (§5).

apply_operations(ops, *, ops_store, derived)

Apply dialogue operations: record each as a claim relation and re-project.

A Supersede mints its conclusion C′ as a DERIVED artifact grounded on the WARRANT'S K₀ anchors (Item 9; supersession-lifecycle.md §4.2), so γ^d bounds it (I10/I5) and C leaves the active projection without C′ entering as an authored peer (the §2 failure avoided). Explicit op.anchors win; empty falls back by C's type — a DERIVED C inherits its leaf_refs (never [C], which decays); an AUTHORED C grounds on [C] (bedrock, g=1, so the revision is not weightless). On supersession we also compute Stale(C) (§5) — grounding-descendants to flag for re-examination — surfaced in the report for the digest; nothing is cascade-retracted. Budgets floored (PD4).

open_claim_op_store(config=None)

code_observations

OBSERVED-only store for code observations (ratified code-observation-projection.md B-b).

One row = one symbol-grain reading of one commit: the repo is an instrument, commits are its readings, and the code sensor (ops/code_sensor.py) is the sole interpreter φ_code (§2.2 — deterministic, transform-attributed, sole path in). Observations enter through the CodeSensingHandoff seam (core/sensing.py, the sensing-seam sibling) and land here wearing observed — there is deliberately NO provenance parameter on any API surface, so a caller physically cannot launder a code reading into an authored (or any other) class: the same structural move as DerivedStore.add and SensedObservation.to_row.

Mirror-opacity (§2.6): observed is not in MIRROR_READABLE, so a MirrorView refuses these rows by construction and the self-model never reads them. The only typed container is ObservedView (all_rows returns view-compatible dict rows).

HONESTY NOTE (finding-0020 class): the daemon (Ouroboros) does NOT consume these rows yet. This store is write-side only — like the dispositional stores, the substrate lands before its consumer (the Track-D correlator / detangling instruments read ObservedView when they arrive). references_out is carried as a typed JSON column but is emitted EMPTY by B-b; the deterministic reference extractor (Lane 1, V4-seeded patterns) is B-c / bp-013.

Engine: SQLite (plan Q2) — an identity-keyed append-style ledger, the runs/versions/ snapshots convention, not the DuckDB telemetry lane. Reset semantics (plan Q4): this store is CORPUS-side (the observed stratum) and joins reset_targets() — wiped with the corpus, unlike the snapshot LEDGER (build history, reset-guarded). [cross-ref: extension, bp-018] That corpus-side call covers the current READINGS only, which rebuild by re-projection from git; the worldview HISTORY — generations superseded when a bumped interpreter re-projects — lives in the ledger-class, reset-guarded sidecar (core/stores/observation_history.py; dn-self-sensing §2.5 ruling).

KINDS = ('module', 'class', 'function', 'async_function') module-attribute

MissingHistoryError

Bases: RuntimeError

A superseding write arrived with history=None — refusing to silently drop a worldview generation (bp-018 §6(c): archive-then-replace, never replace-and-forget).

CodeObservation dataclass

One symbol-grain reading of one commit (note §2.3, verbatim columns).

Deliberately has NO provenance field: like SensedObservation, the class label is minted at to_row() with no parameter — the wire payload (to_dict) carries nothing a caller could forge a class with.

commit_sha instance-attribute
path instance-attribute
qualname instance-attribute
kind instance-attribute
signature = '' class-attribute instance-attribute
docstring = '' class-attribute instance-attribute
references_out = () class-attribute instance-attribute
from_dict(d) classmethod

Parse one handoff wire payload (the seam's inbound half, SensedObservation shape).

to_dict()

The handoff wire payload — schema fields only, NO provenance (nothing to forge).

to_row()

The observed-tier row. Provenance is HARDCODED — there is no parameter, so no caller can launder a code reading into another class (the SensedObservation.to_row move, verbatim). ObservedView admits these rows; MirrorView refuses them (§2.6).

CodeObservationStore dataclass

The observed stratum's code-observation table. Writes observed UNCONDITIONALLY — no method on this class accepts a provenance value (Item 3 falsifier, ruled out by construction and pinned by test).

path instance-attribute
add_batch(observations, *, interpreter, history=None)

Land one projection batch under a declared interpreter version. Returns (new rows, superseded rows). Three cases per identity key (§6(c)):

  • no existing row → INSERT (a new reading);
  • existing row, SAME interpreter → no-op (idempotence unchanged — the B-b falsifier, inverted);
  • existing row, DIFFERENT interpreter → archive the existing generation to history (store='code'), then replace: versioned supersession (§2.2), and the main table stays exactly latest-per-identity by construction. A superseding write with history=None raises MissingHistoryError — a generation is never silently dropped.
all_rows(*, provenances=None)

Full scan, optionally restricted to provenance classes (the RowSource shape). Every stored row is observed, so a filter containing OBSERVED sees ALL rows and any filter excluding it sees NONE — there is no third case.

rows_for(commit_sha)
count()
is_projected(commit_sha, interpreter=None)

Was commit_sha projected under interpreter? With interpreter=None: under ANY interpreter — the pre-bp-018 read, kept for callers asking only "was this sha ever projected?" (finding-0047: the §6(c) pin required the argument; one out-of-scope caller wants exactly the any-generation semantic, so the default carries it honestly). The sensor always passes its version — Item 4's bump→re-projects test pins that end-to-end.

mark_projected(commit_sha, content_hash, interpreter)

Record that φ_code-at-interpreter projected commit_sha. INSERT OR IGNORE on (commit_sha, interpreter): first mark per worldview wins, and a NEW interpreter's mark is a NEW row — the versioned supersession §2.2 promised, mechanical since bp-018 (this comment described intent only, pre-B-a).

chain_for(commit_sha, path, qualname, history)

The queryable worldview chain at one identity key (§2.4): archived generations + the current row, oldest → current. Each element carries its own interpreter — the second orthogonal history (across interpreter at fixed identity), readable without touching default reads.

close()

batch_content_hash(observations)

Content hash of a projection batch — sha256 over the canonical (sorted-key, sorted-row) JSON of the wire payloads. Deterministic (§2.2): re-running φ_code over the same commit yields the same hash, so the project_observations attestation is content-addressed.

open_code_observation_store(config=None)

The open_* helper: data/code_observations.sqlite (plan Q2 — the sibling-store convention beside derived_store, no dedicated cfg path; registered in reset_targets() as a corpus-side wipe target, plan Q4).

curated_store

The curated literature vector store (dn-external-grounding §2.2/§2.4).

A SECOND VectorStore instance, physically separate from the authored-mirror vector_store, holding the embedded open-access full text of curated references (the EMBED tail, bp-029). It reuses the proven LanceDB VectorStore unchanged — same schema, same embedder/dimension — at a DISTINCT path (cfg.paths.curated_store, default data/research_curated.lance).

Two invariants are load-bearing and both hold STRUCTURALLY here, not by convention:

  • Never pollute the mirror. Curated rows carry provenance="curated" — a class that is deliberately excluded from MIRROR_READABLE (core/provenance.py), so a mirror/dreaming read (provenances=MIRROR_READABLE) cannot surface curated content. And the store is a separate file from the mirror, so objective-about-the-world text never lands in subjective-about-owner space even by accident.
  • Inv 11 (the corpus never transits a third party; full text never enters git). The path lives under data/ (gitignored) with a *.lance/ suffix (also gitignored) — the full source text is never committed and never egresses; the sealed core reasons over it offline.

This module is deliberately thin: it does NOT edit or subclass VectorStore (if the base store needed a change, that is a spec-defect finding, not an edit here — bp-029 §5). It is only the factory that aims the proven store at the curated path.

open_curated_store(config=None)

Open the curated-literature store at cfg.paths.curated_store (a separate LanceDB).

Mirrors open_vector_store but for the curated path; the embedding dimension is shared (the same local embedder embeds both corpora — §8 derived layer).

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.kernel.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)

memberships

The membership store — occupancy as a first-class relation (dn-vector-membership-store, bp-152).

A point is a point: geometry, assertion-free. Meaning lives in membership — who contains it — and history in lineage — which occupancy chains pass through it. The vector plane (core/stores/vectorstore.py) holds ONE row per distinct idea-atom (layer, content_hash), append-only; this store holds everything that used to be duplicated onto those rows once per version:

  • A version is a fiber. M(path, blob_sha) — the complete projection of one file version. Landing a version writes its fiber; re-landing the same blob writes nothing new, because derivation is a pure function of (path, source) so the fiber is equal by construction.
  • A re-land is idempotent BECAUSE reconciliation converges — never because the call short-circuits. This is the C1 lesson the repo has already learned once (core/stores/versions.py:22-27: content-keyed identity cannot hold a revert). On A → B → A the fiber for blob A already EXISTS with current=false; a lander that returns early on "fiber exists" leaves B current, which is silent D3 corruption with nothing to see. So reconcile_currency runs on every land, including the ones that wrote no row.
  • current_any is a cache, this store is the truth (D8/R3). The lance current column is the cheap ANN prefilter; membership rows are what it caches, and repair_current_any rebuilds it from them. Write order is vector inserts FIRST (append-only, an unreferenced atom is harmless dormant geometry), fiber SECOND (one SQLite transaction), currency LAST — every step after the first is re-derivable, so a crash anywhere is repaired by the next land.
  • Append-only, with ONE removal. No API here deletes a vector except purge_atom (D5, finding-0164 — owner-gated, privacy outranks lineage), and a purge leaves a RECORDED HOLE: the row is gone and its memberships are tombstoned, never silently dropped.

Lineage is DERIVED here, never stored (PD-3): slot_runs / slot_edges collapse a path's first-parent blob chain into runs of equal occupants per slot, so a revert reads as 3 runs and 2 edges rather than being flattened away. Chain members are a strict subset of the version set (D4/F3 — a side-branch fiber is a real member of M with no place on any chain), so the edge invariants are quantified over the chain that is handed in, never over all fibers.

EmbedderIdentity dataclass

The geometry an atom's stored vector came from — EmbeddingConfig.model + dim.

Embed REUSE (D2 step 2, "insert only the atoms absent from the plane") is only valid within one embedder, and the owner pinned that explicitly (2026-08-01, issue #27 sub-confirmation 2): an atom already in the plane carries a vector from whichever embedder landed it, and serving it beside freshly-embedded atoms mixes two geometries in ONE ANN space — a corruption no downstream measurement can detect. So atom PRESENCE is keyed to (layer, content_hash) and this identity; a hit whose embedder differs from the live config is not a hit.

query_instruction is deliberately excluded: it conditions queries, not stored document vectors, so two configs differing only there share a geometry.

model instance-attribute
dim instance-attribute
from_config(config) classmethod

Membership dataclass

One occupancy: "atom content_id sits in version (path, blob_sha) at this slot".

The key is (path, blob_sha, layer, chunk_index) — the occupancy's COORDINATES, not its content. That is the multiset pin (the note's F5): two byte-identical L0b windows in one blob differ only in chunk_index, and keying on content would silently merge them.

slot_line_start / slot_line_end are the SLOT's declared extent — where the symbol lives — and never the atom's text coverage (Amendment A2, issue #34). They coincide for leaf symbols. For a symbol with nested children the children's lines are carved OUT of the parent's chunk while the span still reports the parent's full lineno..end_lineno; for the module shell the span is the ENTIRE FILE by construction. The columns are named for what they measure precisely so the bad reading loses the vocabulary it was hiding in: a consumer that wants "where is this symbol" uses the span, and a consumer that wants the atom's content uses the stored text. Rendering the span and calling it the atom renders, for the shell, the whole file.

path instance-attribute
blob_sha instance-attribute
layer instance-attribute
chunk_index instance-attribute
content_id instance-attribute
slot instance-attribute
slot_line_start instance-attribute
slot_line_end instance-attribute
current = False class-attribute instance-attribute
tombstoned = False class-attribute instance-attribute

CurrencyReport dataclass

What D2 step 4 actually changed. Both counts are rows whose flag MOVED, so a converged re-land reports (0, 0) — the honest reading of "idempotent by convergence".

made_current = 0 class-attribute instance-attribute
superseded = 0 class-attribute instance-attribute

PurgeReport dataclass

The recorded hole (D5). A purge that silently no-ops satisfies "never deletes" vacuously, so both halves are counted and a caller can assert the hole EXISTS.

vector_rows_deleted = 0 class-attribute instance-attribute
memberships_tombstoned = 0 class-attribute instance-attribute

MembershipStore dataclass

The occupancy relation, in SQLite beside the vault catalog — outer ring, never imported by the kernel.

Reads are cheap point/range queries over the two indexes; the whole surface is deliberately small, because everything history-shaped (runs, edges, forks, joins) is DERIVED from these rows rather than stored (PD-3: one source of truth for lineage).

path instance-attribute
known_atoms(content_ids, embedder)

Which of content_ids are already in the plane UNDER THIS EMBEDDER (D2 step 2).

The embedder half is the whole point: an atom landed by another model is present as a row but not usable as a reuse, because reusing it would put two geometries in one ANN space. A suite that only ever exercises one embedder cannot see the difference, which is why the pin carries its own test case.

record_atoms(atoms, embedder)

Record (content_id, layer) pairs as landed under embedder. Returns rows written.

A re-land under a CHANGED embedder overwrites the identity (the atom's vector really was re-embedded), so the ledger always describes the geometry currently in the plane.

ledger_atom_ids()
forget_atom(content_id)

Drop one atom from the ledger — the purge's other half (D5). Never called by anything else: nothing else removes geometry.

write_fiber(rows)

Write a version's fiber (D2 step 3). Returns rows actually inserted.

INSERT OR IGNORE: an existing fiber's rows STAND, because derivation is pure so the re-derived fiber is equal by construction. Returning 0 is therefore the correct, expected answer on a re-land — and is precisely why it must NOT be read as "nothing to do": the caller still owes step 4. One transaction, so a crash leaves the fiber whole or absent, never half (D8: SQLite is the reference truth).

fiber(path, blob_sha)

One version's complete projection, in chunk_index order — i.e. in the order the pure derivation emitted the chunks, so a fiber read back IS the version's chunk list.

blobs_of(path)

Every blob this path has a fiber for, oldest-recorded first (by insertion rowid).

fibers()
fiber_sizes()
count()

|M| — the total number of occupancies.

reconcile_currency(path, head_blob_sha)

Set current=1 on exactly the fiber whose blob is the path's HEAD, and current=0 on the path's every other fiber. NEVER skipped, even when the fiber write was a no-op.

This is the whole reason a re-land is idempotent. The tempting shortcut — "the fiber already exists, so return" — is the C1 bug in its exact original form: land A, land B, land A again; A's fiber exists with current=false, the short-circuit skips this call, and the store is left claiming B is HEAD. Nothing raises, nothing logs, and every default (current-view) read is now wrong. Convergence, not short-circuiting.

Idempotent by construction: the counts are of rows whose flag actually MOVED, so a second call reports (0, 0) and writes nothing.

n_doc(content_id, *, current_only=True)

Distinct PATHS holding this atom — the document-frequency reading, immune to within-file repetition. current_any(v) ⇔ n_doc(v, t) > 0 is the carried invariant (R3).

n_occ(content_id, *, current_only=True)

Membership ROWS holding this atom — the multiset reading, where L0b's repeated windows count. Never conflated with n_doc (the F5 pin: they are different questions).

atom_ids_of_path(path)
currently_held(content_ids)

Which of content_ids have at least one current, un-tombstoned occupancy — i.e. the set whose current_any should be true. One query, so D2 step 5's crossing arithmetic is cheap even when a path's history is long.

occupancies(content_id, *, include_superseded=False)

Where this atom lives. One atom may resolve to SEVERAL occupancies — that is the feature, not a defect: a hit natively answers "this idea lives in versions v3–v7 of X and also in Y". Default consumers see current occupancies only (D3).

slot_runs(path, chain)

Per slotted (path, slot), the occupancy chain collapsed into RUNS of equal occupants along chain (the path's FIRST-PARENT blob sequence, oldest first).

ADJACENT collapse, never distinct-collapse — this is the C1 formulation and the whole reason §4 was re-keyed on runs: A → B → A gives runs [A, B, A], three runs and two edges, so a revert stays visible. Collapsing distinct occupants would report one edge and quietly erase the revert, which is the same failure core/stores/versions.py documents at note grain. ops/code_lineage.py:151-156 already collapses only adjacent repeats at file grain, so the two grains agree.

chain is handed in rather than read from this store because chain MEMBERSHIP is not a membership fact: chains are first-parent while the ledger walks all commits, so a side-branch fiber is a real member of M that sits on NO chain (D4/F3). Quantifying over every fiber instead of over chain members is the error this signature makes hard.

Slotted means L0a — the LAYER, not a non-empty name (D1/R4). L0b and L1 carry qualname='' as built and are membership-only, honestly chainless; the L0a MODULE SHELL also carries '', but that empty string is a real slot (the shell is a symbol-shaped region of the file) and it chains like any other. Reading slottedness off slot != '' would silently drop the shell's lineage — the one slot every file has.

slot_edges(path, chain)

The supersession edges: consecutive-run pairs per slot, so per slot |edges| = |runs| - 1 by construction. Edge identity is (path, slot, old_hash -> new_hash, at blob transition); the endpoints differ by construction because runs collapse equal adjacent occupants.

tombstone_atom(content_id)

Mark every occupancy of a purged atom as a hole. Returns rows tombstoned.

orphan_atom_ids()

Atoms in the plane with NO occupancy at all — dormant geometry from a land that crashed between the vector insert and the fiber write (D8's named window).

The state is deliberately OBSERVABLE rather than merely harmless: "nothing dangles by reference" is true of a store that never wrote anything, so a crash test that cannot see the orphan proves nothing about repair. Nothing here deletes them — the idempotent re-land adopts them at zero embed cost, which is exactly why the write order puts vectors first.

close()

ResolvedHit dataclass

One ANN hit, resolved (D3). row is the atom — geometry, text, _distance if the hit came from a search — and occupancies are the places it lives.

occupancies carry the SLOT's extent, not the atom's text coverage (A2). A consumer rendering slot_line_start..slot_line_end as "this atom" is wrong, and maximally wrong for the module shell, whose extent is the whole file: the atom's content is row["text"].

row instance-attribute
occupancies instance-attribute

resolve_occupancies(memberships, hits, *, include_superseded=False)

The D3 read-path join: top-k atoms -> their occupancies (path, blob, slot, lines, ...).

Rows that are not shed atom rows (note rows, and any pre-D1 per-version code row) resolve to no occupancy and keep their own coordinates — this join adds a lane, it does not take one away.

repair_current_any(vectors, memberships)

Rebuild the current_any cache from membership truth (D8/R3). Returns (raised, lowered).

current_any is a cache with a rebuild path, and this is the rebuild path: the flag is recomputed as n_doc(v, t) > 0 over every atom in the plane, and only the DRIFTED rows are written. Called after a crash, or on cadence as the R3 ratchet.

current_any_drift(vectors, memberships)

Atom ids where the lance flag disagrees with membership truth — the R3 ratchet's reading, and the current_any(v) ⇔ n_doc(v, t) > 0 invariant as a checkable list (empty ⇒ holds).

purge_atom(vectors, memberships, content_id)

The ONE removal (D5, finding-0164 — owner-gated; privacy outranks lineage).

Delete the vector row, tombstone its memberships, forget its ledger entry: a RECORDED HOLE, never a silent one. Both counts are returned because "no code path deletes a vector" is satisfied vacuously by a purge that does nothing — the criterion is that the hole EXISTS.

open_membership_store(config=None)

The configured membership store — SQLite beside the vault catalog, exactly as open_version_store sites the version history.

observation_history

The history sidecar for the observation-store family (bp-018, dn-self-sensing §2.4/§2.5).

ONE store for the FAMILY, discriminated by member name (store: 'code' today; 'agent' lands with bp-019). When a member store replaces a row because a NEW interpreter version re-projected the same identity key (archive-then-replace, code_observations.add_batch), the superseded generation lands here verbatim (row_json) — so the chain across interpreter at a fixed identity key is the fossil record of the changing worldview (§2.4's second orthogonal history), queryable oldest-first via chain().

Reset semantics split by re-derivability (owner ruling 2026-07-12): current READINGS are corpus-class (wiped with the corpus, rebuilt by re-projection from git); this HISTORY is ledger-class, reset-guarded — a superseded generation was produced by an interpreter that no longer exists at HEAD, so a wipe would be unrecoverable erasure of the epistemology record the stratum exists to keep.

No provenance parameter exists anywhere in this module: rows are archived VERBATIM — whatever class label the member row carried rides inside row_json, and this store mints nothing (it is bookkeeping about generations, not a provenance boundary).

Engine: SQLite — an identity-keyed append-only ledger (the runs/versions/snapshots convention), same reasoning as the member stores' Q2.

IDENTITY_KEYS = {'code': ('commit_sha', 'path', 'qualname'), 'agent': ('commit_sha', 'stream', 'subject_id', 'key')} module-attribute

ObservationHistoryStore dataclass

APPEND-ONLY: no delete/update method exists on this class — ledger-class is structural, like the no-provenance-parameter move (the test suite sweeps the class surface and pins the absence).

path instance-attribute
archive(store, rows)

Land superseded generations: each element is (identity-keyed row verbatim, its interpreter, the superseding interpreter). INSERT OR IGNORE on (store, identity_json, interpreter) — re-archiving the same generation (a crashed archive-then-replace re-run) changes NOTHING. Returns the number of NEW rows.

chain(store, identity)

Archived generations at one identity key, oldest first (insertion order — supersession only ever appends, so rowid IS the generation order). Each element is the superseded row verbatim (parsed row_json, its own interpreter inside).

count(store=None)
close()

open_observation_history_store(config=None)

The open_* helper: data/observation_history.sqlite (the sibling-store convention, no dedicated cfg path). GUARDED (bp-018 Item 4): named in _RESET_GUARD, never a reset target — dn-self-sensing §2.5, history does not rebuild.

reference_edges

The Lane-1 reference-edge store — typed cross-references, balance-isolated (B-c).

One row = one deterministic reference observed at one commit: a code docstring citing a design note (code_to_corpus), a design note naming a code path (corpus_to_code), or (bp-026) a design note/finding/brainstorm citing another one (corpus_to_corpus) via front-matter (design_ref/links/depends_on/warrant/supersedes/superseded_by), inline note-citation, or [[wikilink]]. Extraction is φ_code (ops/code_sensor.py), the sole interpreter, minting within project_observations.

[banner: schema v2 — bp-026] The v1 schema (code-observation-projection.md's original shape, bp-013) had ASYMMETRIC endpoints: a code side (commit_sha, code_path, qualname) fixed as the first slot and a corpus side (corpus_ref, corpus_kind) fixed as the second, with direction ∈ {code_to_corpus, corpus_to_code} stored to say which was which. That shape had no room for a corpus_to_corpus edge (findings 0059/0061/0062 — the reference graph is doc→doc-blind, and the store's own field names encode the gap). v2 replaces the fixed code/corpus slots with a SYMMETRIC (source_kind, source_ref, source_detail) → (target_kind, target_ref, target_detail) pair, kind ∈ KINDS = ("code", "corpus") on EITHER side, admitting code_to_corpus | corpus_to_code | corpus_to_corpus (and code_to_code, reachable, not minted anywhere yet). direction is now DERIVED (f"{source_kind}_to_{target_kind}"), never a stored column — it cannot drift from the endpoints that define it. The migration is WIPE + RE-PROJECT (bp-026 §3 Q1/Q2: this store is corpus-class, a deterministic projection of git history with near-zero reader blast radius — no in-place row surgery, no attempt to preserve v1 edge_ids; every edge is re-minted under the v2 identity formula from scratch. See docs/build-plans/bp-026/plan.md §6(b) for the pinned interface and Q1/Q2/Q3 for the migration-safety argument.

Why a dedicated store and not EdgeStore (v1 Q1, unchanged by v2 — the isolation rationale). These edges are geometry-class authority (observer-independent facts, the edge model's §2 ownership rule) BUT their endpoints may be CROSS-STRATUM: an observed-stratum code symbol on one side, an authored/curated corpus artifact on the other (or, since v2, both sides authored/curated). The mirror's reasoning complex 𝔎|_MR is authored-only, and EdgeStore feeds A_signed — so a cross-stratum edge landing there would smuggle observed-stratum structure into the introspective balance math. The separation pattern is core/stores/versions.py's: a store build_complex has no parameter for. build_complex(view, *, edges=None, derived=None, sim_floor=...) cannot receive this store; core/complex/** never imports this module (grep-asserted in the isolation test). Separated not because the edges carry intent (they don't — they are facts), but because their endpoints may cross strata.

The balance math holds no handle to this store. The 2026-07-10 survey's standing fact — nothing mints E_geom fibers; the balance math runs on recomputed cosine (plus EdgeStore polarity overlays) — remains TRUE for E_geom/A_signed after this store exists: reference edges live entirely outside the complex, and no instrument (balance, Laplacian, curvature, clustering) can observe their presence or absence. Falsifier (B-c, verbatim): "any instrument result changes when reference edges are added or removed" — automated forever in tests/integration/test_reference_edge_isolation.py.

Endpoints (v1 Q2, generalized). source_kind/target_kindKINDS. For kind="code": ref = file path within the tree, detail = qualname ('' = file/module grain) — the observed reading's coordinates. For kind="corpus": ref = repo-relative path (design-note/findings/brainstorm target), detail = '' (path-kind) or a digest (reserved for vault-note targets when one becomes resolvable — none arise from bp-011's validated patterns, so detail='' in every edge minted to date).

Direction (v1 Q3, unchanged in spirit). Stored AS EXTRACTED, but no longer as its own column — it is DERIVED from (source_kind, target_kind): code_to_corpus, corpus_to_code, and (v2) corpus_to_corpus are different assertions by different authors; this store never auto-symmetrizes. Consumers may symmetrize on read.

Append-only. Identity-keyed (source endpoint, target endpoint, ref_type, source_line) via a content-derived edge_id; INSERT OR IGNORE — the first reading of an identity wins and is never mutated (re-projection of the same commit is a no-op; a φ_code upgrade is a versioned re-interpretation, §2.2, never an in-place overwrite).

Reset semantics (v1 Q4, unchanged). Corpus-layer, like code_observations.sqlite: wiped with the corpus via reset_targets() (registration is the orchestrator's post-merge step, oq-0013 concurrence), unlike the snapshot LEDGER (build history, reset-guarded).

Consumers: the detangling instruments, the parked s(C,D) external-corroboration feature (finding-0021), and (bp-026's warrant) the dn-core-query-protocol algebra's references_to query surface — this store is its prerequisite substrate, not yet its consumer. Zone A, no network, no model anywhere in the path.

REF_TYPES = ('note-citation', 'path-mention', 'symbol-mention', 'design-ref', 'dn-slug', 'finding-id', 'inherits', 'calls') module-attribute

KINDS = ('code', 'corpus') module-attribute

DIRECTIONS = ('code_to_corpus', 'corpus_to_code', 'corpus_to_corpus', 'code_to_code') module-attribute

CORPUS_KINDS = ('path', 'digest') module-attribute

ReferenceEdge dataclass

One typed, directed reference — symmetric endpoints (bp-026 v2), no sign, no weight: this is a FACT record, not a balance input (no EdgeSign here on purpose — nothing about this row is assembled into any adjacency).

edge_id instance-attribute
ref_type instance-attribute
commit_sha instance-attribute
source_kind instance-attribute
source_ref instance-attribute
source_detail instance-attribute
target_kind instance-attribute
target_ref instance-attribute
target_detail instance-attribute
source_line instance-attribute
created_at instance-attribute
direction property

DERIVED, never stored (bp-026 §6(b)) — cannot drift from the endpoints that define it: f"{source_kind}_to_{target_kind}".

code_path property
qualname property
corpus_ref property
corpus_kind property
mint(*, source_kind, source_ref, target_kind, target_ref, ref_type, commit_sha, source_detail='', target_detail='', source_line, created_at=None) classmethod

Construct with the content-derived identity; validates the closed vocabularies at the boundary (a typo'd kind/ref_type is unrepresentable in the store).

ReferenceEdgeStore dataclass

The Lane-1 reference-edge table. Append-only: INSERT OR IGNORE on the content identity — a re-extracted reference never mutates the first reading. Deliberately unreachable from core/complex/** (see module docstring; isolation test-pinned).

path instance-attribute
add_batch(edges)

Land extracted edges. Idempotent on identity (INSERT OR IGNORE, append-only) — a second extraction of the same commit changes NOTHING. Returns NEW rows.

all(*, direction=None, ref_type=None, source_ref=None, target_ref=None)

direction filters on the DERIVED value (source_kind_to_target_kind), computed server-side from the two kind columns — no stored direction column exists. source_ref/target_ref are bp-026's addition: the "references TO doc X" query (all(target_ref=...)) and its dual (all(source_ref=...)).

for_commit(commit_sha)
count()
close()

open_reference_edge_store(config=None)

The open_* helper: data/reference_edges.sqlite — the sibling-store convention beside code_observations.sqlite. Corpus-layer (Q4): a reset_targets() wipe target (registered by the orchestrator post-merge, oq-0013 concurrence).

runledger

The run ledger — the harness's append-only record of dream runs + claims (E2, bp-043).

Carried from Track L L1 (the protocol annex of record in the superseded live-adoption-and-longitudinal-harness.md §2 — its dream_runs / dream_claims column lists are honored VERBATIM here). Two append-only tables:

  • dream_runs — one row per (pipeline, snapshot) execution (phase7 | dream_v2), carrying the run's config_fingerprint + corpus_digest (so two pipelines over "one snapshot" are provably comparable — dn-evaluation-harness §2.2) and cheap descriptive stats.
  • dream_claims — one row per claim, addressed by a content-addressed claim_id = sha256(kind ‖ canonical(support) ‖ polarity) — EXCLUDING surface wording + confidence (§2.2): "the same tension, found twice" is one id, however it was worded. novel is computed ON INSERT against ALL prior runs (an INDEX on claim_id backs the check), so re-emitted claims are marked novel=False and inherit prior verdicts (E6's job).

SQLite/WAL, scheduler single-writer (scheduler/__init__.py — one supervisor owns the queue; the run ledger follows the same discipline, written only by the trough handler in production; unit tests write a tmp/:memory: ledger directly). Append-only: no update, no delete. This store holds no model and no network — it is a passive record. The distinct SQLite engine (not E1's DuckDB eval-results table) is the A-4 routing pin: the access pattern here is point lookups + inserts, not analytical group-by.

DEFAULT_POLARITY = '+' module-attribute

RunLedger dataclass

Append-only SQLite/WAL ledger. Writes (start_run, add_claim) and reads (runs, claims) are distinct method groups over one single-writer handle (SQLite single-writer discipline, §3 Q5). path accepts ":memory:" for tests as well as a real on-disk Path.

path instance-attribute
start_run(*, pipeline, config_fingerprint, corpus_digest, node_count, edge_count, duration_s, spectral_stats)

Open a run (one (pipeline, snapshot) execution) and return its fresh run_id. A run is an EVENT — a new id every invocation even for the same snapshot (claims carry the identity, runs carry the occurrence).

add_claim(run_id, *, kind, confidence, support, surface_text, polarity)

Append one claim; compute its claim_id + novel (unseen claim_id across ALL prior runs, this row included on re-emit within the same run). Returns novel. Append-only: a re-emitted claim is a NEW row marked novel=False, never an update of the first.

runs(*, pipeline=None)
claims(*, run_id=None, novel_only=False)
close()

claim_id(kind, support, polarity)

Content-address a claim EXCLUDING its surface wording + confidence (§2.2). The canonical support set is sorted(set(...)) so identity is order-insensitive and duplicate-insensitive — "the same pattern, found twice" hashes to one id.

polarity_for(kind)

The polarity of a claim kind for claim_id (§3 Q3). Unmapped kinds default +.

polarity_and_flag(kind)

(polarity, defaulted)defaulted=True when kind has no explicit polarity mapping and fell back to + (§3 Q3's "unknown -> + flagged"). Lets a caller log the under-specified kind once per run rather than silently.

open_run_ledger(config=None)

Open the configured run ledger — its SQLite file lives beside the derived store (the telemetry precedent). Import of config.loader is lazy so the module stays dependency-light.

staging

The HYPOTHETICAL staging store (dn-synchronic-diachronic-dreamer §2.6-2/3/4; bp-081 H-1).

Staged hypotheses live ONLY here — never in a durable store. The design is laundering-proof by CONSTRUCTION, not by a gate: there is no promotion path from HYPOTHETICAL to anything (note §2.6-3). A hypothesis the owner comes to believe enters the corpus the way everything does — the owner authors it, or its real source ingests through the normal pipeline. So this store has no promote, no commit_to, no durable-store handle; it can only append staged rows, read them by generation, and tombstone them on expiry. That absence is the whole invariant, and the API surface scan in the tests asserts it structurally (the plan's spine).

The clock (§2.6-2). Admission and expiry are append events on the store's OWN chain — a per-stratum event clock N_hyp whose ticks are GENERATIONS. Every write (a stage admission, a sweep tombstone tick) advances the generation by one and logs a bookmark in staging_generations. Reads are GENERATION-ADDRESSED (read_at(g)), so a dream report that pinned generation g stays reproducible AS A RECORD even after the rows expire. Wall time (at, ttl_wall) is a COORDINATE CHART for owner-convenience lookup only — it NEVER orders anything (Law C4); the wall→generation resolution the sweep does is interval-valued and ambiguity-widening (D8), and lives sweep-side.

Stratum ≠ provenance. A staged row records its would-be stratum (where it would live if it were real — mirror, interpreted, …) AND its would-be provenance, both as ROW DATA. The row's overlay stratum is always HYPOTHETICAL (implicit — that is the visibility class). A would-be stratum of HYPOTHETICAL or FOUNDATION is refused: the overlay is not a promotion target and the denylist is never a home.

IllegalWouldBeStratum

Bases: ValueError

A staged row named a would-be stratum that is not a durable home (HYPOTHETICAL / FOUNDATION). A staged row's would-be identity is where it WOULD live if real — never the overlay class, never the denylist. Refused at admission (fail-closed).

StagedItem dataclass

One hypothesis to admit: its would-be stratum + provenance (ROW DATA — stratum ≠ provenance), its content digest (the content address the conditioning law's derives tails cite), and an opaque payload. No generation here — the store assigns it at stage.

would_be_stratum instance-attribute
would_be_provenance instance-attribute
content_digest instance-attribute
payload = '' class-attribute instance-attribute

StagedRow dataclass

A staged row as stored: its identity (row_id), the subspace_id it belongs to, the generation it was admitted at, its would-be stratum/provenance + content digest, its opaque payload, an optional wall-denominated TTL (ttl_wall — owner convenience, resolved to a generation at sweep; NEVER an ordering key), the generation a sweep tombstoned it at (tombstoned_at_gen, None while live), and the wall at bookmark of its admission.

row_id instance-attribute
subspace_id instance-attribute
generation instance-attribute
would_be_stratum instance-attribute
would_be_provenance instance-attribute
content_digest instance-attribute
payload instance-attribute
ttl_wall instance-attribute
tombstoned_at_gen instance-attribute
at instance-attribute

StagedBatch dataclass

The result of one stage admission: the generation the batch was admitted at and the ids of the rows appended (so a caller can pin the (subspace, generation) it staged).

generation instance-attribute
row_ids instance-attribute

GenerationEvent dataclass

One tick of N_hyp — a bookmark in the generation chain. kind ∈ {genesis, admission, sweep}; at is the wall bookmark (lookup only — never orders). The sweep's wall→generation resolver reads these to bracket a wall time into a generation interval (D8).

generation instance-attribute
at instance-attribute
kind instance-attribute

StagingStore dataclass

Append-only, generation-clocked staging for the HYPOTHETICAL overlay.

The store's ONLY handle is its own sqlite connection — no durable store is reachable from here, which is what makes "a staged row reaches a durable store" unrepresentable rather than forbidden (the no-promotion spine invariant, note §2.6-3). Generations are monotone and owned here; wall time is a bookmark, never an ordering key.

path instance-attribute
current_generation()

The latest N_hyp tick — the generation a bare read is addressed at.

generations()

The N_hyp chain — the generation bookmarks the sweep's wall→generation resolver reads (D8). Ordered by generation (never by wall).

stage(subspace_id, items, *, ttl_wall=None)

Admit a batch of staged items under subspace_id. ONE admission = ONE generation tick (an append event on N_hyp); every row in the batch shares that generation. Append-only — rows are never mutated after admission except a sweep's tombstone stamp.

Refuses (fail-closed) a would-be stratum that is not a durable home (HYPOTHETICAL/FOUNDATION, IllegalWouldBeStratum). Returns the generation + the appended row ids so the caller can pin the (subspace, generation) it staged.

read_at(generation=None)

The staged rows VISIBLE at generation (default: the current tick) — admitted at or before generation and NOT tombstoned as of it. Generation-addressed and reproducible: the same generation always yields the same rows, so an expired dream stays a record. Ordered by (generation, row_id) — by the event clock, NEVER by wall (Law C4).

subspace_at(subspace_id, generation=None)

The visible rows of one subspace at generation — the counterfactual overlay a composed read consumes (its digests are the conditioning law's derives tails).

all_rows()

EVERY staged row, tombstoned or not — the audit view (append-only, so history is whole). Ordered by (generation, row_id).

tombstone(row_ids)

Advance N_hyp by one (a sweep tick) and TOMBSTONE row_ids at that generation — the SD-d default disposition (append-only discipline; the row leaves every read_at(g') for g' ≥ the tick, but the record survives). Idempotent on already-tombstoned rows (they keep their original tombstone generation). Returns the sweep tick's generation.

This is the ONLY mutation after admission, and it is read/tombstone-only: it moves NO row anywhere — least of all durable-ward. There is deliberately no inverse (no un-tombstone, no promote).

count()

Total rows ever staged (append-only; tombstoned rows still count as records).

close()

open_staging_store(config=None)

Open the staging store beside the other core stores (the store-layer house pattern). Sqlite-backed (parked engine default: sqlite, not in-memory — reading records must outlive the process).

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 = 3 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.

record_harness_cost(run_id, *, wall_clock_s, models_resident, cells_completed, cells_skipped, note=None)

Record one harness run's cost/residency (dn-evaluation-harness §2.4, bp-044 Item 10) — surfaced as the report's cost appendix. run_id links to the run ledger (E2).

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)
harness_costs(run_id=None)

The harness cost ledger's rows (bp-044 Item 10), ordered (ts, run_id) deterministically — the report's cost appendix reads through here. Read-only; no clock, no model.

harness_cost_count()

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

LAYER_PROSE = 'prose' module-attribute

LAYER_CODE_AST = 'code_ast' module-attribute

LAYER_CODE_TEXT = 'code_text' module-attribute

LAYER_CODEDOC = 'codedoc' module-attribute

ATOM_ROW_SHED = {'digest': '', 'source_path': '', 'chunk_index': 0, 'qualname': '', 'line_start': 0, 'line_end': 0} 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, so the escaping is belt-and-braces rather than load-bearing.

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, so this read must keep carrying the vector column.

[banner: correction] Was: materialize the WHOLE table Arrow→Python, vectors included, and keep the rows whose source_path matched — the read half of finding-0169. Two premises of that shape were wrong. The first, that a Python-side filter avoided "a quoting hazard on arbitrary source paths", died with bp-100: _sql_str solves the hazard. The second, that the predicate could not be pushed down, died with bp-103: scan() (the shim's honest name for LanceDB's search(None)) takes the predicate server-side, so only the matched path's rows ever cross into Python. limit(0) means UNLIMITED — verified empirically against the installed 0.33.0 on a 137-row path, and pinned by a ratchet in tests/unit/test_typedshim_lancedb.py, because a silently reintroduced default cap would under-read a deep path and corrupt the amendment it feeds.

NO select() projection here, deliberately: vector is the column the caller came for.

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.

[banner: correction] Was: materialize the WHOLE table (rows_for_source), rebuild an id IN (…) list from it, delete by that. The path is itself a predicate — pushing it down removes the scan entirely (finding-0169, and this is the note-amendment hot path at core/ingest/index.py:87, not only the code lane). Selecting on the same source_path column the Python filter used, it deletes exactly the same rows — and strictly fewer in the one case they differ: an id is {doc_id}:{chunk_hash} where doc_id may diverge from source_path (a rename, an id:: property), so the old id-list could reach a row belonging to another path.

supersede_source(source_path)

Keep-and-link (dn-temporal-code-corpus D2, bp-099): flip every CURRENTLY-current row of source_path to current=false while RETAINING it — a superseded code version is never deleted. Returns the number of rows flipped (0 if the path had no current rows).

ONE predicate, pushed down: a filtered count_rows for the return value, then a single in-place update. No read, no re-land, nothing materialized into Python — the cost is a function of the matched rows, never of the store's size, which is exactly finding-0169's bound and the condition on the daemon restart. Idempotent by construction: a path with nothing current counts 0, skips the write entirely, and returns 0.

The vector column is never named, so it is never read, so it cannot be re-derived or dropped (§8). That is a strictly STRONGER guarantee than the byte-equality assertion in test_vectorstore_supersede.py — which is kept anyway, as a regression net on the shape of the method rather than on this implementation of it.

[banner: correction] Was: read the whole path → delete it → re-add every row with the flag flipped, justified as staying "portable — no dependency on a LanceDB in-place update". That portability claim was false at the pinned version (bp-100 Q3, re-verified by bp-103 against the installed package rather than the docs): lancedb 0.33.0 has Table.update(where, values), and the re-land was paying a full O(total store) materialization — vectors and all — to flip one boolean. The re-land is now deleted entirely, not merely bounded; bp-100's interim [cross-ref: extension] note (which recorded it as retained-but-halved, blocked on the typedshim) is retired with it. Warrant finding-0176; the two xfail(strict=True) ratchets it cites are now ordinary green tests in tests/unit/test_store_cost_ratchet.py.

The count comes from count_rows(where) and NOT from update(...).rows_updated: UpdateResult is the 0.33 return shape, while pyproject.toml:12 pins lancedb>=0.10, a range whose older members returned None. A filtered count is server-side and materializes nothing, so portability costs no scan (bp-103 §11, first parked decision — re-entry is a plan that owns pyproject.toml and raises the floor).

[cross-ref: extension] The predicate names current, so unlike the old body this raises on a PRE-bp-099 store whose schema has no such column (the migrations arm on add, and code_corpus.sync supersedes before it adds). The old body silently returned 0 there and then let add stamp every row current=true — leaving the superseded version AND the new HEAD both current, i.e. silent D3 corruption. Failing loudly is the improvement; see finding-0180 for the migration-arming question, which is a design call, not a builder's.

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, include_atom_rows=False)

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.

[banner: correction] The shed-atom guard (bp-152 Item 3, the note's §3 Q5 gap). An UNSCOPED read (provenances=None) now excludes shed CODE-atom rows unless the caller asks for them by name. The reason is a silent corruption, not tidiness: every structural group-by-digest consumer keys on a column an atom row does not have. core/kernel/stores/sourceset.py's source_sets(store) defaults to ALL strata by design ("a structural grouping utility, not a mirror read"), and group_sources keys on r["digest"] — so shed rows, all carrying digest='', would collapse into ONE bogus SourceSet keyed ''. MixedProvenanceError cannot catch it (it needs a digest spanning several provenances; these rows are uniformly CODE), so it fails with no visible error at all. core/curator/curator.py's prune_candidates has the identical shape and would report the whole atom plane as one orphaned digest.

The guard is here rather than in sourceset deliberately: the kernel module may never learn about the outer-ring membership store (the C5/D3 ring pin — an import would demote sourceset from the inner-ring fixed point), so the shed side owns the consequence of its own shed. Excluding is the fail-closed half of the note's "excludes them or raises": a structural grouping over geometry-without-occupancy is not a smaller answer, it is a wrong one, and a code consumer reaches source objects through membership fibers (D3) — never through group-by-digest.

A caller that genuinely wants the whole physical table passes include_atom_rows=True; a caller that wants the code lane passes provenances={CODE}, which is how CodeCorpusSync and the eval battery already read it, and is unaffected.

atom_rows()

Every shed CODE-atom row (D1) — the vector plane V, as rows. len(atom_rows()) is |V| for the append-only invariant (§4: no test may ever observe |V| decrease except across a logged purge).

set_current_any(ids, value)

Set current_any on exactly the named atom rows (D2 step 5). Returns rows written.

current is a lance column, so a flip rewrites fragments — which is why D2 step 5 and the note's §3 pin say only the atoms whose current-membership count crossed 0↔1. This method takes the crossing set and nothing else; computing it is the lander's job (it is pure membership arithmetic and belongs in SQLite, not here). An empty set writes nothing.

Predicates are chunked so a large crossing set cannot build an unbounded SQL string.

delete_atom(content_id)

Delete one atom row by its (layer, content_hash) id — the D5 PURGE path, and the ONLY machinery in this module that removes a vector row (finding-0164; owner-gated, privacy outranks lineage). Returns rows deleted, so a purge that silently no-ops is observable: "never deletes" is satisfied vacuously by a purge that does nothing, so the count is the thing a test asserts on. The membership half of the hole (tombstoning) is the membership store's — see core.stores.memberships.purge_atom, which owns both halves.

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

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

Default retrieval is CURRENT-VIEW (dn-temporal-code-corpus D3, bp-099): a superseded code version (current=false, kept by keep-and-link) never surfaces unasked, so every existing consumer is unchanged (note rows carry the vacuous current=true). A temporal consumer opts into history with include_superseded=True.

Under D1 (bp-152) the same clause serves the atom plane with the current_any reading — "does ANY current membership contain this atom" — so an atom whose every occupancy is superseded drops out of the default search exactly as a superseded version used to. This search returns ATOMS; resolving each hit to its occupancies (path, blob, slot, lines) is the membership join (D3, core.stores.memberships.resolve_occupancies), and the join is where a code consumer learns where a hit lives.

is_code_atom_row(row)

Is this row a shed CODE atom row (D1) rather than a source-object chunk row?

An atom row is geometry with no occupancy: its occupancy lives in the membership relation (core/stores/memberships.py), so it carries no source_path and no digest. This predicate is the ONE place that reading is spelled out, so all_rows' structural guard and the membership store's repair pass cannot drift apart.

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.

One owner-gated identity migration is admitted (bp-034; §11 ruling 2026-07-14): migrate_rekey_doc_id relabels which doc_id a chain is filed under — needed exactly once per identity switch (the id:: mint) so the switch does not FORK lineage, which is the outcome append-only exists to prevent. doc_id is the RESOLVED identity label (provisional == source_path until diverged — see the DDL note), not historical content: the relabel preserves every row's (version_seq, digest, at) exactly, refuses to merge two chains, and is fail-closed on owner authority. Runtime paths remain append + reads only.

RekeyRefusedError

Bases: RuntimeError

An owner-gated doc_id re-key was refused by a safety gate — a bad/empty key, or a request that would MERGE two live lineages onto one id (old and new both hold rows). The PurgeRefusedError pattern: fail-closed, named, never a silent merge or partial write. Owner authority is refused separately, via MachineAuthorityRefused at the same boundary.

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).

migrate_rekey_doc_id(old, new, *, declaration)

Owner-gated identity migration: relabel a chain's doc_id from old to new, keeping every row's (version_seq, digest, at) byte-for-byte (bp-034; §11 ruling 2026-07-14). A RELABEL, never a history rewrite — the label moves, the sequence/contents/order never do; the ONE admitted write to this append-only store, needed once per id:: mint so an identity switch does not fork lineage. Returns rows relabeled.

Fail-closed on owner authority (verify_owner_declaration — a machine caller refused here). CHECK ORDER matters — the no-op cases are decided BEFORE the merge refusal, so a partial run (the note re-keyed but interrupted before the next store) converges on re-run instead of raising: (i) old == new → no-op (0): nothing to relabel. (ii) old holds NO rows → no-op (0): nothing to move — this is what makes a re-run of an already-migrated chain converge. (iii) old AND new both hold rows → REFUSE (RekeyRefusedError): never merge two lineages. (iv) else → relabel (one UPDATE, one statement, one txn).

count()
close()

open_version_store(config=None)