fix(flows): route flows run through the DAG-honoring swarm code preset#94
Merged
Conversation
`nilcore flows run` flattened a flow's agent_task DAG into a newline-joined goal string handed to the `decompose` preset, which runs children independently off HEAD and merges in order. That discarded the flow's produces→requires edges (decoded into spawn.Subtask.DependsOn): a dependent agent_task was coded against the original tree, not its dependency's output — collapsing exactly the structure a flow exists to express. Lift the agent_task subtasks into a planner.Tree (id/goal/DependsOn verbatim + title & id@version provenance on the run goal) and dispatch through the verified swarm path with the `code` preset (Shape=DAG, FanIn=merge, Role=implementer, packs software+code). Swarm honors DependsOn: a dependent shard runs in a later pass on the integrated tip of its dependencies, not HEAD. Completion stays verifier-owned (I2): each shard is pack-verified and the merged tip re-verified. - swarm.TreeSharder + shared treeToShards: map a pre-built planner.Tree onto run-namespaced shards, re-namespacing DependsOn onto Shard.Deps (the same mapping PlanSharder uses, minus the model call). - swarmMain tail refactored into reusable swarmRun(swarmDeps); swarmDeps gains an optional `tree *planner.Tree` seam consumed by buildInitialShards. - flowsRun builds default swarm flags, forces preset=code + the flow's dir, and calls swarmRun with the injected tree. No new dispatch loop. Tests: TreeSharder re-namespaces a 2-dep diamond + carries routing; the flow→tree→shards path preserves the implement-depends-on-plan edge as a real Shard.Dep and stamps title+provenance. Docs updated off the stale "decompose preset" wording. make verify + golangci-lint green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
nilcore flows runconsumed a portable agentic-flows workflow by composing everyagent_tasknode into a single newline-joined goal string and handing it to thedecomposepreset.decomposeruns its children independently offHEADand merges them in declaration order.That silently collapsed the flow's DAG. The adapter correctly decodes each
produces→requiresdataflow edge into a realspawn.Subtask.DependsOn— but flattening the subtasks into a goal list threw those edges away. A dependentagent_taskwas then coded against the original tree instead of its dependency's output, which is exactly the structure a flow exists to express.The fix
Route
flows runthrough the verifiedswarmpath with thecodepreset instead:agent_tasksubtasks into aplanner.Tree—id/goal/DependsOncarried verbatim, plus the flow title +id@versionprovenance on the run goal.codepreset (Shape=DAG,FanIn=merge,Role=implementer, packssoftware+code). Swarm honors the tree'sDependsOn: a dependent shard runs in a later pass on the integrated tip of its dependencies, notHEAD— so the flow's structure survives.Mechanics
swarm.TreeSharder(+ a sharedtreeToShards) maps a pre-builtplanner.Treeonto run-namespaced shards, re-namespacingDependsOn(plan-task ids) ontoShard.Deps(shard ids) — the same mappingPlanSharderuses, minus the model call.swarmMain's tail is refactored into a reusableswarmRun(swarmDeps);swarmDepsgains an optionaltree *planner.Treeseam consumed bybuildInitialShards(falls back to the flag/preset sharder when nil — unchanged).flowsRunbuilds default swarm flags, forcespreset=code+ the flow'sdir, and callsswarmRunwith the injected tree. No new dispatch loop.Invariants
Pure additive seam. I1 backend contract untouched; I2 preserved (verifier still owns "done"); no I3/I4/I5/I6/I7 surface changed.
flows/swarm/agenticflows/docs file sets are disjoint from the currently-open #93.Tests
TestTreeSharderReNamespacesDeps— a 2-dep diamond (wiredepends onscaffold+impl) maps ontoswarm-<run>-{0,1,2}withDepsre-namespaced, routing carried, state queued.TestFlowRunPreservesDAGThroughSwarm— the full flow→flowTree→TreeSharderpath keepsimplement-depends-on-planas a realShard.Dep, and stamps title +id@versionprovenance on the run goal.Gate
make verify(119 pkgs) ·go vet·golangci-lint(0 issues) — all green locally.🤖 Generated with Claude Code