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
{{ message }}
Repository navigation
squad_state fails on every call in a linked team with the orphan or two-layer backend #2320
In a linked project (teamRoot pointing at a separate team repo) with "stateBackend": "orphan" (same for two-layer), the squad_state tools can't touch the team's state at all.
Repro
A team repo with .squad/, and an app repo whose .squad/config.json is { "version": 1, "teamRoot": "../team-repo", "stateBackend": "orphan" }.
Start squad state-mcp from the app repo (or build the registry with createStateMcpToolRegistry(appRepo)).
Call memory.write, squad_state_write or squad_state_read.
Every call fails with:
[squad] toRelative: path is outside squadDir and cannot be used as a state key.
path: …/team-repo/.squad/memory/config.json
squadDir: …/app-repo/.squad
squad_state_write returns "Failed to write state: …" and squad_state_read returns "Failed to read state: …" with the same message.
Why
createStateMcpToolRegistry roots the tools at context.paths.teamSquadDir (packages/squad-cli/src/cli/commands/state-mcp.ts:76-87). For git backends, resolveSquadState builds the storage as new StateBackendStorageAdapter(backend, paths.projectDir) (packages/squad-sdk/src/resolution.ts:973-975), so the adapter only accepts paths under the project's .squad. Every tool path is under the team's .squad, so each call trips the traversal guard. The comment just above says the root is meant to match teamSquadDir, which is what the local backend does (new FSStorageProvider(paths.teamSquadDir)).
It probably hasn't been noticed because linked teams usually run on the local backend.
Open question
For a linked team on a git backend, which repo should own the squad-state branch: the team repo or the project? The answer decides whether the fix is rooting the adapter at the team's .squad (and resolving the backend there), or rejecting this combination with a clear error.
I found this while fixing #2316 (#2317 makes squad memory go through the same store, so it inherits this). Happy to send a PR once the direction is clear.
Thanks for filing this with such a clear repro, Jose. I confirmed this on dev:
esolveSquadState in packages/squad-sdk/src/resolution.ts:975 constructs
ew StateBackendStorageAdapter(backend, paths.projectDir) (even though the comment right above at line 969 says
ootDir is paths.teamSquadDir), while createStateMcpToolRegistry in packages/squad-cli/src/cli/commands/state-mcp.ts:81 roots the tools at context.paths.teamSquadDir. For a linked team on orphan or wo-layer, the team repo is the one that owns .squad/, so rooting the backend and adapter at the team repo's .squad (or failing with an explicit message when the team root is not a git repo) makes sense. We are currently in a release freeze for 2.0.0-preview.2 and will take this in the next preview. If you want to send a PR for it, we would be very happy to review and merge it after the freeze.
Thanks Tamir! The PR is up: #2351. A linked project on orphan or two-layer now runs the git backend in the team repo, with the adapter rooted at the team's .squad. If the team root isn't in a git repository, it stops with an explicit message. It's tested end to end through state-mcp for both backends.
One related thing I left out of the PR on purpose: squad link never writes stateBackend, so if the team repo itself runs on orphan, a linked project still resolves to local and writes into the team repo's working tree. Happy to open a separate issue if you'd like the linked project to follow the team's setting.
Activity
tamirdresher commentedon Oct 26, 2026
Thanks for filing this with such a clear repro, Jose. I confirmed this on dev:
esolveSquadState in packages/squad-sdk/src/resolution.ts:975 constructs
ew StateBackendStorageAdapter(backend, paths.projectDir) (even though the comment right above at line 969 says
ootDir is paths.teamSquadDir), while createStateMcpToolRegistry in packages/squad-cli/src/cli/commands/state-mcp.ts:81 roots the tools at context.paths.teamSquadDir. For a linked team on orphan or wo-layer, the team repo is the one that owns .squad/, so rooting the backend and adapter at the team repo's .squad (or failing with an explicit message when the team root is not a git repo) makes sense. We are currently in a release freeze for 2.0.0-preview.2 and will take this in the next preview. If you want to send a PR for it, we would be very happy to review and merge it after the freeze.
joslat commentedon Oct 26, 2026
Thanks Tamir! The PR is up: #2351. A linked project on
orphanortwo-layernow runs the git backend in the team repo, with the adapter rooted at the team's.squad. If the team root isn't in a git repository, it stops with an explicit message. It's tested end to end throughstate-mcpfor both backends.One related thing I left out of the PR on purpose:
squad linknever writesstateBackend, so if the team repo itself runs onorphan, a linked project still resolves tolocaland writes into the team repo's working tree. Happy to open a separate issue if you'd like the linked project to follow the team's setting.fix(sdk): keep a linked team's state on the team repo's git backend
fix(sdk): keep a linked team's state on the team repo's git backend