Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions packages/libs/EngramLib/docs/AGENT_MAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
doc: AGENT_MAP
package: EngramLib
repo: moot-core
authored_commit: b2a5c30b794cf477e18022c55e2fea348614d337
authored_date: 2026-07-04
sources:
- path: Sources/EngramLib/EngramLib.swift
blob: b7f236bfaa193967daf5c5d6061ef4591207ed22
- path: Sources/EngramLib/Match.swift
blob: 5c61fd65888a795c703ab9f52022dffeae9c7595
---

# AGENT_MAP: EngramLib

PURPOSE: product-facing similarity/retrieval/aggregation API over 256-bit engrams (`Fingerprint256`). Thin wrapper that hides substrate kernel selection: distance, batch-distance, top-K nearest, radius filter, OR-union. Two files total; no internal multi-stage pipeline.

DEPS: imports SubstrateTypes (`Fingerprint256`, aliased `Engram`), SubstrateKernel (`SubstrateKernel` protocol, `PortableKernel.kernelForCurrentPlatform()`). Imported by (within SDK): VectorKit, CorpusKit (kits, moot-memory repo: kits compose libs, never the reverse). Rust port in `rust/` (`engram_lib` crate) mirrors the full API 1:1 against `substrate-types`/`substrate-kernel` Rust crates; conformance is behavioral parity checked by mirrored test suites (`rust/tests/engram_lib_tests.rs` vs `Tests/EngramLibTests/`), not a shared fixture file.

ENTRY POINTS (most callers need only these):
- EngramLib.swift:77 `EngramLib.distance(_ a: Engram, _ b: Engram) -> Int` | Hamming distance, 0...256
- EngramLib.swift:99 `EngramLib.findNearest(probe:in:k:) -> [Match]` | top-K nearest, distance-asc / index-asc tiebreak
- EngramLib.swift:216 `EngramLib.session() -> Session` | reusable kernel handle for hot loops

## Symbol Table

### Engram type: EngramLib.swift
- :43 `public typealias Engram = Fingerprint256` | substrate type, product-facing name; do not add a wrapping struct
- :45-53 `extension Engram { init(blocks b0:_ b1:_ b2:_ b3:) }` | convenience 4-block constructor
- :33 `internal let _engramLibCachedKernel: any SubstrateKernel` | resolved ONCE at module load via `PortableKernel.kernelForCurrentPlatform()`; every call path reads this, never re-resolves

### Static API: enum EngramLib (EngramLib.swift:71)
- :77 `distance(_:_:) -> Int` | single-pair Hamming distance
- :84 `distances(probe:candidates:) -> [Int]` | batched, same order/count as `candidates`; `[]` on empty input
- :99 `findNearest(probe:in:k:) -> [Match]` | `[]` if `k <= 0` or candidates empty; clamps to `min(k, candidates.count)`
- :112 `findNearest(probe:in:) -> Match?` | k=1 convenience; `nil` on empty candidates
- :124 `findWithin(probe:in:maxDistance:) -> [Match]` | `[]` if candidates empty OR `maxDistance < 0`; inclusive upper bound
- :149 `union(_ engrams: [Engram]) -> Engram` | bitwise OR-reduce; zero engram is the empty-input identity
- :154 `union(_ a:_ b:) -> Engram` | pairwise OR convenience, delegates to `Fingerprint256.union(_:)`
- :222 `private static func kernel() -> any SubstrateKernel` | single choke point back to the cached kernel

### Session: struct EngramLib.Session (EngramLib.swift:166), Sendable
- :169 `init()` | captures `_engramLibCachedKernel`
- :173/:177/:184/:193/:210 `distance` / `distances` / `findNearest` / `findWithin` / `union` | instance mirrors of the static functions above, byte-identical results, no new behavior
- :216 `EngramLib.session() -> Session` | factory, equivalent to `Session()`

### Result type: Match.swift
- :14 `public struct Match: Hashable, Sendable, Codable` | `index: Int` (position in caller's candidate array), `distance: Int` (Hamming distance from probe)
- :18 `init(index:distance:)` | public memberwise init
- :24-28 `extension Match: Comparable` | `<` compares `distance` first, `index` second; this IS the sort/tiebreak contract used by `findNearest`/`findWithin`

## INVARIANTS / GOTCHAS

- Kernel is resolved exactly once per process (`_engramLibCachedKernel`, EngramLib.swift:33), at module load, not per call. Do not reintroduce per-call kernel resolution: it was removed as a measured, needless dispatch cost (see file header comment, Phase 4.3 / decision 2026-05-28 §6.4.3).
- DO NOT REIMPLEMENT SUBSTRATE MATH (EngramLib.swift:14-24 banner comment). Every primitive this library exposes, Hamming distance, batch distance, top-K, OR-reduce, already exists in SubstrateTypes/SubstrateKernel/SubstrateML; EngramLib only forwards to it.
- Tie-break rule is fixed and load-bearing: distance ascending, then candidate index ascending (`Match.<`, Match.swift:25-27). `findNearest` and `findWithin` both depend on this for deterministic, reproducible output: same probe + same candidate list + same order = same result, always.
- All empty/degenerate-input guards return empty collections or `nil`, never throw and never crash: empty candidates → `[]`/`nil`; `k <= 0` → `[]`; `maxDistance < 0` → `[]`; empty `union` input → `Engram.zero` (the OR identity).
- `Session` and the static functions are result-identical by construction: both bottom out in the same cached kernel. `Session` exists only to save the small per-call cost of re-touching the static cache in a hot loop; it is not a different code path.
- `Engram` is a type alias, not a wrapper type. Treat the underlying representation (`Fingerprint256`, four `UInt64` blocks) as substrate-owned; construct via `Engram(blocks:_:_:_:)` or substrate constructors, not by touching blocks directly from product code.
- Whole library is stateless and thread-safe (file header, EngramLib.swift:7-10): every static method creates no persistent state; `Session` is `Sendable` and safe to share across tasks.
- Rust and Swift legs must stay behaviorally identical: same guard conditions, same tie-break order, same identity values. There is no shared byte-fixture gate here (contrast LatticeLib's conformance JSON): parity is enforced by keeping `rust/tests/engram_lib_tests.rs` and `Tests/EngramLibTests/` mirrored. Changing a guard or ordering rule in one leg without the other is a silent cross-platform divergence.
- Package has no pinned artifacts, no build-time-only code, and no privacy seams: unlike larger sibling libs (e.g. LatticeLib), there is nothing here gated by an environment variable or a resource bundle.
180 changes: 180 additions & 0 deletions packages/libs/EngramLib/docs/DETAILS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
---
doc: DETAILS
package: EngramLib
repo: moot-core
authored_commit: b2a5c30b794cf477e18022c55e2fea348614d337
authored_date: 2026-07-04
sources:
- path: Sources/EngramLib/EngramLib.swift
blob: b7f236bfaa193967daf5c5d6061ef4591207ed22
- path: Sources/EngramLib/Match.swift
blob: 5c61fd65888a795c703ab9f52022dffeae9c7595
---

# EngramLib Details

This document walks through every source file in the package. Read
`OVERVIEW.md` first for the big picture. The package has two files: the
public API surface, and the result type it returns.

## EngramLib.swift

This file holds the entire public surface of the library. It defines the
`Engram` type alias, the `EngramLib` enum with its static comparison and
retrieval functions, and the `Session` type. Callers that run many
comparisons in a loop use `Session`.

### How It Works

The file opens with a type alias: `public typealias Engram =
Fingerprint256`. `Fingerprint256` is a 256-bit code defined in the
substrate's `SubstrateTypes` package. It packs four 64-bit blocks into one
fixed-size fingerprint. EngramLib does not define its own bit shape. It
reuses the substrate's shape, under a product-facing name. This choice
matters for one reason. If a future substrate version widens the shape,
product code that only ever wrote `Engram` never has to change. An
extension adds `Engram.init(blocks:_:_:_:)`. This convenience initializer
takes the four blocks as separate arguments. Callers can write
`Engram(blocks: 0xDEAD, 0xBEEF, 0xCAFE, 0xBABE)` instead of naming the
underlying block parameters.

Every comparison in the file calls a kernel in the end. A kernel is an
object, supplied by the substrate's `SubstrateKernel` package, that knows
how to run bit operations well on the current device. The file resolves
this kernel exactly once, at module load time. It stores that kernel in
the private constant `_engramLibCachedKernel`. Every static function then
reads that same cached value, instead of asking the substrate to choose
again. The file's own comment explains why. The kernel choice does not
change while the program runs. Resolving it on every call was pure
overhead, paid on every single check.

### Function Coverage

`EngramLib.distance(_:_:)` returns the Hamming distance between two
engrams. Hamming distance counts the bit positions where two equal-length
codes differ. Identical engrams return zero. Engrams that are exact bit
inverses of each other return 256, the full width of the fingerprint.
This function matters because every other comparison in the file builds
from it.

`EngramLib.distances(probe:candidates:)` computes the distance from one
probe engram to every candidate in a list. It returns an array in the
same order as the input. This matters because computing distances one at
a time, in a loop, would call into the kernel once per candidate. This
function hands the whole batch to the kernel in one call. The kernel then
processes it more efficiently. It returns an empty array right away when
the candidate list is empty, so no work goes to waste.

`EngramLib.findNearest(probe:in:k:)` returns the `k` candidates closest
to the probe. It sorts them by distance, from nearest to farthest. Ties
break by the candidate's position in the input list. This is the
library's core retrieval function. It answers which memories look most
like this one, bounded to a fixed count. It returns an empty array when
`k` is zero or negative, or when the candidate list is empty. Neither
case counts as an error. When `k` exceeds the candidate count, the
function returns every candidate rather than failing.

`EngramLib.findNearest(probe:in:)` is the two-argument overload. It is a
convenience for the common case of wanting only the single closest match.
It calls the three-argument version with `k` fixed to one. It returns the
first result, or `nil` when the candidate list is empty. This overload
matters because finding the closest one is common enough to earn its own
name. Callers should not have to unwrap a one-element array by hand.

`EngramLib.findWithin(probe:in:maxDistance:)` returns every candidate
whose distance from the probe sits at or under `maxDistance`. It sorts
results the same way as `findNearest`. This matters because some callers
do not know in advance how many results they want. They want everything
within a similarity radius, however many candidates that turns out to
include. The function builds the full distance array first, then filters
it. It sorts only the matches that passed the filter. So the sort never
touches a candidate it is about to drop. The function also guards against
a negative `maxDistance`. A negative value cannot match any real
distance. Without the guard, it would silently match nothing, which
would confuse a caller.

`EngramLib.union(_:)` runs a bitwise OR across a list of engrams. The
result carries a one-bit at every position where at least one input
engram had one. This matters because a memory system often wants one
summary engram for a whole group, such as a cohort of related memories,
or the union of a topic's structural traits. An empty input returns the
zero engram. That is the correct identity value for OR, since combining
it with anything leaves that thing unchanged.

`EngramLib.union(_:_:)` is the two-argument overload. It ORs exactly two
engrams together. It exists as a light convenience for the common
pairwise case. A caller merging two engrams does not have to build a
two-element array just to call the list version.

`EngramLib.Session` is a struct that holds one kernel reference. It
exposes `distance`, `distances`, `findNearest`, `findWithin`, and `union`
as instance methods. Each mirrors the behavior of its static counterpart.
It matters for one reason: reuse. The static functions already share one
cached kernel inside, so a `Session` unlocks no new capability. It exists
so that call sites processing thousands of comparisons in a tight loop
can hold one reference. That reference skips the small, repeated cost of
reaching back into the static cache on every call. A session is
`Sendable`. So the same session can be shared safely across concurrent
tasks. `EngramLib.session()` is a static factory that returns a new
`Session`. It is equivalent to calling `Session()` directly. It exists so
callers do not need to know the type's name to build one.

The private function `kernel()` is the single choke point every static
function calls through to reach the cached kernel. Routing every call
through one small function keeps that indirection in one place. This
matters should the caching strategy ever need to change.

## Match.swift

This file provides `Match`, the value every retrieval function in
`EngramLib.swift` returns.

### How It Works

A `Match` is a pair of two integers. `index` is the position of the
matched candidate in the caller's original input array. `distance` is the
Hamming distance from the probe to that candidate. The type carries no
reference to the engram itself, only its position and its distance,
because the caller already holds the original candidate array. The caller
can look the engram up by index if needed. Keeping the type this small
makes it cheap to build in bulk during a retrieval scan.

`Match` conforms to `Hashable`, `Sendable`, and `Codable`. It can be
stored in sets. It can be passed safely across concurrent code. It can be
serialized for storage or logging, without any extra work.

### Function Coverage

`Match.init(index:distance:)` is the memberwise initializer. It is public
so that code outside the package can build a `Match` directly. This
covers test suites, or a caller assembling results from its own logic,
rather than only receiving a `Match` from an `EngramLib` function.

The `Comparable` conformance sits in a small extension. It defines the
order that every retrieval function in `EngramLib.swift` relies on.
Distance ascends first. For two matches at equal distance, index ascends
second.

This order is what makes `findNearest` and `findWithin` deterministic.
Given the same probe and the same candidate list, the result always
sorts the same way. The tie-break rule never depends on anything but the
candidate's fixed position in the input.

## Rust Port and Conformance

The `rust/` directory holds a second implementation of the same API, in
two files. `lib.rs` defines the `EngramLib` struct with its associated
functions (`distance`, `find_nearest`, `find_within`, `union`, and the
rest), plus the `Session` type. `matchx.rs` defines the `Match` struct
with its `Ord` implementation. The function names differ only in the
ways Swift and Rust naming conventions differ. `findNearest` in Swift is
`find_nearest` in Rust. `findNearest(probe:in:)` becomes
`find_nearest_one`, since Rust has no argument-label overloading. The
behavior matches the Swift version function for function, including
every empty-input guard and every tie-break rule. Both legs depend on
their own language's version of `SubstrateTypes` and `SubstrateKernel`.
Both route their bit math through the same kernel abstraction. So a
Swift caller and a Rust caller, comparing the same two engrams, compute
the same distance. `rust/tests/engram_lib_tests.rs` mirrors the Swift
test suite in `Tests/EngramLibTests/`. A change to one leg's behavior
requires the same change to the other leg, and to its tests.
79 changes: 0 additions & 79 deletions packages/libs/EngramLib/docs/INTERFACE_DOCTRINE.md

This file was deleted.

Loading
Loading