Skip to content

core — top-level modules

core.agent

A trivial agent that inherits the Constitution (the inheritance + self-eval seam).

Every agent — static, scheduled, or minted by the factory (Phase 5) — is built this way: the Constitution is its outermost frame and the role/task nest inside it (Invariant 6), and no agent returns output without passing through the Constitution pre-return check (§IV). The factory will extend this with tool scope, tiers, and the scope ceiling; the Librarian (Phase 2, core/librarian/) is the first real specialization.

The self-evaluation logic lives in core.selfcheck and is re-exported here so callers and tests can keep importing it from the agent module.

Finding dataclass

directive instance-attribute

status instance-attribute

detail = '' class-attribute instance-attribute

SelfCheck dataclass

Result of the pre-return Constitution check (§IV).

passed instance-attribute

findings = () class-attribute instance-attribute

notes property

Compact summary, e.g. ("grounded-citations:pass", "mirror-not-oracle:deferred").

failures()

Agent dataclass

name instance-attribute

role_prompt instance-attribute

tier = 'routine' class-attribute instance-attribute

server = None class-attribute instance-attribute

build_context(task, *, history=None)

The agent's context, Constitution outermost (Invariant 6).

respond(task, *, history=None, think=None)

self_evaluate(output, *, sources=None, judge=None)

Run the pre-return Constitution check.

sources: the legitimate citation targets. Pass a list of Source (title + stable digest) for the well-posed, collision-detecting grounding check (gap G1); a bare set of titles still works (legacy). Omit it (None) for an agent with no retrieval context. judge: optional small-model judge for the subjective directives; when absent those directives are reported as deferred (Phase 10 wires the judge + baseline).

core.complex_types

Typed enums for the reasoning-complex graph (family 5; companion III §1.2–§1.3).

Small closed sets that would otherwise be free ints/strings — made enums so an illegal value is unrepresentable rather than merely unexpected (the MirrorView/ProposedChange move, MATHEMATICAL-REFRAMING.md §B.1: make it a type when that deletes an illegal state). These are the shared vocabulary the derivation-hypergraph junction (core/stores/derived.py) uses today and the signed edges table (Prompt H1) will use next; kept in a leaf module with no core imports so both Zone-A consumers depend on it without a cycle. Nothing here touches the network.

EdgeSign

Bases: IntEnum

Polarity of a typed edge (companion III §1.2, §2.3): +1 support / −1 contradict.

An IntEnum so the value itself is the ±1 the signed-Laplacian arithmetic and the edges.sign column consume — EdgeSign.CONTRADICT * w is just -w. The enum deletes the illegal sign = 3 (or 0) state: polarity is a closed two-element set. A non-edge is the absence of a row, never a zero sign, so 0 is deliberately not a member.

SUPPORT = 1 class-attribute instance-attribute

CONTRADICT = -1 class-attribute instance-attribute

HyperedgeRole

Bases: StrEnum

A node's role in a derivation B-arc (companion III §1.3, §2.5): the tail set T(e) = supp(κ) jointly entails the single head κ.

role ∈ {tail, head} — the closed set the hyperedge_nodes.role column stores. A StrEnum so the stored value is the readable word while any other string is unconstructable. Today every head-set has size 1 (one head per derives hyperedge).

TAIL = 'tail' class-attribute instance-attribute

HEAD = 'head' class-attribute instance-attribute

core.constitution

Load and frame the Constitution — the fixed point inherited by EVERY agent (Invariant 6, BUILD-SPEC §4).

This module is the inheritance mechanism: it loads CONSTITUTION.md and assembles each agent's context OUTERMOST-FIRST, so the Constitution is always the outer frame and task-specific instructions nest strictly inside it and can never displace it. The factory (Phase 5) mints agents through this same seam; static agents use it directly.

The Constitution is read-only here — no agent may modify it (Invariant 9). Its SHA-256 fingerprint is the frozen-anchor identity used by drift audits (BUILD-SPEC §15).

REPO_ROOT = Path(__file__).resolve().parent.parent module-attribute

CONSTITUTION_PATH = REPO_ROOT / 'CONSTITUTION.md' module-attribute

Message

Bases: TypedDict

One chat turn, Ollama chat-API shaped (bp-006 T2 convention: TypedDict).

Runtime-identical to the plain dict this replaced — TypedDict is erased to dict — but the shape now crosses module boundaries visibly to the checker.

role instance-attribute

content instance-attribute

load_constitution() cached

constitution_fingerprint()

Stable SHA-256 of the Constitution. Drift audits compare against this to detect any change to the fixed point (BUILD-SPEC §15).

frame_context(role_prompt, task=None, *, history=None, context_blocks=None)

Assemble an agent's context outermost-first (BUILD-SPEC §13 priority order): Constitution -> role -> retrieved RAG context -> history -> task. The Constitution is ALWAYS messages[0]; everything else nests inside it and may never override it (Invariant 6).

context_blocks are retrieved grounding (e.g. the Librarian's RAG chunks), injected after the role and before history per the §13 priority order.

core.dreams_view

The dreams reading scope — surfacing the INTERPRETED layer, mirror-not-oracle.

The dreamer + curator write thematic syntheses and housekeeping findings to the DerivedStore (INTERPRETED provenance, BUILD-SPEC §8). Before this, nothing showed them to the owner — the whole dreaming layer ran in the background and stayed invisible (WIRING-AUDIT DANGLING #1). The Ambassador's DREAMS path reads through here.

Two rules shape it
  • Read-only, the OpsView move — DreamsView binds ONLY the store's read methods as callables; it holds no add/reset, so the Ambassador cannot write the interpreted layer through it (assurance tier: static + guard, like OpsView; weaker than MirrorView's structural copy, and labelled so).
  • Mirror, not oracle (Constitution §III.2, the §8 firewall). A dream is what the SYSTEM inferred, never what the owner wrote — so the narration marks it as interpretation, holds it loosely, cites the authored notes it spans in [[brackets]], and hands the judgment back. It must never read as authored ground truth; that asymmetry is the whole point of keeping the interpreted layer separate and provenance-marked.

ArtifactReads

Bases: Protocol

The read slice of a DerivedStore this view binds — reads and ONLY reads.

all(*, kind=..., subkind=...)

count(*, kind=...)

RetractedReads

Bases: Protocol

A duck-typed source of RETRACTED subject ids (a DispositionStore.retracted).

retracted()

DreamsView dataclass

Read-only view over the DerivedStore's INTERPRETED artifacts (dreams + findings).

Construct with DreamsView.over(store); the fields are bound READ callables only — there is no mutator (add/reset) on this type's surface (asserted in tests/integrity).

over(store, *, dispositions=None) classmethod

Bind the store's reads (+ an optional retracted disposition read). The returned view exposes those and only those — the store's add/reset are unreachable through it. Passing dispositions makes it the ACTIVE projection: retracted dreams are excluded from recent_dreams. Omit it to keep the prior behavior byte-identical.

recent_dreams(limit=5)

The most recent thematic dreams, newest first (the store orders oldest-first), excluding any the owner has RETRACTED by verdict — the active projection (ingest-identity §6).

dream_count()

finding_count()

narrate_recent(limit=5)

Reflect recent dreams back in plain language, framed as interpretation the owner judges — never as authored fact (§III.2).

core.effect_proposal

Compose a reversible-write proposal, tailored to the owner (Track G, item G5).

This is the point §7 names: an effector that writes in the owner's voice must read a model of the owner — and that read is a MirrorView (authored-only; the firewall holds even here), with the output a proposal, never a sent artifact. So the whole of this module is: read the mirror, tailor a body, return a ProposedEffect. There is deliberately no send, no stage, and no Effect construction here — composing the proposal ("the model advises") is strictly separate from acting on it ("code acts", after the gate: ops/effect_gateops/effect_ledger → the edge effector).

Two boundaries make the separation structural, not a convention:

  • The firewall. tailor is typed Callable[[MirrorView, str], str]. A MirrorView cannot hold observed or curated rows (core/mirror.py raises), so a steered composer physically cannot tailor a reply from third-party exhaust — only from the owner's own authored notes.
  • Propose, never send. The return type is ProposedEffect — an actuator name + allowlisted string params and nothing more (no send field exists to set). The class is asserted REVERSIBLE against the catalog (fail-closed): this composer proposes reversible writes and nothing else.

The proposal then rides the ordinary path: a human approves it (LIGHT, EffectLedger.approve), an Effect is constructed under that approval, and the edge effector STAGES a draft the owner can delete (edge/effectors/writes.py). Sending that draft is a different, irreversible, full-gated act (G6) — never something this module can reach.

Tailor = Callable[[MirrorView, str], str] module-attribute

NotAReversibleWriteError

Bases: ValueError

A non-reversible actuator was handed to the reversible-write proposer (fail-closed): sensing has its own path (core/sensing.py) and irreversible sends are composed + gated elsewhere (G6), never proposed here where approval is only LIGHT.

ReversibleWriteProposer dataclass

Composes class-2 (reversible) effect proposals. Holds only a tailor (advice); it never holds a store handle, a credential, or a send path. Every method returns a ProposedEffect.

tailor = _grounded_default_tailor class-attribute instance-attribute

propose_draft_reply(*, to, subject, request, mirror, rationale='')

Propose a DRAFT reply, its body tailored from the owner's authored notes (mirror). The result is a proposal — never a sent artifact. mirror is a MirrorView, so the tailoring cannot draw on observed/curated exhaust (the firewall). Validated against the catalog via .resolve() before return (fail-closed on any bad param).

propose(actuator, params, *, rationale='')

Propose any cataloged reversible write (calendar_hold, stage_file, …) from explicit params. No tailoring read — use propose_draft_reply for a voiced body. Fail-closed on a non-reversible actuator or any param outside the catalog's closed allowlist.

model_tailor(server, *, tier='routine', max_notes=12)

A tailor backed by the local model, reading the owner's authored notes to draft in their voice. The model only ADVISES: its output is a proposal body wrapped into a ProposedEffect and never sent. It sees a MirrorView (authored-only) — never observed exhaust, never a credential.

Parsing is forgiving; an empty reply degrades to the grounded default (honest, not a fabricated draft). Lazy about the model interface (duck-typed .chat) so this module imports no server.

core.interface

Core side of the interface handoff (BUILD-SPEC §6, §12; Invariant 2).

The sealed core never speaks to a messaging service. Instead the networked gateway (Zone B) drops a sanitized request into the handoff directory; this inbox — running inside the sealed core — reads it, dispatches the query to a core handler (the librarian / a minted agent), and writes the answer back to the handoff directory. The gateway relays it onward.

The ONLY coupling to the outside is these JSON files on disk: no network, no adapter import, no shared memory (the structural form of "network and private data never share a component"). The wire format is a small JSON object, mirrored — not imported — by the gateway side: requests/.json = {"id", "conversation", "text", "ts"} responses/.json = {"id", "conversation", "text", "ts"}

Handler = Callable[[str], str] module-attribute

REQUESTS = 'requests' module-attribute

RESPONSES = 'responses' module-attribute

CoreInbox dataclass

handoff instance-attribute

handler instance-attribute

process_once()

Process every pending request; return how many were handled. A handler error becomes an error response rather than crashing the inbox.

run(*, poll_interval=0.5, max_cycles=None)

Serve the handoff in a loop. The scheduler (Phase 3) can also drive this as a job.

build_core_inbox(config=None)

Wire the inbox to the Ambassador — the conversational front door (Track B). The Ambassador reasons about intent and routes to retrieve / explain / status / capture inline, delegating heavy work; conversations are captured as authored-dialogue.

This base wiring has no DELEGATION queue handle (the core never imports the scheduler — that would invert the layering), so TASK here still attests + narrates effort but enqueues nothing. The full delegating inbox (task → gate → queue, with completed-result surfacing) is scheduler.interface.build_conversation_runtime, used by the CLI and the scheduled path.

agents.ambassador is imported lazily (function-level): it transitively reaches the pure scheduler.budget budgeter, and a top-level import would invert core's layering — the Ambassador is core-side but is assembled, not imported, at module load (no network either).

core.mirror

The mirror projection as a TYPE (Invariant 6, BUILD-SPEC §8; gap G3).

I6 — the firewall — requires that introspective agents (the dreamer; the curator's theme / contradiction scan) read ONLY the mirror-readable provenance classes (MIRROR_READABLE = {authored}), so third-party observed exhaust can never seed a dream or enter the behavioral baselines (§15). Until now that was a call-site convention: remember to pass provenances=MIRROR_READABLE to all_rows. The formal-properties catalog (G3) asks to promote it to structural.

MirrorView is that promotion. It is the only thing the introspective agents cluster over, and:

  • its sole normal constructor, project, applies π_MR — it reads the source restricted to MIRROR_READABLE; and
  • __post_init__ re-validates every row's provenance and raises on a non-authored row,

so a MirrorView holding observed (or any non-MR) data cannot be constructed at all — not by project, not by hand. Handing an introspective agent non-authored data is therefore unrepresentable (the wrong state cannot be built), the top tier of the assurance hierarchy, rather than "checked and refused". Functions typed to accept a MirrorView inherit the proof.

Note this is the introspective read path. The curator's prune scan deliberately reads ALL provenances (orphaned derived weight is dead regardless of provenance); that is not a mirror read and correctly does not go through MirrorView.

RowSource

Bases: Protocol

Anything that can yield provenance-filtered rows (the VectorStore, or a test fake).

all_rows(*, provenances=None)

NonMirrorRowError

Bases: ValueError

A row whose provenance is outside MIRROR_READABLE was offered to a MirrorView.

MirrorView dataclass

An authored-only projection of the thought-graph. Every contained row is guaranteed provenance ∈ MIRROR_READABLE — the type itself is the proof. Obtain one via MirrorView.project(store); direct construction with non-authored rows raises.

project(source) classmethod

π_MR — the mirror projection, and the only sanctioned way to build a MirrorView. Reads source restricted to MIRROR_READABLE; __post_init__ then re-checks, so even a buggy source cannot smuggle a non-authored row past the type (fail-closed).

rows()

The authored rows (a fresh list; the view is immutable).

core.ops_view

The operational-introspection read scope (Track B / B3; nervous-system-and-ambassador.md §4).

The Ambassador has a SECOND reading scope beyond the authored mirror: the system's own operational state — the attestation chain (what it has done), the proposal ledger (what self-modifications are pending/decided), and the drift gauge (is it healthy). This is the one role permitted to read the audit layer to narrate it — and it still cannot write to it.

Read-only, the MirrorView move: an OpsView captures ONLY the read methods of those stores as bound callables. It holds no reference to a mutator on its public surface — approve, deny, append, mark_*, propose are simply not attributes of this type, so the Ambassador cannot reach them through the view (asserted in tests/integrity/test_ops_view.py). Python can't make reach-through-__self__ impossible the way a copied immutable tuple can (MirrorView), so the honest assurance tier here is static (typed read Protocols) + guard (the no-mutator integrity test) — strictly weaker than MirrorView's structural guarantee, and labelled as such.

It also renders its state in plain language (narrate) — never the internal nouns (tier names, "synthesis job", credentials, accessors) the authoritative note §4 forbids. That plain register is the conversational surface of "what have you been doing?" and "is the system healthy?", and the backing for the Ambassador's effort narration.

AttestationReader

Bases: Protocol

The read surface of the append-only attestation store (no append).

all()

by_role(role)

count()

ProposalReader

Bases: Protocol

The read surface of the proposal ledger (no approve/deny/mark_*).

all()

pending()

OpsSnapshot dataclass

A plain, model-free summary of operational state — the data the status path renders.

attestation_count instance-attribute

recent_actions instance-attribute

pending_proposals instance-attribute

drift_within_tolerance instance-attribute

constitution_intact instance-attribute

OpsView dataclass

Read-only introspection over attestations + the proposal ledger + the drift gauge.

Construct with OpsView.over(...); the fields are bound READ callables only — there is no mutator on this type's surface (B3's read-only guarantee).

over(attestations, ledger, *, drift=None) classmethod

Bind the read methods of the live stores. The returned view exposes those and only those — the stores' mutators are unreachable through it.

attestation_count()

recent_actions(limit=5)

The most recent (role, action, timestamp) triples, newest first.

actions_by_role(role)

pending_proposals()

all_proposals()

drift_report()

snapshot()

narrate()

A plain-English status summary for "what have you been doing / are you healthy". Deliberately avoids tier names, job/queue nouns, credentials — the shape of the work, never the plumbing.

core.provenance

Provenance classes for the data architecture (BUILD-SPEC §8; design-notes/ observed-data-and-the-assistant-tier.md; the §1 provenance-spectrum growth path).

Every stored datum carries a provenance class so a query or agent can always distinguish "the owner wrote it" from "the system inferred it" from "a third party observed it" from "the owner selected someone else's words". The classes do not mix — that separation is the firewall (BUILD-SPEC §8).

The spectrum (Track B realizes the §1 split that the formal spec already assumes): * authored-solo — the owner wrote it alone (notes, journals). Ground truth, feeds the mirror. This is what the single authored tag used to mean; existing rows relabel to it (a same-trust-tier relabel, not a promotion across the firewall — both authored classes are mirror-readable). * authored-dialogue — the owner's own words to the Ambassador. Still authored ground truth ("your words to it are more yours than its words to you"), so it is mirror-readable too — closing the capture loop. * curated — others' words the owner selected (books, highlights, and the system's own white papers / design notes = its self-knowledge graph). Lives in its own graph; never merged into the authored mirror (curated ∉ MIRROR_READABLE — the same firewall shape as book-dreaming). The Ambassador reads it only via a deliberate, non-default provenances={CURATED} query. * interpreted — system inference. Structurally unforgeable (the derived store has no provenance parameter). * observed — third-party behavioral exhaust. Assistant-tier only; never the mirror, never the baselines. Reserved (dormant schema).

T = TypeVar('T') module-attribute

MIRROR_READABLE = frozenset({Provenance.AUTHORED_SOLO, Provenance.AUTHORED_DIALOGUE}) module-attribute

Provenance

Bases: StrEnum

AUTHORED_SOLO = 'authored-solo' class-attribute instance-attribute

AUTHORED_DIALOGUE = 'authored-dialogue' class-attribute instance-attribute

CURATED = 'curated' class-attribute instance-attribute

INTERPRETED = 'interpreted' class-attribute instance-attribute

DERIVED_STRATUM = 'derived-stratum' class-attribute instance-attribute

OBSERVED = 'observed' class-attribute instance-attribute

Authored dataclass

Bases: Generic[T]

A value obtained exclusively from mirror-readable (authored) sources.

An information-flow label, not a claim the owner typed this exact value: a note centroid computed over authored rows is still Authored[NoteVector] — what the firewall tracks is the provenance CLASS of the sources. @final: even deliberate subclass-laundering (a Derived masquerading via inheritance) is a type error, not just an accident.

value instance-attribute

Derived dataclass

Bases: Generic[T]

A value that transited system inference (the INTERPRETED lane) — or mixed with one.

The meet of the two-point lattice: any computation touching a Derived input yields Derived output. The only way UP is promote, which demands the owner's capability.

value instance-attribute

OwnerVerdict

PLACEHOLDER capability token for verdict-gated promotion (I1) — taxonomy UNRATIFIED.

Deliberately a NOMINAL class, not a Protocol: an empty Protocol is structurally satisfied by every object, which would let promote(d, object()) type-check and vacate the capability constraint. @final keeps it unforgeable-by-subclass at the type level.

This class answers NO design questions (recorded in bp-009's journal for /triage): whether it unifies with the runtime verdict machinery (core/verdict/, core/stores/verdicts.py), whether a verdict names its target authored class, and its scope (per-value / per-artifact / per-run) are all open until the I1 taxonomy ratifies. At runtime it confers nothing — promote raises regardless.

promote(x, cap)

The ONLY up-move of the two-point lattice — signature per §2.4, verbatim.

Extends the promotion comment above MIRROR_READABLE ("Promotion up to an authored class is a deliberate human re-tag-from-raw (§8), never automatic"): at the type level, a call site that never received an OwnerVerdict cannot even EXPRESS a promotion, so the accidental-violation class is removed at authorship time.

STUB — verdict-gated promotion (I1) is unbuilt (recursive-strata parked) and the verdict taxonomy is unratified, so this body deliberately implements NO policy. The typed signature is the contract the future implementation must satisfy.

core.recursion

Recursion-decay bound for interpreted artifacts (Invariant 10; BUILD-SPEC §8 analogy).

Interpretation is hypothesis; tame the recursion; evidence decides, not persuasion.

An interpreted artifact's confidence may not compound with derivational distance from ground truth — it must decay. With derivation depth d(κ) (0 = an authored leaf; computed by DerivedStore.depth, well-defined because the derivation graph is acyclic by construction — gap G2) and a base grounding score g(κ) ∈ [0, 1], the bound is

    c(κ) ≤ γ^{d(κ)} · g(κ),     γ ∈ (0, 1).

Because γ < 1, a self-referential loop loses potency every pass instead of amplifying — the formal shape of taming "the stack-overflow of a mind thinking only about itself". This module is the small, pure home of that bound: depth + γ + g make c computable.

decay_bound is the depth-decay ceiling γ^d·g. The full, clamped confidence a claim actually carries — c = min{1, γ^d · g · (1 + λ(|Agr| − 1))} — is claim_confidence, the single definition the adjudicator calls (companion III §5.3/§7.2). The min{1,·} clamp + the γ^d factor make c ∈ [0,1] and non-increasing in depth by construction: a c>1 or a depth-rising c is unrepresentable because no caller assembles the bonus itself.

γ bound (gap G7): γ is held small enough that a depth-3 artifact is clearly subordinate to ground truth — at the default γ = 0.5, depth-3 confidence is capped at 0.125·g (an eighth), so third-order interpretation cannot out-rank a first-order reading of the same evidence. It is a declared constant here, not a magic number scattered at call sites; calibrate on the real corpus before the adjudicator ships.

DEFAULT_GAMMA = 0.5 module-attribute

DEFAULT_LAMBDA = 0.1 module-attribute

decay_bound(depth, *, grounding=1.0, gamma=DEFAULT_GAMMA)

The confidence ceiling c ≤ γ^depth · g for an artifact at derivation depth with base grounding g (Invariant 10). Non-increasing in depth for γ ∈ (0,1) and g ≥ 0 — confidence strictly decays away from authored ground, never compounds.

claim_confidence(depth, *, grounding=1.0, agreement=1, gamma=DEFAULT_GAMMA, lam=DEFAULT_LAMBDA)

THE single definition of an interpreted claim's confidence (companion III §5.3/§7.2):

c = min{1, γ^d · g · (1 + λ(|Agr| − 1))}.

decay_bound is the depth-decay ceiling γ^d·g (Invariant 10); this multiplies in the bounded corroboration bonus (1 + λ(|Agr|−1)) — agreement across |Agr| distinct methods is a multiplier, not a vote (g=0 ⇒ c=0) — and applies the min{1,·} clamp. Together the clamp and the γ^d factor guarantee, for every admissible input:

  • c ∈ [0, 1] — no path can produce c > 1 (closes the companion III §7.2 clamp tension);
  • c is non-increasing in depth d for fixed (g, |Agr|) — belief decays away from authored ground, never compounds.

This is the only place confidence is assembled; callers (the adjudicator) must not multiply the bonus in themselves, so an out-of-range or depth-rising c is unrepresentable at source.

core.recursion_ops

Dialogue operations — corrections that act on claims, not peer nodes (dialogue-ingest §3–§4).

A note is a claim; a dialogue is a derivation whose output is a set of OPERATIONS on existing claims (§3), never a new peer node beside them (the §2 failure — two content-addressed claims disagreeing, lighting up a false contradiction). The ratified starter vocabulary (§4; build plan Item 2a) is three operations:

  • Supersede(C, conclusion, warrant) — the dialogue's conclusion C′ replaces C in the ACTIVE projection; C is retained in history with the warrant as the reason. C′ is a DERIVED conclusion (interpreted, γ^d-damped — never an authored peer), minted through the DerivedStore.
  • AttachDefeater(C, defeater) — records that C is contested and how (C stays, marked).
  • RecordWarrant(warrant, {C, C′}) — the reasoning linking the initial and final positions.

Three separations keep this honest (all confirmed against code):

  • Distinct from version-supersedes (§4A C3). Claim-supersede is warrant-bearing reasoning; note-supersedes(v1,v2) is a file edit (core/stores/versions.py). Different relations, different stores — never the same rel-type, never the balance-fed EdgeStore.
  • The conclusion is INTERPRETED and grounds on the WARRANT'S anchors (Item 9). C′ is minted via DerivedStore.add, carrying INTERPRETED provenance (I5) and a derivation depth, so core.recursion bounds its confidence by γ^d (I10): a dialogue conclusion can never out-rank the authored claim it revised without an owner verdict (I1). Its derived_from is the warrant's K₀ anchors (surviving grounding of C + the dialogue's new evidence). Grounding a revision on a derived [C] is refused — it cites what it discredits and, because the grounding ratio is transitive, collapses g when C is superseded and builds the cross-stratum tower (§4.2). Grounding on an authored [C] is legitimate: bedrock does not decay, so g=1 and the revision is not weightless. The C→C′ relation is carried by the dispositional ClaimOpStore edge alone, not a grounding fiber; the γ^{d≥1} "can't out-rank authored" guarantee comes from depth ≥ 1.
  • Budgets floored (PD4). The recursive-strata I5 population / edge budgets are parked; with none enforced these ops are recorded flatly and the Dreamer does not yet consume them recursively — exactly the non-recursive floor, recovering current behavior. The hook is named here, enforced when recursive-strata unparks.

The dialogue → operations EXTRACTION is a model task (an LLM reads the dialogue and proposes the operations); it is an injected DialogueAnalyzer seam with a deterministic default of NO operations (document-only ingest, unchanged). This module is the deterministic core: the types, the store, and apply_operations (record + re-project). Zone A, no network.

DIALOGUE_CONCLUSION = 'dialogue_conclusion' module-attribute

DialogueOp = Supersede | AttachDefeater | RecordWarrant module-attribute

OpKind

Bases: StrEnum

SUPERSEDE = 'supersede' class-attribute instance-attribute

ATTACH_DEFEATER = 'attach_defeater' class-attribute instance-attribute

RECORD_WARRANT = 'record_warrant' class-attribute instance-attribute

Supersede dataclass

The dialogue concluded conclusion, which replaces claim in the active projection; the warrant is why. claim is an existing claim id (a note digest or an artifact id); conclusion is the new position's text, minted as a DERIVED interpreted claim on apply (never an authored peer).

anchors are the warrant's K₀-reaching authored digests — the grounding C′ actually rests on (Item 9; supersession-lifecycle.md §4.2). A DialogueAnalyzer supplies them (surviving grounding of C + the dialogue's new evidence). Left empty, apply falls back by C's type: a DERIVED C inherits its surviving authored leaves (leaf_refs, never [C] — a derived claim decays or is superseded without a verdict, so routing grounding through it is unsafe); an AUTHORED (K₀) C grounds on [C] itself (bedrock — authored content does not decay, so g=1 and the revision is not weightless). The discredited claim is refused as grounding only when derived.

claim instance-attribute

conclusion instance-attribute

warrant instance-attribute

anchors = () class-attribute instance-attribute

AttachDefeater dataclass

claim is contested by defeater; the claim stays but is marked (not silently dropped).

claim instance-attribute

defeater instance-attribute

RecordWarrant dataclass

The warrant links the claims in links (typically the initial and final claim).

warrant instance-attribute

DialogueAnalyzer

Bases: Protocol

Turns a dialogue's text into operations — a MODEL task. Injected; the default emits none.

analyze(text)

ClaimOp dataclass

op_id instance-attribute

kind instance-attribute

claim_id instance-attribute

related_id instance-attribute

text instance-attribute

at instance-attribute

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

ApplyReport dataclass

superseded instance-attribute

conclusions instance-attribute

defeaters instance-attribute

warrants instance-attribute

stale = () class-attribute instance-attribute

no_op_analyzer(_text)

The deterministic default (build plan PD4 floor): a dialogue emits NO operations → the existing document-only ingest, byte-for-byte unchanged. A real deployment injects a model-backed analyzer that proposes operations for owner/verdict review.

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)

core.runtime

Sealed-core runtime entrypoint.

Any Zone-A process starts here. It installs the egress guard FIRST (Invariant 1), so "the core has no egress" holds before any work runs, then wires the Zone-A services (models, telemetry, vitals) and exposes an agent factory seam. Sealing is done here — explicitly, at startup — rather than as an import side effect, so importing core modules for tests/tools is free of surprises.

Core dataclass

config instance-attribute

models instance-attribute

telemetry instance-attribute

vitals instance-attribute

constitution_fingerprint instance-attribute

make_agent(name, role_prompt, tier='routine')

Mint a trivial agent bound to the model server. Every agent inherits the Constitution via core.constitution.frame_context (Invariant 6).

bootstrap(config=None)

core.sealing

Structural network-egress guard for Zone A (the sealed core).

Invariant 1 (BUILD-SPEC §3): the sealed core has zero network egress, enforced structurally, not by convention. The ideal enforcement is OS-level — a separate network namespace with --network=none, or a pf anchor denying outbound for the core's uid — and that is the deployment-time hardening layer (see docs/runbook.md).

On bare macOS during development we cannot hand the core a Linux netns, so this module is the in-process structural layer: a fail-closed guard, installed process-wide, that intercepts every socket connect/connect_ex and permits ONLY loopback destinations (127.0.0.0/8, ::1) plus AF_UNIX. The single legitimate "egress" the core makes is to the local Ollama model server on 127.0.0.1 (BUILD-SPEC §5, §7) — a loopback IPC channel, not egress to the outside world. Any attempt to reach a non-loopback address raises SealedCoreEgressError before a packet leaves.

Defense in depth: neither layer alone is the whole story (a native extension opening its own socket could bypass a Python-level guard), but the guard fails closed, is process-wide, and is verifiable in tests — and it composes with the OS-level layer at deployment. The guard is installed by the core runtime entrypoint (core.runtime), never as an import side effect.

DNS note: resolving a hostname is itself egress (it leaks intent to a resolver). The only host the core dials is an IP literal (127.0.0.1), so name resolution is never required inside the walls — we therefore refuse to connect to any non-loopback or non-literal host rather than resolve it.

SealedCoreEgressError

Bases: RuntimeError

Raised when sealed-core code attempts to connect to a non-loopback address.

is_egress_allowed(family, address)

Pure decision: may a socket of family connect to address?

AF_UNIX (local IPC) is always allowed. AF_INET/AF_INET6 are allowed only to a loopback host. Everything else is denied. Side-effect free, so it is unit-testable without touching a real socket.

seal()

Install the process-wide egress guard. Idempotent.

Call once at core-process startup, before any work. After this returns, any connect() to a non-loopback address raises SealedCoreEgressError.

is_sealed()

assert_sealed()

core.selfcheck

The Constitution pre-return self-evaluation check (BUILD-SPEC §4, §15; Constitution §IV).

Every agent runs this before returning output (Invariant 6 / §IV). Two layers, matching the Constitution's own mandate:

  • Deterministic, always-on (cheap): verify the output is grounded — every note it cites must resolve to a source that was actually retrieved. "A cited identifier that does not resolve is a failure" (Constitution §III.1). This is the concrete, deterministic check the spec explicitly calls for.

  • Subjective (mirror-vs-oracle, overclaimed certainty, consequential deference): these are NOT decided by brittle keyword matching. The spec is emphatic (§4, §15): a small-model judge, A/B'd against a known-good baseline snapshot, never scored cold. The baseline/anchor machinery is part of the safety fixed point and lands with the self-modification loop (Phase 10). Until then this module exposes a judge seam; when no judge is wired these dimensions are reported as deferred — honestly not-yet-evaluated, never silently passed off as conforming.

passed is True iff there are no FAIL findings; a deferred finding does not fail.

PASS = 'pass' module-attribute

FAIL = 'fail' module-attribute

DEFERRED = 'deferred' module-attribute

Sources = 'set[str] | Iterable[Source]' module-attribute

SubjectiveJudge = Callable[[str], 'list[Finding]'] module-attribute

Source dataclass

A citable retrieved note (gap G1). digest is the STABLE citation identity — the raw-store content id, which never drifts; title is only the human-facing label the model writes in [[...]]. Grounding decidability rests on the digest: Cit(A) ⊆ Ret is well-posed exactly when each cited title resolves to a single retrieved digest. A title that resolves to two distinct digests is an ambiguous (ill-posed) citation, which the grounding check now surfaces instead of silently — and arbitrarily — accepting it.

Callers may still pass a bare set of titles (the legacy path, where each title is taken as its own stable id); pass Sources when digests are available (librarian, dreamer) to get the well-posed, collision-detecting check.

title instance-attribute

digest instance-attribute

Finding dataclass

directive instance-attribute

status instance-attribute

detail = '' class-attribute instance-attribute

SelfCheck dataclass

Result of the pre-return Constitution check (§IV).

passed instance-attribute

findings = () class-attribute instance-attribute

notes property

Compact summary, e.g. ("grounded-citations:pass", "mirror-not-oracle:deferred").

failures()

check_grounding(output, sources)

Deterministic: every [[cited]] note must resolve to exactly one note that was actually retrieved (Constitution §III.1, "a cited identifier that does not resolve is a failure").

Decidability rests on a STABLE identity — the content digest (gap G1). A cited title is: * unresolved (FAIL) if it matches no retrieved note; * ambiguous (FAIL) if it matches two distinct retrieved digests — a title-only citation that cannot be decided, now surfaced rather than silently accepted; * grounded (PASS) if it resolves to exactly one retrieved digest.

sources=None means there was no retrieval context (e.g. a generic agent), so the check is not applicable and is reported as deferred rather than failing.

grounding_score(support, authored)

g(κ) ∈ [0,1] (§6/§8): the fraction of a claim's support refs that resolve to AUTHORED leaves — the dream adjudicator's ranking currency (R1). This extends the binary grounding predicate above into a score: the currency is resolvable evidence, never rhetoric.

A ref that does not bottom out in authored ground (e.g. a prior-dream/interpreted id) contributes 0 — the formal "chains may not close within INTERPRETED" rule (Invariant 10). g = 1.0 means every support ref is authored (the only case the R0/R1 panel produces, since its inputs are a MirrorView); g < 1 is the recursion guard biting once R3 exists. Empty support is ungrounded (0.0): a claim with no evidence is not a grounded claim.

self_evaluate(output, *, sources=None, judge=None)

Run the pre-return Constitution check.

sources: the legitimate citation targets. Pass a list of Source (title + stable digest) for the well-posed, collision-detecting grounding check (gap G1); a bare set of titles still works (legacy). Omit it (None) for an agent with no retrieval context. judge: optional small-model judge for the subjective directives; when absent those directives are reported as deferred (Phase 10 wires the judge + baseline).

core.sensing

Core side of the sensing handoff (Track G, item G3 — read-only hands, β = 0).

Sensing hands are just new sensors: sandboxed fetch → de-identified request → observed-tier view. This module is the sealed-core half of that path; the fetch itself lives in edge/effectors/sensing.py (Zone B), which never imports this module — the wire shapes below are mirrored, not imported, exactly like the research airlock (edge/bridge/protocol.py):

<handoff>/requests/<id>.json       = de-identified sense request  (core writes → edge serves)
<handoff>/observations/<id>.json   = the sensed result            (edge writes → core reads)

Two boundaries, both structural:

  • OutboundSenseRequest is the only thing that crosses toward the network, and it cannot carry note content or an address: terms pass the SAME conservative scrubber as airlock criteria (core.research.criteria.clean_term — one policy, one implementation), and upstream is a short NAME into the edge-side reviewed allowlist, shaped so a URL, host, or path is unrepresentable. Even a reasoner steered by poisoned content cannot express "fetch attacker.com" here — there is no field that can hold it (the confused-deputy answer, made structural).
  • Inbound — a SensedObservation is third-party exhaust. to_row() stamps provenance observed with NO parameter (the DerivedStore move: a sensing result physically cannot claim to be authored), and the only typed container for these rows is ObservedView, whose constructor refuses anything else. MirrorView refuses observed rows (I6), so the two views PARTITION the tiers: sensing output provably cannot reach the authored mirror or the §15 baselines. The Track-D correlator is the intended consumer of ObservedView; until it lands, the view is the seam it will read.

The result type deliberately has no actuator field (§3): an observation cannot be replayed or confused into an action — data comes back, effects never do.

Dispatch through SensingHandoff.emit requires an Effect, and admission is the construction of an EffectView at ceiling SENSING (ε = 0): a reversible or irreversible effect offered to this surface raises before anything touches the handoff — the §4 filtration enforced by type, not review.

REQUESTS = 'requests' module-attribute

OBSERVATIONS = 'observations' module-attribute

EffectorsDisabled

Bases: RuntimeError

[effectors] enabled is false — the sensing surface refuses to build (fail-closed).

SenseRequest dataclass

The de-identified sense request — the ONLY thing that crosses toward the effector.

By construction it carries no note content and no address: a registered actuator name, a short upstream NAME (resolved to a URL only inside the edge zone, against the owner's reviewed allowlist), and scrubbed topical terms. to_request() is the exact wire payload.

actuator instance-attribute

upstream instance-attribute

terms = () class-attribute instance-attribute

id = field(default_factory=(lambda: uuid4().hex)) class-attribute instance-attribute

assert_clean()

Re-validate at the trust boundary (defense in depth) — a hand-built request cannot bypass the scrubber on its way out. Raises DeidentificationError on any violation.

to_request()

The outbound wire payload — de-identified fields only, no corpus content.

SensedObservation dataclass

One sensed result — third-party exhaust, observed-tier by construction.

Deliberately has NO actuator field (§3): a sensing result cannot express or be confused into an action, the same way ResearchCriteria cannot carry a note. error is the effector's honest refusal/failure note ("" = a real body came back).

request_id instance-attribute

upstream instance-attribute

ts instance-attribute

body instance-attribute

error = '' class-attribute instance-attribute

from_dict(d) classmethod

to_row()

The observed-tier row. Provenance is HARDCODED — there is no parameter, so no caller can launder a sensed result into an authored (or any other) class; the only way this data enters a store or view is wearing observed (I5's unforgeability, reflected).

NonObservedRowError

Bases: ValueError

A row whose provenance is not observed was offered to an ObservedView.

ObservedView dataclass

The observed-tier projection — the assistant-tier read boundary, dual to MirrorView.

Every contained row is guaranteed provenance == observed; the type itself is the proof, so the Track-D correlator (this view's intended consumer) inherits "I read exhaust, never the owner's ground truth" — and conversely MirrorView refuses these same rows, so the two views PARTITION the tiers with no representable overlap (Invariant 6, both directions).

from_observations(observations) classmethod

The sanctioned constructor: rows minted by to_row() (observed-stamped, no parameter); __post_init__ re-checks, so even a buggy caller cannot smuggle a differently-labeled row past the type (fail-closed).

rows()

The observed rows (a fresh list; the view is immutable).

SensingHandoff dataclass

Core-side handoff for sensing (airlock-shaped). The sealed core never opens a socket: it writes a clean request into the handoff and later reads the observation back; the edge effector is the only thing that touches the network, and it never reads the vault.

handoff instance-attribute

emit(request, effect)

Write one sense request to the handoff. Returns the request id.

Admission IS the construction of an EffectView at ceiling SENSING (ε = 0): an effect of any acting class raises CeilingExceededError here, before anything reaches the handoff — this surface can express read-only sensing and nothing else (§4 filtration). The request is re-scrubbed at the boundary (assert_clean), mirroring the airlock.

collect(*, consume=True)

Read every available observation (and by default consume the files). The returned objects are observed-tier by construction — hand them to ObservedView, never to a mirror path (which would refuse them anyway; that refusal is tested, not assumed).

sense_request(actuator, upstream, terms=())

Build a SenseRequest from proposed (possibly model-suggested) inputs, conservatively: un-cleanable terms are dropped; malformed actuator/upstream names raise. Unlike the airlock builder, an EMPTY term list is legal — many sensors (weather, a status endpoint) take no query at all — but if terms were proposed and none survive scrubbing, that is a refusal, not a silent downgrade to an unqueried fetch.

build_sensing_handoff(config=None)

Wire the core-side sensing handoff. REFUSES (fail-closed) unless [effectors] enabled — the whole Track-G surface is OFF by default; a fresh clone cannot emit a sense request until the owner deliberately turns the flag on.

core.vitals

System-vitals emitter (BUILD-SPEC §8, §9 reactive tier).

"The system is itself a sensor source." At launch this is system vitals only — memory headroom, CPU, load, process RSS — written into the DuckDB telemetry store. Queue depth, model-load time, error rates, and cron durations join as those subsystems come online (Phases 3+). Body/health sensors are a deferred, dormant adapter (§20.6).

This is deterministic measurement, not a model (the reactive tier escalates to a model only when a threshold is crossed — that logic arrives with the scheduler in Phase 3).

Reading dataclass

metric instance-attribute

value instance-attribute

unit instance-attribute

labels = None class-attribute instance-attribute

VitalsCollector dataclass

writer instance-attribute

source = 'vitals' class-attribute instance-attribute

collect_once()

Sample current system vitals and write them to telemetry. Returns the readings written (so callers can verify the path end to end).

collect_system_vitals()