Repository navigation
Conversation
In a linked project on the orphan or two-layer backend, resolveSquadState ran the git backend in the project repo and rooted the storage adapter at the project's .squad, while every squad_state tool builds paths under the team's .squad. Each memory.*, squad_state_write and squad_state_read call failed with "path is outside squadDir". The team repo owns the state: - in linked mode with a git backend, the backend runs in the team repo's git top level, and resolution fails with a clear message when the team root is not in a git repository; - the adapter is rooted at the team's .squad, the root the local backend and the tools already use (unchanged outside linked mode); - the config parsing in resolveStateBackend moves to configuredStateBackend() so the type can be checked first. Closes github#2320
🟡 Impact Analysis — PR #2351Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affecteddocs (1 file)
root (1 file)
squad-sdk (2 files)
tests (1 file)
This report is generated automatically for every PR. See #733 for details. |
🛫 PR Readiness Check
PR Scope: 📦🔧 Mixed (product + infrastructure)
|
| Status | Check | Details |
|---|---|---|
| ✅ | Single commit | 1 commit — clean history |
| ✅ | Not in draft | Ready for review |
| ✅ | Branch up to date | Up to date with dev |
| ❌ | Copilot review | No Copilot review yet — it may still be processing |
| ✅ | Changeset present | Changeset file found |
| ✅ | Scope clean | No .squad/ or docs/proposals/ files |
| ✅ | No merge conflicts | No merge conflicts |
| ✅ | Copilot threads resolved | No Copilot review threads |
| ❌ | CI passing | 15 check(s) still running |
Files Changed (5 files, +127 −9)
| File | +/− |
|---|---|
.changeset/linked-team-git-backend.md |
+5 −0 |
docs/src/content/docs/features/state-backends.md |
+2 −0 |
packages/squad-sdk/src/resolution.ts |
+37 −4 |
packages/squad-sdk/src/state-backend.ts |
+16 −3 |
test/cli/state-mcp.test.ts |
+67 −2 |
Total: +127 −9
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
|
Thanks Jose, this is the right fix direction and the storage adapter part is solid (roots at teamSquadDir, normalizes Windows separators and case, rejects outside paths and dot segments, keeps the root delete guard). One boundary issue we need closed before merging, found in our security pass on 0ef7eb3: In packages/squad-sdk/src/resolution.ts around line 975, What we need:
Not a question of intent, just a boundary we have to hold since this path decides where state gets written. Happy to review again as soon as it is pushed. |
|
👋 Friendly nudge — this PR has had no activity for 7 days. What needs attention:
If this PR is abandoned, please close it. If it's blocked on something external, leave a comment so the team knows. |
What
A linked project on the
orphanortwo-layerstate backend can usesquad_stateagain. The git backend runs in the team repo, and the storage adapter is rooted at the team's.squad. If the team root isn't in a git repository, resolution fails with a message that says so.Why
Closes #2320
resolveSquadStateran the git backend in the project repo and rootedStateBackendStorageAdapterat the project's.squad, whilestate-mcproots every tool at the team's.squad. Everymemory.*,squad_state_writeandsquad_state_readcall therefore failed with "path is outside squadDir". As Tamir confirmed on the issue, the team repo owns.squad/, so its state belongs on the team repo'ssquad-statebranch.How
resolution.ts,resolveSquadState.orphanortwo-layerbackend,repoRootbecomes the team repo's git top level (git rev-parse --show-toplevelfrom the team's.squad, then from the team root).paths.teamSquadDir, the same root the local backend (FSStorageProvider) and every tool already use. Outside linked modeteamSquadDirequalsprojectDir, so nothing changes there.repoRootin the returned context is now the repo the backend uses, which is what its doc comment describes. None of the five callers in the CLI reads it.state-backend.ts. The config parsing inresolveStateBackendmoves intoconfiguredStateBackend(), soresolveSquadStatecan check the type before creating the backend. It's not exported from the package index, andresolveStateBackendbehaves the same.What stays the same:
config.json. I left that alone on purpose; there's a related question about it below.Related question, not changed here:
squad linkwrites onlyteamRootandprojectKeyto the project'sconfig.json, neverstateBackend. If the team repo itself runs onorphan(set in the team's ownconfig.json), a project linked withsquad linktherefore resolves tolocaland writes straight into the team repo's working tree instead of itssquad-statebranch. Should a linked project take the team'sstateBackendwhen its own config doesn't set one? I can open a separate issue for that.Testing
New tests in
test/cli/state-mcp.test.ts, through a realcreateStateMcpSessionfrom the project dir:orphanand withtwo-layer:squad_state_write,squad_state_readandmemory.writesucceed;refs/heads/squad-state(fororphan, the written key is on it);squad-statebranch and no notes;orphanwhose team root isn't in a git repository:resolveSquadStatethrows the message above. This one runs under the system temp dir, since anywhere inside the checkoutgit rev-parsefinds the Squad repo itself.Results:
distrebuilt from it (the CLI imports the SDK fromdist), all 3 new tests fail. With the change, they pass.cli/state-mcpandstate-backend: 164 passed.devand on this branch (a Windows symlink test inplugin-extensibilityand render timeouts inrepl-uxandhostile-integration); the rest were timeouts from running 81 files at once.npm run lint: clean..changeset/linked-team-git-backend.md, sdk patch)PR Readiness Checklist
Branch & Commit
dev(notmain)dev(c3f54c2)Build & Test
npm run lintpassesDocs
features/state-backends.md: a short "Linked Projects" note (state stays with the team repo, and the error when the team root isn't in a git repository)Breaking Changes
None for working setups. A linked project on a git backend whose team root isn't in a git repository now gets a clear error when it starts, instead of a failure on every state call.
Waivers
None.