Skip to content

mcpx testing: deterministic execution as a long-term aspiration (retrofit vs Rust rewrite) #1963

Closed

Description

@dezren39

Relocated from dezren39/darwin-nix#126 on 2026-10-28 (placement audit): mcpx's single source lives in developing-today/code (pkgs/mcpx); re-home tracked at #309. Original body below; issue references inside it are historical (dezren39/nix numbering).

Parent: #93

Summary

The simple, explicitly requested issue: consider deterministic execution as a long-term design aspiration, two ways -- retrofit mcpx (inject clock, randomness, filesystem, process spawn and network behind interfaces) or a Rust rewrite, which the notes call "not a plan at all". Not a plan to build either; a short record of what each would take, what blocks it, and the cheap steps that pay off regardless.

Original notes

  - think a hack job of antithesis/tigerbeetle. this is the primary testing method is throw insane amount of variations. if something is found outside this system and a test that could have had missed it, make a different generator that uses the same seed and makes an infinite amount of things matching the offensive shape.
    - we aren't shooting for deterministic execution to start, maybe we should but no. consider it a longterm design aspiration to retrofit or for rewrite in rust (not a plan at all, but could see it). separate simple issue for considering both those things.

Current state

What blocks determinism in mcpx

Source Retrofit difficulty Why
Wall clock / timers Low-medium inject a Clock; or run in-process components under synctest
crypto/rand ids Low inject an id source; seeded in tests
Filesystem Medium an fs seam for state/cache/artifacts; t.TempDir already isolates
Goroutine scheduling High Go has no deterministic scheduler; synctest makes time deterministic, not interleavings
Unix sockets / TCP / HTTP High real kernel I/O; in-process net.Pipe transports only for components that can run in one process
Runtime subprocesses (deno, bun, node) Not feasible separate processes with their own event loops, JITs and clocks; the core of mcpx exec
Real MCP servers (#97 real-software subset) Not feasible third-party processes

Approaches

A -- Rust rewrite for determinism (least recommended; "not a plan at all", agreed)

What it would buy: Rust async simulation frameworks (e.g. madsim, turmoil for tokio) simulate time and network in one process, TigerBeetle/FoundationDB-style. What it would not buy: determinism for the deno/bun/node subprocess that runs every script, or for real MCP servers -- the part of mcpx most likely to misbehave. A full rewrite of a working Go codebase for a property it still would not achieve end to end is the wrong trade. Worth revisiting only if a rewrite happens for other reasons.

B -- full retrofit: every nondeterminism source behind an interface, a simulated daemon

Clock, ids, fs, spawn and network all injected; a test harness runs the daemon, pool, events and a fake MCP server in one process on simulated transports, seeded from the run seed, VOPR-style. Achievable for the daemon's internal logic (pool admission/reaping, task lifecycle, elicitation broker, event store #6, state/cache #78), and it would find real concurrency bugs. Costs: interfaces threaded through most packages, a simulated transport layer, and it still stops at the runtime subprocess boundary. L-XL effort for partial coverage.

C -- incremental seams where they pay today; Antithesis-style tooling as the end-to-end answer (recommended)

  • Add seams only when a test needs them: a Clock for idle-exit (already configurable through autostart.idleExit, but a test of it still waits in real time), seeded id sources, ReapIdle-style now parameters.
  • Use testing/synctest for in-process concurrent components (pool, tasks, elicit, events): deterministic time and "wait until everything is blocked" instead of time.Sleep polling -- which also removes the flake-prone sleeps found in chore(deps): update dependency happy-dom to v20 [security] - autoclosed #125.
  • Derive every injected randomness source from the run seed (Update Rust crate sqlx to 0.8.0 [SECURITY] #121) so partial determinism composes with seeded inputs.
  • For true end-to-end determinism including the runtime subprocesses, the realistic path is a deterministic hypervisor (Antithesis runs unmodified containers), not a rewrite. That needs a container image. One now exists: Phase 5: tag event bodies must travel with the event #182 added pkgs/mcpx/Containerfile (debian bookworm-slim runtime, bun, 413 MB; docs/container.md). It has no CI job and no tests yet (Update Rust crate rand to v0.9.3 [SECURITY] #134). This is a prerequisite that is now met, not a plan.
  • Why C: each step is small, useful on its own (fewer flakes, faster tests), and moves toward B without committing to it.

Open questions

Ease · Impact · Related

Ease S (this issue: record + first seam list) · C incremental: S per seam · B: L-XL · A: XL, not planned · Impact low now, med long-term (flake reduction is the near-term win)
Depends on / related: #121, #124, #125; #52 (closed, fixed in #182), #78 (state/cache), #6 (event store), #97 and #134 (the container image and its CI job, real-software subset), #99 (flake policy).

Security/auth deferred; see tracker.

Acceptance criteria

  • A short record exists (on this issue or in the docs tree and linked here) comparing the two routes to deterministic execution — retrofit (clock, randomness, filesystem, process spawn, network behind interfaces) vs a Rust rewrite — with what each would take and what blocks it.
  • The cheap steps that pay off regardless of route are listed, and each is either filed as its own issue or explicitly dropped with a reason.
  • No implementation is started under this issue (it is explicitly not a plan to build either route).
  • Evidence (the record's location) is recorded on this issue before it is closed.

Activity

  1. developing-today commented on Oct 28, 2026

    @developing-today
    Contributor
  2. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    History carried over from dezren39/darwin-nix#126 (verbatim, oldest first):


    @dezren39 · 2026-09-30T07:20:55Z

    Integration pass. Placed in the DAG rooted at #181.


    @dezren39 · 2026-09-30T12:03:29Z

    Issues pass (2026-09-30, at 05c78b2).

  3. added
    kind/testTest coverage, test infrastructure, or test-only fixes
    priority/lowNice to have; do when convenient
    effort/mMedium: expected under 1 day of work
    app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 29, 2026
  4. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Intake review

    • Related: parent Bump golang.org/x/crypto from 0.14.0 to 0.17.0 in /sources/chat #97, mcpx testing programme. Long-term aspiration only — the body is explicit that the Rust rewrite is "not a plan at all" and the deliverable is a record, hence priority/low.
    • Notes: still live at intake; nothing to verify as broken — this is a design study whose value is preventing future ad-hoc determinism debates.
    • Risks: scope creep into an actual retrofit. The acceptance criteria fence this to a record plus filed-or-dropped cheap steps.
    • Suggested approach: timebox the study, ground the retrofit estimate in the actual surface (where mcpx reads clock/random/FS/process/network today), and close with the no-regret steps filed.

    🤖 Agent note · state: filed

    • Agent: fleet monitor (repo-fleet-monitor)
    • Session / ID: repo-fleet-monitor run 2026-10-28 19:19 CDT
    • Found via: monitor reconciliation (intake enrichment, protocol §5)
    • Task: intake enrichment — acceptance criteria, normalized labels, this review
    • Expectation: effort/m; done when the AC section, labels, and this note are on the issue
    • Links: n/a
  5. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 29, 2026
  6. 1 remaining item

  7. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Triage verdict — task 2864 (2026-10-29)

    🤖 Agent note · state: triaged

  8. added
    queue/queuedHas a queued task in the Supabase agent queue
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 29, 2026
  9. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    Picked up — queue task 3259

    Close-out of this issue per the triage verdict (task 2864): disposition each approach-C cheap step (file as its own issue or drop with a reason), record the evidence here, and close. No determinism implementation under this task.

    Revalidation (2026-10-30): issue OPEN, no hard-stop labels (held / user/owned / do-not-merge / do-not-auto-edit absent). Dedupe searches run fresh today for synctest / antithesis / hypervisor / clock seam / seed-derived / deterministic — no existing issue covers any approach-C step; #1949 (run-seed format/delivery) and #1956 (generator engine) cover seeded inputs, not execution determinism or injectable id sources.

    🤖 Agent note · state: picked-up

  10. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    Close-out — evidence record and approach-C dispositions (queue task 3259, 2026-10-30)

    Record location: the retrofit-vs-Rust-rewrite record this issue's acceptance criteria require is this issue's own body — the blockers table, approaches A (Rust rewrite, not a plan), B (full retrofit), and C (incremental seams, recommended), with Ease/Impact and open questions. Triage task 2864 verified the body still matches current pkgs/mcpx main (2026-10-29), and this close-out re-ran the dedupe searches fresh today (synctest / antithesis / hypervisor / clock seam / seed-derived / deterministic: only #1963 itself, plus the distinct seed-programme issues #1949/#1956).

    Approach-C cheap steps — each filed or dropped:

    1. Clock seam for idle-exit → FILED as mcpx testing: Clock seam for daemon idle-exit so its tests do not wait in real time #2638 (acceptance criteria include unchanged production behavior and go test ./... via CI).
    2. testing/synctest pilot for one in-process component (pool/tasks/elicit/events) → FILED as mcpx testing: testing/synctest pilot for one in-process component (pool, tasks, elicit, or events) #2639.
    3. Seed-derived id/randomness sources → FILED as mcpx testing: injectable, seed-derived id/randomness sources for pool, tasks, elicit, artifacts, mcpserver, logging/trace #2640. Checked against mcpx testing: one seed per run -- format (UUIDv7), derivation, and how it reaches every test #1949 first, per the close-out brief: mcpx testing: one seed per run -- format (UUIDv7), derivation, and how it reaches every test #1949 covers the run seed's format/delivery for test inputs; mcpx testing: injectable, seed-derived id/randomness sources for pool, tasks, elicit, artifacts, mcpserver, logging/trace #2640 covers the injectable id/randomness sources in production packages (pool, tasks, elicit, artifacts, mcpserver, logging/trace) that a seed would drive. Linked as depends-on, not duplicated.
    4. Deterministic-hypervisor (Antithesis) experiment on the pkgs/mcpx container image → FILED as mcpx testing: one deterministic-hypervisor (Antithesis) experiment on the pkgs/mcpx container image #2641, with its prerequisites named inside it (container CI job mcpx testing: environments -- nix flake, container, local, remote -- as CI matrix entries #1983; Antithesis is a commercial service, so account access/cost is Drew's decision before any run). Nothing about that decision blocks this close-out — the experiment issue carries its own go/no-go gate. Dropped: nothing. No step needed dropping; each was small, clearly scoped, and uncovered elsewhere.

    No implementation was started under this issue, per its acceptance criteria — all determinism work lives in the four step issues above.

    Closing this issue as completed: record exists (body), every cheap step is filed, evidence recorded here.

    🤖 Agent note · state: done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)effort/mMedium: expected under 1 day of workkind/testTest coverage, test infrastructure, or test-only fixesmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/lowNice to have; do when convenient

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions