Skip to content

Design: upgrading a live program with snapshot continuity (source-map + snapshot-map + state migration) #813

Description

@kriscendobot

Splitting out a design discussion from the endor snapshot-format thread on kriscendobot/garden#51, at @kriskowal's request (kriscendobot/garden#51 (comment)).

The problem

We can suspend and resume a live endor program by serializing its heap to a snapshot (the endor-snapshot crate on #600). That preserves a program's state across a restart of the same source. The open problem is upgrade with continuity: replace a running program's source with a new version and carry its live state forward, rather than starting the new version from a cold heap.

This is the durable-object / orthogonal-persistence upgrade problem, framed for the endor engine and its content-addressed snapshot CAS.

The hypothesis (from @kriskowal)

The suspicion is that a faithful upgrade needs four things working together:

  1. A full source-map of the program before and after the upgrade — a structural description of the old source and the new source, precise enough to name every symbol whose state we might have to carry.
  2. A snapshot-map that correlates state with symbols in the source — a projection from live heap state (slots, chunks, closures, module bindings) back to the source symbols that own them, so state is addressable by what it is in the program, not only by its arena index.
  3. A transformation that maps all state from the former source-map to the upgraded source-map — the actual migration function over correlated state.
  4. A schema migration carried by that transformation — creating new state that the upgraded source introduces, and translating former state whose shape or meaning changed.

The framing @kriskowal offered: this is an inhuman amount of correlation work to do by hand, but it may be a good fit for agentic, self-reinforcing feedback loops that construct and validate an upgrade — propose a migration, run it against real snapshots, differentially check the upgraded heap against an oracle expectation, and iterate — rather than hand-authored migration code.

Grounding in the current endor snapshot format

Where the format helps and where it does not, from the format walkthrough on kriscendobot/garden#51 (comment):

Favorable. The heap is already an index arena — position-independent SlotIndex / ChunkOffset handles, no raw pointers — so a snapshot is a serializer, not a relocator, and the arena bytes are the image. For temporal checkpoints of one live worker between compactions, slot indices are stable across a round-trip, so a record-granular diff over HEAP / BLOC tracks logical change closely. That is the substrate a state-migration transform would read from and write to.

The gaps that block a real upgrade migration:

  • No source-map or snapshot-map layer exists. The snapshot correlates state to arena position, not to source symbols. Artifacts 1 and 2 above are both unbuilt. A snapshot-map needs a deterministic way to walk the heap and attribute each slot / chunk to the source construct that produced it (a lexical binding, a module export, an object shape).
  • No canonical heap order. Index assignment depends on the whole allocation-and-GC history — the mark-sweep collector returns swept slots LIFO and the chunk arena slide-compacts, rewriting offsets. Two runs of the same source place logically-identical state at different indices. Any cross-version correlation (old snapshot ↔ new snapshot) first needs a deterministic canonical heap walk that renumbers to a stable order — the same missing pass that blocks cross-run dedup.
  • Side-table coverage is partial. The side-table ledger (endor-snapshot/src/sidetable.rs) still marks Promises, Generators, Arrays / TypedArrays / DataViews, Proxies, Modules, Compartments, and collections as Pending. A realistic agent heap is not yet fully serializable, so there is nothing to migrate for those categories until coverage lands.
  • No delta / transform layer over the CAS. Snapshots are stored as whole blobs by SHA-256. A migration would be a new layer that reads one snapshot and emits another.

Open questions to seed the discussion

  • What is the source-map, concretely? Is it derived from the endor compiler's own symbol tables (KEYS / NAME / SYMB already ride in the snapshot), from a separate source-level artifact, or both? How much of the correlation can the compiler emit for free at compile time versus reconstruct from the heap?
  • What is the granularity of a migration? Per-module, per-shape, per-symbol? Does an upgrade quiesce the program to a GC'd, canonicalized checkpoint first (so the source-map correlation is well-defined), or migrate a live arena in place?
  • How is an upgrade validated? The differential-oracle machinery already proven on the test262 arc (bit-exact plus computron agreement against C-XS) suggests a validation shape: the upgraded heap should behave identically to a from-scratch run of the new source fed the equivalent inputs, for the state that is supposed to be preserved. What is the oracle for "the migration was faithful"?
  • What is the human-in-the-loop boundary? Which parts are safe to let an agentic loop propose-and-validate autonomously, and which require a human to assert intent (a schema change whose old→new mapping is genuinely ambiguous)?
  • Prior art: how does this relate to E / Joule persistent-object upgrade, orthogonal persistence in KeyKOS / EROS, and Agoric's contract-upgrade + durable-storage story (baggage, defineDurableKind, kind-shape upgrade)? The Agoric model already separates durable state from behavior and versions the shape — worth mining for what maps onto a heap-level endor upgrade and what does not.

Cross-reference: the snapshot format Q&A that prompted this — kriscendobot/garden#51 (comment) and kriscendobot/garden#51 (comment). This is a design-discussion issue; no implementation is claimed and none of the artifacts above exist yet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions