You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Package:agent-bundle (mcp run), at main da5df1d (PR #52, RFC #50 Phase 1). Found empirically by the movie-library Phase-2 migration onto that commit.
What happens
Under mcp run, the manifest's ${PLUGIN_ROOT} token — and with it the AGENT_BUNDLE_PLUGIN_ROOT env anchor that adapters inject on every stdio server since PR #49 (#47's fix) — expands to the artifact target root, not the project root:
resolveMcpStdioLaunch (src/services/mcp-run.ts) resolves tokens with pluginRoot: targetRoot, where targetRoot = joinArtifact(artifact, target) — e.g. artifact/portable.
Every emitted stdio server carries env.AGENT_BUNDLE_PLUGIN_ROOT = "${PLUGIN_ROOT}" (withPluginRootEnvAnchor, src/adapters/types.ts), so under mcp run the documented state anchor points into the build output directory.
runMcpForeground spawns with env: { ...process.env, ...launch.env } — expanded manifest env last — so an operator's export AGENT_BUNDLE_PLUGIN_ROOT=… before mcp run is silently overridden. (A config-declared anchor wins over the injected one by design, per the comment on withPluginRootEnvAnchor, but the operator-shell layer has no such escape.)
Contrast with the same function one layer up: runMcp (src/api.ts) already computes workspaceRoot = resolve(options.root) and anchors ${PLUGIN_DATA} durably at <project>/.agent-bundle/mcp-run/<target>/<server>. The framework already chose project-root durability for plugin-data; the plugin-root anchor is the inconsistent one.
Empirical consequence
Movie-library's state resolution trusts the anchor exactly as #47's fix intends. A bare mcp run handshake against the checkout's artifact/ wrote persistent state to artifact/portable/.runtime — fragmenting per rebuild and dying with artifact regeneration. That is precisely the state-fragmentation defect (per-tracker policy ledger, live Trakt device auth) the anchor was introduced to prevent in host installs. The consumer's wrapper therefore stays, pinning MOVIE_LIBRARY_ROOT / MOVIE_LIBRARY_STATE_DIR (layer 1 of its state-root chain, which beats the anchor).
The semantics question
Both expansions are defensible, which is why this is filed as a question and not just a defect:
Under a host install, ${PLUGIN_ROOT} = install root is correct — the artifact is the installation, and it is durable.
Under mcp run against a checkout's artifact/, the artifact is an ephemeral build product; the "plugin root" in the operator's mental model is the checkout.
One nuance constrains the fix: ${PLUGIN_ROOT} in args/cwd genuinely must remain the artifact target root — args[0] is the content-hashed bundle inside it. It is specifically the env-anchor semantics that mislead runtimes into anchoring durable state on a directory with build-product lifetime.
Suggested fix directions
Under mcp run, expand env-value occurrences of ${PLUGIN_ROOT} (or at minimum the injected AGENT_BUNDLE_PLUGIN_ROOT) to the resolved project root, keeping args/cwd on the artifact target root. The machinery exists: the token resolver already supports per-field token→root maps (McpPathTokenMap, src/services/mcp-path-tokens.ts), and runMcp already holds workspaceRoot. Tradeoff: dev runs gain checkout-state continuity (matching wrapper behavior and host-install durability), at the cost of mcp run no longer being a byte-faithful rehearsal of a copied-artifact launch — a --plugin-root <path> override restores that when wanted.
Keep artifact-root expansion as a documented default, add --plugin-root <path>, and let the operator's environment win: today export AGENT_BUNDLE_PLUGIN_ROOT=… is silently beaten by the manifest anchor because of the env spread order. Weaker: every consumer learns the flag only after state has fragmented once.
Related: #58 (companion mcp-run finding, missing .env loading), #50 (RFC #50 Phase 1 shipped mcp run), #47 (the anchor this expansion undermines was introduced there and fixed by PR #49) — empirically resolved by the movie-library Phase-2 migration against da5df1d.
Package:
agent-bundle(mcp run), at mainda5df1d(PR #52, RFC #50 Phase 1). Found empirically by the movie-library Phase-2 migration onto that commit.What happens
Under
mcp run, the manifest's${PLUGIN_ROOT}token — and with it theAGENT_BUNDLE_PLUGIN_ROOTenv anchor that adapters inject on every stdio server since PR #49 (#47's fix) — expands to the artifact target root, not the project root:resolveMcpStdioLaunch(src/services/mcp-run.ts) resolves tokens withpluginRoot: targetRoot, wheretargetRoot = joinArtifact(artifact, target)— e.g.artifact/portable.env.AGENT_BUNDLE_PLUGIN_ROOT = "${PLUGIN_ROOT}"(withPluginRootEnvAnchor,src/adapters/types.ts), so undermcp runthe documented state anchor points into the build output directory.runMcpForegroundspawns withenv: { ...process.env, ...launch.env }— expanded manifest env last — so an operator'sexport AGENT_BUNDLE_PLUGIN_ROOT=…beforemcp runis silently overridden. (A config-declared anchor wins over the injected one by design, per the comment onwithPluginRootEnvAnchor, but the operator-shell layer has no such escape.)Contrast with the same function one layer up:
runMcp(src/api.ts) already computesworkspaceRoot = resolve(options.root)and anchors${PLUGIN_DATA}durably at<project>/.agent-bundle/mcp-run/<target>/<server>. The framework already chose project-root durability for plugin-data; the plugin-root anchor is the inconsistent one.Empirical consequence
Movie-library's state resolution trusts the anchor exactly as #47's fix intends. A bare
mcp runhandshake against the checkout'sartifact/wrote persistent state toartifact/portable/.runtime— fragmenting per rebuild and dying with artifact regeneration. That is precisely the state-fragmentation defect (per-tracker policy ledger, live Trakt device auth) the anchor was introduced to prevent in host installs. The consumer's wrapper therefore stays, pinningMOVIE_LIBRARY_ROOT/MOVIE_LIBRARY_STATE_DIR(layer 1 of its state-root chain, which beats the anchor).The semantics question
Both expansions are defensible, which is why this is filed as a question and not just a defect:
${PLUGIN_ROOT}= install root is correct — the artifact is the installation, and it is durable.mcp runagainst a checkout'sartifact/, the artifact is an ephemeral build product; the "plugin root" in the operator's mental model is the checkout.One nuance constrains the fix:
${PLUGIN_ROOT}inargs/cwdgenuinely must remain the artifact target root —args[0]is the content-hashed bundle inside it. It is specifically the env-anchor semantics that mislead runtimes into anchoring durable state on a directory with build-product lifetime.Suggested fix directions
mcp run, expand env-value occurrences of${PLUGIN_ROOT}(or at minimum the injectedAGENT_BUNDLE_PLUGIN_ROOT) to the resolved project root, keepingargs/cwdon the artifact target root. The machinery exists: the token resolver already supports per-field token→root maps (McpPathTokenMap,src/services/mcp-path-tokens.ts), andrunMcpalready holdsworkspaceRoot. Tradeoff: dev runs gain checkout-state continuity (matching wrapper behavior and host-install durability), at the cost ofmcp runno longer being a byte-faithful rehearsal of a copied-artifact launch — a--plugin-root <path>override restores that when wanted.--plugin-root <path>, and let the operator's environment win: todayexport AGENT_BUNDLE_PLUGIN_ROOT=…is silently beaten by the manifest anchor because of the env spread order. Weaker: every consumer learns the flag only after state has fragmented once.mcp runthe anchor points intoartifact/<target>and durable state should ride${PLUGIN_DATA}(already durably anchored at<project>/.agent-bundle/mcp-run/…). Weakest: it contradicts the claude adapter drops cwd for source-built stdio servers: no working-directory anchor on bare installs (portable and codex both provide one) #47-era guidance to trust the anchor, and plugins that share state between host installs and dev runs (movie-library does) cannot split roots per launch mode.Related: #58 (companion mcp-run finding, missing .env loading), #50 (RFC #50 Phase 1 shipped
mcp run), #47 (the anchor this expansion undermines was introduced there and fixed by PR #49) — empirically resolved by the movie-library Phase-2 migration againstda5df1d.