Problem
Seven modules in the core agent-orchestration path form one tightly-coupled circular cluster (confirmed via madge --circular):
server/services/cos.js → cosAgents.js → cosAgentLifecycle.js → agentManagement.js → agents.js → subAgentSpawner.js → agentLifecycle.js, with several edges reaching back up the chain.
There is no correctness bug today — the team has already broken the static cycle at the load-bearing edges with await import(...), and cosAgentLifecycle.js:453-526 documents exactly why ("a static import here would invert the layering").
The cost is comprehension and change-safety: no module clearly owns agent lifecycle, so changing any one module's exported surface risks a ripple through the other six, and reasoning about a lifecycle transition means holding all seven files in mind at once.
Work
Consolidate the mutually-recursive lifecycle transitions (pause / kill / complete / spawn) behind a single agentOrchestrator facade that owns the state machine, with the current seven modules becoming leaf implementations it calls one-directionally.
This is a Complex refactor in the most load-bearing path in the app, which is why the audit that found it deliberately did not attempt it automatically. It should be done deliberately, with the existing cosAgents.test.js / cleanupAgentWorktree.test.js coverage as the regression net, and probably in more than one PR.
Suggested sequencing:
- Map every current cross-module call and classify it as "lifecycle transition" vs "leaf operation"
- Introduce the facade with the transitions, leaving leaves in place
- Migrate call sites one module at a time
- Remove the dynamic-import workarounds that exist only to break the cycle
Found by a /do:better audit sweep.
Problem
Seven modules in the core agent-orchestration path form one tightly-coupled circular cluster (confirmed via
madge --circular):server/services/cos.js→cosAgents.js→cosAgentLifecycle.js→agentManagement.js→agents.js→subAgentSpawner.js→agentLifecycle.js, with several edges reaching back up the chain.There is no correctness bug today — the team has already broken the static cycle at the load-bearing edges with
await import(...), andcosAgentLifecycle.js:453-526documents exactly why ("a static import here would invert the layering").The cost is comprehension and change-safety: no module clearly owns agent lifecycle, so changing any one module's exported surface risks a ripple through the other six, and reasoning about a lifecycle transition means holding all seven files in mind at once.
Work
Consolidate the mutually-recursive lifecycle transitions (pause / kill / complete / spawn) behind a single
agentOrchestratorfacade that owns the state machine, with the current seven modules becoming leaf implementations it calls one-directionally.This is a Complex refactor in the most load-bearing path in the app, which is why the audit that found it deliberately did not attempt it automatically. It should be done deliberately, with the existing
cosAgents.test.js/cleanupAgentWorktree.test.jscoverage as the regression net, and probably in more than one PR.Suggested sequencing:
Found by a
/do:betteraudit sweep.