Skip to content

feat: Phase 48 β€” Multi-Agent Orchestration (v4.0.0-alpha.8) - #56

Merged
aarambh-darshan merged 1 commit into
mainfrom
feat/phase48-multi-agent-orchestration
Aug 17, 2026
Merged

feat: Phase 48 β€” Multi-Agent Orchestration (v4.0.0-alpha.8)#56
aarambh-darshan merged 1 commit into
mainfrom
feat/phase48-multi-agent-orchestration

Conversation

@aarambh-darshan

Copy link
Copy Markdown
Member

Summary

Implements Phase 48 β€” Multi-Agent Orchestration (ARCHITECTURE_V4.md
Β§62), the natural successor of Phase 47 (sandboxed tool execution). One
top-level orchestrating reasoning process now delegates independent
sub-tasks to multiple parallel sandboxed tool-execution sub-chains
(each governed entirely by Phase 47's boundaries), then merges their
results back into its own context via the existing ToolResult
ingestion path applied recursively.

Bumps the workspace version from 4.0.0-alpha.7 to 4.0.0-alpha.8,
matching the ROADMAP_V4.md milestone (git tag v4.0.0-alpha.8).


What's new

crates/aarambh-studio-agent/src/orchestrator.rs (new file, ~1100 lines)

Public API:

  • Orchestrator β€” built once from operator-set OrchestrationLimits
    and the orchestrator's own AuthorizationScope.
  • DelegationPlan + DelegatedSubTask β€” the model/operator-authored
    plan, validated before any sub-chain runs.
  • SubChainOutcome + SubChainStatus β€” one outcome per sub-task, in
    plan order, always present (never missing, never malformed).
  • OrchestrationLimits β€” operator-set, non-model-influenceable
    ceilings: max_sub_agents (default 4, range 1..=64) and
    max_total_time_ms (default 30,000).

Each sub-chain is a ToolChain backed by a SandboxedToolProvider
constructed with the sub-task's narrowed AuthorizationScope (via
AuthorizationScope::intersect), so execution plugs into the existing
chain with zero chain changes β€” sub-chain outputs re-enter the
orchestrator's own context via the unchanged result_ingestion path,
applied recursively.

Three hard, non-negotiable bounds

Enforced as operator-set configuration, never as something the
orchestrator's own output can influence. Verified at validate_plan
time, before any sub-chain runs:

  1. Maximum sub-agent count β€” a DelegationPlan with more sub-tasks
    than max_sub_agents is rejected. The model cannot request
    unbounded fan-out by emitting a larger plan. Range 1..=64 matches
    the per-chain max_steps ceiling so an orchestrator cannot fan out
    wider than a single chain could step.
  2. Maximum total execution time budget β€” the sum across all
    sub-chains, not per sub-chain, so many small sub-agents cannot
    collectively exceed the same ceiling one large one would hit. Once
    exhausted, every not-yet-started sub-task is refused with
    SubChainStatus::BudgetExceeded.
  3. Sandbox scope containment β€” a sub-agent's AuthorizationScope
    may only be a subset of its orchestrator's. Verified by
    parent.intersect(&child) == child (true iff child βŠ† parent).
    Additionally, every tool name a sub-task declares must be
    is_authorized in that sub-task's own scope. Orchestration can
    never be used as an escalation path to reach tools the operator
    did not explicitly enable at the top level.

Failure isolation

One sub-agent's failure or execution error is contained to that
sub-chain's own outcome β€” it does not corrupt or silently swallow
sibling sub-agents' results. Each sub-chain runs inside a
std::panic::catch_unwind boundary; panics become
SubChainStatus::Failed with the panic payload rendered into the
fail-closed ToolResult::error text. The orchestrator's aggregation
step receives an explicit failure marker for that sub-chain rather than
a missing or malformed entry.

CLI surface (aarambh-studio/src/cmd/agent.rs)

Five new opt-in flags on the agent command:

Flag Default Purpose
--orchestrate off Switch from single-chain mode to orchestration mode
--delegation-plan <PATH> (required with --orchestrate) JSON file describing the DelegationPlan
--max-sub-agents N 4 Hard ceiling on sub-agent count
--max-orchestration-budget-ms MS 30,000 Hard ceiling on summed sub-chain wall-clock
--sub-agent-allow-tool <NAME> (inherits --allow-tool) Per-sub-agent authorized tool name (repeatable)

When --orchestrate is absent, the command behaves exactly as in
Phase 47 β€” zero behavior change for non-orchestrating use.


Tests

  • 10 new orchestrator tests in
    crates/aarambh-studio-agent/src/orchestrator.rs (5 roadmap-named
    acceptance tests + 5 supporting tests), all using a FakeDecoder
    mirroring chain.rs::tests::FakeDecoder and
    sandbox.rs::tests::FakeDecoder so they run in milliseconds:
    • orchestrator_cannot_exceed_configured_max_sub_agent_count
    • orchestrator_cannot_exceed_configured_total_execution_time_budget
    • sub_agent_sandbox_scope_is_never_wider_than_orchestrator_authorization
    • result_aggregation_correctly_merges_multiple_sub_chain_outputs
    • one_sub_agent_failure_does_not_silently_corrupt_sibling_sub_agent_results
    • limits_validation_rejects_zero_ceilings
    • validate_plan_rejects_subtask_declaring_unauthorized_tool
    • intersect_equals_child_when_child_is_subset
    • run_revalidates_plan_defense_in_depth
    • orchestrator_sub_chain_can_execute_tools_through_sandbox
  • 32 total agent crate tests pass (22 existing + 10 new) β€” no
    regressions.
  • scripts/phase48_smoke.sh β€” runs the orchestrator unit tests,
    verifies agent --help surfaces the new flags, verifies
    --orchestrate errors on missing --delegation-plan and missing
    --allow-tool, verifies a plan exceeding --max-sub-agents is
    rejected at validation time before any model is loaded, and writes a
    scorecard to artifacts/phase48_orchestration_smoke.json.
  • Phase 47 smoke still green β€” regression verified.

CI gates β€” all green

  • cargo fmt --all --check βœ…
  • cargo check --workspace --all-targets --locked βœ…
  • cargo test --locked -p aarambh-studio-agent --lib βœ… (32 passed)
  • cargo clippy --workspace --all-targets --locked -- -D warnings -D clippy::undocumented_unsafe_blocks βœ…
  • RUSTDOCFLAGS="-D warnings -D missing_docs" cargo doc --workspace --no-deps --locked βœ…
  • scripts/phase47_smoke.sh βœ… (regression)
  • scripts/phase48_smoke.sh βœ… (new)

Files changed

New files:

  • crates/aarambh-studio-agent/src/orchestrator.rs (~1100 lines, the implementation + 10 tests)
  • docs/phase48_orchestration.md (319-line runbook mirroring docs/phase47_sandbox.md)
  • scripts/phase48_smoke.sh (smoke script mirroring scripts/phase47_smoke.sh)
  • configs/orchestration_smoke.json (two-sub-task plan fixture)
  • data/sandbox_workdir/notes.txt (sandbox fixture file)
  • data/tools_sandbox_smoke.json (tool definitions referenced by both Phase 47 and Phase 48 smoke scripts)
  • PHASE48_PLAN.md (the implementation plan document)

Modified files (strictly additive):

  • Cargo.toml β€” workspace version 4.0.0-alpha.7 β†’ 4.0.0-alpha.8
  • Cargo.lock β€” updated to match
  • crates/aarambh-studio-agent/src/lib.rs β€” one pub mod orchestrator; line + re-exports + module docstring
  • crates/aarambh-studio-agent/src/chain.rs β€” added serde::Serialize, serde::Deserialize derive to ToolChainConfig (needed so DelegatedSubTask can round-trip through JSON)
  • aarambh-studio/src/cmd/agent.rs β€” added 5 --orchestrate flags, run_orchestrate() function, SubChainShared struct, build_sub_chain_decoder() helper, validate_orchestration_config(), print_orchestration_outcomes()
  • README.md β€” Phase 48 in intro paragraph, docs index, current boundaries, citation version
  • ROADMAP_V4.md β€” "Status: shipped in v4.0.0-alpha.8" blockquote on the Phase 48 section
  • ARCHITECTURE_V4.md β€” "Implemented in v4.0.0-alpha.8" note on Β§62
  • CHANGELOG.md β€” full ## [4.0.0-alpha.8] entry

Honesty boundary

Sub-chains run sequentially by default (CPU-first honest default).
The spec's wording β€” "Sub-chains run (conceptually parallel; actual
concurrency bounded by configured limits below)"
β€” is honored:
max_sub_agents and max_total_time_ms together bound the total work
even when run sequentially. True parallelism would require a
ChainDecoder whose implementor is Send + Sync, which is out of
scope for the source release because the InferenceEngine holds a
Candle device that is not safely cloneable across threads. The CLI's
per-sub-task decoder factory rebuilds a fresh InferenceEngine per
sub-chain so each sub-chain owns its own &mut decoder. This is
documented in the orchestrator module docs, the runbook, the
CHANGELOG, and the ARCHITECTURE_V4.md note.

No new crate. No new dependency. No new ingestion mechanism.
Orchestration is purely additive to what Phase 47 built.

@aarambh-darshan
aarambh-darshan merged commit c779a4b into main Aug 17, 2026
3 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.

1 participant