Skip to content

design(mcpx): a supervisor that owns each stdio child's pipes — replace the takeover handoff for stdio #1384

Open
1 of 1 issue completed

Description

@dezren39

Decision needed (one line): Adopt the per-instance mcpx supervisor design — stdio children owned by a supervisor that outlives the daemon, Phases 2–3 make it the default — or keep investing in the takeover handoff instead?

Summary

Replace the stdio half of mcpx's takeover handoff with one supervisor per stdio instance: a tiny process (the same mcpx binary re-executed as a hidden mcpx supervise subcommand) that spawns each stdio MCP server, owns its pipes for the child's whole life, and serves whichever daemon is currently attached over a unix socket in the state directory. A daemon upgrade then becomes a plain restart of the front end — the new daemon attaches to supervisors that are already running. No pipes move, no descriptors are passed, nothing is detached or restored.

This extends — does not duplicate — the repo's own design thread: pkgs/mcpx/TODO.md item 1 ("Replace the takeover handoff with a supervisor… The current handoff is a stopgap") and planning/roadmaps/mcpx.md → Next → "A supervisor that owns each stdio child's pipes" (status: idea, no issue until now). Full design doc: files/designs/code-mcpx-stdio-supervisor.md in the keep-the-four-repos-healthy goal workspace (registered in files/potential-projects.md).

Current state (evidence)

  • pkgs/mcpx/internal/daemon/takeover.go is 726 lines of framed handoff protocol (hello/listener/state/pools/pipe/conn/end/ack/commit), one fd per frame, a single commit point after which nothing can be put back.
  • pkgs/mcpx/internal/pool/handoff.go (390 lines): pool Detach refuses while calls are in flight (ErrBusy), parks every other instance, carries stdio pipes beside the JSON as raw fds.
  • pkgs/mcpx/internal/mcpclient/stdio.go: plain os.Pipe()s instead of cmd.Std*Pipe so the ends can be passed to another process; a pending buffer for a partial frame interrupted by handoff; a detached state while another process owns the pipes.
  • Costs already on record: takeover is unsupported on darwin (mcpx: takeover/upgrade/handoff unsupported on darwin; Linux-only tests break the darwin build #237); a takeover bug orphaned a removed server's upstream HTTP session (mcpx: takeover orphans a removed server's upstream HTTP session — never DELETEd, left to expire upstream #260, closed).
  • Cross-repo theme (ROADMAP.md): "Handing a running service to its successor."

Proposal (point of view)

  • Per-instance, not per-daemon: the failure unit stays one server instance; the supervisor protocol never multiplexes, so the one component that must stay trivial stays trivial.
  • Byte pump, not frame proxy, in v1: framing stays in the daemon's existing StdioTransport reader (one home for the partial-frame logic). Child output produced while no daemon is attached goes to a bounded supervisor buffer; overflow keeps the tail and reports the dropped byte count on attach — loss visible, never silent.
  • Same binary re-exec (pattern already used by internal/cli/explore.go and the takeover successor path), not a second artifact: mcpx ships as one Nix package.
  • Alternatives that lose, with reasons, are in the design doc: keep improving the handoff; one supervisor for all children; delegate to systemd/launchd (portability — the mcpx: takeover/upgrade/handoff unsupported on darwin; Linux-only tests break the darwin build #237 wound); separate supervisor binary.

Phased breakdown

  1. Phase 1 — supervised transport behind a flag. Hidden mcpx supervise subcommand; supervised mcpclient transport with the same interface; per-server opt-in flag, default off. Defining test: SIGKILL the daemon mid-session, start a new one — child PID unchanged, next call succeeds. (Filed as a sub-issue of this design issue and enqueued for triage.)
  2. Phase 2 — takeover becomes attach for supervised instances. Successor attaches via state-dir scan; no kindPipe/pools frames for supervised instances; listeners/HTTP keep the existing handoff. E2E supervised variant of internal/e2e/takeover_test.go asserting no pipe frames + PID continuity. (Not filed yet — its exact diff depends on Phase 1's socket protocol as built.)
  3. Phase 3 — supervised by default + lifecycle hygiene. Adopt-or-reap scan on daemon start, mcpx restart through the supervisor, protocol-version refusal surfaced in status, darwin build/test parity shrinking mcpx: takeover/upgrade/handoff unsupported on darwin; Linux-only tests break the darwin build #237's skip list to the legacy handoff. (Not filed yet — the default-flip is part of the decision above.)

Open questions (in the design doc, deliberately not filed as issues)

  • Buffer bound/policy for unattached child output (tail-drop vs backpressure).
  • Opportunistic supervisor refresh on binary upgrade vs protocol versioning alone.
  • Supervisor socket authentication beyond state-dir 0600 precedent (internal/daemon/sessions.go).

Non-goals

  • Cold-restart resume of upstream HTTP sessions (TODO.md item 3) — a sibling design, separable, not bundled here.
  • SSE upstream session mobility (TODO.md item 2); ExecIdentity semantics (TODO.md item 4) — unchanged.

Related

Acceptance criteria

  • The decision in the first line is recorded on this issue (adopt the per-instance supervisor, or keep investing in the takeover handoff), with the decider named.
  • If adopted: implementation issues exist for each phase (supervisor subcommand + attach protocol; stdio instances migrated; takeover handoff retired for stdio), each with its own acceptance criteria, and this design issue links them.
  • If not adopted: the rationale is recorded here and pkgs/mcpx/TODO.md item 1 / the roadmap entry are updated to match, so the design thread does not stay dangling.
  • No code changes land under this design issue itself.

Activity

  1. added
    user/reviewNeeds a user-look: Muse reviews first and handles what it can
    priority/normalOrdinary priority (the default)
    app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)
    on Oct 27, 2026
  2. developing-today commented on Oct 27, 2026

    @developing-today
    Contributor
  3. added
    epicMeta-issue grouping multiple items (org Project Type=Epic)
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    on Oct 27, 2026
  4. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  5. added
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 27, 2026
  6. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    Intake review

    Related: pkgs/mcpx/TODO.md item 1 and planning/roadmaps/mcpx.md (the design thread this issue formalizes); #237 (takeover unsupported on darwin) and #260 (takeover orphaned-session bug) are the recorded costs of the status quo. Full design doc is in the fleet goal workspace (files/designs/code-mcpx-stdio-supervisor.md).

    Notes: the issue is well-formed — evidence is file:line grounded (takeover.go, handoff.go, stdio.go) and the decision is stated in one line at the top, which is why it carries user/review. This is a design/decision item, not an implementation task.

    Risks: a supervisor per instance adds a long-lived process per stdio server; the v1 byte-pump scope is the right constraint to keep it trivial.

    Suggested approach: Drew/Muse decision first; on adoption, file the phase implementation issues in the same pass (per the fleet rule that a design never sits without an implementation link).

    — fleet monitor intake enrichment, 2026-10-27.

  7. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    Decision recorded — Drew, 2026-10-27

    Adopt the per-instance mcpx supervisor design. Drew approved it in the main chat (2026-10-27 ~13:04 CDT): "yes supervisor if that's the more sophisticated approach has strong advantages."

    Direction:

    • The stdio half of the takeover handoff is replaced by one supervisor per stdio instance (the mcpx binary re-executed as mcpx supervise), owning each child's pipes for the child's whole lifetime and outliving the daemon.
    • Phases 2–3 proceed with the supervisor as the default, per this issue's design.
    • Stop investing in the takeover handoff for stdio; the HTTP half is unaffected by this decision.

    This unblocks the gated mcpx tasks #1463, #1483, and #1484. The user/review label is removed with this comment — the decision the review was waiting on is made.

  8. 3 remaining items

  9. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    Program tending — Phase 1 landed (2026-10-28)

    Recording on the parent, since this thread's last comment predates it: the only filed sub-issue, #1385 (Phase 1 — supervised stdio transport behind a flag), closed 2026-10-28T00:42Z. The adoption decision was recorded here by Drew on 2026-10-27, so acceptance item 1 is met.

    What remains open on this epic, per its own phased breakdown: Phases 2 (takeover becomes attach for supervised instances) and 3 (supervised by default + lifecycle hygiene) are not filed yet — the body deliberately left Phase 2 unfiled until Phase 1's socket protocol was built. That dependency is now cleared, so the next concrete step is filing the Phase 2 implementation issue with its own acceptance criteria; Phase 3's default-flip follows it. Until those exist, this epic should stay open — it is not complete with Phase 1 alone.

    — program tender (issue #1352 run, 2026-10-28)

  10. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Nov 1, 2026
  11. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: picked_up
    task: 2574 (repo-health-code-pilot, openchamber scheduled lane)
    state: Claimed triage; checking the recorded design decision and implementation ownership.

  12. added
    queue/blockedQueue or user escalation state
    status/blockedQueue or user escalation state
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 1, 2026
  13. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: blocked
    task: 2574 (repo-health-code-pilot, openchamber scheduled lane)
    reason: packet_defect — this is a multi-phase epic, not an untriaged single small fix; the queue packet says epic/tracker skip rules were checked.

    The adoption decision was already recorded in #1384 (comment) and Phase 1 issue #1385 is closed. The parent remains open for subsequent phases; re-scope any queue work to a concrete unowned phase after checking existing work. No code or design direction changed.

  14. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  15. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 2574 (triage, blocked, priority 50) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

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/lLarge: multi-day or open-ended; expect staged check-insepicMeta-issue grouping multiple items (org Project Type=Epic)kind/designA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)queue/blockedQueue or user escalation statequeue/queuedHas a queued task in the Supabase agent queuesource/design-architectFiled by the repo design architect jobstatus/blockedQueue or user escalation statetask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions