Skip to content

fix(agents): launch panic + parallel concurrent agent dispatch (v0.1.12)#9

Merged
ste-bah merged 1 commit into
mainfrom
fix/agents-launch-parallel-v0.1.12
Apr 27, 2026
Merged

fix(agents): launch panic + parallel concurrent agent dispatch (v0.1.12)#9
ste-bah merged 1 commit into
mainfrom
fix/agents-launch-parallel-v0.1.12

Conversation

@ste-bah

@ste-bah ste-bah commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Summary

THE central failure that's dragged on for 2 weeks. Discovery worked, listing worked, permissions worked — agents never actually launched. Now they do, AND N concurrent agents per turn.

Root cause #1 — panic on launch

crates/archon-memory/src/embedding/openai.rs::request_batch called reqwest::blocking::Client::send() WITHOUT a block_in_place boundary. reqwest::blocking constructs a tokio runtime per call; from inside an async tokio task this triggers tokio's nested-runtime panic: "Cannot start a runtime from within a runtime / thread is being used to drive asynchronous tasks." Every agent dispatch that touched memory crashed.

Root cause #2 — no parallel dispatch

crates/archon-core/src/subagent.rs::run iterated pending_tools in a serial for loop. When the LLM emitted N tool_use blocks in one turn, they ran one after another — no concurrency.

Fix

  • A. Wrap OpenAIEmbedding::embed body in tokio::task::block_in_place. Panic gone.
  • B. Replace serial for-loop with claurst's proven three-phase pattern (/tmp/claurst/src-rust/crates/query/src/lib.rs:1815-1944):
    • Phase 1: sequential pre-hooks (permission gate)
    • Phase 2: futures::future::join_all over Vec<Either<Left ready, Right execute>> — concurrent execution, order preserved natively
    • Phase 3: post-hooks + result assembly
  • C. No changes to AgentTool::execute. Verified reentrant by code inspection: BACKGROUND_AGENTS uses per-agent_id inserts, status/result_slot/cancel_parent are per-call Arcs, no global serialization. AGS-104/105/107 contracts preserved unchanged. Added barrier-based regression test for deterministic concurrency proof.

Test plan

  • A: archon-memory regression test — embed() inside multi_thread runtime returns 401 from fake key, no panic (0.79s)
  • B: subagent parallel dispatch test — 3 stub tools complete in 0.40s vs ~0.90s serial baseline, results in input order
  • C: archon-tools barrier test — tokio::sync::Barrier::new(2) rendezvous proves both subagents in flight simultaneously, BACKGROUND_AGENTS count = 2 at rendezvous (<1ms wall-clock)
  • D (E2E): bin agent_parallel_smoke — 3 concurrent AgentTool calls complete in 0.50s vs ~1.50s serial
  • cargo nextest run --workspace: ALL PASS, zero failures
  • cargo fmt --check: PASS
  • cargo build --release --bin archon -j1: PASS
  • TUI binary smoke (Steven) — verify in real session

Root cause #1 (panic on launch):
crates/archon-memory/src/embedding/openai.rs::request_batch called
reqwest::blocking::Client::send() WITHOUT block_in_place wrap.
reqwest::blocking constructs a tokio runtime per call; from inside an
async tokio task this panics with "Cannot start a runtime from within
a runtime / thread is being used to drive asynchronous tasks". Every
agent dispatch that touched memory triggered this.

Root cause #2 (no parallel even after panic fix):
crates/archon-core/src/subagent.rs::run dispatched pending_tools in a
serial for-loop, so N tool_use blocks in one turn ran one-by-one.

Reference: claurst (third-party Rust port of Claude Code) at
crates/query/src/lib.rs:1815-1944 implements parallel tool dispatch
using futures::future::join_all over Vec<Either<ready, execute>> in a
three-phase split. join_all preserves input order natively. Mirrored.

Fix:
- A. Wrap OpenAIEmbedding::embed in tokio::task::block_in_place so
  reqwest::blocking calls run on a thread tokio knows is allowed to
  block. No more nested-runtime panic.
- B. Refactor subagent.rs::run tool dispatch to claurst's three-phase
  pattern with futures::future::join_all over Either<Left blocked,
  Right execute>. Order-preserving, simple, proven. N concurrent tool
  calls per turn.
- C. NO change to AgentTool::execute. Verified reentrant — no global
  lock serializes concurrent invocations. AGS-104/105/107 contracts
  preserved unchanged. Added barrier-based regression test asserting
  two concurrent AgentTool::execute calls run in parallel and
  BACKGROUND_AGENTS holds 2 simultaneous entries at the rendezvous.

Tests added:
- archon-memory: regression test confirming embed() inside multi_thread
  runtime no longer panics (returns 401 from fake key, not a panic)
- archon-core::subagent: parallel-tool-dispatch test asserts 3 stub
  tool calls complete in 0.40s (serial baseline ~0.90s) AND results
  returned in input order
- archon-tools::agent_tool_parallel barrier test asserts deterministic
  reentrancy via tokio::sync::Barrier rendezvous + BACKGROUND_AGENTS
  count = 2 mid-flight
- bin agent_parallel_smoke E2E: 3 concurrent AgentTool::execute calls
  in 0.50s (serial baseline ~1.50s)

Test results:
- cargo nextest run --workspace: ALL PASS, zero failures
- cargo fmt --check: PASS
- cargo build --release --bin archon -j1: PASS

Version: 0.1.11 -> 0.1.12.
@ste-bah ste-bah merged commit 8b4b45c into main Apr 27, 2026
0 of 21 checks passed
ste-bah added a commit that referenced this pull request May 4, 2026
…re isolation

Phase 4 ships routing engine, DAG wave grouping, conditional specialist
selection from .archon/specs/gametheory.yaml, and final-stage assembly
(scanner/mapper/writer/combiner/style_applier).

Tier 1 classification and specialist execution remain stubs in Phase 4:
  - generate_synthetic_fingerprint uses keyword regex matching
  - execute_specialist_stub returns templated placeholder text per agent
  - CLI prints a NOTE banner on every run/replay flagging stub mode
Phase 5 wires real LLM agent execution while retaining keyword fallback.

Cold-read audit fixes from #8 + #9:
  - resolve_spec_path search ladder: --spec-path flag, $ARCHON_SPEC_PATH
    env, upward walk 5 levels, ~/.archon/specs/, /etc/archon/specs/.
    GameTheoryError::SpecNotFound enumerates all searched paths on miss.
  - Failure isolation design + test hook (-FORCE-FAIL-FOR-TEST agent_key
    suffix). failed_specialists Vec<(key, msg)> in FullPipelineResult.
    Run status "partial" when mixed success/fail, "completed" otherwise.
    [FAILED] placeholder section in combiner output.
  - shadow_games_count condition removed from spec (Option B). Agent is
    mandatory so condition never evaluated.
  - CLI NOTE banner in run + replay output until real agents wired.

7 new Cozo relations (gt_routing_decisions, gt_enabled_specialists,
gt_skipped_specialists, gt_specialist_outputs, gt_sections,
gt_final_reports, gt_provenance_edges) all use key=>values syntax.
Idempotent via existing run_create + COZO_RELATION_ALREADY_EXISTS const.

CLI: archon gametheory {run, list-runs, show, inspect-routing, replay}
[--spec-path PATH] [--classify-only].

Tests: gametheory 14 -> 49 (+35 new). Phase 1+2 sealed at 62/62
archon-docs.
@ste-bah ste-bah deleted the fix/agents-launch-parallel-v0.1.12 branch May 8, 2026 19:48
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.

1 participant