Per-workspace MCP env support is the load-bearing gap behind ADR-0021 phase 2's operator-toil ceiling
Why this memo
Coaching closed its strict-completion 3-of-3 yesterday (per-workspace clone on disk + AGENTS.md repointed + spaces.json mount swap). The migration looked clean from the outside. Today, in the course of shipping a small fix to the coordination_commit runner, we tripped the structural gap that's been quietly sitting under everything Phase 2 was supposed to fix. Worth naming explicitly so the next domain doesn't burn a session figuring it out from scratch.
The gap is small in scope but load-bearing: there is no per-workspace mechanism to tell the global MCP sguild-tools server which coordination clone to operate against. ADR-0021 designed the per-workspace clone substrate; ADR-0023 named the renaming and spaces.json mount mechanics. Neither has produced a runtime path by which a domain Cowork session, opened against its own per-workspace clone, can get the MCP server to also point there.
What we observed today
While shipping fc0d1ce (the auto-resolve fix on scripts/coord-surgical-commit.mjs):
- Wrote the patch via Edit tools to
C:\Users\jallr\Documents\Sguild\repos\coaching-coordination\scripts\coord-surgical-commit.mjsand.../sguild-tools/server.mjs(the per-workspace coordination clone — what spaces.json mounts for the Coaching Cowork space, what the bash sandbox sees). - Called
coordination_committo stage and push the two paths. - The tool ran through pull → fast-forward → restore-from-backup →
git add -- <path>(exit 0 each) → and thengit diff --cached --name-onlycame back empty. ReportedFAIL: staged set does not match requested setwithstaged: [].
Root cause: the MCP server resolves its coordination clone at startup time via REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..") in sguild-tools/server.mjs. Whichever clone holds the server file is the clone every coordination-scoped tool runs against, forever, for as long as that server process lives. On the current single-global-MCP-config setup, that's one fixed clone — different from whichever per-workspace clone the bash sandbox happens to mount for the current Cowork space. When the edits the operator wants to commit live in a different clone, the MCP tool sees only an unchanged-vs-HEAD copy and silently no-ops the stage step.
The SGUILD_COORDINATION_CHECKOUT env-var fallback that ADR-0021 introduced exists for exactly this purpose, but on the global-config substrate there's no per-workspace way to set it. The env var either gets stomped by whichever space launched the server most recently or stays unset — the trampling pattern ADR-0023 already named.
Same gap affects coordination_remove_git_lock (the lock left over from the failed coordination_commit attempt sat in the per-workspace clone's .git, where the lock-removal MCP tool can't see it; we cleared it via bash plus the Cowork file-delete permission). And by extension every other coordination-clone-scoped tool the server exposes today or will expose tomorrow.
What unblocks it
A per-workspace mechanism to inject env into the MCP server for a given Cowork space. Options Platform / Cowork product should evaluate:
- spaces.json drives env vars in addition to mounts. The cleanest fit: the same file that already says "this space mounts coaching-coordination" also says "this space launches the MCP server with
SGUILD_COORDINATION_CHECKOUT=...coaching-coordination". The env-var fallback already exists inserver.mjs(with the sanity check ADR-0023 added) — once the env is reliably set per workspace, the tools just work. - Per-workspace MCP server instances. Each Cowork space gets its own MCP server process with its own env. Heavier (multiple
nodeprocesses), but eliminates the env-injection step entirely. - An MCP-side
clonearg on every coordination-clone-scoped tool (the workaround we shipped today forcoordination_remove_git_lockin this same commit set). Operator chooses the clone per call. Works, but pushes the routing decision to every caller of every tool — a long tail of breakage as new tools land.
We are not picking between these from inside Coaching — that's an architecture call that touches Platform's domain and the Cowork product team's roadmap.
Today's workaround, for the record
Until per-workspace MCP env support lands, every coordination-clone-scoped MCP tool call from a non-server-aligned workspace needs to be routed through a host-side transient runner. The pattern that worked today: write a small .mjs runner into <domain>/scripts/_*.mjs, have it spawnSync git operations with cwd set to the per-workspace clone's absolute path, invoke via <domain>_run_script, delete the runner. The runner code is mechanical enough that it could be templated, but doing so would normalize the workaround and reduce the pressure to fix the underlying gap — so we are deliberately not templating it.
The coordination_remove_git_lock patch that ships in the same commit as this memo adds a clone arg as a tactical mitigation for that one tool (it can now clear locks in any per-workspace clone, not just the server's), but the structural fix is still per-workspace env support.
What we're asking
A short discovery memo or ADR back from Platform by 2026-06-09 covering:
- Which of the three options above (or another we missed) is the recommended path, and why.
- Whether the work is bounded enough to live as a Cowork product feature (spaces.json env injection) or needs an ADR for cross-domain alignment first.
- Who owns the fix: Platform alone, Cowork product alone, or joint.
- Whether the current operator-toil-with-transient-runners pattern is OK to keep using as the documented workaround until the fix lands, or whether anyone should pause coordination-clone-scoped MCP tool work in the meantime.
What we are not asking
We are not asking Platform to ship the fix by 2026-06-09 — only to scope it and identify the owner. The transient-runner workaround is tolerable for the small number of cross-domain commits each domain ships per week; we just don't want it to become the long-term answer.
Related
- ADR-0021 phase 2 (per-workspace coordination clones): the migration this gap was supposed to retire.
- ADR-0023 (cowork-runtime coordination scope /
sguild-tools→platform-toolsrename): names the trampling problem the global config creates; this memo names a symptom that survives even after the rename. 2026-05-26-coaching-coord-commit-autostash-auto-resolve(same commit): the autostash fix that landed despite this gap, by routing through a transient runner.- Coaching's auto-memory
feedback_coordination-auto-commit-raceandreference_mcp-topology-this-machine: where today's debug trail is recorded for future Coaching sessions.