Skip to content

spawn_agent cross-runner: remaining work (codex origin + deferred) #361

Description

@mariorha

Remaining work from the design doc rsworkspace/docs/spwan-agent-cross-runner.md, confirmed via a two-sided adversarial audit against the real implementation (commit 97b5993b, worktree swarm/20260618T212853Z-0d60/spawn-cross-runner). No todo!/unimplemented!/stubs in the spawn paths.

Already implemented (verified — do not redo)

  • PR1–PR9: DONE. model + find_by_model fail-loud on multi-match + unavailable-model error (registry.rs, spawn_routing.rs); spawn Bridge retargeted to the resolved prefix with a per-prefix cache; spawn_depth via meta, persisted atomically + MAX_SPAWN_DEPTH=3 guard before spawning (G4); shared plain-NATS forwarding primitive + session_id rewrite + dead HTTP path removed (spawn_handler.rs) (G1); multi-level cancel cascade (G5); bounded structured result with transcript_ref/branch_ref with a real producer (publish_transcript) (G6); isolated workspace fail-loud + uncommitted overlay + change-aware teardown + GC by TTL/parent-close (G7); prefix-qualified disambiguation (PR6); tracked handle (status/result subjects) + collect_spawn + idempotency (reuse_or_insert) (PR7); async concurrency + per-parent caps + per-spawn tool allowlist enforced destination-side (PR8); identity + OTel + emitted protobuf accounting event (PR9). Protos under proto/trogonai/spawn/v1/. Typed config.
  • Kill-switch / feature-flag (ADR-0007): cross_provider_enabled / env SPAWN_CROSS_PROVIDER_ENABLED, enforced by cross_provider_gate (same-runner always allowed).
  • G8 two layers: registry pre-check (error) + runtime failure → handle Failed.

Verification status: cargo build --workspace ✅ compiles clean; unit tests ✅ (runner-tools+registry 258, runners 802, spawn-specific 41 — 0 failures).

Remaining work

Codex (PR10 — partial by design)

  • Codex as spawn ORIGIN: expose spawn_agent (and collect_spawn) as MCP tools to the Codex CLI subprocess. Today it only works as a destination.
  • Codex destination does not parse SpawnMeta: new_session (trogon-codex-runner/src/agent.rs:339) ignores req.meta → it does not honor spawn_depth, per-spawn allowed_tools, identity, or destination-side provisioning. (G7 isolation still holds via the parent's workingDirectory.)

Cross-runner environment (G3)

  • Opt-in propagation of MCP/egress via the spawn contract: SpawnMeta (proto + domain) has no mcp/egress fields. The "do not propagate" default is implemented; the opt-in mechanism to propagate specific MCP servers / egress policy when a task needs them is missing.

Hosted plane (deferred — HARD dependency for hosted)

  • Auth/tenant/quota Gateway: net-new, general infra. Spawn ships the hooks (identity via meta, accounting, caps), but the plane that triggers/enforces them is missing.
  • Cancel-on-quota: the serve_spawn_quota_cancel hook (spawn_cancel.rs) exists but is not wired into any runner main → inert until (a) a runner serves it and (b) the gateway's quota trigger exists.

Other deferred

  • Genuinely-remote cross-host: clone_workspace_at clones from a local repo_root and is gated off (destination_provisioning=false). The remote descriptor (repo + ref + sync from another host) is missing. v1 = shared FS.
  • Tool-capability guarantee (runner-owned): no supports_tools in registry/runner; v1 assumes "advertised ≡ tool-capable".
  • Registry record protobuf migration (ADR-0009): acp_prefix/models are still untyped JSON in metadata (registry.rs:205). Migrate-when-touched.

Optional optimizations (not v1)

  • Parallel dispatch (async/handle already provides concurrency).
  • Auto-merge fast-path (CI-gated); today a changed worktree is persisted for explicit reconciliation.

Verification (pending: needs infra/credentials) — belongs to THIS issue

  • Integration over real NATS (spawn_forwarding_integration.rs): run with NATS/Docker up (streaming with rewritten session_id + spawn_id attribution; status/result on the handle subjects). Not executed today without a server.
  • Cross-provider normalization test (v1 gate, after PR4): verify ACP normalizes forwarded session/update between providers (a parent of one provider correctly receives chunks from a child of another). Does not exist — the current integration test only exercises plain-NATS forwarding + handle subjects with a mock, not the normalized cross-provider new_session/prompt path.
  • Cross-provider E2E with real APIs (P3): spawn a sub-agent of another provider end-to-end with real API calls (grok spawns claude and it responds with tokens): acp.grok → claude-opus-4-6, acp → grok-4, acp → openai/gpt-4o. Requires the runners up with ANTHROPIC_TOKEN/XAI_API_KEY/OPENROUTER_API_KEY (ask before using credentials). Not to be confused with Test mid-session model switching end-to-end (incl. real credentials) #359 (that is the E2E for mid-session model switching, a different feature).

Note (not a bug, worth tracking)

  • The status/result/accounting messages are protobuf types but are serialized with serde_json::to_vec on the internal {prefix}.spawn.* subjects, whereas the doc says "binary on internal paths". The contract stays typed (drift-proof); it is an encoding detail, not a missing feature.

Design doc: rsworkspace/docs/spwan-agent-cross-runner.md · Implementation: commit 97b5993b · Related: #291

Metadata

Metadata

Assignees

Labels

rustPull requests that update rust code

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions