feat(workflows): per-item fan-out — dispatch N harnesses from one node - #124
Conversation
Bumps vendor/openhuman to the per-item fan-out engine, so an agent node can dispatch one harness task per input item concurrently — array in, array out — instead of the author hand-writing N sibling nodes. Adds workflows.maxParallelAgents (default 4). Here a fanned-out item is a whole coding session, so the useful ceiling is what the worker pool can serve, not what a graph asks for; an over-wide fan-out is throttled, never refused. The agent runner and the LLM provider share one limiter, since both dispatch to the same pool and separate semaphores would make the real ceiling double what the operator configured.
There was a problem hiding this comment.
senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (11)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 52d5406a6c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| #[derive(Default)] | ||
| pub(super) struct ConcurrencyProbe { | ||
| live: std::sync::atomic::AtomicUsize, | ||
| peak: std::sync::atomic::AtomicUsize, | ||
| calls: std::sync::atomic::AtomicUsize, | ||
| } |
There was a problem hiding this comment.
Split the oversized fan-out test module
Move the new fan-out fixtures and tests into a focused submodule: this addition grows cases.rs from 468 to approximately 606 nonblank, non-comment lines, exceeding the repository's mandatory 500-line ceiling for edited Rust files.
AGENTS.md reference: AGENTS.md:L58-L60
Useful? React with 👍 / 👎.
| "httpAllowlist": config.http_allowlist, | ||
| "allowCode": config.allow_code, | ||
| "runTimeoutSecs": config.run_timeout_secs, | ||
| "maxParallelAgents": config.max_parallel_agents, |
There was a problem hiding this comment.
Report the normalized parallel-agent ceiling
When an operator explicitly configures maxParallelAgents = 0, CapabilitySettings::from_config deliberately normalizes it to the default of 4, but workflow_host reports the raw value 0. Since the new agent contract tells workflow authors to consult this tool for the current ceiling, they receive a value that is not actually enforced; normalize this field before returning the host facts.
Useful? React with 👍 / 👎.
What
Top of a three-repo chain that lets a workflow node multiply an array of input into N concurrent units of work — array in, array out, like a bounded
Promise.all.Until now the only way to run harnesses in parallel was to hand-author N sibling nodes, so the width had to be known when the graph was written. Now it follows the data:
split_out→agent(concurrency: 4)→mergeworks through whatever list reaches it and hands the merge one result per item.examples/workflows/fan-out-per-file.jsonis the shape end to end.The chain
concurrency/on_item_erroron every mapping node, plusper_itemonsub_workflowMerge bottom-up. This PR's
vendor/openhumanbump is only meaningful once the two below land.Changes here
vendor/openhumangitlink bump, which carries the engine.workflows.maxParallelAgents(default 4) — new config, surfaced onworkflow_host, threaded intoCapabilitySettingsand enforced as a semaphore inHarnessAgentRunner.The engine bounds a single node's width. This bounds the run, and on this host that is the limit that matters: a fanned-out item is a whole coding session occupying a worker for the length of a task, so the useful ceiling is what the pool can serve, not what a graph asks for. An over-wide fan-out waits for a slot rather than failing — raising
concurrencypast the ceiling slows a run down, it does not break it.Two details worth a reviewer's eye:
build_capabilities_innerand handed to both viawith_limiter.Authoring surface.
node_contracts.rsalready delegates to the engine catalogue, so the newexecution/concurrency/on_item_errorfields reach the MCP tools and the copilot automatically. Added two host notes: that a per-item agent node is a full harness session per item (fan out over an already-narrowed list, not a raw fetch), and that the ceiling throttles rather than refuses.Validation
cargo test— 1793 lib + all integration suites greencargo clippy --all-targets -- -D warnings— clean (remaining warnings are pre-existing, in vendored openhuman)cargo fmt --check— cleanNew tests in
src/sdk/src/workflows/run/tests/cases.rsrun through the real engine and real capability seam with only the harness dispatch stubbed:concurrency: "all", and the run still succeeds;concurrencystays strictly sequential — the back-compat guard.Live per-item progress
A fanned-out node now renders as N live workers, not one long step.
The engine reports each item as it starts and settles (
on_item_start/on_item_finish), andWorkflowRunObserverturns each into its own sub-agent — opened when the item actually starts, closed when it settles. The existing work pane renders it with no new drawing code, which is the whole reason this observer speaks theharness_workvocabulary rather than inventing one.Two details:
on_step_finishstill fires for the node itself, and emitting both would show N+1 workers for N items. A node that runs once is silent on the item callbacks, so it keeps the single-sub-agent path unchanged.on_step_startis now wired. The engine has had it since this pin; the module doc claiming otherwise was stale. Which node is active comes from the engine instead of being inferred from what has finished — an inference that was exact for a sequential graph but named an arbitrary one of several in a parallel fan-out.Not in this PR
The TUI still discards the live fold (
let (sink, _fold) = folding_sink()inevent_loop/cmd_dispatch/workflows.rsandcommands/workflow.rs), so these frames reach the daemon/orchestrator path but not the TUI'''s own workflow view. Keeping and rendering that fold is a self-contained follow-up.