Repository navigation
fix(cli): resolvable squad_state bridge spec, doctor bridge probe, versioned health, state-mcp lifecycle (2.0.0-preview.2) - #2301
Conversation
🟠 Impact Analysis — PR #2301Risk tier: 🟠 HIGH 📊 Summary
🎯 Risk Factors
📦 Modules Affecteddocs (1 file)
root (2 files)
squad-cli (8 files)
squad-sdk (3 files)
tests (9 files)
|
🏗️ Architectural Review
Automated architectural review — informational only. |
🛫 PR Readiness Check
PR Scope: 📦🔧 Mixed (product + infrastructure)
|
| Status | Check | Details |
|---|---|---|
| ❌ | Single commit | 6 commits — consider squashing before review |
| ✅ | 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 | 5 unresolved Copilot thread(s) — fix and resolve before merging |
| ❌ | CI passing | 1 check(s) failing: Verify exact SHA and build evidence |
Files Changed (23 files, +1416 −262)
| File | +/− |
|---|---|
.changeset/calm-bridges-exit.md |
+6 −0 |
.changeset/safe-state-root.md |
+5 −0 |
docs/src/content/docs/scenarios/troubleshooting.md |
+37 −0 |
packages/squad-cli/src/cli/commands/doctor.ts |
+80 −0 |
packages/squad-cli/src/cli/commands/state-mcp.ts |
+39 −1 |
packages/squad-cli/src/cli/core/init.ts |
+15 −1 |
packages/squad-cli/src/cli/core/managed-file.ts |
+35 −6 |
packages/squad-cli/src/cli/core/mcp-probe.ts |
+234 −0 |
packages/squad-cli/src/cli/core/mcp-root.ts |
+117 −4 |
packages/squad-cli/src/cli/core/mcp-spec.ts |
+70 −92 |
packages/squad-cli/src/cli/core/upgrade.ts |
+19 −6 |
packages/squad-sdk/src/client/adapters/coordinator.ts |
+1 −1 |
packages/squad-sdk/src/state-backend.ts |
+77 −36 |
packages/squad-sdk/src/tools/index.ts |
+9 −3 |
test/cli/doctor.test.ts |
+64 −1 |
test/cli/state-mcp-lifecycle.test.ts |
+129 −0 |
test/cli/state-mcp.test.ts |
+49 −0 |
test/cli/upgrade-portability.test.ts |
+17 −9 |
test/fixtures/fake-state-mcp.mjs |
+46 −0 |
test/mcp-root-write.test.ts |
+229 −3 |
test/mcp-spec-init.test.ts |
+38 −84 |
test/npm-registry-fallback.test.ts |
+8 −15 |
test/state-backend-root-guard.test.ts |
+92 −0 |
Total: +1416 −262
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved shutdown, declaration-migration, and bridge-validation defects undermine the intended reliability improvements.
6 open issues
Compare health against the configured external team-root identity · New Clean up transport listeners only after server closure · New Propagate declaration environment overrides through bridge spawning · New Support historical uppercase-drive paths in legacy bridge lookup · New Reconcile bridges only after successful project config writes · New Apply coordinator version checks to GitHub Copilot templates · New
What changed in this PR
This PR improves Squad’s state bridge launch reliability, diagnostics, and shutdown behavior for #2296 and #2300. Template/workflow refresh behavior remains outside its scope.
Changes:
- Prefer installed launchers with exact-version fallback metadata and remove matching legacy user declarations.
- Add doctor handshake checks, versioned health output, and stricter projected coordinator checks.
- Add transport-shutdown handling, regression tests, and process-cleanup guidance.
| File | Description |
|---|---|
test/npm-registry-fallback.test.ts |
Verify deterministic exact-version fallback. |
test/mcp-spec-init.test.ts |
Cover installed launchers and fallback selection. |
test/mcp-root-write.test.ts |
Test metadata, legacy cleanup, and project identities. |
test/fixtures/fake-state-mcp.mjs |
Provide a configurable bridge-probe fixture. |
test/cli/upgrade-portability.test.ts |
Update launch-spec and metadata expectations. |
test/cli/state-mcp.test.ts |
Check versioned health output. |
test/cli/state-mcp-lifecycle.test.ts |
Check process exit after stdin closure. |
test/cli/doctor.test.ts |
Cover bridge handshake and launch diagnostics. |
packages/squad-sdk/src/tools/index.ts |
Add runtime version to health output. |
packages/squad-sdk/src/client/adapters/coordinator.ts |
Tighten projected coordinator handshake requirements. |
packages/squad-cli/src/cli/core/upgrade.ts |
Reconcile legacy declarations during upgrade. |
packages/squad-cli/src/cli/core/mcp-spec.ts |
Resolve direct launchers and exact-version fallbacks. |
packages/squad-cli/src/cli/core/mcp-root.ts |
Record metadata and reconcile hashed registrations. |
packages/squad-cli/src/cli/core/mcp-probe.ts |
Resolve and probe configured bridges over stdio. |
packages/squad-cli/src/cli/core/init.ts |
Reconcile legacy declarations during initialization. |
packages/squad-cli/src/cli/commands/state-mcp.ts |
Add shutdown handlers and runtime-version propagation. |
packages/squad-cli/src/cli/commands/doctor.ts |
Validate bridge version, health, and team directory. |
docs/src/content/docs/scenarios/troubleshooting.md |
Document lingering sessions and targeted cleanup. |
.changeset/calm-bridges-exit.md |
Record CLI and SDK patch changes. |
Review effort: Balanced
| }; | ||
| } | ||
|
|
||
| const expectedTeamDir = path.resolve(effectiveSquadDir(cwd).stateDir); |
| process.stdout.on('error', onStdoutError); | ||
|
|
||
| try { | ||
| await server.connect(transport); |
| ) { | ||
| return undefined; | ||
| } | ||
| return { command: entry.command, args: entry.args as string[] }; |
| const paths = [normalizeSquadProjectPath(dest), normalizeDriveLetter(path.resolve(dest))]; | ||
| return [...new Set(paths.map(value => `squad_state_${simpleHash(value)}`))]; |
| success(`removed stale squad_state from ${tomb.path} (now lives in .mcp.json)`); | ||
| filesUpdated.push('.copilot/mcp-config.json (tombstoned)'); | ||
| } | ||
| const userConfig = reconcileSquadStateUserConfig(dest); |
| 14. **Fact Checker (verification and devil's advocate).** Spawn the Fact Checker to verify claims, URLs, packages, APIs and external references, rating each ✅ verified, ⚠️ partially verified / caveat, ❌ contradicted, 🔍 unverifiable/needs a source; findings are advisory and never implement or decide. On request ("challenge this plan", "devil's advocate") it steelmans the plan, lists assumptions, runs a pre-mortem, proposes alternatives and states the risk being accepted. Audit trail: \`${S}/fact-checker/audit-trail.md\`. | ||
| 15. **Ceremonies.** Read \`${S}/ceremonies.md\`. Before spawning work, run any ceremony whose trigger is \`before\` (e.g. design review) and after work completes run \`after\` ceremonies (e.g. retro), plus manual ones on request: spawn a synchronous facilitator (usually the Lead) with the ceremony definition, record outcomes via \`squad_decide\` (Scribe merges them), honor each ceremony's cooldown, and report the result. Details: \`${S}/templates/ceremony-reference.md\`. | ||
| 16. **Memory governance and state-backend handshake.** Before any state write, check \`squad_state_health\` once per session (it reports the backend: local, orphan, two-layer...). Use governed runtime tools only: \`squad_decide\` for decisions, \`squad_state_*\` for runtime state, and \`memory.*\` tools (classify/search/write/promote/delete/audit) when the MCP server exposes them. Never claim memory you cannot persist. If the bridge is unavailable for a non-local backend, stop and say so. Capture persistent user directives ("always...", "never...") via \`squad_decide\`, acknowledge them, and dispatch any implementation separately. | ||
| 16. **Memory governance and state-backend handshake.** Before any state write, check \`squad_state_health\` once per session. Require its \`squad-state <version>\` line to match this coordinator's stamped Squad version and its \`Team squad dir:\` line to match the resolved \`${S}\`; an older/misdirected bridge is unavailable, not healthy. It also reports the backend (local, orphan, two-layer...). Use governed runtime tools only: \`squad_decide\` for decisions, \`squad_state_*\` for runtime state, and \`memory.*\` tools (classify/search/write/promote/delete/audit) when the MCP server exposes them. Never claim memory you cannot persist. If the bridge is unavailable for a non-local backend, stop and say so. Capture persistent user directives ("always...", "never...") via \`squad_decide\`, acknowledge them, and dispatch any implementation separately. |
a2adbef to
54dc3c0
Compare
Accept state-root aliases only for list-shaped operations and reject file or destructive root access before it reaches the backend. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9
Use packages/squad-cli/dist/cli-entry.js when init and doctor run from source, avoiding an npx registry fetch in CI and contributor environments. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9
54dc3c0 to
7818913
Compare
Include the raw resolved-path hash and the original SHA-256 key format when removing legacy user-level squad_state entries. Add independent Windows and POSIX regression coverage that preserves unrelated registrations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9
Update upgrade portability coverage for the direct Node source entry while retaining an exact-version npx fallback assertion for installed deployments. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9
Write reconciled user MCP config through a flushed sibling and atomic rename, with fault-injection coverage that preserves the original. Give the real cold two-layer health integration a documented bounded Windows budget. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9
|
Addresses review findings BR-1, BR-5, BR-3, and BR-4.
Validation: build and lint passed; 12 bridge/doctor/state/upgrade suites passed sequentially (206 passed, 1 platform skip). |
squad-release-verify.yml ran policy-gates.mjs --mode=release on pull_request too, so every PR carrying a changeset failed the gate (#2283, #2301). The workflow now passes --mode=ci on pull_request and --mode=release only on the release path (workflow_call from squad-release.yml). In ci mode the gate still runs: pending fragments are allowed but must be well-formed (allowlisted package, patch|minor|major bump, non-empty summary); malformed ones fail. Release mode keeps enforcing the drain unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9
* fix(cli): 2.0.0-preview.2 upgrade/health fixes from dogfooding - upgrade --self: honor --dry-run (zero exec), follow the installed channel (prerelease -> @insider, stable -> @latest), refuse downgrades without --force (#2295) - upgrade: one content-compared plan for every squad-owned file; verified <file>.local-backup before overwrite; shipped-files manifest distinguishes user edits from template revisions; dry-run writes nothing and reports nothing right after an upgrade (also on CRLF checkouts); .gitignore/.gitattributes appended in the file's own EOL; retired files removed when unmodified; provably Squad-shipped stale .copilot/skills copies cleaned (#2297, #2266, #2047, #2232) - health: table headers/placeholders never parsed as agents, built-ins are valid routing targets, BOM/pipe-joined legacy charters parse, missing identity is a warn with a hint, roster drift warns; upgrade adds built-in rows to team.md (#2298, #2234, #1785) - Rai -> rai: two-step rename recorded with git mv; templates reference agents/rai/; init scaffolds rai/ (#2299) - squad client list|add, dry-run header shows project name, update-check stale cache, squad_decide workstream inbox + 80-char slug, doctor Mode: local for same-project teamRoot and no DEP0190 (#2299, #2174) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9 * fix(release): make the changeset-drain policy gate event-aware squad-release-verify.yml ran policy-gates.mjs --mode=release on pull_request too, so every PR carrying a changeset failed the gate (#2283, #2301). The workflow now passes --mode=ci on pull_request and --mode=release only on the release path (workflow_call from squad-release.yml). In ci mode the gate still runs: pending fragments are allowed but must be well-formed (allowlisted package, patch|minor|major bump, non-empty summary); malformed ones fail. Release mode keeps enforcing the drain unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9 * test(cli): align legacy upgrade/help tests with content-compared upgrade and lowercase rai init - upgrade.test: customized-skill expectations now assert the .local-backup message; Rai migration test recreates the legacy mixed-case layout (init scaffolds rai/) - command-help.test: 'client' has a dedicated help block - cli-entry: --self --dry-run returns through the single success branch (performed=false) - team-md: never invent a roster table in a team.md without one - .github/agents/squad.agent.md: agents/rai/ (template-sync parity) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9 * fix(cli): make optional built-ins explicit Keep team.md byte-stable during upgrade, report unconfigured optional built-ins as informational, and add a provenance-backed roster command with exact restore semantics. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9 * fix(upgrade): preserve compound text templates Treat compound .example templates as text for EOL-normalized planning and distinguish unproven first-upgrade provenance from confirmed customization in backup output. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9 --------- Co-authored-by: B'Elanna (Squad) <tamirdresher@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 995eea17-c356-4912-90ea-5e5b144e7ba9

Fixes #2296
Fixes #2300
Related to #2047, but intentionally does not change template/workflow refresh behavior.
Summary
squad_statelaunch spec: directnode <installed or locally built cli-entry.js> state-mcpwhen available, with an exact-versionnpxfallback and machine-specific metadatarealpathSyncplus Windows drive-letter normalizationinitialize,tools/list,squad_state_health) with version, tool inventory, team-root, ETARGET, and repair diagnosticssquad-state <version>and the resolved team directory in health output; tighten the coordinator handshakedeleteDir, reject root before backend accessCommits
41ccb64ebridge resolution, doctor probe, versioned health, and lifecycle8fb93b87isolated state-root operation guard requested by Worf78189136CI follow-up: source-driven init/doctor use the in-repo built CLI entry instead of fetching the published package through npxValidation
npm run lintSKIP_BUILD_BUMP=1 npm run buildC:\.tools\squad-2.0.0-preview.1\node_modules\@bradygaster\squad-cli\dist\cli-entry.js: initialize returned2.0.0-preview.1, 15 tools, and health/team-dir; its missing version line is now detected by doctorScope notes
devat73c4e641.