✨ Slice A of #848: project REPL views from durable history - #849
Merged
Merged
Conversation
Slice A of #848: the durable reconstruction kernel. Code outside the runtime can take an array of the repository's real `Yield | Close` events and obtain a deeply frozen view of one execution, for the whole Journal or for one inclusive checkpoint prefix, and can address any part of that view with a URL. There is no REPL yet — no storage, no session, no component tree, no command — and nothing in core's execution semantics changed. `<Elicit>` now retains the compiled schema beside its answer. The validated answer is still the record's result and the fingerprint over schema and message is still the only guard; the schema travels in the same description under `executablemd.elicitation-schema`, where a source position already travels, so `type` and `name` still decide what a replay matches. Historical inspection needs to know which fields the person was shown, and the provider that could have said so is not running any more. `@executablemd/core/host` publishes the field name and a parsing reader for it, because a description is journal data and a schema that merely looked plausible would reach a form as one. `packages/cli/src/repl/model.ts` projects the vocabulary an ordinary execution already appends — root and nested `import_component`, `eval`, `generated_xmd`, `elicit`, and the root `Close` — into immutable structural values, deep-frozen, with no REPL record, no cache, and no `DurableEvent` leaving the module. Every retained value is detached from the event that carried it — copied, then frozen — so the model cannot be changed by whoever still holds the events, and the events are neither frozen nor modified by having been read: a projector that froze its input would make a caller's own data immutable as a side effect of being looked at. A checkpoint marker is derived from protocol identity, `yield:<coroutine>:<ordinal>` or `close:<coroutine>`, so the same event has the same marker in every process that reads the file and nothing extra is written down to make that true. Attribution is by the source path a position names: an effect whose owner is absent, ambiguous or unreadable refuses, because a binding attached to a guessed scope is a value shown where nothing published it. `packages/cli/src/repl/route.ts` is the pure half of navigation. `decodeLocation` decides only what a grammar can decide, `encodeLocation` emits one canonical spelling, and `resolveLocation` answers with the exact objects the projection holds. The two are separate so that a typo in a location is never answered with a guess about the history. Evidence runs against a journal produced by really executing the reference entry rather than a recorded array, so the projector is held to the vocabulary the current runtime writes. The fixture holds one of each thing this slice projects: a durable evaluation publishing JSON, one nested component occurrence whose source the run retains, one generated fragment admitted from a value that evaluation published, and one validated question whose answer changes what renders after it. One thing the vocabulary turned out to say that a reader has to handle: the root `Close` records three outcomes, not one — a document result, a protocol `err` for a run that failed before producing one, and `cancelled` — so `ReplTerminal` has three statuses and only the first carries rendered output. A serialized `err` also carries a stack naming host paths, so the transcript shows its message and nothing else.
Slice A adds `repl-model.test.ts` and `repl-route.test.ts`, so the corpus moved and every runtime's partition was reading a fallback weight for both. Measured by **Measure test weights** run 36270414251 against `970bc0db`, on `ubuntu-latest`, and committed exactly as the artifact came out. The shard counts do not move. The measured floors are 14, 8 and 4 for Deno, Node and Bun; the installed counts are 15, 10 and 5, so each is already above its floor and no five-run evidence asks for a change.
4 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Issue #848 asks for a REPL that can run one XMD entry and reconstruct it from a
URL. It is delivered as five slices; this is Slice A, the durable
reconstruction kernel — the layer that turns one execution's Journal into
something a screen could be drawn from. It references #848 and does not close
it: only Slice E makes
xmd replexist.What changes
Before: nothing outside the runtime can read one execution's history back as a
view.
<Elicit>records its answer, but not the question's shape, so a readerof the history cannot say which fields the person was shown.
After: given an array of the repository's real
Yield | Closeevents, code canobtain a deeply frozen
ReplModelfor the whole Journal or one inclusivecheckpoint prefix, and address any part of it with an
xmd://repl/...locationthat decodes, canonically encodes and resolves to the exact objects in that
model.
<Elicit>retains its compiled schema as descriptive input beside theanswer.
There is intentionally no executable REPL after this slice.
How it works
projectReplwalks the vocabulary an ordinary execution already appends — rootand nested
import_component,eval,generated_xmd,elicit, and the rootClose— and recognizes no record of its own. A checkpoint marker is derivedfrom protocol identity (
yield:<coroutine>:<ordinal>,close:<coroutine>), sothe same event has the same marker in every process that reads the file and
nothing extra is stored to make that true.
Review guide
Start with:
packages/cli/tests/repl-model.test.tsThen review:
packages/cli/src/repl/model.ts— projection, refusals, detachmentpackages/cli/src/repl/route.ts— the pure codec and the resolverpackages/core/src/elicit-journal.ts— the one durable changepackages/cli/tests/fixtures/repl/— the entry the evidence really runsLook carefully at:
ownerOf— attribution is by the source path a position names, and an ownerthat is absent, ambiguous or unreadable refuses rather than being guessed.
detach— what the model retains is copied out of the events, so projectionneither freezes nor modifies its input.
What must stay true
elicitrecord's identity is stilltypeandname, and theschema-plus-message fingerprint is still the stale-input guard — enforced by
keeping the schema in the description beside them, checked by
packages/core/tests/elicit-component.test.ts.detach, checked by M3.projectRepl, checked byM1.
DurableEventreaches a consumer of the model — checked by M3's structuralwalk.
How to verify it
deno task test \ packages/core/tests/elicit-component.test.ts \ packages/cli/tests/repl-model.test.ts \ packages/cli/tests/repl-route.test.ts deno task check9 tests, 49 steps. Every journal under test is produced by really executing the
reference entry, so the projector is held to what the current runtime writes
rather than to a recorded array.
and frozen, and fails if an unknown marker, a second entry, work after
settlement, a repeated close or an unreadable payload were accepted.
removed, repointed or corrupt source position were attached to a guess.
frozen everywhere reachable, that it shares no object with the event graph,
and that the events come out unfrozen and byte-identical.
that replay still skips the provider, and that a changed schema or message is
refused.
and every missing target or illegal combination refuses.
Nine deliberate defects were introduced one at a time and each makes the
corresponding test fail.
Scope
Included
packages/cli/src/repl/{model,route}.tsand their focused teststest-weights.jsonIntentionally unchanged
are Slices B–E
specs/repl-spec.mdand no broad architecture rules yetGenerated or mechanical changes
test-weights.jsonis the artifact of Measure test weights run36270414251
against
970bc0db, committed unchanged. The shard counts do not move: themeasured floors are 14/8/4 and the installed counts are already 15/10/5.
Risks and limitations
elicitrecord predates this change retains no schema, andthe projector refuses it rather than showing an answer without its question.
Nothing in the repository writes such a record and reads it through this
projector, so no migration affordance is offered.
Closehas three shapes — a document result, a protocolerr, andcancelled— and only the first carries rendered output.Scope confirmation