feat(runtime): separate the framework state root from the plugin code root - #640
Merged
Conversation
… root Artifact-hosted shells (stdio MCP entry, Flight worker, artifact CLI bin and render worker, standalone hook wrappers) resolve their state root under ~/.agent-bundle/state/<plugin>-<digest> (or $XDG_STATE_HOME/agent-bundle/...) instead of <plugin root>/state, so a read-only install launches and two installs never share state. AGENT_BUNDLE_PLUGIN_ROOT keeps naming the code root; AGENT_BUNDLE_STATE_ROOT overrides the state root. Closes #637
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
🦋 Changeset detectedLatest commit: f04259b The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
…ons to <epoch>/state, ignore relative XDG_STATE_HOME Self-review findings: rendered script workers kept the cwd fallback; dev and Workbench MCP sessions would have derived one user-data root per build epoch; a relative XDG_STATE_HOME would have anchored state on the shell's cwd. uninstall --purge-data / doctor coverage of the derived root is #641.
…DG absolute-path wording
ScriptedAlchemy
enabled auto-merge (squash)
September 5, 2026 18:11
ScriptedAlchemy
added a commit
that referenced
this pull request
Sep 5, 2026
…o feat/592-authoritative-manifest
This was referenced Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #637 (follow-up to #628; part of #592).
Problem
resolveWebLaunch()setsAGENT_BUNDLE_PLUGIN_ROOTto the installed artifact,resolvePluginRoot()computedstateRoot = join(root, 'state'), andgeneratedStateOwner()opened the SQLite kernel there — so a plugin with a workspace-durablesrc/state.tstried to create SQLite beneath a possibly read-only installed artifact. #628 moved only the author-facing web${PLUGIN_DATA}directory out of the artifact.Contract
Two roots, resolved once per generated process by
resolvePluginRoot(@agent-bundle/runtime):plugin.root)AGENT_BUNDLE_PLUGIN_ROOT, else the shell's fallbackplugin.stateRoot)AGENT_BUNDLE_STATE_ROOT(explicit override,resolve()d,${…}token = unset with one stderr warning)stateAnchor: 'user-data'(every artifact shell: MCP entry, Flight worker, artifact CLI bin + render worker, standalone hook wrappers)~/.agent-bundle/state/<plugin>-<digest>, or$XDG_STATE_HOME/agent-bundle/<plugin>-<digest>when setstateAnchor: 'root'(default; npm package bin, route-unit / in-memory harnesses)<root>/state— unchanged<plugin>is the code root's basename when it is a safe path segment (elseplugin);<digest>is the first 16 hex chars of SHA-256 over the code root's realpath — the same spelling as theweb-datasegment from #628 (pinned byweb-launch.test.ts). Two installs never share state; symlinked spellings of one install do.ResolvedPluginRootgainsstateSource: 'native' | 'derived'.New public surface:
PLUGIN_STATE_ROOT_ENV_ANCHOR,PluginStateAnchor,pluginStateSegment,userStateHome,userDataStateRoot(@agent-bundle/runtime);pluginStateRootEnvAnchor(agent-bundle).resolveWebLaunchandlaunch.tsbehavior are unchanged: the web launch still names only the code root and never pointsAGENT_BUNDLE_PLUGIN_ROOTat a data directory.Existing installs relocate their state: after upgrading and rebuilding, an installed plugin starts from an empty state root;
AGENT_BUNDLE_STATE_ROOT=<old root>/statekeeps using the old one. The changeset says so (minor,agent-bundle+@agent-bundle/runtime).Manifest v2 (#604) does not record the state location; commented there that it must stay that way (never artifact-relative).
Tests
packages/agent-bundle/tests/packed-readonly-state-root.test.ts—serves a state-writing tool from a read-only installed artifact without writing beneath it: installs the packed tarballs into a clean consumer, builds the newdurable-web-surfacefixture (generated MCP server + web-exposed App + workspace-durable state + CLI command), removes the source,chmods the artifact tree read-only, launches throughresolveWebLaunchwith no state env, records twice, asserts the artifact listing is byte-identical,<artifact>/stateabsent, SQLite underuserDataStateRoot(artifact, env), the artifact CLI bin reads the same entries, a fresh MCP process sees CLI-visible state, and the manifest never mentionsAGENT_BUNDLE_STATE_ROOT.packed-stdio-projection.test.tsnow asserts the derived state root and that nothing lands under the artifact.plugin-root.test.ts: override, blank/token override, user-data derivation (home,XDG_STATE_HOME), unsafe basenames, realpath.entry-shell.test.ts:stateAnchor: 'user-data'emitted for'artifact'shells, absent for'cwd'; byte pins updated.rstest.worker-isolation.tssets a per-workerXDG_STATE_HOMEso spawned shells never write under the developer's home.Docs
runtime-environment(newAGENT_BUNDLE_STATE_ROOT/XDG_STATE_HOMErows, rewritten "Durable state"),mcp,package-entries,cli,installation— en + zh;docs/entry-conventions.md,docs/framework-mode.md.Gate
pnpm build && pnpm typecheck && pnpm lint && pnpm test:unitgreen;test:route-unit,test:projection, targeted integration (generated-route-server,hooks,artifact-cli-bin,workbench-surface,test-harness-manifest,mcp-session-service,cli-routes-build) green; packed pool:packed-readonly-state-root+packed-stdio-projectiongreen;pnpm docs:site:buildgreen.Deslop: Claude Fable 5.1, 6 edits (deduplicated the three identical
launch.tsdoc comments, tightenedlaunch-env-shell.ts/hook-contract.tscomments, removed a repeated clause in en/zhcli.mdx).Self-review
Reviewer: GPT-5.6 Sol (
generalPurpose;change-risk-reviewerwas blocked on the TraceDecay daemon). Author lanes: Grok / Sol / Fable-high; orchestrator Claude Fable 5.1.Pass 1 — 5 findings:
scripts/<name>-flight.mjs) kept the'cwd'state fallback — fixed:build/entries.tspassesstateFallback: 'artifact';cli-routes-build.test.tsasserts the emitted worker carriesstateAnchor: 'user-data'.AGENT_BUNDLE_PLUGIN_ROOTand would have derived one~/.agent-bundle/state/<epoch>-<digest>per rebuild — fixed:dev/mcp-session/mcp-session-launch.tspinsAGENT_BUNDLE_STATE_ROOT=<epoch>/state(main's location; declared env still wins);mcp-session-service.test.tsasserts it.uninstall --purge-dataleaves the derived state root — deferred to uninstall --purge-data and doctor do not address the derived framework state root (~/.agent-bundle/state) or web-data (follow-up to #637, #628) #641 (same gap fix(564): port the #620 review follow-ups from the merged branch — declared-projection web launch, identity-keyed sessions, opening-tool policy, out-of-artifact web state #628 left forweb-data); disclosed in the changeset and in en/zhcli.mdx/installation.mdx.doctorinventories only<root>/state— deferred to uninstall --purge-data and doctor do not address the derived framework state root (~/.agent-bundle/state) or web-data (follow-up to #637, #628) #641, same disclosure.XDG_STATE_HOMEwould anchor state on the shell's cwd (the read-only artifact) — fixed:userStateHomehonors absolute values only (XDG spec); test + docs updated. Emptyos.homedir()left as is: Node returns the platform home or throws; no artifact shell runs without one.Pass 2 — no code blocker; 3 doc warnings, all fixed:
--purge-datarow scoped to legacy state (en/zh), zhplugin.value.root, changeset says "absolute path" forXDG_STATE_HOME.