Research / Effects, recovery and replay

P17 · short · draft

Seeded Deterministic Interleaving Tests for Lifecycle Operations, With Auto-Mocks Derived From Declarations

partial short Effects, recovery and replay

State of the work. Seeded scheduler, shrinker and auto-mocks are shipped on the Python reference tier with tests; no bug-finding or coverage measurements, and the deadlock check has no test.

This is a draft, not peer reviewed. Repository references link to revl at commit 45bec431f.

Planned to be combined with P16 (Testing Recovery Claims: Exhaustive Fault Sweeps and Inverse Round-Trips Across Runtimes) into a single paper.

Pitch

A seed is the tape a deterministic chooser reads at every scheduling decision, so a failing lifecycle interleaving is a short integer vector that replays to the same schedule, and a greedy shrink over that vector reached one non-canonical choice in the worked example.

Abstract

Concurrent compositions of components fail for a reason that failure-point testing does not cover: independent lifecycle operations, activation and teardown of different components, run in an order the author never pictured. Existing schedule explorers (CHESS, PCT, Loom, Shuttle) need to control a nondeterministic runtime, and most capture-and-replay tools fix an order after the fact. We describe schedule testing in revl, a language whose reference runtime has no wall clock, no threads, and cooperative waits only. A seeded PRNG indexes the canonically sorted ready set at each decision; the sequence of chosen indices is the schedule, so the same seed replays the identical interleaving. For each completed schedule the harness checks residue (reusing the fault sweep's four snapshot deltas plus unreleased host resources), deadlock (a fiber that can never activate), and stability of the observable end state against the canonical sequential run, which includes the order of irreversible emissions. A failing choice vector is shrunk by a greedy pass in the delta-debugging style, toward the canonical index 0, and printed as the reproduction. Separately, the auto-mock facility derives in-memory providers from service declarations, where emissions are counted and never crossed; schedule testing does not yet run against those mocks. The scheduler ships on the Python reference tier with 17 tests; its alphabet is two operations (activate, teardown), exploration is sampled rather than exhaustive, and the capability and use-after-withdrawal checks are not yet substantive. We report the design, the checked properties, the limits, and a worked two-consumer example whose emission-order divergence minimizes to a single non-canonical choice.

Problem and claim

Lifecycle bugs in a plugin-style composition (leaked registrations, order-dependent emissions, stranded consumers) depend on which independent component comes up or goes down first. A fault sweep that splices a fail into every step finds failure points but runs one fixed order. Testing interleavings normally needs either a controlled scheduler over a nondeterministic runtime, or flaky stress runs.

The claim: when the runtime is deterministic by construction, interleaving testing reduces to choosing an order for the harness-issued lifecycle operations. A seed then is a complete, replayable description of the schedule, the ready set has a canonical order so index 0 means the same thing on every replay, and the properties to check are the ones the language already defines oracles for (residue, deadlock, observable end state).

The claim is deliberately narrow. It covers two lifecycle atoms on one tier, with sampling, not a proof that no interleaving violates a property. The design doc says so itself (see limits).

Contributions

  1. A seed-as-tape scheduler: a seeded PRNG indexes the ready set sorted by (operation-kind rank, fiber load-order index, name), with a canonical always-index-0 baseline and a replay chooser built from a saved choice vector (src/revl/schedule.py:92-105, src/revl/schedule.py:199-234).
  2. A per-schedule property check over completed schedules: residue-free, no deadlock, stable observable end state versus the canonical run, where the fingerprint includes the ordered emission sequence (src/revl/schedule.py:269-325, docs/schedule-testing.md:62-92).
  3. A greedy shrink over the choice vector in the delta-debugging style: drop trailing choices, then reset each choice toward index 0, keeping a change only if it fails with the same signature (src/revl/schedule.py:463 (minimize), docs/schedule-testing.md:94-115). The code is one-choice-at-a-time, not ddmin.
  4. A teardown gate that restricts explored schedules to valid reverse-dependency orders, so every explored schedule is a legal one (docs/schedule-testing.md:49-55).
  5. Auto-mocks: a typed, seeded, deterministic in-memory provider per service declaration, with emission operations counted and not crossed (src/revl/mocks.py:1-34, src/revl/mocks.py:97-158, docs/auto-mocks.md:1-10).

Evidence on main

Tree: revl main at commit 45bec431f.

Claimpath:line or testStatusNote
--schedule-seeds N samples N interleavings plus a baseline; --schedule-seed S replays onedocs/schedule-testing.md:13-16; src/revl/schedule.py:513-556; src/revl/test.py:1424 (schedule_command)shippedSeeds are range(N) (src/revl/schedule.py:536), so "sampled" means seeds 0..N-1
Seed is a tape over the canonically sorted ready setdocs/schedule-testing.md:26-36; src/revl/schedule.py:92-105,202-215shipped
Same seed replays the identical scheduletest_same_seed_reproduces_the_same_schedule (tests/test_schedule_testing.py:173)shippedCompares choices, atoms, emissions and fingerprint, not bytes; needs cordis-py, otherwise skipped
Different seeds reach different interleavingstest_seeds_explore_distinct_interleavings (tests/test_schedule_testing.py:186)shipped (one assert is vacuous)40 seeds on a 3-component fixture. The or canonical == ... clause is always true, so only len(schedules) > 1 tests anything; that the canonical order is reachable is not tested
An order-dependent bug is found, minimized and reproducibletest_order_dependent_issue_is_found_and_reproducible (tests/test_schedule_testing.py:200)shippedAsserts the minimized vector has exactly one non-zero choice
An order-invariant composition passes every seedtest_order_invariant_composition_passes_every_seed (tests/test_schedule_testing.py:221)shipped60 seeds plus baseline = 61 schedules
Residue-free check per completed schedulesrc/revl/schedule.py:269; test_completed_schedule_is_residue_free (tests/test_schedule_testing.py:231)shippedReuses src/revl/fault.py snapshot deltas
Deadlock checksrc/revl/schedule.py:315-318; src/revl/schedule.py:161 (stuck_fibers)partialCode on main; no test touches it. The test_check_properties_* tests cover only residue (tests/test_schedule_testing.py:144) and unstable state (:134), not deadlock
Stable final state including emission ordersrc/revl/schedule.py:255-266,323-324; test_fingerprint_folds_emission_order (tests/test_schedule_testing.py:124)shippedStrict equality only, so it flags any order dependence. The commutativity skip that design open question 2 recommends (docs/design/295-schedule-testing.md:319-330) is not implemented: no code reads a commutative flag in src/revl/schedule.py, and line 334 names it only inside a message
Teardown order correctnessdocs/schedule-testing.md:49-55shipped by constructionThe gate is tested (test_teardown_gated_on_dependents, tests/test_schedule_testing.py:84); the order itself is not checked as a property, since explored schedules cannot violate it
No use-after-withdrawalsrc/revl/schedule.py:294-305placeholderThe function returns an empty list; the docs describe it as a guard for future finer atoms (docs/schedule-testing.md:87-92)
Per-fiber capability violation checkdocs/schedule-testing.md:57-60; docs/design/295-schedule-testing.md:269-271designed (conditional)Ships in v1 only if the runtime enforces per-fiber capability at emit, otherwise the first follow-on (docs/design/295-schedule-testing.md:265-271, open question 3)
Intra-activation atoms (resume, complete, fire)docs/schedule-testing.md:57-60designedFollow-on
Exhaustive or partial-order-reduced explorationdocs/design/295-schedule-testing.md:280-286designedFollow-on, framed as the upgrade from "sampled" to "none violates"
Py tier only; missing runtime is a skip, not a passdocs/schedule-testing.md:117-126; test_command_skips_without_cordis (tests/test_schedule_testing.py:153)shipped
Auto-mocks per service declaration, typed and seededsrc/revl/mocks.py:53-60; docs/auto-mocks.md:1-10; test_gen_value_is_seeded_deterministic (tests/test_auto_mocks.py:368)shipped
Emissions counted, not crossedsrc/revl/mocks.py:97-158; tests/test_auto_mocks.py:605shipped
Mock world is reproducible across runstest_mock_world_is_reproducible_across_runs (tests/test_auto_mocks.py:627)shipped
Property tests derived from interfacesnot located in docs/auto-mocks.md or src/revl/mocks.pynot foundNot claimed

Numbers that exist

Paper outline

  1. Introduction: lifecycle interleavings as a missing test axis; the claim and its narrow scope.
  2. Background: the deterministic py reference runtime, the fault sweep and its oracles.
  3. Design: atoms, ready-set order, seed tape, teardown gate, properties.
  4. Minimization and reproduction: delta-debugging over the choice vector, the worked example.
  5. Auto-mocks: derived providers, counted-not-crossed emissions, how they would compose with the scheduler.
  6. Evaluation: mutants, detection rate, coverage against full enumeration (to be run).
  7. Related work, limits, and the partial-order-reduction follow-on.

Threats to validity and limits

Fact-check

Claims in this draft were checked against revl main at commit 45bec431f: 30 checked, 12 corrected.

Cite this draft

@misc{revlp17,
  author       = {Thomas Moussajee},
  title        = {Seeded Deterministic Interleaving Tests for Lifecycle Operations, With Auto-Mocks Derived From Declarations},
  year         = {2026},
  howpublished = {\url{https://inso1337.github.io/revl/research/P17.html}},
  note         = {Research draft, not peer reviewed. Written against revl commit 45bec431f. Licensed CC BY 4.0.}
}

Text licensed CC BY 4.0, author Thomas Moussajee.

← All research drafts