fix(spawn): replay service caller invokes durably

Merged
KH
1d ago
+497-239

Outcome

  • Bind external service-caller spawns to Relaycast's durable, caller-scoped strict spawn claim instead of a bounded local receipt ledger.
  • Send external invokes only through the dedicated /v1/actions/spawn/strict-invoke route and require the server-owned spawn_contract_applied proof.
  • Recover already accepted work through replay-only /v1/actions/spawn/replay when the requested directory can no longer be safely materialized; the recovery path cannot create, place, or dispatch work.
  • Keep current harness and directory authorization on every attempt while allowing the authorized canonical worker_cwd to be rematerialized for the same stable logical request and idempotency key.
  • Preserve the legacy session-caller request bytes and target preflight. Existing session calls do not gain target_node_id, logical-request, or strict-contract fields.
  • Keep the public Core Bridge compatibility surface out of this PR. Its separately reviewed package work landed through #2133; the strict variance follow-up is #2146.

This is the Relay client half of a paired change and requires Relaycast PR #644. Replay-capability strengthening remains stacked separately in #2142 with Relaycast #647.

Current exact head: 8258e208424b45f457bbb152ed014a54cbc72240. It normally merges current main 503020b94bc9d1acd065c6f37afc4aaa3fdfd461 into the reviewed feature tree. The only substantive conflict was the session-only target preflight: it now uses current-main workspace_http(workspace) and targets(workspace.kind(), ...), while external callers retain strict invoke/replay and return before that preflight. The #2133 Core Bridge compatibility split remains byte-identical to current main.

Verification

Literal fresh-install, Core Bridge, TypeScript, formatting, and 39-test Rust control evidence for the exact tree is captured in the exact-head verification comment.

Independent exact-head source review found no conflict-resolution defect. All hosted checks at this exact head completed successfully.

Current exact diff:

$ git diff --stat 503020b94bc9d1acd065c6f37afc4aaa3fdfd461...8258e208424b45f457bbb152ed014a54cbc72240
 CHANGELOG.md                                       |   1 +
 crates/relay-node/src/relay/spawn/control.rs       | 150 +++---
 .../relay-node/src/relay/spawn/control/claims.rs   |  69 ---
 .../src/relay/spawn/control/tests/callers.rs       | 530 ++++++++++++++++-----
 4 files changed, 504 insertions(+), 246 deletions(-)

$ git diff --check 503020b94bc9d1acd065c6f37afc4aaa3fdfd461...8258e208424b45f457bbb152ed014a54cbc72240
# exit 0, no output

Boundary

This is not a full Flows #707 or live Desktop E2E signoff. It does not merge or release Relaycast, Relay, Core Bridge, or Flows. The complete path still requires the paired server changes, reviewed human merges, official package release, the Flows pin, and live local/remote lifecycle acceptance.