Skip to content
View source

Hot swap & validation

Entelechy’s central promise: nothing invalid publishes, and a rejected change leaves the running version alive. The CLI’s --watch, the Player and the protocol share the same authority rules.

save .ent ──► read & compile off the frames ──► prepare (validate, build nothing) ──► publish atomically on the next sync
│ │
└──────── fails: rejected ◄─────┘ the running version keeps computing
  1. Prepare: parse, type-check, check capabilities, lower to IR and validate. Preparing builds nothing — a device builds a Kernel when first needed (or as it publishes, where a node will surely run there).
  2. Off the frames: a changed document is read and compiled on another thread and the replacement prepared on a worker; evaluate runs the previous version meanwhile. sync reports preparing, and published or rejected on a later sync.
  3. Publish: atomic, checked against a generation. Each KernelID has a generation in the registry, g1, g2, ….
  4. Retire: a Kernel that leaves its document retires.

A changed document republishes only the Kernels whose text changed.

The real log recorded in the --watch tutorial: a broken Kernel saved, then fixed:

rejected gw/kernels/grayscale.ent:8:59: `nope` is not defined
scene loaded
evaluated in 51.856µs: executed [], reused 2 -> gw.png
scene loaded
evaluated in 1.30081ms: executed [gray+source, blocks], reused 0 -> gw.png

After the refusal nothing re-ran (executed []) and the output still came from the old version; once fixed, the affected nodes (the fused gray+source, and blocks) ran again.

A publish over the protocol may carry expect: it replaces only if the registry still holds that generation. Publishing twice against the same expect refuses the second (real output):

{"id":1,"ok":{"generation":3,"id":"demo.tint@1"}}
{"error":{"message":"cannot replace demo.tint@1 g2: the registry holds g3"},"id":2}

So several agents (or an agent and a person) editing at once never overwrite each other. At the document level the counterpart is compose’s expect (the document’s version digest).

Case Outcome
syntax/type error rejected with line and column; the old version keeps running
a capability not granted refused when prepared, naming what it lacks; what ran before keeps running
one of a scene’s own Kernels refused the whole scene is refused (scene_rejected), none of its Kernels published
an evaluation fails state does not commit; the failing node is reported (a migration’s also port, migration)
a compose fails its check no document is written; the error names the path and version at fault

Replacing a Kernel keeps the graph’s state: <~ ports hold what was committed. A new version that changes a state port’s layout names a migrate Kernel, or the state restarts from initial and is listed under reset in the evaluation report. See Graph & state.

  • A session’s position is (revision, frame): the revision moves when the world changes other than by running; the frame counts evaluations committed within it.
  • Every change is recorded with its origin; sources and documents are kept once each by digest; refused attempts are recorded apart, moving nothing. Read it with the protocol’s history; kernel with a generation reads an old source back.
  • snapshot takes committed state whole with its laws (the published Kernels’ sources); restore installs it, proves the world by one frame, and puts everything back if it cannot live.

Reality changes atomically; history records why. (docs/DECISIONS.md)

Door Grain Operations
Structure a statement at a time compose: edits the scene’s documents, several as one, all written or none
State a committed port at a time write, reset, restore
Parameters the params the document declares param

Nothing else reaches in. Setting a node’s value from outside is refused — that is the document’s structure, changed by compose (see Agent protocol).