You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
perf(workflow): engine overhead dominates a long uncapped for_each #1162
A for_each over 1,000 items with no max_parallel takes about 42 s with --driver local, even when each item only runs true. The commands take almost none of that time: the engine takes nearly all of it.
Shape (real CLI, --driver local)
Whole run
uncapped, 1,000 items
42 s
max_parallel: 8, 200 items
3.5 s
Where the time goes
each next / report about 2 calls per item
read run file parses the whole file through the schema
reconcile settles every running item's workflow
write run file writes the whole file
Every call pays both costs, and both grow with the run. Once the settling work is cut, parsing the file is about a third of the run.
Options
Option
Gain
Catch
Validate the run file more cheaply on read, for example without the per-step schema pass
up to about a third
that read is what guards against a corrupt or hand-edited file
Persist what is left to reconcile, and settle only those scopes
the settling cost
an edited file must still fall back to a full reconcile
Tried and dropped in #1148: an in-memory incremental reconcile. It was correct, but it only took the run from 41 s to 33 s, and only when one process drives the whole run.
Done when
A 1,000-item uncapped run spends most of its time in its commands, measured with the real CLI.
#1175 adds work to report that grows with the run. On each successful report that carries changes, branchStillUsed plans the branch of every unsettled step (engine.ts:1141-1149), and each plan runs planWorkspace, which scans run.steps several times (ancestorsOf, itemsOf, itemsAfter). In an uncapped for_each with workspace: new, that is O(N²) per report. I have read this from the code, not measured it. A 1,000-item workspace: new run spends most of its time on git worktree add and commits, so I'd add it to the measurements with engine time per report kept apart from git and tool time, and check that it stays flat from 100 to 1,000 items. A branch-to-unsettled-users index in the persisted reconcile state is one fix, if the measurement calls for it.
Activity
SaulMoro commentedon Oct 20, 2026
#1175 adds work to
reportthat grows with the run. On each successful report that carrieschanges,branchStillUsedplans the branch of every unsettled step (engine.ts:1141-1149), and each plan runsplanWorkspace, which scansrun.stepsseveral times (ancestorsOf,itemsOf,itemsAfter). In an uncappedfor_eachwithworkspace: new, that is O(N²) per report. I have read this from the code, not measured it. A 1,000-itemworkspace: newrun spends most of its time ongit worktree addand commits, so I'd add it to the measurements with engine time perreportkept apart from git and tool time, and check that it stays flat from 100 to 1,000 items. A branch-to-unsettled-users index in the persisted reconcile state is one fix, if the measurement calls for it.