Attractor → pi agent backend

Replacing the Anthropic-only Claude Agent SDK with the pi harness for multi-provider agent nodes — migration, live proof, and the sub-workflow plan.

branch feat/pi-agent-backend PR #9 579 tests pass ruff + pyright clean

1. What changed

Layers 1 (LLM transport) and 2 (agent loop) were hard-bound to the Claude Agent SDK. They now delegate to pi --mode rpc. The engine seam run_agent_node(AgentConfig) → AgentOutcome is unchanged, so the orchestrator and its tests were untouched.

BEFORE engine (Layer 3) agent runner (L2) claude-agent-sdk Anthropic one provider, SDK is the contract AFTER engine (Layer 3) agent runner (L2) pi --mode rpc Anthropic Codex Cerebras… provider/model string selects the route — pi is the contract

Two heavy deps removed (claude-agent-sdk, anthropic); pi is now a runtime dependency on PATH. Net −1443 / +1288 lines.

Removed

  • claude-agent-sdk + anthropic deps
  • The in-process MCP report_outcome tool
  • ClaudeSDKError import/catch in the engine

Added

  • agent/pi_rpc.py — subprocess + JSONL transport
  • agent/report_outcome.ts — the one tool, as a pi extension (ships in the wheel)
  • pydantic as an explicit dep (was transitive)

2. Multi-provider, proven live

Same code, same report_outcome round-trip, three providers and three auth styles — only the provider/model string differed.

AgentConfig.model "provider/model" pi RPC anthropic/claude-haiku-4-5 · API key openai-codex/gpt-5.3-codex · OAuth cerebras/gpt-oss-120b · API key
ProviderModel stringAuthLive result
Anthropicanthropic/claude-haiku-4-5API key✓ 4.4s
OpenAI Codexopenai-codex/gpt-5.3-codexChatGPT subscription (OAuth)✓ 8.6s
Cerebras (GPT-OSS)cerebras/gpt-oss-120bAPI key✓ 1.2s

3. Parallel workflow run

A real run: one pi agent node (Cerebras) feeds a fan-out to three concurrent tool nodes, which fan back in at a join and route to exit. Completed clean.

start implementagent · cerebras fan-out tests lint typecheck join exit SUCCESS

implement reported SUCCESS in 1.14s (5061 in / 41 out tokens); three tool nodes ran concurrently (~5–7ms each); run COMPLETED.

4. Proposed: house sub-workflow node

Not built yet — deferred in SPEC §14. The plan (goal-subworkflow-house.md) adds a node that runs a child workflow in its own seeded sub-worktree, recombines on success, and maps the child's RunStatus to the node's OutcomeStatus.

PARENT RUN start houseworkflow="child.dot" next recover SUCCESS FAILURE CHILD RUN — own worktree + refs, seeded from parent commit start …nodes… exit COMPLETED → SUCCESS · INCOMPLETE/ABORTED → FAILURE on success: recombine child tree into parent worktree (§6.10.3) runtime cycle/depth guard · v1 forbids nested human gates dispatch & await child Engine.run()

Touch points

  • NodeKind.SUBWORKFLOW + house shape map
  • validation: require workflow=, resolve+validate child, forbid inside parallel regions
  • engine dispatch + recombination + recursion guard
  • additive child_run_id journal field

Open questions

  • Nested human-gate propagation (v1 fails the node)
  • Recombination conflict policy (replace vs merge)
  • Named child outputs vs. whole-tree recombine
  • Child event surface (forwarded vs summarized)

Phase 1 is the SPEC §5/§6/§14/§15 edit + two new REQ-…-SUBWORKFLOW ids — a sign-off gate before code, per AGENTS.md.

Generated as a session summary. Source of truth: SPEC.md, goal-subworkflow-house.md, PR #9 on feat/pi-agent-backend. Diagrams are inline SVG (no external assets).