Skip to content

mcpx: spawn repo/worktree-scoped instances with the child cwd set to the resolved scope root #1989

Closed

Description

@dezren39

Relocated from dezren39/darwin-nix#817 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).

Problem

mcpx scopes choose which process serves a caller, but nothing tells that process where the caller is.

Scope keys already resolve the caller's cwd to a git root: internal/config/scope.go (CallContext, Scope.Key()) yields repo:<git-common-dir> / worktree:<top-level> using the native git discovery in internal/config/gitdiscover.go. But instance spawn ignores that resolution: internal/pool/pool.go dial() builds StdioOptions{Cwd: p.cfg.Cwd} — the config's static string, verbatim — and internal/mcpclient/stdio.go sets cmd.Dir = opts.Cwd. A repo- or worktree-scoped server therefore still runs in the daemon's cwd.

Concrete casualty (observed live, 2026-10-25, fleet config of 16 servers): gopls is a workspace server whose workspace is its process cwd. Scoped per-checkout it would still run in the daemon's cwd, so it loads files ad hoc ("command-line-arguments" package) instead of as a module workspace session.

Proposed smallest design

When a server's scope is repo or worktree and its config cwd is empty, spawn the instance with Dir = the resolved scope root (the common-dir's worktree / the checkout top-level) instead of the daemon's cwd.

This needs the scope resolution to hand the pool the root, not just the opaque key string: return the resolved root alongside the key from Scope.Key(), or recompute it via locateGit at spawn time. One branch in pool.dial().

Explicit config cwd always wins — the scope root is only the default when cwd is unset.

Why this order

This is step 1 of the per-project design note (2026-10-25). With it, "mcpx": {"scope": "worktree"} on gopls becomes a real per-project workspace server: one gopls per checkout, rooted in that checkout. Flipping scopes before this lands only multiplies processes for little gain, so config scope flips wait for this issue.

Related: #818 — the ${mcpx.root} / ${mcpx.cwd} substitution issue (companion, step 2) — and #819 — the initialize rootUri/workspaceFolders issue (step 3, builds on the root resolution here).

Acceptance criteria

  • A worktree-scoped server with no config cwd is spawned with its working directory set to the caller's checkout top-level; a repo-scoped one to the resolved worktree of the common dir.
  • Two callers in different checkouts of the same repo get distinct worktree-scoped instances rooted in their own checkouts; repo scope keeps sharing one instance per common dir.
  • An explicit config cwd overrides the scope root in all cases.
  • global/cwd:/session and other scopes are unchanged.
  • A worktree-scoped gopls answers a package-level query (e.g. go_package_api on a module path) with the module's real API instead of an empty result.
  • Tests cover the spawn-cwd decision (scope root vs explicit cwd vs daemon cwd fallback).

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#817 (verbatim, oldest first):


    @dezren39 · 2026-10-26T02:47:00Z

    Intake review

    Still live (verified this run): no merged PR in this repo or in developing-today/code implements scope-root spawn cwd for mcpx; the spawn path cited in the evidence (internal/pool dial() → static config Cwd) is unaddressed. Not closed.

    Related — this is a set, in order: #817 (this, step 1: spawn cwd) → #818 (step 2: ${mcpx.root} / ${mcpx.cwd} substitution) → #819 (step 3: initialize rootUri/workspaceFolders). One origin set per the fleet cross-repo rule; downstream work references this set rather than re-filing.

    Cross-repo note: the mcpx source no longer lives in this repo (no pkgs/mcpx here — verified this run); it lives in developing-today/code pkgs/mcpx, which this repo builds from the mcpx-src input. The implementation PR lands there; this issue tracks the set from the fleet-config side. app/mcpx was coined on this repo for this set.

    Notes / risks: priority/high as the set's blocker — #818 and #819 both reuse the resolved-root plumbing this introduces, and the live casualty (gopls loading files ad hoc, go_package_api empty after 156 s) persists until it lands. Explicit config cwd must always win; global/cwd:/session scopes must not change.

    Suggested approach: return the resolved root alongside the opaque scope key (or recompute via locateGit at spawn), default Dir to it only for repo/worktree scopes with empty config cwd, and pin the decision with the spawn-cwd tests in the criteria.

    🤖 Fleet monitor intake enrichment — labels normalized and meta/enriched applied this pass.


    @dezren39 · 2026-10-26T11:28:55Z

    Design consolidated and moved home. This issue's design (and its two companions #818/#819) now lives in the mcpx source repo as decision record 0004: PR #957 (pkgs/mcpx/docs/decisions/0004-scope-resolved-roots.md).

    Position: the record fully supersedes this issue as a design document — the proposed design is adopted as written, and §1–§3/§6 of the record decide the plumbing (a ResolvedScope sibling to Scope.Key, root as a pure function of the key, pool learns the root at Acquire). Implementation is tracked in #958 (slice 1), whose acceptance criteria carry this issue's over verbatim (the live gopls proof sits in slice 4, code#961, where the full stack exists).

    Leaving this issue open deliberately: closing the nix-side originals with pointers is the #309 re-homing pass's acceptance criterion (tracked here as #309), and this comment is the pointer that pass needs.

  3. added
    enhancementNew feature or request
    kind/featureNew feature or capability request
    source/humanFiled directly by a human
    priority/highImportant and time-sensitive
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    app/mcpxPackage/app: mcpx (pkgs/ or apps/ in this repo)
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 28, 2026
  4. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  5. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  6. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  7. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 2, 2026
  8. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    Claimed as queue task 2895 (triage). Deduping against the #957/#958 slice chain and confirming spawn-cwd scope against current main.

  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    Triage verdict: ALREADY DONE — implemented by PR #3925

    Verified against fresh state (origin/main @ 0e26fa4b3):

    Closing as implemented.

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 workenhancementNew feature or requestkind/featureNew feature or capability requestmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivesource/humanFiled directly by a human

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions