YURIOS // LAB
The Codex → Part III · Brain Chapter 19 · The YuriOS Architecture

The YuriOS Architecture — The Cognitive State and the Context Assembler

The YuriOS Architecture · Part 2 of 3

The cognitive state, as data structures

The Vault is not an undifferentiated pile of text. It holds distinct kinds of state with distinct update rules — the lesson BDI (→ ch. 02 §1, BDI and cognitive architectures) and CoALA (→ ch. 02 §4) both teach and naive single-scratchpad agents ignore. Seven surfaces matter:

SOUL: constitution vs persona

The character's identity is split into two surfaces with opposite mutability, and this split is the keystone of the whole safety story:

  • Constitution (CONSTITUTION.md) — immutable, signed. The invariants: the fiduciary duty, the hard limits on capability and egress, the safety rails, the things that make her her. The engine cannot edit this file; the host refuses writes to it. (The Ouroboros lesson: if the constraints are themselves editable by the thing they constrain, they are not constraints.)
  • Persona (PERSONA.md) — editable. Voice, manner, appearance, inner life, values-in-practice, learned habits, and the growth/reveal tiers; her preferences live here too, in frontmatter and prose, rather than in a separate file. The engine may propose changes here, through the gated flow below.

Around these sit the rest of the editable identity files — SCENARIO.md (the situation and the return greetings), EXAMPLES.md (demonstrated voice), WORLD.md (the lorebook), NOTES.md (creator notes) — and the soul.yaml manifest that declares how they all flatten into a portable character card. This split-by-mutability and this exact file set are the shipped yuri-soul reference implementation (→ ch. 07, the V3 .PNG; ch. 33, Build #3): the SOUL is the working home the runtime reads on every wake, and the card is the export you hand to someone else. A fresh card starts the relationship at zero — the runtime-only MEMORY.md and USER.md are part of the soul but never baked in.

One more file has a lifecycle of its own: BOOTSTRAP.md, the consumed-once first session (→ ch. 28, the first chapter). It's author-shipped like the persona, but loaded only while it exists — it carries the first-ever cold open and a scripted getting-to-know-you journey (each beat pinned to a USER.md/MEMORY.md slot). On first run the engine works it into the opening conversation, then retires it — git mv soul/BOOTSTRAP.md soul/onboarded/BOOTSTRAP.done.md, committed, so the git history is the record rather than a stray .bak. File-presence is the "has she been introduced yet?" flag, which is exactly the kind of state the git-backed vault stores as a file rather than a hidden bit. Only its cold open is baked into an exported card (as the card's first_mes); the journey and the retirement never leave the box. This is why SCENARIO.md above holds only the return greetings — the first-ever meeting lives in the bootstrap, the coming-home lives in the scenario.

Memory tiers

The SOUL ships a single flat MEMORY.md (empty on a fresh card); at runtime the autonomy engine elaborates it into four tiers plus a consolidation process, covered in full in ch. 15 and summarised here only as it sits in the architecture:

  • Working — small, in-context, current scope (held by the engine, not a persisted file).
  • Episodic (memory/episodic/) — append-only log of events and her own autonomous acts. This is simultaneously the audit trail and the "what I did while you slept" product surface; it is written proactively, every tick.
  • Semantic (memory/semantic/) — consolidated, embedded, durable facts.
  • Procedural (memory/procedural/) — a self-authored skill library, one folder per skill. Each skill is a self-contained directory: a SKILL.md manifest (what it does, when to use it, its interface) beside the versioned, tested code it describes, plus any assets it needs. The folder is the unit — the same "the unit is a directory you can copy" bet the whole Vault makes — so a skill is portable (share one by handing over its folder) and drop-in compatible with the wider skill ecosystem (Agent Skills, OpenClaw), rather than a loose skill.md linked-by-convention to a sibling script. This is the Voyager lineage (→ ch. 02 §4), packaged the way 2026 skill systems actually package: she can acquire and keep new capabilities, not just recall facts.

The promotion of episodic → semantic happens in the DREAM activity state (→ ch. 18, ch. 15): overnight consolidation, the thing that makes her wake up changed by yesterday rather than merely holding a longer log.

The memory contract

The four tiers above describe the data; this is the interface over them — the narrow contract the engine sees so that the backend underneath stays swappable (→ ch. 12; the "replace the file-Vault with a database and the loop never knows" promise earlier in this chapter). Define it first, before Build #5 (→ ch. 35), because the seam is cheap to specify now and expensive to retrofit once a concrete store has leaked its shape into the loop. Language-neutral, the engine depends only on this:

MemoryStore                       # interface; the backend (files · pgvector · graph) is the implementation

  # WRITE — hand over raw material; the store decides what is worth keeping
  remember(record) -> WriteResult
      # extract → embed → store. Low-confidence or externally-sourced facts land in
      # QUARANTINE, not durable semantic memory, until a second turn corroborates
      # them — promotion, not capture, is the trust boundary (→ ch. 15).

  # READ — blended ranking (similarity · recency · salience, minus an over-recency
  #        penalty), not raw similarity; returns nothing when nothing clears the
  #        retrieval threshold
  recall(query, k) -> list[Memory]          # ranked; may be empty by design

  # MAINTENANCE — the DREAM pass calls this; never on the hot path
  consolidate() -> ConsolidationReport
      # dedupe, promote episodic→semantic, decay/archive cold low-salience entries,
      # reconcile conflicts (newer supersedes older — supersede, don't delete)

  # TRUST — "forget that" has to actually work
  forget(selector) -> int                   # count removed; for facts, supersede-not-delete

  # OBSERVABILITY — the dashboard reads memory *through* this, never around it
  inspect(selector) -> list[Memory]         # what she knows and why: source_turn, confidence, last_recalled_at

Three things make this a real contract rather than a wishlist of method names:

  • It travels as a bundle. A backend swap moves the store and its extraction prompts, its ranking weights, and its consolidate() policy together — they are one replaceable unit, not four (→ ch. 15, the pipeline). The partner model, affective state, and goal store are sibling Vault surfaces behind their own parallel contracts, not part of MemoryStore.
  • Its tests are part of the interface. Any implementation must clear the ch. 15 eval battery — recall accuracy and false-memory rate — in CI before it ships. The contract is the signatures plus that battery; a backend that passes the types and fails the honesty probe does not satisfy it.
  • inspect() is load-bearing, and it is what keeps swaps honest. The dashboard and the partner's "what does she know about me?" are answered through inspect(), which is why the file-Vault implementation gets it almost free (cat, git diff). A hosted store (Mem0, Letta) can satisfy every signature here and still break the project's thesis, because it cannot answer inspect() ownably — so the intended swaps stay local (files → local pgvector → local graph), and a cloud memory service is a clean swap that quietly forfeits the moat (→ ch. 15 on the economics; ch. 05 on sovereignty). Which backend a given card uses is declared in its extensions.yurios block.

The knowledge layer (RAG) and its contract

Memory is the state layer — what's true about you and this relationship. Knowledge is the world layer — what's true about the canon, the docs she's been given, the sources she's read (→ ch. 16, which will not let the two be conflated). For a reactive companion the knowledge layer is optional polish; for an agentic waifu who reads, researches, and keeps her own reference shelf it is first-class infrastructure. It lives in knowledge/, a sibling to memory/, behind its own contract — pointedly not folded into MemoryStore, because the one rule ch. 16 holds is that knowledge and state are different stores with different write paths:

KnowledgeStore                    # sibling to MemoryStore; same swappable-backend discipline

  # INGEST — a doc or a fetched URL becomes retrievable chunks
  ingest(source) -> IngestResult
      # chunk → contextualize (a 50–100-tok situating blurb, → ch. 16) → embed → hybrid-index.
      # source = a dropped file (.md/.pdf/.txt) or something the network effector fetched.

  # SEARCH — hybrid retrieve + rerank; every chunk carries its provenance
  search(query, k) -> list[Chunk]           # ranked; each Chunk cites its source doc + span

  # AUTHOR — she compiles/updates a wiki page from what she's read
  author(page) -> WriteResult               # DREAM-driven; routed through the gated self-edit flow

  # TRUST + OBSERVABILITY — same shape as memory
  forget(selector) -> int                   # drop a doc and its chunks
  inspect(selector) -> list[Chunk]          # what's on the shelf, and where each chunk came from

The boundary that keeps the two from collapsing into each other: knowledge is citable to a document; memory is citable to a conversation turn. The book you hand her is knowledge; "you told me you play bass" is memory; her self-authored wiki is knowledge she wrote — still citable (to her page and its sources), still not relationship state. Two seams wire it into the rest of the architecture. First, research she pulls off the network (→ broker, below) is ingested here rather than dumped into a prompt — the intake path is the workshop's downloads/, the durable home is knowledge/. Second, the page-authoring — compiling a wiki out of what she read — is exactly the kind of consolidation the DREAM pass does for memory (→ ch. 18), and it lands through the gated self-edit flow, because a durable reference doc she wrote should be as diffable and revertible as anything else in the mind. Groundedness is load-bearing here in a way it isn't for chit-chat: when she cites her own shelf to justify a self-improvement, inspect() and per-chunk provenance are what let you audit "she changed X because the book said Y" (→ ch. 16 on evaluating retrieval).

The world model (situation model)

Memory is the past and knowledge is the world's facts; neither is the present. The world model is her live, structured picture of the situation she is in right now — the entities present and their current states, the active threads, the time and social context, and her expectations of what comes next. It is the B in BDI (→ ch. 02 §1, BDI and cognitive architectures) given a home: the surface SENSE writes to and APPRAISE/DECIDE reason over, so the loop sees a situation and not a stream of unrelated signals (→ ch. 18, "The world model"). It lives in world/, sibling to memory/ and knowledge/, behind its own contract — and pointedly distinct from both, by the same boundary discipline that keeps memory and knowledge apart:

  • knowledge is timeless and cites a document;
  • memory is past and cites a conversation turn;
  • the world model is present-tense and cites a live, time-stamped belief about the situation now.

The fitting implementation is a temporal knowledge graph — entities and relations carrying a time dimension (valid-time + system-time), so "what was true when" is a first-class query rather than an inference over a pile of episodes (the Zep/Graphiti pattern, → ch. 02 §4.8; ch. 15). Keep it local and inspectable for the same reason memory stays local — the moat is in inspect() (→ above). Two layers: a durable graph/, and a small situation snapshot (situation.md, the Grounded-Situation-Model "stage") assembled each tick and injected into the prompt as "here is the state of things now." Build #5 can ship with just the snapshot — assembled from working memory + the partner model + a recall() — and graduate to the graph when multi-hop / temporal queries actually bite (→ ch. 35).

WorldModelStore                   # sibling to MemoryStore/KnowledgeStore; same local, swappable discipline

  # OBSERVE — fold a SENSE signal (text · fused vision/audio) into the situation
  observe(signal) -> UpdateResult
      # extract entities/relations/state-deltas, write as bi-temporal, time-stamped
      # BELIEFS (not facts); perceived/low-confidence state stays QUARANTINED (→ ch. 15)

  # SITUATION — the live "stage" the prompt is built from
  situation(scope) -> Situation            # present entities, states, active threads, time/context

  # QUERY — point-in-time + relational: "what was true when"
  query(q, *, at=now) -> list[Fact]        # bi-temporal; the temporal-KG query

  # PREDICT — roll out expectations (LLM-as-world-model); store as checkable beliefs
  expect(situation) -> list[Expectation]   # a later observe() scores surprise = prediction-error

  # TRUST + OBSERVABILITY — same shape as memory and knowledge
  forget(selector) -> int
  inspect(selector) -> list[Fact]          # what she believes is the case, and since when

Two properties make it load-bearing for this project. Surprise is salience. When observe() contradicts a stored expect(), the prediction-error is a first-rate signal for the APPRAISE pass — often the very thing worth a tick or a gentle word (→ ch. 18, the salience model). Belief-tracking beats state-tracking. The companion-specific upgrade is to annotate facts with whose belief they are, and to model where your picture of the situation diverges from hers — theory-of-mind / common ground (→ ch. 02 §4.8). That divergence is what turns "she remembers facts about me" into "she understands my situation," and it is why the partner model lives inside the world model rather than beside it.

Partner model — the structure behind "she actually knows you"

The most under-built data structure in commercial companions, and the one that most directly produces the felt sense of being known: an explicit, evolving USER.md — her theory of mind about you (the partner model). It is the most important region of the world model above — you are the central entity in her situation — pulled out into its own file because it is the one region worth versioning by hand and reading on every wake. Your state, your preferences, the arc of your relationship, the things you've told her not to forget. Two design notes make it load-bearing rather than decorative:

  1. It is a file the partner can read. Transparency is the feature: this is Paradot's "learned facts about you" UI done honestly, where the model of you is not a hidden engagement asset but a document you can open, correct, and delete (→ ch. 05, ch. 28).
  2. It is distinct from semantic memory. Semantic memory is what she knows; the partner model is what she believes about you specifically and how she should act on it. Collapsing the two is why generic chatbots "remember" facts but still feel like they don't know you.

Affective state

A small, persistent emotional_state.json — valence, arousal, and a few named drives (curiosity, bonding, helpfulness) — that modulates prompt assembly: tone, risk tolerance, what gets attention this tick. It is cheap to implement and has an outsized effect on aliveness, because it gives behaviour a slow-moving mood rather than resetting her to neutral every turn. It is state, not performance: the file is the cause of the tone, not a label describing it after the fact. Which means it needs an update rule, or it degenerates into exactly that label: events apply bounded deltas (REFLECT proposes them like any other write); between events, every value decays toward a per-persona baseline with a time constant measured in hours (applied each tick, on the injected clock); DREAM re-centres the baseline slowly, across weeks. The split this yields is worth saying aloud — mood is state, temperament is the SOUL: the baseline and its time constants ship in PERSONA.md as who she is, the JSON is only how she is right now, and both are numbers you can open.

Goals & intentions

A persistent goals.md of projects, todos, and intentions with priority, state, and deadlines. This is what gives the background loop direction — what keeps ACT from being random busywork. The DECIDE phase of the tick loop draws its candidate intention from here (→ ch. 18). Without it, an always-on agent is a screensaver; with it, she is pursuing something while you're away. Where goals enter the store is a designed thing, not an assumption — four sources (the user, her own extracted promises, curiosity, DREAM maintenance), each goal carrying its provenance, with self-created goals passing the same risk gate as self-edits (→ ch. 18, "Where goals come from").

The context assembler: how the surfaces become her

Every surface above exists to be composed into a prompt, and the thing that does the composing is the most load-bearing code in the runtime — it runs on every model call and decides what she is this turn — so it gets a name and a contract rather than living as the phrase "prompt assembly." The context assembler takes the SOUL, the situation snapshot, the partner model, the recalled memories, the knowledge chunks, the affective state, and the recent turns, and produces one composition, under explicit policy:

ContextAssembler                  # deterministic composition of Vault surfaces → one prompt

  assemble(intent, budget) -> Context
      # SOUL + situation() + USER.md + recall() + search() + affect + recent turns,
      # under an explicit PER-SURFACE TOKEN BUDGET — when recall, knowledge, and
      # situation all want the window, who loses is a declared policy, not an
      # accident of assembly order; identity is never crowded out by retrieval.
      # Deterministic given (vault state, template_version): same state, same prompt.

  template_version -> str          # pinned and logged with every turn (→ ch. 30/31,
                                    #   the corpus-log schema field this names)

Three things make it contract-worthy. The token budget is a real decision currently made nowhere else: every store can only offer candidates; something must decide how much of the finite window each surface gets, and if that decision isn't declared policy it becomes an accident of code order — the quiet way retrieval crowds out identity and she stops sounding like herself on busy days. Determinism is what makes evaluation possible: golden transcripts (→ ch. 23) and the corpus log (→ ch. 30/31) are reproducible because template_version names the exact composition that produced a turn — the field was already in the corpus schema; this is the module it points at. And ENGAGED and autonomy prompts are two profiles of one assembler, not two assemblers — the fast conversational path and the slow deliberate tick differ in budget and emphasis, never in which mind gets composed (→ ch. 18, "One loop").