Adds a restart-surviving demo host - #15
Merged
Merged
Conversation
Introduces the demo embedder that drives a multi-step chart through the loop with no Session process: a durable ledger, a volatile runtime, an ADR-0051 invoke handler, and a host struct whose executor seam records every effect. The first scenario runs the chart straight through to completion with no restart, proving the host and the seam before Phase 2 adds the kill point. Refs: sp-4an.4
Refs: sp-4an.4
Kills the volatile runtime mid-run and proves the loop finishes anyway: recover/1 re-arms durable timers unconditionally and re-establishes only the invocations the engine still considers live, and restart/1 composes stop, boot, and recover into one cold start. The restart test walks the kill point, the dead pid, the freshly recompiled chart, and the tail to completion, then asserts no side effect or executor call ran twice across the restart. Refs: sp-4an.4
Asserts the executor call log on exact contents - identical to the straight-through run's, so the restart re-emitted nothing - and the idempotency ledger on exact keys (the previous uniqueness check was vacuous against a ledger that dedups by construction). Notes boot/4 and recover/1 as the host moduledoc's two deliberate cold-boot exceptions. Both Phase 1-2 sabotage mutations (run_status :done -> :active; write_run position: :skip on :update) were re-run, confirmed red, and reverted; the plan's manual checkboxes are ticked. Refs: sp-4an.4
Carries run_id on the across-restart result so the test can assert the ledger holds no open timer row once the run completes - the reminder-timer armed after the restart was cancelled and dropped, and the fired sla-timer's row was consumed. Refs: sp-4an.4
Phase 3 of docs/plans/260822-sp-4an.4-restart-demo-host.md.
Scenario.replay/4 drives the recorded tape against a fresh store with
no Demo.Runtime at all; the test asserts the configuration sequence
and the effect sequence reproduce the original struct for struct. The
one generated create input - the session id MachineState.new/2 stamps
into the datamodel_init effect - is part of the recorded inputs, so
the replay re-supplies it via initialize: [session_id: ...]. The
across-restart scenario now carries the input tape over the restart
(the recorder's own log; boot/4 deliberately restores no tape).
The wrong-revision test steps the stored run against a different
compilation and asserts {:error, {:identity_mismatch, expected,
actual}} with both identities present and the stored position
untouched. Both sabotage mutations (identity-mismatch arm collapsed
to :chart_not_found; write_run position: :skip on :update) were run,
confirmed red on the intended assertions, and reverted.
Refs: sp-4an.4
Phase 4 of docs/plans/260822-sp-4an.4-restart-demo-host.md: the same three scenario bodies - straight-through, across a restart, replay - driven against Storage.Ecto over the ADR-0005 harness, with the default AdapterLock serialization taking the advisory-plus-row lock on every step. The restart variant asserts the post-restart boot re-read the chart and run from the database and that the executor call log is exactly the straight-through run's; the replay variant re-supplies the recorded session id and reproduces the path struct for struct. Sabotage coverage is carried by the shared scenario bodies, per the plan's Testing Strategy. Refs: sp-4an.4
Phase 5 of docs/plans/260822-sp-4an.4-restart-demo-host.md: docs/restart-demo.md walks a host author through the kill point, the survives/does-not-survive split, and the five obligations the position leaves to the host, pointing at the tests as the executable truth; the README gains a Surviving a restart pointer; ADR-0004 gains a dated validation note recording that decisions 3, 4 and 6 were driven end to end, with the one finding named - byte-identical replay needs the recorded session id, already covered by initialize: [session_id: ...]. Also converts the plan's review-type manual-verification items back to deferred status for the operator's verify walk; only literally-executed checks (sabotage runs, repeated runs) stay checked. Refs: sp-4an.4
The gate's sabotage scanner reads the note directly above each test; the module-level pointer alone left the three Ecto variants unnamed. Each note names the shared InMemory-variant mutation that reds it. Refs: sp-4an.4
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
The sp-4an charter's validation gate: the design rule says the first
production embedder drives the API, so before calling the storage
behaviour, lifecycle, and executor seam stable, a concrete embedder-shaped
pipeline has to drive them end to end. sp-4an.4 asks for a demo host that
runs a multi-step chart across a simulated restart with no Session
process - persist mid-run, drop everything volatile, cold-boot from the
run id alone, continue, finish - with a pending durable timer and an
in-flight async invocation live at the kill point.
What
A demo embedder under
test/support/demo/(Host, durable Ledger,volatile Runtime, an ADR-0051 invoke handler, and the Scenario bodies),
driven by ordinary tests so
mix qualityis the acceptance gate::completed, executor call log asserted onexact contents;
enriching, boot from the run id,recover/1re-arms timers from durable rows and re-establishes the invocation
through the handler palette (different live pid, dead old pid), the
run finishes with the identical call log - nothing re-emitted, per
st-ADR-0060's "resume restores position, not liveness";
the one generated create input) against a fresh store reproduces the
configuration sequence and the effect structs field for field;
different compilation returns
{:error, {:identity_mismatch, expected, actual}}with both identities and an untouched stored position;Storage.Ecto/Postgres, exercising theadvisory-plus-row lock on every step;
docs/restart-demo.mdwalkthrough, a README pointer, and adated validation note on ADR-0004 naming the one finding (byte-identical
replay needs the recorded session id;
initialize: [session_id: ...]already covers it - no new surface).
Notes
mix qualitygreen before this push (142 tests, 95.8% coverage,mix gate.verifyattested: scope all, no profile). No changelogfragment: tests and docs only, no public
lib/surface changed.since-halted detached loop agent; the takeover worker verified all of
it hands-on (every sabotage mutation re-run and confirmed red against
the intended assertion, then reverted) and the full gate above ran
against the final HEAD.
prose, doc readability) are deferred in
docs/plans/260822-sp-4an.4-restart-demo-host.mdfor the operator'sverify walk; only literally-executed checks are marked done.
"Route B"); handing
SendDelayed/Cancelto a real scheduler isstatifier_oban's charter and a natural mirrored follow-up recorded in
the plan's Open Questions, not filed here.
Closes: sp-4an.4