| doc | DETAILS | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| package | LocusKit | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| repo | moot-memory | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| authored_commit | daa855b9e43c6978d8481561f1e073533059735b | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| authored_date | 2026-07-23 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sources |
|
DrawerStore now seeds its HLC generator from wall time on open. That keeps new audit stamps after already committed events across a restart.
Drawer inserts and updates refresh content_fingerprint.
The read path fails loudly if the stored fingerprint is missing or malformed.
Estate.activeDrawersAfter exposes bounded paging for backfill jobs.
addTunnel copies the maximum endpoint sensitivity into the tunnel bitmap.
DatasetHandleContent links a drawer to one typed data table.
captureDatasetHandle is the only path that makes this drawer kind.
The handle can store table and column proof hashes.
An erase can drop the table and add an audit event.
Active tunnel reads require active state and a clear retired bit. They also apply the drawer recall sensitivity ceiling. Tunnel review only accepts proposed edges. Association inserts ignore an existing stable endpoint pair.
This document walks through every source file in the package. Read
OVERVIEW.md first for the big picture. Files appear here in
pipeline order. First come the module surface and the estate
lifecycle. Next come the nine verbs and their frame types. Then comes
the storage engine. After that comes the drawer value type and its
shared bitmap machinery. Next comes the recall filter and its
evaluator. Then come the remaining eight noun types and their bitmap
decoders. After that comes the containment tree and the bundle
algebra. Next comes the fingerprint and integrity machinery. Then
comes the manifest and setup data. Next come the recall-result
and trace types. Telemetry comes last.
This file defines the data drawer payload. It stores the table ID, columns, row count, and source note. Empty proof fields allow later work without a schema change.
captureDatasetHandle creates the correct dataset content kind.
It sets a private export rule and the chosen sensitivity.
Data handles do not enter the vector path.
findDatasetHandles finds all handles for one table ID.
patchDatasetHandleSignatures updates the proof fields.
drawerById supports the erase cascade before content is zeroed.
appendAuditEvent logs the table drop beside the drawer erase.
This file provides the module surface. It carries a doc comment describing the package. That comment points to the files holding the real public types. LocusKit has no module-level namespace enum of its own, unlike some sibling libs. Every public type lives in its own file. This file exists only to orient a reader arriving at the package for the first time.
This file provides LocusKitError, the single error enum every
LocusKit method throws. Centralizing every failure mode in one type
lets a caller recover in exact ways. For example, a caller can treat a
missing drawer as a routine miss. It can still propagate a genuine
SQLite failure.
The cases split into three groups. The not-found group names a
missing row by id: drawerNotFound, tunnelNotFound,
diaryEntryNotFound, and recallTraceItemNotFound. The storage group
describes the storage layer misbehaving: databaseUnavailable,
sqliteError, schemaTooNew, and corruptStoredValue.
corruptStoredValue names the table, column, and raw stored text. So
a caller can locate and repair the offending row, rather than receive
a fabricated default value. The domain group describes LocusKit's own
validation refusing a write: invalidContent, disciplineViolation,
and notSupported. invalidContent carries a human-readable rule
violation. Tests assert on this message text, as a validation-test
contract. disciplineViolation names an illegal state transition or
forbidden bitmap combination. It uses the raw integer state values,
rather than the State enum. So the error type does not need to
depend on Adjectives.swift.
LocusKitError.description renders each case as plain English,
without Swift's EnumCase(...) noise. GeniusLocusKit consumes this
description at its boundary. A caller there parses the English text
back out, to decide how to react to a rejected write. So the wording
is a soft contract, not just a log message.
This file provides LocusKitSchema, the entire on-disk schema. The
schema is declared as data, not as SQL text. Storage.open(schema:),
a PersistenceKit primitive, reads this declaration. It creates every
table, generated column, and index from it. No file in LocusKit
issues a raw CREATE TABLE or CREATE INDEX string. A hand-rolled
SQL implementation used to do this. This declarative approach
replaced it. So the schema is portable across SQLite, PostgreSQL, and
an in-memory test backend. It needs no three copies of the same DDL.
LocusKitSchema.schema assembles seventeen tables. Fourteen of them
are: drawers, tunnels, diary, manifest, kg_facts,
proposals, associations, learnedReferences, source_catalog,
node_bundles, container_fingerprints, recall_trace, keys, and
nodes. Three more tables come from two sibling schema fragments:
ErasureLedgerSchema and SnapshotSchema. Every persistent noun
table carries a nullable .json column named ext. This column is a
forward-compatible extension slot. It absorbs an unforeseen future
field, without a schema migration. Version 1.0 writes NULL into it
and never reads it. LocusKitSchema.version is currently 8. The
file's header comment documents each version bump. Bumps include a
BLOB Merkle root, a nullable content_hash column, and the nodes
table, among others. The comment states plainly that there is no
incremental migration ladder. No estate data has shipped under an
earlier version.
The file's most distinctive feature is its generated columns.
g_state_cluster on drawers is one example. It is computed as
adjectiveBitmap & 0x3F. A generated column is a column whose value
the backend derives on its own. The backend computes it from an
expression over other columns in the same row. So a generated column
can be indexed like an ordinary column, without the application
computing and storing it by hand. LocusKitSchema.indices then
declares an ordinary index on each generated column. This turns a
hand-written functional index into a declarative, backend-portable
statement. The old form looked like CREATE INDEX ... ON drawers (provenance & 0xF). The file's header comment also documents a
bitmap reservation map. This map shows which bit ranges of each Int64
bitmap column are assigned, and which are free headroom. So adding a
future flag means claiming a documented free bit, not running a
schema migration.
This file provides LocusKitVocabulary, LocusKit's contribution to
the write gate's vocabulary. A vocabulary here is the set of legal
values a bitmap field may take. It is the fixed rulebook the write
gate checks every proposed value against. A value outside the
vocabulary is refused before it ever reaches storage.
LocusKitVocabulary.unionSlots declares one FieldSlot per
operational and provenance field LocusKit defines. These fields
include capture channel, content kind, feature flags, and source
type. Each slot names its bitmap column, bit position, and width. An
enumerated field's slot also lists its legal raw values. The
adjective axis's vocabulary covers state, sensitivity, exportability,
and trust. The write gate itself supplies that vocabulary, from
SubstrateLib, not this file. unionSlots is purely LocusKit's own
consumer-side addition. LocusKitVocabulary.frozen() compiles this
set into a Vocabulary value once, at estate open, through
VocabularyValidator.freeze. Every later write in the estate's
lifetime is checked against this frozen vocabulary. It never changes
after that first freeze.
This file provides five small supporting types used across the
estate surface. RowID is a plain String type alias. It names a
row's stable identifier. FrameFilteredDrawers is the result of a
frame-aware by-id load. It pairs the frame-admissible drawers with
the full set of ids that physically loaded. OwnerCredentials is the
estate owner's identifier. It is validated non-empty at open or
create time. LatticeAnchor is the four-field classification anchor
every drawer carries. It holds a UDC code, plus optional Wikidata
enrichment. EstateError is the error enum for Estate lifecycle
failures, distinct from LocusKitError.
FrameFilteredDrawers.loadedIDs is reported apart from
admissible, for a specific reason. A caller needs to tell two cases
apart. The first case is an id that loaded but failed the frame's
filter, a legitimate drop. The second case is an id that never
loaded, a transient or partial read that must be treated as degraded,
not silently dropped. LatticeAnchor.udc(_:) is a convenience
constructor. It handles the common case of a bare UDC code with no
Wikidata enrichment, the shape most content has before an enrichment
daemon has run over it. EstateError covers three failure shapes.
substrateUnavailable means the backing storage failed to open.
manifestMismatch means a stored manifest value does not match what
this build expects. Most importantly, the bitmap layout version must
match. emptyOwnerIdentifier is raised before any storage call. So a
caller gets a structurally distinct error, rather than a generic one.
keychainError covers a failing Keychain call during identity key
handling.
This file provides Estate, the single public entry point to a
LocusKit-backed memory store. An Estate is an actor. An actor is a
Swift type that serializes access to its own state. So concurrent
callers can never interleave two operations against the same estate
unsafely. Estate owns one DrawerStore, the storage engine
described below. It owns one ContainerFingerprintStore, the
recall-pruning totals. It owns one NodeStore, the containment
tree. It is the only public surface most callers should ever touch.
Estate.open(storage:owner:identityKeyStore:) opens an existing
estate. It refuses to open a database whose manifest carries a
different bitmap_layout_version than this build expects. Bitmap bit
positions are part of the durable on-disk contract. So opening a
database written by an incompatible future schema would silently
misread every bitmap field. On first open, it also mints a fresh
Curve25519 Ed25519 keypair for the estate's federation identity. The
public half is written to the manifest, safe because a public key has
no confidentiality requirement. The private half is handed to an
injected EstateIdentityKeyStore, the Keychain in production, an
in-memory store in tests. It is cached for the lifetime of the
Estate instance. Estate.create(storage:owner:manifest:) is the
sibling constructor for a brand-new estate. It does not mint the
identity keypair, because that only happens on the first open after
creation.
The bulk of the file is a set of typed pass-through reads. These
exist so higher kits, chiefly GeniusLocusKit, never need to import or
construct a DrawerStore. They include allDrawers,
getDrawers(ids:), hydrateBodies, tunnelsFromWing,
recentRecallTraces, allTunnels, retireTunnel,
pruneRecallTraces, countDrawerRows, allProposals,
allAssociations, allLearnedReferences, allKGFacts,
allDiaryEntries, and resolveNodeNames.
getDrawers(ids:matchingFrame:hydrationLevel:) is the most elaborate
of these. It runs the exact same frame-filtering pipeline recall
uses, but over a caller-supplied id set rather than a full corpus
scan. This is the shape a dense-first candidate-pool search needs.
meta(key:) and setMeta(key:value:) expose the estate manifest as
a general per-estate key-value store. Higher kits can use it for
durable state of their own, on the condition that they namespace
their keys to avoid colliding with LocusKit's own typed manifest
keys.
This file provides Estate's audit and history API:
auditTrail(rowID:) and bitmapState(rowID:asOf:). Both delegate to
the row's sealed audit log, rather than to any separate history
table. The audit log, not the live row, is the estate's source of
truth for what happened and when.
auditTrail(rowID:) returns every sealed AuditEvent for a row in
HLC order. An HLC, or hybrid logical clock, is a timestamp format. It
puts events in one order, even across machines with their own
clocks. bitmapState(rowID:asOf:) rebuilds what a row's three
bitmaps looked like at an earlier point in time. It does this by
folding the row's audit log forward, from the beginning up to and
including the requested HLC. This folding runs through SubstrateLib's
AuditLogFold.projectStateAt. This is what lets a caller ask what a
drawer's state looked like a week ago. LocusKit needs no separate
append-only history table alongside the live row for this. The live
row is redundant with the log by construction. The log is the only
place that needs to remember the past.
This file provides the protocol and two implementations for persisting an estate's Ed25519 private signing key outside the manifest table. The manifest table is ordinary, unencrypted metadata. Anyone with database or backup access can read it. So the private half of the estate's federation identity keypair must never be written there. Only the public half is safe to store in the manifest.
EstateIdentityKeyStore declares two methods: loadPrivateKey and
storePrivateKey. Both are keyed by the estate's UUID.
KeychainEstateIdentityKeyStore is the production implementation. It
stores the raw key bytes as a kSecClassGenericPassword Keychain
item. The item stays accessible after the first device unlock
following a restart. It is never synced to iCloud Keychain. This
matches the same device-bound posture as the estate's own SQLite
file. InMemoryEstateIdentityKeyStore is the test double. It is a
plain dictionary guarded by a lock. It carries a _storedPrivateKey
accessor. A test can use this accessor to check what was persisted,
without touching the real Keychain. The real Keychain would require
entitlements. It would also pollute state across test runs.
This file provides the nine verb methods as an extension on Estate:
capture, captureBatch, recall, withdraw, expunge, mutate,
reanchor, propose, learn, and associate. It is declared as a
separate file for a specific reason. It needs to reach Estate.store,
which is declared internal rather than private in Estate.swift
for exactly this reason.
capture(_:CaptureFrame) is the drawer entry point. It validates
that content and room are both non-empty. It also validates the
lattice-anchor UDC code, the actor, and the embedding-model id. It
assembles the three bitmap
columns from the frame's named fields, through BitField.writeField,
never hand-rolled shift-and-mask arithmetic. It resolves the target
wing and room to containment-tree node ids, creating them on demand.
It writes the drawer through addDrawerCovered, the one sanctioned
internal chokepoint. That chokepoint bundles the row insert with the
container-fingerprint update. So a drawer can never be captured
without its fingerprint total being updated in the same call.
captureBatch(_:) is the bulk-import sibling. It resolves all wing
and room node ids up front, with a per-call cache. It batches every
fresh, non-superseding insert into one storage transaction, through
DrawerStore.insertFreshBatch. It defers the Merkle rollup entirely.
This turns a 40,000-drawer import from roughly 34 minutes of per-row
commits into roughly 30 seconds.
capture(_:TunnelCaptureFrame) is the tunnel entry point. capture
is legal on exactly two nouns, drawer and tunnel, and this overload
handles the second. recall(_:RecallFrame) is the read path. It
reads the live, non-tombstoned, candidate rows through liveRows. It
runs them through BitmapEvaluator.evaluate. It optionally writes a
bounded number of recall-trace rows, only when the caller opts in
through frame.traceLimit. So internal and bulk-export scans never
accumulate trace rows. recall returns a RecallStream. Both
liveRows and the evaluation step surface any internal read failure
as a named degraded stage on the returned stream, rather than
silently returning an empty result. This is the mechanism by which a
caller can tell a genuinely empty estate from a recall that failed
partway through.
withdraw(rowID:reason:) moves a drawer's state to .withdrawn,
through mutateState. This is the only correct path, because a state
transition must go through the automaton's legality check.
expunge(rowID:reason:confirmation:now:) is the destructive delete.
It requires an explicit confirmation: true. It walks the drawer's
full lineage chain, every version sharing its lineageID, so a hard
delete scrubs every historical version. It always seals a sealed
audit event recording the fact of the expunge, even though the
content itself is gone. expungeReturningUnsealedEvent and the
paired sealExpungeAudit and sealExpungeOrphanAudit methods exist
for GeniusLocusKit's two-step orchestration. There, a cross-kit
vector delete must happen between the storage tombstone and the
audit seal. Splitting the seal out prevents any caller from
suppressing the audit event by accident.
mutate(rowID:kind:payload:) dispatches on MutationKind to move a
drawer along one of three axes. The first axis is confirmation,
through mutateProvenance. The second is state, through
mutateState. The caller-facing guards for .resolve, .accept,
and .revive are implemented here. This happens before the store's
automaton check even runs. So the error message is clearer. The third axis is
an adjective field, sensitivity, trust, or exportability, through
mutateAdjective. The .revive case is the most elaborate. It
implements the full per-source-state legality table from the
cookbook's revive rule. This includes the living-successor lineage
check. That check refuses to revive a superseded row while a later
version in the same lineage is still alive.
reanchor(rowID:toRoom:toWing:toLattice:) moves a drawer's room,
wing, and lattice position, without touching its bitmaps.
propose(_:now:) and associate(_:now:) build a Proposal or
Association from their respective frames. Each derives its lattice
anchor from a specific source. For propose, the anchor comes from
the target row. For associate, it comes from the first endpoint.
Neither ever fabricates an anchor. learn(_:now:)
catalogs the frame's source, once, keyed by handle. It writes a
LearnedReference anchored to that catalog entry's genuine lattice
position, never a sentinel anchor invented from a bare handle.
seedWing(_:hint:addedBy:embeddingModelID:now:) files one ordinary,
fully recallable drawer per default wing, at estate provision time.
This gives a fresh agent a plain-language description of what each
wing is for.
This file provides the frame structs that carry a verb's arguments:
CaptureFrame, TunnelCaptureFrame, RecallFrame, LearnFrame,
ProposeFrame, and AssociateFrame. It also provides three
supporting enums: MutationKind, HydrationLevel, and Ordering.
Every field on
every frame is named after a domain concern, a capture channel, a
sensitivity tier, a filter chain. No raw bitmap value or bit position
crosses the public verb boundary anywhere in this file.
CaptureFrame carries every named axis a captured drawer's three
bitmaps encode: channel, sensitivity, kind, and the matching
provenance-side channel, source type, sensitivity, confirmation, and
confidence. It also carries lineage, room, lattice anchor, actor, and
embedding model. It carries an optional event time too, for backdated
bulk ingestion. It also carries feature flags, exportability, and an
optional wing. Nearly every field defaults to the value that produced
the same all-zero bitmap a caller got before that field existed. So
adding a new capture-time axis stays source-compatible with every
existing caller. RecallFrame carries the filter chain, hydration
level, page-size limit, and ordering. It also carries an optional
past asOf HLC and traceLimit.
traceLimit is the opt-in recall-trace write count.
MutationKind enumerates the mutate verb's seven cases: .confirm,
.reject, .contest, .resolve, .supersede, .revive, and
.accept. Three more cases carry a new value: .correctSensitivity,
.correctTrust, and .correctExportability. HydrationLevel names
the three read-cost tiers: .bitmapOnly, .structured, and .full.
Ordering names the three result orderings recall supports. A
relevance ordering is absent on purpose. LocusKit has no scoring
signal of its own. That signal lives in VectorKit, composed on top by
GeniusLocusKit. The file's comment explains that shipping a
.byRelevanceDesc case here, without a real relevance signal, would
be an honesty violation.
This file provides DrawerStore, the actor that owns every table in
the schema. It implements the full CRUD and mutation surface for all
nine nouns. It is the largest file in the package. Almost every write
in the estate lands here in the end.
DrawerStore.init(storage:hlc:) opens the schema. This is
idempotent: a second open on an existing database is a no-op for
tables and indices. It freezes the write-gate vocabulary once. It
populates the v1 manifest defaults, writing each key only if absent,
so a first-open value is never overwritten by a later open. It
classifies the manifest's estate_uuid value into one of three
outcomes. The first outcome is absent: a genuinely fresh estate,
resolved to a freshly minted UUID. The second outcome is present and
well-formed, the normal case. The third outcome is present but
unparseable. This third outcome throws corruptStoredValue, rather
than silently fabricating a random replacement UUID, because
conflating corruption with a fresh estate would mask real data loss.
The HLC clock's node id is derived from that same classified value,
by hashing the raw stored text. So a standalone estate gets a stable,
estate-specific clock identity. A Rust port of the same estate
derives that identity identically.
The gated write path is the file's central pattern. It is used by
addDrawer, mutateProvenance, mutateAdjective,
mutateOperational, mutateState, expungeGated, and
reanchorGated. Each of these reads the row's current bitmaps inside
a serializable transaction. Each decomposes the proposed new value
into per-field FieldWrites, never a whole-column replacement. This
is what lets the gate validate each field on its own. It also lets
the gate check the forbidden-combination invariant on the merged
result. Each calls AuditGate.admit, a SubstrateLib primitive. On
success, each writes both the merged projection column and the
sealed AuditEvent, in the same transaction. gatedColumnWrite is
the shared helper behind the three whole-column mutators. It excludes
the adjective bitmap's state field from the field list, because
state is verb-driven and must never move through a field edit.
mutateState also checks the verb against the caller's requested
target state. Both must agree with what the automaton's transition
table produces. The verb itself is a RowVerb value. So an illegal
from-via-to triple is rejected before any row is touched.
addDrawer implements the supersession cascade. Sometimes the new
drawer's lineageID matches an active predecessor.
findActivePredecessor finds this match, through an indexed query on
the generated state-cluster column. When a match exists,
addDrawerWithCascade inserts the successor and a .supersedes
tunnel in one transaction. So a failed tunnel insert never leaves an
orphaned successor row. addDrawerWithCascade then separately flips
the predecessor's state to .superseded, through the normal gated
mutateState path. insertFreshBatch is the bulk-import sibling used
by EstateVerbs.captureBatch. Every drawer in the batch already has
no active predecessor. The caller pre-verifies this before the batch
runs. Each drawer is inserted and gated inside one single
transaction. This eliminates per-row commit overhead.
expungeGated is the lineage-wide hard delete. It tombstones the
target drawer through the gate. It zeroes the drawer's content. It
sets the dreaming_recalc_required obligation flag. It records the
event in the erasure ledger. It then repeats a content scrub across
every other member of the lineage chain. Where the state transition
is legal, it also repeats a full gated tombstone. A sibling whose
state cannot legally transition to tombstoned, an audit-grade
accepted row forbidden by invariant S-3, still has its content
zeroed no matter what. The destruction contract is stronger than the
state machine. Leaving verbatim content behind, when the gate refuses
the state flip, would be a privacy violation. sealExpungeAudit,
sealExpungeOrphanAudit, and sealExpungeOrphanForSweep support
GeniusLocusKit's two-step orphan recovery. This recovery matters when
a crash separates the storage tombstone from its audit seal.
tombstonedRowsWithoutExpungeAudit is the query the recovery sweep
uses to find rows stuck in that crash window.
The remainder of the file provides CRUD for the other eight nouns.
Tunnel CRUD includes addTunnel, getTunnel, tunnelsFrom,
tunnelsTo, and allTunnels. It also includes the tunnel-retirement
pair retireTunnel and unretireTunnel. It includes the outline
helpers outlineChildren, outlineAncestors, and reparentDrawer.
Fact CRUD
includes addKGFact, getKGFact, kgFacts(forDrawerID:), and
withdrawKGFact. Proposal CRUD includes addProposal, getProposal,
and proposals(forTargetRowID:). Association CRUD includes
addAssociation, getAssociation, associationsFrom, and
associationsTo. Learned-reference CRUD includes
addLearnedReference, getLearnedReference, and
learnedReferences(forSourceCatalogID:). Source-catalog CRUD includes
addSourceCatalogEntry, getSourceCatalogEntry, and
sourceCatalogEntry(forHandle:). Diary CRUD includes addDiaryEntry,
getDiaryEntry, and readDiary. The file also provides the
recall-trace surface: insertRecallTrace, insertRecallTraces,
recentRecallTraces, markRecallTraceUsed, markRecallTracesUsed,
pruneRecallTraces, and countRecallTraces. It provides the
manifest surface: setMeta, getMeta, and readManifest. It
provides the node-tree name-resolution helpers: roomNodeIdsInWing,
roomNodeId, and resolveNodeNames. It provides the summary
surface: listWings, listRooms, and taxonomy. It provides the
temporal-read surface: fingerprintsCaptured(in:) and
fingerprintBitSeries. These feed the FFT-based rhythm-spectrum and
moment-summary lenses built on top of LocusKit.
Every row-decode function follows the same resilience pattern.
drawerFromRow and its siblings throw corruptStoredValue for a
clearly malformed stored value, such as a non-UUID lineageID
or an unparseable timestamp. Corpus-scan callers instead route
through decodeDrawerRowsSkipCorrupt. This function logs and skips a
corrupt row, rather than aborting an entire estate-wide scan. Point
lookups fail loud. Corpus scans degrade gracefully.
This file provides Drawer, the value type behind verbatim content.
A Drawer is the thing callers file, and the thing recall returns.
Its content field is preserved unchanged, with no truncation and no
normalization. The verbatim-first contract requires retrieval to
surface exactly what was filed.
Every drawer references its containing room by parentNodeId, a
foreign key into the nodes table, rather than storing wing and room
names. Display names are resolved separately, through
DrawerStore.resolveNodeNames. filedAt and eventTime are two
distinct clocks. filedAt is the ingest clock. It marks when the row
entered the local store. It is monotonic, and it anchors audit
ordering. eventTime is when the content happened, or was
authored, in the world. For streaming capture the two clocks
coincide. A bulk backfill importer instead supplies a real,
possibly much earlier, eventTime. The three bitmap fields,
provenance, adjectiveBitmap, and operationalBitmap, all default
to zero. The four lattice fields are udcCode, udcFacets,
wikidataQID, and wikidataQidsSecondary. They locate the drawer in
the classification lattice. udcCode is required non-empty at every
capture path, per invariant I-5. Drawer's custom Codable
conformance backfills a missing eventTime to filedAt on decode. So
a row encoded before the eventTime column existed still decodes to
a sensible value.
This file provides the derivation of a drawer's 256-bit structural fingerprint. This fingerprint is the coordinate system LocusKit uses for structural similarity and for recall pruning. A fingerprint is built from four 64-bit SimHash blocks. Each block is a projection of one facet of the row, through its own family of random hyperplanes. The first block is bitmap: the three bitmap columns. The second is lattice: UDC prefix, direct Q-ID, and Q-ID closure. The third is lineage-and-temporal: lineage hash and capture week. The fourth is channel-and-source: channel, source type, capture channel, sensitivity, and an estate identity hash.
EstateFingerprintFamilies derives the four hyperplane families for
one estate from its UUID, once, at estate open. Two replicas of the
same estate, started on their own, still share the same UUID. So
they always derive the same families. So they derive the same
fingerprint for the same drawer content. This determinism property
is what recall pruning depends on. EstateFingerprintFamilies.fingerprint(of:)
is the per-drawer entry point. It resolves the drawer's Wikidata
Q-ID, if the drawer has one, through LatticeLib.QIDClosure. This
gives the drawer's full ancestor chain. It folds that chain into a
16-bit hash for the lattice block's qidClosureHash slot. It hands
all four assembled inputs to SubstrateLib.SimHash. Some fields do
not apply to a drawer: lineage-clustering, defer-pattern, and
stream-source data. These fields only apply to a different noun type,
AmbientSample. For a drawer, they default to a null value of zero,
per invariant I-17. This default keeps Hamming distance well-defined
across every noun type that shares the same fingerprint shape.
This file provides the three named axes packed into
Drawer.operationalBitmap. The first is CaptureChannel: how the
content entered the system. Its values are typed, voiced, OCR,
imported file, sensor, and actuator. The second is ContentKind: the
shape of the content. Its values are prose, code, transcript, list,
structured JSON, image caption, and fingerprint-only. The third is
DrawerFeatureFlags: a non-exclusive bitset of seven named flags. The
flags are attachments, voice, image, links, pinned, keystone, and
locked-zone. Feature flags form a bitset, not an exclusive choice. So
DrawerFeatureFlags is declared as an OptionSet. The other two axes
in this file are declared as enums instead.
Each axis has a computed accessor on Drawer: captureChannel,
contentKind, featureFlags, hasFeatureFlag(_:),
stateExtensionActive, and lineageClusteringActive. Each accessor
decodes the relevant bit range. Each falls back to a safe neutral
default, typed input or prose content, for any raw value this build
does not recognize. So a row written by a future version, with a
case this build has never heard of, degrades gracefully instead of
crashing.
This file provides DrawerStateValidator, a thin bridge from
LocusKit's State enum to SubstrateLib's canonical
RowStateAutomaton. This automaton is the single implementation of
the row-state legal-transition table. Every LocusKit noun with a
state axis is validated against it. DrawerStateValidator used to
carry its own parallel transition table. That table permitted four
transitions the specification actually forbids, including accepted
to tombstoned. So it was retired, in favor of using
SubstrateLib itself.
validate(from:to:via:) is the transition-legality-only overload,
used by StateTransitionTests. It bridges State to RowState. It
looks up the legal target for the from-and-via pair in the automaton.
It throws LocusKitError.disciplineViolation when a check fails,
naming the rule violated. A check fails in either of two ways. The
from-via pair may have no legal transition. Or the caller's requested
target may disagree with what the automaton actually produces.
validate(from:to:via:targetingFields:) is the overload
DrawerStore.mutateState actually calls. It also checks
field-level invariants against the post-write bitmap fields, in the
same call. One such invariant, S-1, requires that an accepted row
have trust at or above canonical. Other invariants cover further
bitmap-combination rules, such as S-2.
This file provides the four cross-noun adjective axes: State,
Trust, AdjectiveSensitivity, and AdjectiveExportability. It also
provides their computed accessors on Drawer. These four enums are
the single source of truth for these axes, across every LocusKit
consumer, including GeniusLocusKit and NeuronKit. Layers beneath
LocusKit cannot import them, because the dependency graph points the
other way. So those layers carry the raw integer encoding instead. A
build-time Guardian tool checks that the two sides never silently
drift apart.
State has ten cases. They partition into three clusters, at
raw-value boundaries sixteen and thirty-two. These boundaries let a
state's cluster be computed as one shift-and-mask operation. Cluster
A is "currently believed": active, pending, contested, and accepted.
Cluster B is "knew past": superseded, decayed, withdrawn, and
expired. Cluster C is terminal: rejected and tombstoned.
Drawer.isCurrentlyBelieved, isKnewPast, and isTerminal expose
these three clusters as predicates.
Trust orders seven levels. The lowest is .verbatim, unqualified
as-filed content. Levels rise through .canonical up to the highest,
.ambient. It conforms to
Comparable. So a filter like "trust at least canonical" reads as an
ordinary comparison, rather than raw-value arithmetic.
AdjectiveSensitivity and AdjectiveExportability are both
scale-gapped. Their case raw values skip numbers, leaving room for
future intermediate tiers without disturbing any existing bitmask.
AdjectiveSensitivity carries three ADR-007-driven predicates:
isBulkExportable, requiresOwnerKeyForBulk, and
isExcludedFromBulk. These are the enforcement hooks VaultKit's
bulk-export path consults, to decide whether a drawer may ride a bulk
channel without additional friction. Drawer.dreamingRecalcRequired
and Drawer.sealed decode two single-bit obligation and trust-hint
flags, living above the four main axes.
This file provides the seven named axes packed into
Drawer.provenance: SourceType, Channel, a mirrored
CaptureChannel reference, Confirmation, Confidence,
Sensitivity, and EnrichmentStatus. It also provides their
computed accessors on Drawer. The adjective bitmap records a row's
current standing. The operational bitmap records mechanical facts
about the content. The provenance bitmap records something
different: how the row came into being, and how it has been reviewed
since.
SourceType names ten origins: user, observed, imported, canonical,
derived, federation-aggregate, tier-aggregate, paired-estate,
ambient, and actuator. Channel names the system surface content
arrived through: UI typed input, UI voiced input, an MCP agent, a
file import, federation, or a dreaming-daemon channel. Confirmation
is the review axis. Its levels run from unconfirmed through
user-confirmed, automated-confirmed, peer-confirmed, and
actuator-confirmed. isUserConfirmed is the
convenience predicate retrieval layers use, to surface only
user-vetted content. Confidence and Sensitivity are both
scale-gapped. Confidence orders from .null to .verified.
Sensitivity mirrors the raw values of
AdjectiveSensitivity on purpose. So the two axes can be compared directly:
sensitivity at capture, versus the estate's current access posture,
which may since have been mutated. EnrichmentStatus tracks a Q-ID
enrichment daemon's lifecycle for a row. It usually ends at
.qidCompleted. When deterministic re-inference fails instead, it
ends at the terminal in-workflow state .qidProposed.
This file provides three small, @inlinable field-extraction
helpers: andMask, thresholdCompare, and shiftExtract.
BitmapEvaluator uses them to translate a Filter case into a check
against a packed Int64 bitmap. These helpers are not math primitives.
They carry no algorithm to prove. They need no platform-specific
optimization to gate. The substrate primitives are different: SimHash,
Hamming distance, and OR-reduction live in SubstrateLib. Those
primitives participate in the cross-platform conformance gate. Each
function here delegates its actual bit manipulation to a SubstrateLib
primitive: BitField.maskedEquals, BitField.extractField, or
BitField.popcount. So LocusKit never hand-rolls a shift-and-mask
sequence that could silently diverge from the Rust port's
equivalent.
andMask(_:mask:expected:) tests whether a field equals an expected,
already-shifted value, in one masked comparison.
thresholdCompare(_:mask:shift:op:value:) extracts a field. It
compares the field against a threshold. The comparison uses one of
three orderings: .lessThan, .lessThanOrEqual, and
.greaterThanOrEqual. This is
the mechanism behind cluster-boundary filters, such as "trust below
the action threshold." shiftExtract(_:shift:mask:) returns a
field's raw integer value. It is used wherever the evaluator needs
the value itself, rather than a yes-or-no comparison.
This file provides ForbiddenCombinationValidator. It documents one
core rule: a forbidden bitmap combination. Where a live
call site exists, it also enforces that rule. The rule: a row must
never carry adjective sensitivity .secret together with adjective
exportability .public_. Storage can physically represent that
combination. Nothing stops the bits from being set. The verb layer
still must refuse to produce it. Current write paths route this
check through AuditGate and SubstrateLib. So this
validator itself has no live call site today. It exists as an
explicit, reviewable statement of the rule. It stays ready to be
wired in, wherever a future write path bypasses the gate.
validate(_:) extracts two fields from a full adjectiveBitmap
value. The sensitivity field spans bits six through eleven. The
exportability field spans bits twelve through seventeen. It throws
LocusKitError.disciplineViolation if both match their forbidden raw
values, 48 and 32. These raw values are hand-derived from the enum
cases' shipped values, rather than imported. This is deliberate, so a
future rename of an enum case cannot silently change what this
validator checks.
This file provides Filter, the named recall-filter algebra. It also
provides five small supporting type aliases: LineageID, RoomID,
WingID, WikidataQID, and ProvenanceChannel, FeatureFlag, plus
the StateCluster enum. No Filter case takes a raw bit position,
mask, or threshold integer. Every case names a domain concern,
such as .trustworthy, .inRoom(_:), or
.contentMatches(_:). BitmapEvaluator is the sole place that
translates a Filter into the bitmap primitives internally.
The cases group by concern. State queries include
.currentlyBelieve, .usedToBelieve, .knewOnceAndErased,
.state(_:), and .stateInCluster(_:). Trust queries include
.trustworthy, .requiresConfirmation, .trust(_:), and
.trustAtMost(_:). Other groups exist too. Sensitivity and
exportability queries form one group. Provenance queries form
another: confirmation, source type, channel, and confidence.
Operational queries form a third: capture channel, content kind, and
feature flags. Structural queries cover room, wing, lineage, time
bounds, lattice anchor and prefix, and Wikidata concept. One content
query, .contentMatches(_:), searches text. Three
composition cases let filters nest arbitrarily: .all, .any, and
.not. A RecallFrame.filterChain is a plain array of Filter
values. It is interpreted as an implicit .all. Every filter in the
chain must pass.
This file provides BitmapEvaluator, the compiler and interpreter
for a RecallFrame.filterChain. This is the heart of the recall
pipeline. It is declared internal rather than public, because it
takes a DrawerStore argument, itself internal. Public callers reach
it only indirectly, through Estate.recall.
evaluate(frame:drawers:store:nodeNames:) runs four stages. These
stages run against the candidate row set its caller has already
narrowed down, through Estate.liveRows, which applies fingerprint
pruning ahead of this call. The first stage is default insertion. It
prepends implicit filters for any concern the caller left
unconstrained. State defaults to .currentlyBelieve. Trust defaults
to .trustworthy. Sensitivity defaults to
.sensitivityAtMost(.elevated), the Normal-tier ceiling per ADR-007.
So recall with an empty filter chain returns only content by default:
currently believed, trustworthy, and normal-or-elevated in
sensitivity. A caller need not spell any of this out. The second
stage is bitmap-tier evaluation. It compiles each Filter case to a
check over the row's three raw bitmaps, using the BitmapOps.swift
primitives. Tombstone exclusion is always enforced here, apart from
the caller's chain. A tombstoned row can never surface
through recall, no matter what the chain says. When frame.asOf is
set, each row's bitmaps are first rebuilt as they stood at that
past HLC. This rebuild runs through
AuditLogFold.projectStateAt, before the bitmap tier runs. The third
stage is structured-tier evaluation. It applies filters that need the
drawer's non-bitmap fields: room, wing, lineage, time bounds, and
lattice. For room and wing filters, it also needs a resolved
node-name lookup. The fourth stage is content-tier evaluation. It
applies .contentMatches, through a case-insensitive substring
search over the verbatim content. A final ordering pass then sorts
the survivors.
chainHasPrunableFilter, chainHasContentPredicate, and
chainHasStructuredNameFilter are classifiers. Estate.liveRows and
Estate.getDrawers(ids:matchingFrame:hydrationLevel:) consult them
before even fetching rows. The first classifier decides whether
fingerprint pruning can apply at all. The second decides whether the
fetch needs to pay for the content blob. The third decides whether a
node-name lookup is needed. containerSurvives(chain:fingerprint:)
is the actual pruning predicate. It returns false only when the chain
provably cannot be satisfied by any row in a container. This is sound
only for set-bit filters, such as .hasFeatureFlag. A threshold or
exact-value filter can never be decided from an OR-reduced fingerprint
alone. So those filters never prune a container. They only ever get
evaluated per row.
This file provides Tunnel, the typed cross-reference between two
locations in the estate. A tunnel is stored directionally: from a
source endpoint to a target endpoint. Each endpoint carries a wing and a
room. Each endpoint also carries an optional specific drawer id. A
nil id means the room itself is the endpoint. So a query asking
what this side knows about never needs to scan both directions.
kind is a TunnelKind. This is the closed, indexed relationship
vocabulary the retrieval layer dispatches on. label is
a free-form, unvalidated human annotation. orderKey is a
fractional-index sibling ordering value. It is used only by
.parent tunnels in the outline graph, per ADR-017. Siblings under
the same parent sort by ascending orderKey. So no sibling needs to
be rewritten when a new one is inserted between two existing
siblings.
This file also provides Tunnel.adjectiveSensitivity. This computed
accessor decodes bits six through eleven of adjectiveBitmap as an
AdjectiveSensitivity. An unrecognised raw value falls back to
.normal. This fail-closed default matches KGFact.adjectiveSensitivity
and Drawer.adjectiveSensitivity. The accessor is named
adjectiveSensitivity, not sensitivity, so it does not collide with
the provenance-bitmap sensitivity accessor pattern used elsewhere in
LocusKit.
This file provides TunnelKind, the ten-case closed relationship
vocabulary. Its cases are: .supersedes, .references, .blocks,
.validates, .contradicts, .derivesFrom, .covers,
.elaborates, .respondsTo, and .parent, the outline-containment
edge. This file also provides the four operational axes packed into
Tunnel.operationalBitmap: TunnelDirection, TunnelLifecycle,
TunnelOriginClass, and TunnelStrength, plus their accessors.
Two bits above those four axes carry standalone boolean flags,
rather than enumerated fields. hasInverse records whether a paired
reverse tunnel exists. isRetired is bit 13. The T13 mechanism, per
ADR-021, added this bit for dreaming retirement. withRetired() and
withUnretired() return a copy of a tunnel with that bit flipped.
This is the reversible mechanism by which the dreaming pipeline's
OMEGA cycle suspends a tunnel from active reads, without tombstoning
it. So a later co-recall can re-propose the same pairing. A separate
provenance-bitmap section adds isDreamed, bit 0 of
provenanceBitmap. This flag is set only for tunnels the dreaming
pipeline itself proposed and later accepted. It is never set for a
user-explicit, imported, or federated tunnel. OMEGA's retirement
predicate requires isDreamed == true. So a declared tunnel is never
retired, no matter how much or little it is recalled.
This file provides DiaryEntry. This is the first-person record of
what an agent thought, did, or learned at a point in time. A diary
entry is stored alongside drawers. It is queried separately, keyed by
agentName and an unvalidated topic tag. So one agent's diary
never leaks into another agent's wing-filtered recall by accident.
This is a convention, not an enforced constraint. wing and room
are plain required strings the caller supplies. reward and
rewardProvenance are the explicit quality-signal channel. When a
caller has a direct quality score for an entry, such as a user rating
or a model confidence value, it is recorded here. Otherwise
it is left for the dreaming daemon's implicit recall-based reward
inference to guess at later.
This file provides the four axes packed into
DiaryEntry.operationalBitmap, plus a single flag bit and their
computed accessors. DiaryEventClass has twelve cases. It records
what kind of substrate event this entry describes: capture, mutation,
withdraw, expunge, propose, associate, learn, signal emission,
maintenance, migration, training, or audit-tombstone. DiarySeverity
is scale-gapped, with four levels: trace, info, warning, and error.
DiaryActorClass records who produced the entry: user, substrate
daemon, MCP agent, migration tool, or federation peer.
DiaryBatchMembership has four values: standalone, batch-start,
batch-member, and batch-end. The single flag bit is
requiresFollowup.
This file provides KGFact, a subject-predicate-object triple
distilled from a verbatim drawer. It retains sourceDrawerID as a
backreference, so a fact's provenance is always recoverable.
subject, predicate, and object are all free-form strings at
this rung. The value type itself enforces no entity vocabulary. That
enforcement, when it arrives, belongs to a later federated
knowledge-graph layer. sourceDrawerID may legitimately be an empty
string. An empty string is an unanchored-fact sentinel. A caller uses
it when asserting a freestanding triple, one not extracted from any
specific drawer.
KGFact reuses the same three-bitmap pattern every other
bitmap-backed noun uses. Its adjective accessors are state,
adjectiveSensitivity, exportability, and trust. These decode
the same bit layout Drawer uses. So a fact and its source drawer
can be filtered by the same retrieval predicates. They share the same
encoding.
This file provides the four axes, plus one flag, packed into
KGFact.operationalBitmap. KGExtractorClass has six cases. They are ordered
roughly by rigor: manual, foundation-model, specialized-model,
rules-based, imported-KG, and federated. KGAssertionKind has four
cases: asserted, inferred, hypothesized, and contradicted. The last
case records that another fact disputes this one, without either
fact being retracted. Resolution is deferred to retrieval time.
KGSpecificity and KGConfidenceBand are both scale-gapped and
Comparable. So a filter can read as fact.specificity >= .specific. The isCanonical flag marks a fact promoted to
estate-wide, load-bearing status.
This file provides Proposal, a suggested change awaiting
confirmation. It is the durable record produced by the substrate's
only autonomous write surface. Proposal structurally mirrors
KGFact, with one addition KGFact predates: a required
latticeAnchor. KGFact was written before the cookbook
universalized the every-row-has-an-anchor invariant. candidateState
is the adjective set the proposal would apply to its target, if
accepted. The accept path reads this value, to know what to write.
Proposal.state decodes the proposal's own lifecycle position, from
its adjective bitmap's state field, the same field Drawer.state
decodes. This position starts at pending, while the proposal awaits
confirmation. It then moves to accepted, rejected, or withdrawn.
This file provides the five typed axes packed into
Proposal.operationalBitmap. ProposalKind has nine cases. It
records what kind of write this proposal proposes: .newTunnel,
.mutateDrawer, and seven more, including the newer
.actionProposal, .recordObservation, and .tierAdvisory.
ProposalTargetObjectType records which kind of row this proposal
targets. This includes the .noneBrandNew sentinel, for a
not-yet-existing target. It also includes .systemState, for a
proposal about the estate itself. ProposalConfirmationSource has
four values: human,
agent, automated threshold, and actuator. ProposalGeneratedByClass
has five values: dreaming daemon, MCP agent, federation sync, manual,
and tier aggregator. ProposalConfidenceBucket is scale-gapped and
Comparable.
This file notes that the operational layout for ProposalKind and
its siblings comes straight from the engineering cookbook, rather
than being LocusKit-internal. So a conformance test pins every field
position and raw value against the cookbook table.
composeOperational(kind:targetObjectType:generatedBy:confidence:)
assembles a full operational bitmap from four of the five axes, in
one call. Confirmation source is left at its default, until a
confirmation step actually runs. Autonomic daemon sinks use this
function. They need to stamp genuine provenance on the proposals they
emit.
This file provides Association, the graph edge recording that two
rows belong together. This is a statistical or dreaming-derived
pairing, distinct from a Tunnel's typed semantic claim.
Association structurally mirrors Tunnel: a source and target
endpoint, three bitmap columns, and the Rev 1.0 soft-delete
reservation. Two differences set it apart. It carries no kind
column. An association's semantics live entirely in its operational
bitmap. It also carries a required latticeAnchor, anchored to the
lattice midpoint of its two endpoints. Tunnel predates this
requirement, so it lacks the field.
This file provides the three axes packed into
Association.operationalBitmap. AssociationSignalSources is a
twelve-bit bitset, an OptionSet rather than an enum, because more
than one signal can support the same pairing on its own. Its
signals include a co-recall signal, a co-confirmation signal, a
dream-pairing signal, a vector-similarity signal, a shared-entity
signal, an explicit-human signal, a fingerprint-similarity signal,
and three v0.36 additions: cross-estate, cross-tier, and
action-outcome. AssociationDecayClass is scale-gapped and
Comparable, with four levels: pinned, slow, normal, and fast. This
axis records how quickly an association ages out of relevance.
AssociationArity is binary today. An n-ary form is reserved for a
future version, per invariant I-23's current binary-only limit.
This file provides LearnedReference, the durable record of an
external reference the grounding-driven learn verb brought into
the estate. It structurally mirrors Association: an id, content
columns, a required latticeAnchor, three bitmap columns, and the
soft-delete reservation. Association was chosen as the template
because it is the freshest content-bearing noun that already honors
the anchor requirement. sourceCatalogID is a
foreign-key reference to the SourceCatalogEntry this reference was
learned from. It is stored as an identifier string, the same way
KGFact stores sourceDrawerID, rather than embedding the full
catalog entry inline. handle is the reference's own locator. It is
distinct from the source's own locator, on the catalog entry it
points into.
This file provides the four axes packed into
LearnedReference.operationalBitmap. RefreshPolicy is scale-gapped
and Comparable. Its order runs from none through monthly, weekly,
daily, on-demand, and realtime. DriftSeverity is scale-gapped and
Comparable, with four levels: none, minor, major, and critical.
This axis records how far a reference has drifted from its source,
since it was last re-grounded. LearnMode is a single bit. It
records how the reference was acquired. .byReference means the
reference is held by pointer. .byIngestion means its content was
ingested wholesale. LearnedReferenceSource records where the reference was acquired
from: user, federation, household pairing, fleet pairing, tier
inheritance, or paired estate. This is distinct from
sourceCatalogID, which names which catalog entry the reference came
from, not how the reference arrived.
This file provides SourceCatalogEntry, the durable, queryable
record of an external source from which references are learned. It
is the substrate behind the source slot of the learn verb. Its
reason for existing is grounding integrity. Every LearnedReference
must carry a genuine lattice anchor, never a fabricated sentinel. An
anchor is a property of the source: a web domain, a document corpus,
or a paired estate. It is not a property of each individual handle
learned from it. Cataloging a source once lets every reference from
it inherit that catalog entry's anchor. This is what makes a genuine
anchor available, without inventing one per reference. SourceKind
names six source classes: user, federation, household pairing, fleet
pairing, tier inheritance, and paired estate. This is the same
vocabulary LearnedReferenceSource uses, on purpose. The acquisition
channel a reference was learned through is the same vocabulary as the
kind of source it came from.
This file provides Node, a container node in the estate's
containment tree. The estate root sits at depth zero. A wing sits at
depth one. A room sits at depth two. Every node carries two name
fields. displayName preserves whatever casing the first writer
used. lookupName is a normalized form: Unicode NFC, trimmed, with
internal whitespace collapsed and casefolded. This normalized form is
used for lookup and to keep names unique. So "Personal " and
"personal" resolve to the same node. The node's display name still
shows the casing it was first written in. lifecycle has two
values, active or tombstoned. A pair of HLC-typed timestamps,
createdHlc and tombstonedHlc, support this. They let the
containment tree read its own past state, the same way drawers do.
Neither needs a wall-clock timestamp to double as the
ordering key. Node.normalizeLookupName(_:) is the single,
conformance-relevant normalization function. Both the Swift and Rust
ports must produce byte-identical results from it.
This file provides NodeStore, the actor that owns the containment
tree's storage. It also owns the tree's create-on-demand lookup.
Given a display name and a parent id, it finds the active node with
that normalized lookup name under that parent, or creates one if
none exists. NodeStore is an actor for a specific reason. This
find-then-insert sequence stays race-free without needing an INSERT OR IGNORE clause or a conflict-column mechanism. Two concurrent
requests to create the same wing under the same estate serialize
through the actor. So they produce exactly one node. The second
request's findActiveNode call cannot run until the first request's
insert has completed.
createNode(displayName:parentId:now:) enforces two structural
invariants before writing. The parent must already exist, per
invariant I-NT-5. The new node's depth is the parent's depth plus
one. This depth must not exceed two, per invariant I-NT-2. A room cannot have a
child, because there is no fourth tier. createRoot(displayName:now:)
is the once-per-estate seed for the depth-zero root. It enforces that
exactly one root exists, per invariant I-NT-1, by returning the
existing root unchanged if one is already present. Tombstoned nodes
are invisible to lookup. A tombstoned wing does not block a later
request for a wing with the same name. It also cannot be reused by
mistake, for that later request. This is the no-resurrection guard, per
section five.
This file provides NodeBundleStore, persistence for the
bundle-algebra count-vector totals. One row exists per node, room
or wing, per bundle kind. Each row is stored in the node_bundles
table. A count-vector here is a 256-element array of counts, one per
fingerprint bit, encoded as 1024 bytes of little-endian UInt32
values. BundleKind.activeA is the active centroid, a fold of a
node's currently active members. BundleKind.departedB is the
departed accumulator, eager-folded at departure time. The per-row
drawer fingerprints that feed these totals are never themselves
stored. Only the folded total is stored. This keeps the table
small, no matter the estate size.
put, get, and rooms(forWing:kind:) are the read-and-write
surface. encodeCounts and decodeCounts handle the wire format.
decodeCounts throws LocusKitError.invalidContent, rather than
trapping, when a stored blob is not exactly 1024 bytes. So a corrupt
row surfaces as a recoverable error, instead of crashing the process.
This file provides BundleMaterializer, the operation that computes
a Bundle A total from live drawers. It is the first
real consumer of SubstrateKernel.countFold256. Bundle A cannot be
maintained bit by bit. Active membership changes over time, and
the fold operation has no subtraction. So Bundle A is fully
recomputed on demand, typically by a periodic dreaming tick.
materializeRoom(wing:room:now:) fetches every non-tombstoned drawer
in a room. It filters to State.isClusterA rows, the currently
believed ones. It derives each survivor's Fingerprint256, through
EstateFingerprintFamilies. It folds the set with
kernel.countFold256. It stores the result. rollUpWing(wing:now:)
merges a wing's already-materialized room bundles into one
wing-level bundle. The count-vector fold is associative. So this
merge produces the same result as folding every active drawer
in the wing. Rooms may be materialized in any order before
the wing roll-up runs.
This file provides a small marshaling convention. It packs a single
Int64 bitmap column into block zero of a Fingerprint256, with
blocks one through three zeroed. This lets a per-column bit
operation route through a SubstrateLib primitive instead: an OR, an
XOR, or a popcount. That primitive operates at the substrate's
canonical 256-bit width. LocusKit avoids reimplementing the same math at Int64 width this way.
This file is explicit that the convention is purely a type-shape
choice, with no algorithm of its own. The file also warns that this
packing differs from the cookbook's Bitmap-LSH SimHash interpretation
of block zero. A caller must not mix the two uses of the same 64-bit
lane.
init(int64Column:) and the int64Column computed property are the
pack and unpack directions. They are used throughout
ContainerFingerprintStore.
This file provides ContainerFingerprintStore, the per-container
OR-reduction totals. These totals make fingerprint-based
recall pruning possible. For every room, the store keeps the bitwise
OR of the three bitmap fields, across every active drawer in that
container. It also rolls this total up to every wing. OR is
monotone. So a container's total can only ever gain bits as content
is added. So a bit absent from the total is provably absent from
every row in the container. This is exactly the soundness property
recall pruning needs. A filter requiring that bit can safely skip the
whole container, without fetching a single row.
orIn(wing:room:adjective:operational:provenance:now:) is the
incremental maintenance path. It runs on every drawer capture. It
folds the new drawer's bitmaps into both the room-level and the
wing-level row, through ContainerFingerprint.merging(_:). That
function itself routes through SubstrateLib.ORReduce.reduce, at
canonical 256-bit width, using the Fingerprint256 packing
convention above. The clear side is absent on purpose. Withdrawing
or expunging a drawer never clears bits from the total. A stale
set bit is a harmless over-approximation. It can only cause an
unnecessary scan, never a missed match. Meanwhile, three other
functions exist for a different purpose. They are rebuildRoom,
rollUpWing, and rebuildAll. They tighten the total back down,
now and then, from a fresh full scan. rebuildAll is what
Estate.open calls once at startup. So an existing estate's
total is guaranteed complete, and thus sound, before any
recall runs against it.
This file provides the Merkle content-integrity rollup, an extension
on Estate. It computes a hash tree from the bottom up: room, then
wing, then estate. This hash tree covers the same containment tree
NodeStore maintains. A Merkle tree is a hash tree where each
parent's hash is computed from its children's hashes. So a single
top-level root hash can attest to the exact contents of an entire
subtree. Changing any leaf changes every hash on the path up to the
root. A caller must invoke the rollup by hand. It does not run on
its own after every write. Computing it inline on every drawer
capture would be
costly. The cost would be proportional to room size, on every write.
This would peg the CPU during a bulk import.
computeRoomMerkleRoot(roomNodeId:) hashes a room's live drawers. It
reads each row's stored content_hash column when present, written
by a separate hash-on-write hook. It computes a leaf hash on demand
from the drawer's content when the column is absent. It always
excludes two kinds of rows from the snapshot. The first kind is
tombstoned rows. These rows are irreversibly deleted. The second kind
is withdrawn rows, at state raw value 18. A withdrawn drawer's
content has been retracted by the user. So it must not remain
retrievable through a content-attesting snapshot.
computeEstateOrWingMerkleRoot(parentNodeId:) hashes a set of
children's already-computed roots. The same function serves two
levels. At the wing level, it hashes room roots. At the estate level,
it hashes wing roots. rollupMerkleRoots(roomNodeId:now:) runs all
three levels for one changed room. recomputeAllMerkleRoots(now:) is
the full bottom-up recompute, used after bulk import, migration, or
corruption recovery. rollupAllMerkleRoots(now:) is a documented
alias of the full recompute, named for the batch-capture reindex
path. createSnapshot(label:now:additionalAttestations:) recomputes
the full tree, so a snapshot is never taken against stale roots. It
then writes an attestation row for the estate root and every wing,
plus any attestations a higher composition-layer kit supplies.
This file provides ManifestKey, the eighteen required and seven
optional typed keys of the v1 manifest key-value table. It also
provides ManifestValues, the typed, read-only snapshot
DrawerStore.readManifest() produces. The manifest table itself is
a plain key-value store. This file turns a table of plain strings
into a fully typed record a caller can rely on.
ManifestKey.ed25519PublicKey and ed25519PrivateKeyWrapped are the
two identity-related keys. ed25519PublicKey is the estate's
federation public key. It is safe to store here, because a public
key has no confidentiality requirement. ed25519PrivateKeyWrapped is
different: a reserved, never-written seam. It exists only for
backward read-compatibility, with an estate opened before the
Keychain migration. The private key itself lives exclusively in
EstateIdentityKeyStore.
This file provides the seven default wing definitions. These are
seeded into a fresh estate at provision time. It also provides three
related constants. defaultWingName is "Agentic Memory," the wing
capture uses when a caller supplies no explicit wing. hintRoom is
"AI_Charter_Hint," the room each seeded wing's hint memory lives in.
hintUDCCode is "001," the UDC Knowledge-class code stamped on hint
drawers. Each WingDefinition pairs a wing name with a
plain-language hint describing the wing's role. The seven wings are:
Agentic Memory, User Canon, Source Corpus, Personal, Professional,
Projects, and Temp. Each hint is seeded as an ordinary, fully
recallable, user-deletable drawer. So a fresh agent has a working
orientation to its own memory structure, from its first session. The
set is only a suggestion, not a fixed schema. An agent may
create any extra wing it needs.
This file provides RecallStream, the paged async sequence
Estate.recall returns. Iterating a RecallStream produces one
RecallPage at a time. The first page arrives synchronously, on the
first next() call. Later pages arrive lazily. isLast is true only
on the final page. So a caller can drive a uniform for await loop,
without special-casing an empty result. A genuinely empty corpus
still emits exactly one page, with zero rows and isLast == true.
degradedStages is the channel by which a failed internal read
becomes observable to a caller. A failed read might come from several places: the bounded corpus
scan, the room-fingerprint enumeration, a room's drawer read, or the
bitmap evaluator itself. recall never throws.
Section 7.8.1 of the specification requires it stay non-throwing. So
an internal failure would otherwise be indistinguishable from a
genuinely empty result. A non-empty degradedStages array is what
lets a caller, chiefly GeniusLocusKit's RecallDirector, tell the
two apart. AsyncIterator.hydrate(_:) applies the requested
HydrationLevel per page. At .bitmapOnly, it rebuilds each drawer
with content blanked to the empty string, while preserving every
bitmap and metadata field. At .structured and .full, rows pass
through unchanged. The content-stripping decision for .structured
was already made upstream, at the storage-fetch layer, not here.
This file provides RecallTraceItem, one record of a single drawer
returned by a recall operation. This is the substrate for the "later
two-source reward" mechanism. A dreaming daemon uses this mechanism
to distinguish rows a user acted on from rows that were
returned but ignored. The used flag is bit 0 of operationalBitmap,
decoded through a computed property rather than stored as a separate
Bool column. So the type carries no stored boolean fields at all.
Every bitmap-backed LocusKit noun follows this convention. score,
when present, is the recall's own similarity score for the row. A
nil score means the recall that produced this trace carried no
score, for example an ordinary ordered-by-capture-time query.
This file provides BitmapState, the snapshot of a row's three
bitmap columns at a specific past HLC. Estate.bitmapState(rowID:asOf:)
returns this snapshot. BitmapState exists as a small, focused
return type, for a precise reason. The reconstruction that produces
it, AuditLogFold.projectStateAt, is a SubstrateLib primitive. It
folds a row's sealed audit events forward in HLC order. Yet it
returns its result in a different shape. BitmapState is LocusKit's
own public-facing wrapper. It names the three bitmaps by hand,
rather than exposing the substrate's internal projection type
directly.
This file provides WingSummary and RoomSummary, the two small
summary types DrawerStore.listWings and listRooms produce. Both
are computed projections over the current drawer set. LocusKit has
no separate wings or rooms table. Wing and room identity lives
entirely in the nodes table. A summary's drawer and room counts are
simply whatever the live query finds, at the moment it runs. They are
not a maintained running total.
This file provides LocusKit's opt-in telemetry emission functions.
They are wired through IntellectusLib.Intellectus.report(_:). Every
emit function here follows the same three rules, stated in the
file's header. First, the reported value is always an
@autoclosure. It is never evaluated when monitoring is disabled.
The off-path cost is one atomic boolean load and a branch: no lock,
no allocation. Second, the now timestamp is always caller-supplied,
never read from a clock inside the function. This preserves
IntellectusLib's own determinism contract. Third, the metric
namespace follows the fleet-wide <kit>.<noun>.<field> convention.
Examples include locuskit.drawer.capture_latency_ms,
locuskit.kgfact.add_count, and locuskit.gate.reject_count.
emitDrawerCapture, emitDrawerQuery, emitKGFactAdd,
emitKGFactQuery, emitTunnelAdd, emitGateAdmit, and
emitGateReject share one pattern. Each is called from its
corresponding DrawerStore method. Each call happens at the exact
moment its operation completes. For the gate functions, the call
happens when AuditGate.admit returns instead. Telemetry is purely additive. It is never
consulted by any control flow that decides what a method returns. So
every store method's functional behavior is byte-identical, whether
monitoring is enabled or not.
The rust/ directory contains the second leg of the kit. It holds
fifty-seven source and test files, under rust/src/ and
rust/tests/. These files mirror the Swift implementation
file-for-file: drawer.rs, drawer_store.rs, bitmap_evaluator.rs,
merkle_rollup.rs, estate.rs, and more. The Rust side also carries
three concrete DrawerStore backends the Swift side does not
carry: drawer_store_inmemory.rs, drawer_store_sqlite.rs, and
drawer_store_postgres.rs. All three build over a shared,
storage-agnostic DrawerStoreCore.
Conformance tests under rust/tests/ gate byte-for-byte agreement
with the Swift implementation. These tests cover adjective and
operational bitmap conformance, provenance bitmap conformance,
corrupt readback, Merkle rollup, outline proofs, recall pruning,
temporal reads, and the LP0 fixed-vector suite. They gate agreement
on every bitmap layout, every fingerprint derivation, and every
state-transition legality decision. Suppose you change a bitmap layout, a fingerprint derivation, or a
transition rule on either leg. Mirror that change on the other leg.
Then run both test suites. The fixtures
and conformance tests are the contract, not a convenience.