Skip to content

squad_state fails on every call in a linked team with the orphan or two-layer backend #2320

Open

Description

@joslat

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

  1. A team repo with .squad/, and an app repo whose .squad/config.json is { "version": 1, "teamRoot": "../team-repo", "stateBackend": "orphan" }.
  2. Start squad state-mcp from the app repo (or build the registry with createStateMcpToolRegistry(appRepo)).
  3. 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.

Activity

  1. tamirdresher commented on Oct 26, 2026

    @tamirdresher
    Collaborator

    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.

  2. joslat commented on Oct 26, 2026

    @joslat
    ContributorAuthor

    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.

  3. added 2 commits that reference this issue on Nov 10, 2026
    24e6789
    aa2e033
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    wave1:P1Wave 1 PR group P1wave:1-nextWave 1 — immediate next sprint

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions