Purpose of this page: show the memory write path and the memory read path across the same ten layers as the rest of the architecture, so you can see which layer each decision belongs to and where a model is allowed to appear.

Memory — write path and read path

An agent starts every attempt with no experience. The attempt record already holds what happened; that is evidence, and evidence is not memory. Memory is the curated layer above it: a bounded taxonomy of domain / topic / fact that a human can read in full.

The one rule that shapes both paths

The model decides where a candidate belongs. The rules decide what is legal. The registry is closed to the model, the caps bind it, the scope rules bind it, and it never writes. Every stage that touches a model is skippable: with no provider registered, memory still files and still recalls.

Write path

Five stages, and exactly one of them calls a model. A machine capture is durable before anything is derived from it, so a crash between the task transition and the filing transaction loses nothing.

flowchart TB
    subgraph L0["Layer 0 · Actors"]
        agent["Coding agent
finishes a task"] human["Operator
kanthord memory remember"] end subgraph L2["Layer 2 · cli/"] cli["memory remember · resolve
supersede · vocabulary"] end subgraph L3["Layer 3 · http/contract/"] contract["memory.remember · memory.resolve
vocabulary.declare · vocabulary.amend
typed topic-path tuples"] end subgraph L4["Layer 4 · http/server/"] handler["handler
parse → one command → format
no domain branching"] end subgraph L5["Layer 5 · commands/memory/"] capture["capture
writes memory_capture INSIDE
the task transition transaction"] stage1["1 · nominate — zero tokens"] stage2["2 · classify — the only model call"] stage3["3 · admit"] stage4["4 · file — one transaction"] end subgraph L6["Layer 6 · domain/ (pure, zod only)"] admission["memory-admission
two floors · filing cap over the union
exactly one primary filing"] pathd["memory-path
topic path tuple · one renderer"] factd["memory-fact
kinds · required fields
canonical payload hash"] end subgraph L7["Layer 7 · services/ — interfaces"] memif["MemoryStore
accepts the transaction context"] clsif["Classifier
no tools · no workspace · no turn loop"] stif["Storage — owns the transaction"] end subgraph L8["Layer 8 · services/ — implementations"] sqlmem["SqliteMemoryStore
FTS5 MATCH + bm25()
relevance = -bm25()"] piacls["PiAiClassifier
call 1 domains → call 2 topics
returns topic paths only"] end subgraph L9["Layer 9 · external resources"] db[("kanthord.db
memory_fact · memory_filing
memory_journal · memory_index")] provider["model provider API"] end agent -->|"task reaches done"| capture human --> cli cli --> contract contract --> handler handler --> stage1 capture -.->|"sweep reads the open row later"| stage1 stage1 --> stage2 stage2 --> stage3 stage3 --> stage4 stage1 -->|"relevance over descriptor + fact rows"| memif stage2 --> clsif stage3 -->|"pure predicate"| admission stage4 --> pathd stage4 --> factd stage4 -->|"re-nominate and re-admit
inside the transaction"| memif stage4 --> stif memif --> sqlmem clsif --> piacls stif --> sqlmem sqlmem --> db piacls -->|"one memory_inference row per call,
opened BEFORE dispatch"| db piacls --> provider classDef model fill:#fde68a,stroke:#b45309,color:#7c2d12; classDef pure fill:#dcfce7,stroke:#15803d,color:#14532d; class stage2,clsif,piacls,provider model; class admission,pathd,factd pure;
StageLayerModelWhat it decides
capture5nonothing. It names its evidence by blob and stops, inside the task transaction.
nominate5 → 7 → 8nowhich topics share vocabulary with the candidate. Candidate generation, not membership.
classify5 → 7 → 8yeswhere a paraphrase belongs. Two calls: domains, then topics inside them.
admit6nowhat is legal: the registry, the caps, the scope rules, the single primary filing.
file5 → 7 → 8 → 9noone transaction: dedupe, insert, reconcile filings, index, journal.

Read path

Recall is two stages, because that is how a person recalls: pick the subject, then read what you know about it. A KanthorD attempt spends no model call on either stage, because its recall is pinned once and every attempt renders the same bytes.

flowchart TB
    subgraph L0r["Layer 0 · Readers"]
        swe["general@1 / swe@1
the implementer"] rev["re@1
the reviewer"] foreign["Foreign harness
Claude Code · Codex"] opr["Operator
kanthord memory recall"] end subgraph L2r["Layer 2 · cli/"] clir["memory catalog · memory recall
memory export"] end subgraph L3r["Layer 3 · http/contract/"] contractr["memory.catalog · memory.recall
memory.export"] end subgraph L4r["Layer 4 · http/server/"] handlerr["handler · parse → one query → format"] compiler["instruction compiler
typed channels, precedence before rendering"] end subgraph L5r["Layer 5 · queries/memory/"] catalog["stage 1 · catalog
every domain and topic, bounded in bytes"] recall["stage 2 · recall
rank fact rows inside the chosen paths"] pin["pin
stored once, before a task's first attempt"] end subgraph L6r["Layer 6 · domain/ (pure)"] order["memory-recall
1 relevance desc · 2 scope tier · 3 fact ULID
skip a fact that does not fit · never truncate"] disp["dispute pairing
both sides adjacent, or neither"] end subgraph L7r["Layer 7 · interfaces"] memifr["MemoryStore"] clsifr["Classifier — select: model only"] end subgraph L8r["Layer 8 · implementations"] sqlr["SqliteMemoryStore
bm25() over fact rows"] end subgraph L9r["Layer 9 · external"] dbr[("kanthord.db
memory_recall holds the rendered blob")] tree[("exported tree
CATALOG.md · fact/ · index/ · MANIFEST.json")] end opr --> clir clir --> contractr contractr --> handlerr handlerr --> catalog catalog --> recall recall --> order order --> disp recall --> memifr memifr --> sqlr sqlr --> dbr catalog -.->|"select: model — optional, audited"| clsifr recall --> pin pin -->|"stores the ordered ids AND the rendered bytes"| dbr pin --> compiler compiler -->|"channel: recalled memory
below every contract channel"| swe compiler -. "never receives the channel" .-> rev clir -->|"memory export"| tree tree -->|"reads files, its own model chooses"| foreign classDef model fill:#fde68a,stroke:#b45309,color:#7c2d12; classDef pure fill:#dcfce7,stroke:#15803d,color:#14532d; classDef refused fill:#fee2e2,stroke:#b91c1c,color:#7f1d1d; class clsifr model; class order,disp pure; class rev refused;

Three rules the diagrams encode

  1. The recall channel sits below every contract channel, and it is never refused and never truncated. The resolver renders facts in rank order and skips any whose rendered block would not fit, continuing with the next. A single fact can never exceed the budget, because a body over the cap is refused at write time.
  2. re@1 receives no memory. A reviewer that reads what the implementer read makes correlated mistakes, and the review stops being independent. Every fact is implementation guidance by the definition of that channel; anything that governs rather than advises belongs in the repository profile.
  3. A task's recall is pinned before its first attempt, and the pin stores the result rather than the query. A bm25() relevance depends on statistics over the whole index, so a fact written later would reorder an older query. Storing the ordered ids and the rendered bytes is what makes an attempt reproducible — and it is why an agent cannot change the instruction governing its own run.

Where a model may appear, and where it may not

OperationModelWhy
Classify a candidate into topic pathspermittedPlacing a paraphrase is the one thing the lexical index cannot do.
Select paths on read (select: model)permittedOpt-in, audited identically, and never the default for an attempt.
Create a domain or a topicrefusedA model would consume the whole registry budget before a human read the catalog.
Resolve a draft into a factrefusedA concise problem and solution pair is prose and a write. Both fail the inference-service exception.
Declare two facts contradictoryrefusedThe classifier never receives a fact body, so it has no evidence to compare.
Rank facts on readrefusedThe ordering key is total and deterministic. A model in front of it buys variance.

The full specification is in the engine repository under docs/proposal/memory/: the model and the scopes, the vocabulary and its budgets, the write path, the read path, the architecture and the portable format.