Skip to content

feat(backend): add experimental /backend conversation and harness contract [3/4] - #2253

Merged
tamirdresher merged 6 commits into
tamirdresher-microsoft-stack-2-casting-repairfrom
tamirdresher-microsoft-stack-3-experimental-backend
Oct 22, 2026
Merged

tamirdresher merged 6 commits into
tamirdresher-microsoft-stack-2-casting-repairfrom
tamirdresher-microsoft-stack-3-experimental-backend

Conversation

@tamirdresher

Copy link
Copy Markdown
Collaborator

Summary (Stack Layer 3 of 4 — splits #2239)

Adds the experimental @bradygaster/squad-sdk/backend conversation/control plane (SquadBackend / IConversationBackend, HarnessAdapter SPI, LocalSquadBackend, DurableHttpSquadBackend, createStateBackendStore) and opt-in squad_backend_* MCP tools.

Review fixes included in Layer 3

  • Exported from @bradygaster/squad-sdk/backend only (not re-exported from the root SDK barrel) and annotated @experimental (BACKEND_CONTRACT_STAGE = 'experimental').
  • Gated the 11 squad_backend_* tools in squad state-mcp behind explicit opt-in (--experimental-backend, SQUAD_EXPERIMENTAL_BACKEND=1, or SQUAD_BACKEND_URL).
  • Fixed local-backend.ts lock() so permission/read errors inside catch always enforce deadline + sleepSync(10) (never spinning forever), added .squad root lstat checks, and added lost-submission retry recovery for turns stuck in running.
  • Added real lock exclusion to createStateBackendStore (state-backend-store.ts) for non-local state backends (orphan / two-layer).
  • Added per-request AbortSignal.timeout(requestTimeoutMs) (default 15_000ms) to DurableHttpSquadBackend.

@github-actions

Copy link
Copy Markdown
Contributor

🟠 Impact Analysis — PR #2253

Risk tier: 🟠 HIGH

📊 Summary

Metric Count
Files changed 26
Files added 17
Files modified 9
Files deleted 0
Modules touched 5
Critical files 2

🎯 Risk Factors

  • 26 files changed (21-50 → HIGH)
  • 5 modules touched (5-8 → HIGH)
  • Critical files touched: packages/squad-sdk/package.json, packages/squad-sdk/src/backend/index.ts

📦 Modules Affected

docs (5 files)
  • docs/proposals/squad-2.0-security-and-isolation.md
  • docs/specification/README.md
  • docs/specification/adr-001-client-control-execution-boundary.md
  • docs/specification/client-backend-decoupling-v0.2.md
  • docs/specification/client-harness-adapters-v0.2.md
root (4 files)
  • .changeset/squad-2-0-multi-host-clients.md
  • test-fixtures/spec/suite-v0.1/index.json
  • test-fixtures/spec/suite-v0.2/capability-matrix.json
  • test-fixtures/spec/suite-v0.2/golden-trace.json
squad-cli (4 files)
  • packages/squad-cli/src/cli/commands/state-mcp.ts
  • packages/squad-cli/src/cli/core/backend-mcp.ts
  • packages/squad-cli/src/cli/core/command-help.ts
  • packages/squad-cli/src/cli/core/upgrade.ts
squad-sdk (7 files)
  • packages/squad-sdk/package.json
  • packages/squad-sdk/src/backend/durable-http-backend.ts
  • packages/squad-sdk/src/backend/harness-registry.ts
  • packages/squad-sdk/src/backend/index.ts
  • packages/squad-sdk/src/backend/local-backend.ts
  • packages/squad-sdk/src/backend/state-backend-store.ts
  • packages/squad-sdk/src/backend/types.ts
tests (6 files)
  • test/backend.test.ts
  • test/cli/upgrade.test.ts
  • test/specification-suite.test.ts
  • test/squad-20-all-scenarios.e2e.test.ts
  • test/squad-20-real-clients-stdio.e2e.test.ts
  • test/squad-20-security-contracts.test.ts

⚠️ Critical Files

  • packages/squad-sdk/package.json
  • packages/squad-sdk/src/backend/index.ts

This report is generated automatically for every PR. See #733 for details.

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

ℹ️ This comment updates on each push. Last checked: commit 442bcea

PR Scope: 📦🔧 Mixed (product + infrastructure)

⚠️ 2 item(s) to address before review

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 tamirdresher-microsoft-stack-2-casting-repair
❌ Copilot review No Copilot review yet — it may still be processing
✅ Changeset present Changeset file found
✅ Scope clean ⚠️ PR includes 1 docs/proposals/ file(s) — ensure these are intentional
✅ No merge conflicts No merge conflicts
✅ Copilot threads resolved 1 active Copilot thread(s) resolved (8 outdated skipped)
✅ CI passing All checks passing

Files Changed (26 files, +8725 −14)

File +/−
.changeset/squad-2-0-multi-host-clients.md +5 −1
docs/proposals/squad-2.0-security-and-isolation.md +104 −0
docs/specification/README.md +6 −0
docs/specification/adr-001-client-control-execution-boundary.md +73 −0
docs/specification/client-backend-decoupling-v0.2.md +537 −0
docs/specification/client-harness-adapters-v0.2.md +559 −0
packages/squad-cli/src/cli/commands/state-mcp.ts +46 −8
packages/squad-cli/src/cli/core/backend-mcp.ts +359 −0
packages/squad-cli/src/cli/core/command-help.ts +5 −1
packages/squad-cli/src/cli/core/upgrade.ts +44 −3
packages/squad-sdk/package.json +4 −0
packages/squad-sdk/src/backend/durable-http-backend.ts +264 −0
packages/squad-sdk/src/backend/harness-registry.ts +47 −0
packages/squad-sdk/src/backend/index.ts +40 −0
packages/squad-sdk/src/backend/local-backend.ts +945 −0
packages/squad-sdk/src/backend/state-backend-store.ts +192 −0
packages/squad-sdk/src/backend/types.ts +435 −0
test-fixtures/spec/suite-v0.1/index.json +38 −0
test-fixtures/spec/suite-v0.2/capability-matrix.json +155 −0
test-fixtures/spec/suite-v0.2/golden-trace.json +63 −0
test/backend.test.ts +1772 −0
test/cli/upgrade.test.ts +1 −1
test/specification-suite.test.ts +2 −0
test/squad-20-all-scenarios.e2e.test.ts +2056 −0
test/squad-20-real-clients-stdio.e2e.test.ts +487 −0
test/squad-20-security-contracts.test.ts +486 −0

Total: +8725 −14


This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

🏗️ Architectural Review

Authoritative evidence: base-controlled pull_request_target reporter.

⚠️ Architectural review: 3 warning(s).

Severity Category Finding Files
🟡 warning bootstrap-area 5 file(s) in the bootstrap area (packages/squad-cli/src/cli/core/) were modified. These files must maintain zero external dependencies. Review carefully. packages/squad-cli/src/cli/core/backend-mcp.ts, packages/squad-cli/src/cli/core/clients.ts, packages/squad-cli/src/cli/core/command-help.ts, packages/squad-cli/src/cli/core/init.ts, packages/squad-cli/src/cli/core/upgrade.ts
🟡 warning export-surface Package entry point(s) modified with 233 new/changed export(s). New public API surface requires careful review for backward compatibility. packages/squad-sdk/src/index.ts
🟡 warning sweeping-refactor This PR touches 46 files (46 modified/added, 0 deleted). Large PRs are harder to review — consider splitting if possible. —

Automated architectural review — informational only.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved execution races, shared-storage locking gaps, and approval-delivery problems can duplicate work or lose conversation state.

9 open issues
What changed in this PR

Adds an experimental conversation backend layer that separates shared conversation control from host execution.

Changes:

  • Introduces /backend contracts, local persistence, HTTP transport, and harness registration.
  • Adds explicitly enabled MCP conversation tools and CLI help.
  • Adds contract tests, protocol fixtures, specifications, and improved casting-repair diagnostics.
File Description
test/​squad-20-security-contracts.test.ts Tests security and isolation contracts.
test/​squad-20-real-clients-stdio.e2e.test.ts Exercises projected clients through real MCP processes.
test/​specification-suite.test.ts Registers new requirements documents.
test/​cli/​upgrade.test.ts Checks actionable casting-repair errors.
test-fixtures/​spec/​suite-v0.2/​golden-trace.json Defines recovery and cancellation trace.
test-fixtures/​spec/​suite-v0.2/​capability-matrix.json Defines capability negotiation baselines.
test-fixtures/​spec/​suite-v0.1/​index.json Registers draft profiles.
packages/​squad-sdk/​src/​backend/​types.ts Defines experimental backend and harness contracts.
packages/​squad-sdk/​src/​backend/​state-backend-store.ts Adapts configured storage with locking.
packages/​squad-sdk/​src/​backend/​local-backend.ts Implements persisted conversation control.
packages/​squad-sdk/​src/​backend/​index.ts Exports backend APIs and factory.
packages/​squad-sdk/​src/​backend/​harness-registry.ts Registers harnesses and validates extensions.
packages/​squad-sdk/​src/​backend/​durable-http-backend.ts Implements remote transport and retries.
packages/​squad-sdk/​package.json Exposes the /backend subpath.
packages/​squad-cli/​src/​cli/​core/​upgrade.ts Ignores backend state and improves repair diagnostics.
packages/​squad-cli/​src/​cli/​core/​command-help.ts Documents experimental opt-in.
packages/​squad-cli/​src/​cli/​core/​backend-mcp.ts Implements backend MCP tools.
packages/​squad-cli/​src/​cli/​commands/​state-mcp.ts Wires opt-in tools into the server.
docs/​specification/​README.md Indexes new profiles and dependencies.
docs/​specification/​client-harness-adapters-v0.2.md Documents adapter boundaries.
docs/​specification/​client-backend-decoupling-v0.2.md Documents conversation semantics and limitations.
docs/​specification/​adr-001-client-control-execution-boundary.md Records architectural boundaries.
docs/​proposals/​squad-2.0-security-and-isolation.md Describes implemented protections and remaining limitations.
.changeset/​squad-2-0-multi-host-clients.md Records backend release details.

Review effort: Balanced

tamirdresher pushed a commit that referenced this pull request Oct 20, 2026
- Auto-select a single registered in-process harness in `squad_backend_send_message` when `harness` is omitted, and reject an omitted `harness` when multiple harnesses are registered instead of silently queueing; keep remote routing separate
- Default an omitted `limit` in `squad_backend_read_events` to `MAX_READ_EVENTS_LIMIT` (500)
- Read the response body outside the JSON parse `try/catch` in `DurableHttpSquadBackend.probe` so body timeouts and stream errors fail health checks
- Coordinate in-flight `adapter.submit` dispatches by `(conversationId, runId, seq)` and pass a deterministic `operationId` (`${conversationId}:${runId}:${seq}`) for crash-retry deduplication
- Remove queued approval deliveries for a turn when committing cancellation (`markCancelled` / terminal state) so `flushDeliveries` never retries or blocks on an obsolete approval
- Thread the publishing `harnessId` through `ensureSubscribed`, `applyEvent`, and `applyEventLocked` (including synchronous subscription replay) and verify it matches the turn's routed harness
- Reject a second distinct `approval-requested` event while a turn already has a `pendingApproval`
- Derive `createStateBackendStore` lock identity from the canonical shared storage (`.git` common dir across linked worktrees, resolved via `realpathSync.native`) and the state ref (`refs/heads/<branch>` or `refs/notes/<ref>`), unwrapping `TwoLayerBackend`
- Clear ambient `SQUAD_BACKEND_URL`, `SQUAD_BACKEND_TOKEN`, and `SQUAD_EXPERIMENTAL_BACKEND` before applying explicit test environment values in `McpStdioClient`

Refs #2239, #2253

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5ff02830-3b62-4a0a-8752-bc54a0bc3639
Copilot and others added 6 commits October 21, 2026 00:37
…tract (Layer 3)

Carves Layer 3 of #2239 targeting tamirdresher-microsoft-stack-2-casting-repair and applies all Layer 3 review fixes:
- Export the experimental conversation & harness contract from `@bradygaster/squad-sdk/backend` only (`BACKEND_CONTRACT_STAGE = 'experimental'`) without re-exporting from the root `@bradygaster/squad-sdk` barrel
- Gate `squad_backend_*` MCP tools in `squad state-mcp` behind explicit opt-in (`--experimental-backend`, `SQUAD_EXPERIMENTAL_BACKEND=1`, or `SQUAD_BACKEND_URL`)
- Fix `createFileStateStore` lock contention `catch` block so persistent `EACCES`/`EPERM` errors respect `lockTimeoutMs` and sleep between polls instead of spinning forever, and reject symlinked `.squad` or store root directories via `lstatSync`
- Track pending harness submissions in `LocalSquadBackend.sendMessage` so a failed or interrupted `harness.submit` is recovered on a same-`(runId, seq)` retry and returns the original admission cursor
- Implement real cross-process `wx` file locking with stale-lock recovery and symlink guards in `createStateBackendStore`
- Add per-attempt `requestTimeoutMs` (default 15_000 ms) via `AbortSignal.timeout` in `DurableHttpSquadBackend`

Refs #2239

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5ff02830-3b62-4a0a-8752-bc54a0bc3639
…rden durable-http, locks and MCP opt-in

- H5: rewrite test/squad-20-security-contracts.test.ts to exercise product code
  (createFileStateStore/resolveInsideRoot guards, mergeMcpServer/installClients,
  LocalSquadBackend idempotency/epochs/approvals/cursors, backend-mcp, DurableHttp);
  delete self-referential reference guards; proposal no longer claims an
  "executable contract" or CI-blocking gates
- H6: proposal, ADR and spec now describe the shipped behavior: opaque
  harness-supplied approval hash echoed back, cooperative client-asserted
  epochs (explicitly not a security boundary), unsigned cursors; stronger
  properties moved to "Future work / not implemented"
- H7: replace spec TypeScript contracts with the real signatures from
  backend/types.ts (SquadBackend, HarnessAdapter, RunState, ...); golden-trace
  fixture uses real BackendEnvelope.type names and the e2e runner no longer
  needs a translation table
- M10: squad_backend_send_message returns BackendError 'unavailable' when no
  harness is registered instead of queueing forever; health/negotiate expose
  harnesses: [] and executionAvailable: false; tool descriptions and specs
  state the bridge is transcript/event-log only
- M11: git-ignore .squad/backend/ in the upgrade scaffold (no merge=union)
- M12: SQUAD_BACKEND_URL alone no longer enables the tools; DurableHttp
  validates baseUrl (https, or http to loopback), uses redirect: 'error'
- M13: health probes get AbortSignal.timeout; file-store lock acquisition is
  async (lockAsync) for backend methods; read() only treats ENOENT as missing;
  corrupt conversation JSON surfaces as BackendError with the path; temp file
  is fsync'd before rename; spec documents whole-file snapshot limitation
- M14: createStateBackendStore requires a deterministic lock root (no mkdtemp
  fallback) and no longer prunes lock directories after release
- Low: MCP arg validation (role/parts/protocols/limit cap 500), generic
  message for non-contract errors, Windows reserved device names rejected as
  conversation ids, misplaced comment fixed, real-binary tests gated behind
  SQUAD_REAL_CLI_TESTS=1, flaky opt-in test timeout raised

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 92888fd8-c715-4d1d-a60f-893a7a157841
…; keep configured state backend out of .squad/backend; fail-closed casting repair reports instead of crashing

- e2e expectation updates for the audited stack behavior: Codex agent is
  `.codex/agents/squad.toml`; `.gitignore` projections are `# squad:claude`
  plain lines (exact restoration on removal still asserted);
  `normalizeHandwrittenCastingRegistry` callers use the new
  `NormalizeHandwrittenResult` shape; repair archives originals under
  `handwritten-archive-<sha256>/` instead of `.handwritten.bak`; a
  manifest-only recommit bumps the revision by one and keeps agents/history;
  team.md backfill resolves ids from charter folders (`belanna`, not
  `b-elanna`) and `parseTeamRoster` members carry `charterId`; a roster with
  no provable agent folder fails closed with the dropped row named
- role-keyed fixture now uses object entries (`tester: { name }`) since bare
  strings are rejected by design; a dedicated case asserts the bare-string
  rejection reason
- createStateBackendStore: a configured non-local state backend (git-notes,
  orphan, or any backend without a `root`) no longer gets its lock root from
  the team `.squad` dir. Locks live in a deterministic machine-local
  directory derived from the backend identity (`cwd` or the new `lockAnchor`
  option); `createMcpBackend` passes `lockAnchor` instead of `squadDir`, so
  `.squad/backend/` is never created when a non-local backend is configured
- squad upgrade: when the handwritten-registry repair fails closed and the
  pair is not readable as a managed pair, `ensureCastingDefaults` and the
  pre-preflight repair in `runUpgrade` throw an actionable error (reason,
  dropped entries, how to fix) instead of the generic "exactly one
  authoritative file exists" / preflight error

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 92888fd8-c715-4d1d-a60f-893a7a157841
…ction code

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 92888fd8-c715-4d1d-a60f-893a7a157841
…y its generic error with the handwritten-repair reason

The fail-closed handwritten-registry path pre-empted preflightCastingRegistryPair, so legacy`entries` registries (and --dry-run) got the new error instead of the legacy migration/refusal they had before.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 92888fd8-c715-4d1d-a60f-893a7a157841
- Auto-select a single registered in-process harness in `squad_backend_send_message` when `harness` is omitted, and reject an omitted `harness` when multiple harnesses are registered instead of silently queueing; keep remote routing separate
- Default an omitted `limit` in `squad_backend_read_events` to `MAX_READ_EVENTS_LIMIT` (500)
- Read the response body outside the JSON parse `try/catch` in `DurableHttpSquadBackend.probe` so body timeouts and stream errors fail health checks
- Coordinate in-flight `adapter.submit` dispatches by `(conversationId, runId, seq)` and pass a deterministic `operationId` (`${conversationId}:${runId}:${seq}`) for crash-retry deduplication
- Remove queued approval deliveries for a turn when committing cancellation (`markCancelled` / terminal state) so `flushDeliveries` never retries or blocks on an obsolete approval
- Thread the publishing `harnessId` through `ensureSubscribed`, `applyEvent`, and `applyEventLocked` (including synchronous subscription replay) and verify it matches the turn's routed harness
- Reject a second distinct `approval-requested` event while a turn already has a `pendingApproval`
- Derive `createStateBackendStore` lock identity from the canonical shared storage (`.git` common dir across linked worktrees, resolved via `realpathSync.native`) and the state ref (`refs/heads/<branch>` or `refs/notes/<ref>`), unwrapping `TwoLayerBackend`
- Clear ambient `SQUAD_BACKEND_URL`, `SQUAD_BACKEND_TOKEN`, and `SQUAD_EXPERIMENTAL_BACKEND` before applying explicit test environment values in `McpStdioClient`

Refs #2239, #2253

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5ff02830-3b62-4a0a-8752-bc54a0bc3639
@tamirdresher
tamirdresher force-pushed the tamirdresher-microsoft-stack-3-experimental-backend branch from 7b8d410 to 442bcea Compare October 20, 2026 21:44
@tamirdresher
tamirdresher merged commit 009c71d into dev Oct 22, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants