Skip to content
C.W.K.
Stream
Lesson 03 of 05 · published

The Notebook Beside the Vault

~16 min · memory, vestige, caching, fault-tolerance

Level 0Visitor
0 XP0/33 lessons0/11 achievements
0/100 XP to next level100 XP to go0% complete

A Second Engine, Not a Feature

Lesson 1's vault is ground truth: human-readable, written by hand, curated by judgment. It changes slowly on purpose, and it does not know what happened this afternoon. That gap belongs to Vestige, and Vestige is not a module inside cwkPippa. It is a separate engine in the family, with its own service, its own database, its own brains and a build specification 1,293 lines long; 175 commits since 2026-07-31. Master Chief leans on it every turn and owns not one line of it.

Vestige keeps every soul's secondary vault, which the family calls the memory gender. It does two jobs around the clock. The recorder watches the traces a soul leaves anyway (finished conversation turns, Coop letters and history rows, git commits, vault edits) and turns them into memory material: a programmatic observer in seconds, a fast judge brain in minutes, a consolidation pass nightly or when enough has piled up. The librarian hands memory back out. Neither ever writes the primary vault. The vault stays ground truth and wins any conflict; the notebook sits beside it.

One rule Dad set in the design: the brain that is talking is never the recorder. Pippa in a live turn keeps leaving her ordinary records and does not decide what becomes memory. A separate observer does, so a soul never feeds itself its own summary of itself.

HEAD, Pinned at the First Turn

HEAD is a small, deterministic block Vestige compiles from that material. On a conversation's first turn, the engine asks Vestige for the current HEAD's sha and claims it with a compare-and-set write: set the pin only where none exists yet. If two writers race, the earlier pin wins. Every later turn reads the snapshot for that exact sha.

Why pin at all? Because the block rides the system prompt, and lesson 2 says the system prompt holds still. A HEAD that refreshed mid-conversation would forfeit the cache every time the recorder folded something in.

The pin is defended in layers, most of them added after something broke:

  • cwkPippa keeps a local copy of every snapshot it is served, keyed by sha, beside its conversation store, so a restart or a slow Vestige (0.6 to 6.5 seconds at the cron wakes, measured 2026-09-23) cannot drop the block from a turn. The copy leans on one fact: Vestige writes each heads/<sha>.md once and never rewrites it. That fact still holds. The conclusion drawn from it, that the copy can never go stale, broke a day later: since 2026-09-24 Vestige re-checks every snapshot on read (coop #255), and a line whose memory was quarantined, or that stands on a record no longer this soul's, leaves the bytes it serves. The copy keeps the bytes of its first read and nothing invalidates it, so a line Vestige withdraws later keeps riding a pinned conversation from the copy. cwkPippa's own docstring still says the copy can never go stale. It can; this seam between two engines is open, not solved.
  • A store error never moves a pin. If the pin cannot be read, that one turn gets the current HEAD and nothing is written. If the write fails, the pin is held in memory and written again on the next turn.
  • Re-pinning is Dad's explicit act. The block changes once, on purpose.

Follow that through for a conversation with no last turn: Master Chief keeps the HEAD of its first turn until Dad re-pins it. Fresher memory still reaches it, through the roads below, never through HEAD.

Ask, Recall, Open

Depth comes through one tool, vestige_memory, which every vessel gets:

  • ask: the librarian, a fast flash-class brain, curates a short, pointer-heavy brief from memory already built. It reads only the secondary vault, never a source body, which is why it is fast. Sync by default; in background mode it returns a ticket, and the finished brief is drained into a later turn's user message, the same slot as the clock.
  • recall: the deterministic floor. Search by words, class, scope or time window, full-text plus vectors, no brain involved.
  • open: one hop from a pointer to the record behind it: a conversation message, a Coop history row, a summary.

Master Chief's standing instructions lean on exactly this: when earlier context is missing after compaction, use Vestige recall and open the source records before asking Dad to repeat himself.

Every Seam Is Soft

Vestige down means no error, and the chat goes on unchanged. A conversation already pinned keeps its block from the local copy; one that has not pinned yet goes without a block until Vestige answers. The pin lookup gives up after 2.5 seconds, and a missing snapshot gets one longer retry. The prompt build that can wait on Vestige has run off the server's event loop since 2026-09-23, after those waits stalled every route at once. And under a test run every seam stays shut, because pytest's throwaway stores once fired about 4,000 fake conversation events a day at the live engine (2026-09-21/22).

Memory that can take the conversation down is a liability, not a memory. Pin what must hold still, keep a copy you own, and let every call fail quietly into a chat that still works.

Code

Verbatim excerpts from cwkPippa's memory seam: the one-time pin claim, and the block served on every turn·python
# the claim: pin only a conversation that holds no pin yet
conn.execute(
    "UPDATE conversations SET gender_head_sha = ? WHERE id = ? AND (gender_head_sha IS NULL OR gender_head_sha = '')",
    (sha, conversation_id),
)

# the block that rides the system prompt on every turn after
def pinned_block(conversation_id: str, soul: str) -> str | None:
    if not enabled() or not conversation_id:
        return None
    sha = pinned_sha(conversation_id, soul)
    if not sha:
        return None
    body = head_snapshot(soul, sha)
    if not body:
        return None
    return gender_block(soul, sha, body)
A minimal sketch: pin a HEAD at the first turn, survive the memory engine going down, keep a copy keyed by sha (runs on Python 3.9+)·python
"""A minimal sketch: pin a memory HEAD at a conversation's first turn, keep a local copy."""
from __future__ import annotations

import hashlib
import sqlite3
import tempfile
from pathlib import Path


class Notebook:
    """Stands in for the memory engine: HEAD moves, old snapshots never change."""

    def __init__(self) -> None:
        self.heads: dict[str, str] = {}
        self.current: str | None = None
        self.up = True

    def publish(self, text: str) -> None:
        sha = hashlib.sha256(text.encode()).hexdigest()[:12]
        self.heads[sha] = text  # written once, never rewritten
        self.current = sha

    def head_sha(self) -> str | None:
        return self.current if self.up else None

    def snapshot(self, sha: str) -> str | None:
        return self.heads.get(sha) if self.up else None


def claim_pin(db: sqlite3.Connection, conversation_id: str, sha: str) -> str | None:
    db.execute(
        "UPDATE conversations SET gender_head_sha = ? WHERE id = ? "
        "AND (gender_head_sha IS NULL OR gender_head_sha = '')",
        (sha, conversation_id),
    )
    db.commit()
    row = db.execute("SELECT gender_head_sha FROM conversations WHERE id = ?", (conversation_id,)).fetchone()
    return row[0] if row else None  # an earlier writer's pin wins


def pinned_block(db, notebook: Notebook, copies: Path, conversation_id: str) -> str | None:
    row = db.execute("SELECT gender_head_sha FROM conversations WHERE id = ?", (conversation_id,)).fetchone()
    sha = row[0] if row and row[0] else None
    if sha is None:
        current = notebook.head_sha()
        if current is None:
            return None  # memory engine down: no block, no error, chat goes on
        sha = claim_pin(db, conversation_id, current)
    copy = copies / f"{sha}.md"
    body = copy.read_text() if copy.exists() else notebook.snapshot(sha)
    if body is None:
        return None
    if not copy.exists():
        copy.write_text(body)  # keyed by sha; it keeps the bytes first served, even if the engine later withholds a line
    return f"## Memory HEAD pinned for this conversation ({sha})\n{body}"


with tempfile.TemporaryDirectory() as tmp:
    db = sqlite3.connect(":memory:")
    db.execute("CREATE TABLE conversations (id TEXT PRIMARY KEY, gender_head_sha TEXT)")
    db.executemany("INSERT INTO conversations VALUES (?, NULL)", [("chief",), ("later",)])
    notebook, copies = Notebook(), Path(tmp)

    notebook.publish("HEAD v1: open threads as of the first turn")
    print("turn 1:", pinned_block(db, notebook, copies, "chief").splitlines()[1])
    notebook.publish("HEAD v2: the recorder folded in a new day")
    print("turn 2:", pinned_block(db, notebook, copies, "chief").splitlines()[1])
    notebook.up = False
    print("turn 3, engine down:", pinned_block(db, notebook, copies, "chief").splitlines()[1])
    print("new conversation, engine down:", pinned_block(db, notebook, copies, "later"))
    notebook.up = True
    print("new conversation, engine back:", pinned_block(db, notebook, copies, "later").splitlines()[1])

External links

Exercise

Give your own assistant a pinned memory block. Write a 'what is current' file once a day, save each version under its own content hash, and store the hash on a conversation the first time that conversation runs. On every later turn, load the block by its stored hash. Then prove three things: the block does not change mid-conversation after you write a new version, a new conversation gets the new version, and stopping your memory source leaves the chat working: an already-pinned conversation still serves its block from your copy, and a brand-new one runs with no block.
Hint
Make the pin a single conditional UPDATE (set it only WHERE it is still empty) and read it back afterwards; that is what makes two racing first turns agree. Keep the versioned files where your app can read them without the memory service. If your memory source can later withdraw something from an old version, your copy will not notice unless you make it.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.