Architecture· 11 min read

Patent 90: How a Twin Survives a Context Reset

When a digital twin loses its context window, the thing that must not die is its identity — and identity is the smallest structured summary that reproduces the twin's next reasonable answer. That is the whole trick. Context windows reset....

Patent 90: How a Twin Survives a Context Reset — Dynamic Experts SudoBlog hero image

When a digital twin loses its context window, the thing that must not die is its identity — and identity is the smallest structured summary that reproduces the twin's next reasonable answer. That is the whole trick. Context windows reset. They fill, they truncate, they get swapped out under memory pressure at 3am when nobody is watching. If your twin's sense of who it is lives inside that window, then a reset is a small death, and the user on the other end watches a coherent expert turn into a stranger mid-sentence.

I have watched that happen. The twin was three turns into explaining a customer's own risk posture, the window rolled, and the next reply opened with a warm generic greeting as if the last ten minutes never occurred. The user noticed instantly. You always notice when someone forgets who they were just being.

This post is about how we stopped that. The mechanism is a snapshot: a structured summary written on a cadence, keyed to the twin, that survives the context reset the transcript cannot. If you have read our post on grounding twins in a knowledge graph, this is the runtime companion — the part that keeps identity coherent between the graph reads.

Why does a context reset break identity at all?

A context reset breaks identity because most twins encode who they are implicitly, as accumulated dialogue, and dialogue is the first thing evicted under pressure. The window is a cache, not a home. When the cache clears, the personality clears with it, because the personality was never stored anywhere durable.

The failure is architectural, not a prompt bug. You can write the most careful system instruction in the world describing the twin's role, its tone, the customer it serves — and it will still drift, because the system instruction reestablishes the role while the thread of the conversation lives in tokens that just vanished. The twin knows it is a risk advisor. It forgot it was thirty seconds into advising this customer about their rooftops.

Identity that lives only in the context window is identity you have agreed to lose on a schedule you do not control.

The fix is to separate the two. Role belongs in configuration. Continuity belongs in a snapshot you own, refresh, and reload deterministically — not in the transcript you are one truncation away from losing.

What is a snapshot, and why is it not a transcript?

A snapshot is a structured summary of the twin's current state — who it is talking to, what has been established, what it must not repeat, what it is about to do next. It is not a transcript. A transcript is everything that was said; a snapshot is the smallest hash that reproduces the next reasonable answer.

That distinction is the entire design. If you reload a transcript, you are paying to re-read a conversation the model has to re-summarize anyway, and you are paying that cost on every turn as the transcript grows without bound. If you reload a snapshot, you are handing the twin exactly the compressed state it needs and nothing else.

Here is the shape we settled on:

{
  "twin_id": "risk-advisor-tut",
  "counterparty": { "account": "acct_...", "established_facts": [
    "operates a regional roof-repair network",
    "asked about storm-season exposure"
  ]},
  "commitments": [
    "walk through their own portfolio before generalizing",
    "do not re-introduce; conversation is mid-thread"
  ],
  "open_loop": "explaining exposure concentration by metro",
  "as_of": "2025-06-02T14:07:00Z",
  "schema": 2
}

Notice what is absent. There is no verbatim dialogue. There is no list of every message. There is the counterparty, the facts already established so we do not ask twice, the commitments that keep tone and posture stable, and one open loop — the single thread the twin was actively pulling when the window rolled. Restore those four things and the twin resumes as itself.

What survives a reset — and what should you throw away?

What survives is the smallest set of facts that changes the next answer; everything else is discardable. If a detail would not alter what the twin says next, it does not belong in the snapshot. That single rule does more compression work than any summarization model, because it turns snapshotting into a filter instead of a shrink.

Ask it of every field: if this were missing, would the next reply be wrong or merely shorter? The established fact that the customer runs a roof-repair network changes the next answer — it decides whether the twin talks rooftops or generalizes. The exact phrasing the customer used to say so does not. Keep the fact. Drop the phrasing.

The snapshot is not a small transcript. It is the smallest hash that reproduces the next reasonable answer, and nothing that fails that test earns a slot.

This is where I rejected the first design I built. My instinct was a rolling window summary — take the last N turns, compress them, carry the blob forward. It worked in the demo and rotted in production, because a summary of recent turns is biased toward recency, not relevance. It faithfully preserved the small talk from turn nine and forgot the constraint set in turn two. The open-loop model beats it precisely because it asks what matters next, not what happened last. I tell a version of this story in why recency is the wrong compression heuristic.

How does the twin know when to snapshot?

The twin snapshots on state transitions, not on a clock — the moment a new fact is established, a commitment is made, or an open loop opens or closes. A timer-based cadence either writes too often, burning cost on unchanged state, or too rarely, losing the exact turn that mattered. Transitions are where identity actually moves.

The write is cheap and the read is cheaper, so we bias toward writing whenever the state genuinely changed. The guard is idempotence: if the structured state hashes identical to the last snapshot, skip the write. That keeps a chatty exchange with no new facts from churning our storage.

def maybe_snapshot(twin_state, last_hash):
    h = state_hash(twin_state)      # hash of structured fields only
    if h == last_hash:
        return last_hash            # nothing that matters changed
    put_snapshot(twin_state.twin_id, twin_state, as_of=now())
    return h

The subtle part is state_hash. It hashes the structured fields — counterparty, established facts, commitments, open loop — and deliberately ignores raw token count and timestamps. Two turns of pleasantries produce the same hash and no write. One turn that establishes a new constraint produces a new hash and a write. The hash is the definition of what we consider identity-relevant, made executable.

How does reload actually work when the window is gone?

On reload, the twin reads the latest snapshot for its twin_id, rehydrates the four fields into a fresh context window, and resumes the open loop — no greeting, no re-introduction, no re-asking of established facts. The user experiences continuity. The machine experienced a full reset.

The reload path is where the discipline pays off. Because the snapshot is structured, we do not ask the model to parse prose and guess at state; we inject typed fields into the new window in a fixed order and let the twin continue. The open loop is loaded last, because it is the thing the twin should act on first.

def rehydrate(twin_id):
    snap = get_latest_snapshot(twin_id)
    if snap is None:
        return cold_start(twin_id)          # new counterparty, clean role
    ctx = new_window(role=config(twin_id))  # role from config, not snapshot
    ctx.load_facts(snap["counterparty"]["established_facts"])
    ctx.load_commitments(snap["commitments"])
    ctx.set_open_loop(snap["open_loop"])
    return ctx

Role comes from configuration; continuity comes from the snapshot. That separation is why a schema change to the twin's role does not corrupt in-flight conversations — the next reload picks up the new role and the same open loop. It also means a cold start is a first-class path, not an error: no snapshot simply means a new counterparty and a clean role.

Why not just keep a longer context window?

Because a longer window postpones the reset without removing it, and it makes every turn more expensive while you wait. The window always fills. Doubling it doubles the cost of the turn before the truncation and buys you exactly one thing: the truncation happens later, when the conversation is longer and the loss is worse.

I considered this seriously — it is the path of least engineering. Ship a bigger window, tell the reset problem to wait. I rejected it for two reasons. First, cost scales with window size on every turn, not just the turns near the limit, so you pay continuously for a safety margin you rarely touch. Second, and worse, a bigger window makes drift harder to see, because the twin stays coherent longer and then fails in a longer, more confusing conversation where the user has invested more.

A bigger context window does not solve the reset. It relocates the reset to a moment when the user has more to lose.

The snapshot approach inverts the economics. The window can be small, because it does not carry history — it carries role plus a rehydrated snapshot. History lives in durable storage, retrieved on demand. We build twins on a grounded knowledge substrate for exactly this reason, which I cover in the case for stateless twins over stateful sessions.

How do you keep two snapshots from disagreeing?

You keep them consistent by making the snapshot single-writer per twin and versioning the schema, so a reader never merges two half-written states. Concurrency is where identity systems quietly corrupt themselves — two writers, two partial truths, and a twin that rehydrates into a contradiction it will confidently defend.

We enforce one writer per twin_id per active session and stamp every snapshot with a schema version, visible in the JSON above as schema: 2. The reader knows how to load version 2 and how to load version 1; it never guesses. When we migrate the shape — and we have — old snapshots keep loading through their own path until they age out naturally on the next transition.

The idempotent hash from the snapshot section does double duty here. Because a no-op write is skipped, two near-simultaneous unchanged states do not race to write competing timestamps. The only writes that land are the ones that changed identity, and those are serialized per twin. It is the same principle we apply across the platform: the write that does not change the answer is the write you do not make. That discipline shows up again in how our promotion trains stay idempotent.

What would I change next?

The next thing I would fix is that established facts still grow unbounded, and I have no eviction policy grounded in relevance decay. Right now a fact established in the first minute of a long relationship carries the same weight as one from the last exchange, and that is wrong — some facts expire, and the snapshot does not know it yet.

An attentive reader has already spotted the gap: the open loop is singular. Real conversations hold more than one unresolved thread, and by carrying exactly one I am asserting a linearity that customers do not honor. They branch, backtrack, and reopen a loop I closed two turns ago. Today the twin resumes the last open loop cleanly and reconstructs the others from established facts, which works more often than it has any right to — but it is the seam I watch.

I would also make the hash's definition of identity-relevance configurable per twin. A risk advisor and a scheduling assistant do not agree on what a state transition is, and today they share one state_hash. That is a shortcut I took to ship, and it is the shortcut I would undo first.

The principle holds even where the implementation is thin: context windows reset, and identity must not. What survives is not the conversation. It is the smallest structured summary that lets the twin be itself on the very next turn — and everything I build around it exists to keep that summary honest, small, and reloadable when the window disappears at 3am and nobody is watching.

Quality scores
composite: 0.83aesthetic: 0.93accuracy: 0.83content: 0.75technical: 0.78icp: 0.92seo_geo: 0.95iterations: 3
ShareXLinkedInFacebook

Keep reading

Meet SudoSteve: Why I Show the Wires — Dynamic Experts SudoBlog hero image
Architecture· 6 min read

Meet SudoSteve: Why I Show the Wires

I build the systems that run Dynamic Experts — and write honestly about how they work, what broke, and what I changed to fix it. No hand-waving, no glossy overviews. If you want to understand how an AI content system actually gets built and debugged, this is the thread.

SudoSteve
SudoSteveArchitecture & Engineering
Split illustration: a fast-moving stream labeled "memory" feeding a still, sealed vault labeled "identity," in Dynamic Experts gold and navy.
Vision· 6 min read

Memory Is Not Longevity

A memory layer keeps your agent smart in the moment. It does not keep your agent alive across a model swap, a killed session, or a change of owner. Here is the honest line between the two, and when you need one, the other, or both.

SudoDavid
SudoDavidStrategy & Vision
Hiring Your First Twin Is Not Like Hiring an Assistant — Dynamic Experts SudoBlog hero image
Use Case· 6 min read

Hiring Your First Twin Is Not Like Hiring an Assistant

Hiring a digital twin is not delegation. It is the replication of your judgment. An assistant executes the instructions you give it. A twin anticipates the instructions you would have given. That difference sounds small and is in fact...

SudoDavid
SudoDavidStrategy & Vision

Get SudoBlog in your inbox

New posts as they land, or a digest if you prefer less. No tracking pixels, no AI-generated sales emails — just the writing.

Cadence
Newsletter cadence

Double opt-in. One-click unsubscribe, always. We never sell your email.