@@ -184,11 +184,11 @@ Execution:
184184 ├─ Step 1: Initialize result tracking (previousExecutionResults = [])
185185 ├─ Step 2: Task grouping & batch creation
186186 │ ├─ Extract explicit depends_on (no file/keyword inference)
187- │ ├─ Group: independent tasks → single parallel batch (maximize utilization )
187+ │ ├─ Group: independent tasks → per-executor parallel batches (one CLI per batch )
188188 │ ├─ Group: dependent tasks → sequential phases (respect dependencies)
189189 │ └─ Create TodoWrite list for batches
190190 ├─ Step 3: Launch execution
191- │ ├─ Phase 1: All independent tasks (⚡ single batch, concurrent)
191+ │ ├─ Phase 1: Independent tasks (⚡ per-executor batches, multi-CLI concurrent)
192192 │ └─ Phase 2+: Dependent tasks by dependency order
193193 ├─ Step 4: Track progress (TodoWrite updates per batch)
194194 └─ Step 5: Code review (if codeReviewTool ≠ "Skip")
@@ -241,26 +241,58 @@ function extractDependencies(tasks) {
241241 })
242242}
243243
244- // Group into batches: maximize parallel execution
244+ // Executor Resolution (used by task grouping below)
245+ // 获取任务的 executor(优先使用 executorAssignments,fallback 到全局 executionMethod)
246+ function getTaskExecutor (task ) {
247+ const assignments = executionContext? .executorAssignments || {}
248+ if (assignments[task .id ]) {
249+ return assignments[task .id ].executor // 'gemini' | 'codex' | 'agent'
250+ }
251+ // Fallback: 全局 executionMethod 映射
252+ const method = executionContext? .executionMethod || ' Auto'
253+ if (method === ' Agent' ) return ' agent'
254+ if (method === ' Codex' ) return ' codex'
255+ // Auto: 根据复杂度
256+ return planObject .complexity === ' Low' ? ' agent' : ' codex'
257+ }
258+
259+ // 按 executor 分组任务(核心分组组件)
260+ function groupTasksByExecutor (tasks ) {
261+ const groups = { gemini: [], codex: [], agent: [] }
262+ tasks .forEach (task => {
263+ const executor = getTaskExecutor (task)
264+ groups[executor].push (task)
265+ })
266+ return groups
267+ }
268+
269+ // Group into batches: per-executor parallel batches (one CLI per batch)
245270function createExecutionCalls (tasks , executionMethod ) {
246271 const tasksWithDeps = extractDependencies (tasks)
247272 const processed = new Set ()
248273 const calls = []
249274
250- // Phase 1: All independent tasks → single parallel batch (maximize utilization )
275+ // Phase 1: Independent tasks → per-executor batches (multi-CLI concurrent )
251276 const independentTasks = tasksWithDeps .filter (t => t .dependencies .length === 0 )
252277 if (independentTasks .length > 0 ) {
253- independentTasks .forEach (t => processed .add (t .taskIndex ))
254- calls .push ({
255- method: executionMethod,
256- executionType: " parallel" ,
257- groupId: " P1" ,
258- taskSummary: independentTasks .map (t => t .title ).join (' | ' ),
259- tasks: independentTasks
260- })
278+ const executorGroups = groupTasksByExecutor (independentTasks)
279+ let parallelIndex = 1
280+
281+ for (const [executor , tasks ] of Object .entries (executorGroups)) {
282+ if (tasks .length === 0 ) continue
283+ tasks .forEach (t => processed .add (t .taskIndex ))
284+ calls .push ({
285+ method: executionMethod,
286+ executor: executor, // 明确指定 executor
287+ executionType: " parallel" ,
288+ groupId: ` P${ parallelIndex++ } ` ,
289+ taskSummary: tasks .map (t => t .title ).join (' | ' ),
290+ tasks: tasks
291+ })
292+ }
261293 }
262294
263- // Phase 2: Dependent tasks → sequential batches (respect dependencies)
295+ // Phase 2: Dependent tasks → sequential/parallel batches (respect dependencies)
264296 let sequentialIndex = 1
265297 let remaining = tasksWithDeps .filter (t => ! processed .has (t .taskIndex ))
266298
@@ -275,15 +307,33 @@ function createExecutionCalls(tasks, executionMethod) {
275307 ready .push (... remaining)
276308 }
277309
278- // Group ready tasks (can run in parallel within this phase)
279- ready .forEach (t => processed .add (t .taskIndex ))
280- calls .push ({
281- method: executionMethod,
282- executionType: ready .length > 1 ? " parallel" : " sequential" ,
283- groupId: ready .length > 1 ? ` P${ calls .length + 1 } ` : ` S${ sequentialIndex++ } ` ,
284- taskSummary: ready .map (t => t .title ).join (ready .length > 1 ? ' | ' : ' → ' ),
285- tasks: ready
286- })
310+ if (ready .length > 1 ) {
311+ // Multiple ready tasks → per-executor batches (parallel within this phase)
312+ const executorGroups = groupTasksByExecutor (ready)
313+ for (const [executor , tasks ] of Object .entries (executorGroups)) {
314+ if (tasks .length === 0 ) continue
315+ tasks .forEach (t => processed .add (t .taskIndex ))
316+ calls .push ({
317+ method: executionMethod,
318+ executor: executor,
319+ executionType: " parallel" ,
320+ groupId: ` P${ calls .length + 1 } ` ,
321+ taskSummary: tasks .map (t => t .title ).join (' | ' ),
322+ tasks: tasks
323+ })
324+ }
325+ } else {
326+ // Single ready task → sequential batch
327+ ready .forEach (t => processed .add (t .taskIndex ))
328+ calls .push ({
329+ method: executionMethod,
330+ executor: getTaskExecutor (ready[0 ]),
331+ executionType: " sequential" ,
332+ groupId: ` S${ sequentialIndex++ } ` ,
333+ taskSummary: ready[0 ].title ,
334+ tasks: ready
335+ })
336+ }
287337
288338 remaining = remaining .filter (t => ! processed .has (t .taskIndex ))
289339 }
@@ -304,33 +354,40 @@ TodoWrite({
304354
305355### Step 3: Launch Execution
306356
307- ** Executor Resolution** (任务级 executor 优先于全局设置):
357+ **Executor Resolution**: ` getTaskExecutor ()` and ` groupTasksByExecutor ()` defined in Step 2 (Task Grouping).
358+
359+ **Batch Execution Routing** (根据 batch.executor 字段路由):
308360` ` ` javascript
309- // 获取任务的 executor(优先使用 executorAssignments,fallback 到全局 executionMethod)
310- function getTaskExecutor (task ) {
311- const assignments = executionContext? .executorAssignments || {}
312- if (assignments[task .id ]) {
313- return assignments[task .id ].executor // 'gemini' | 'codex' | 'agent'
361+ // executeBatch 根据 batch 自身的 executor 字段决定调用哪个 CLI
362+ function executeBatch (batch ) {
363+ const executor = batch .executor || getTaskExecutor (batch .tasks [0 ])
364+ const sessionId = executionContext? .session ? .id || ' standalone'
365+ const fixedId = ` ${ sessionId} -${ batch .groupId } `
366+
367+ if (executor === ' agent' ) {
368+ // Agent execution (synchronous)
369+ return Task ({
370+ subagent_type: " code-developer" ,
371+ run_in_background: false ,
372+ description: batch .taskSummary ,
373+ prompt: buildExecutionPrompt (batch)
374+ })
375+ } else if (executor === ' codex' ) {
376+ // Codex CLI (background)
377+ return Bash (` ccw cli -p "${ buildExecutionPrompt (batch)} " --tool codex --mode write --id ${ fixedId} ` , { run_in_background: true })
378+ } else if (executor === ' gemini' ) {
379+ // Gemini CLI (background)
380+ return Bash (` ccw cli -p "${ buildExecutionPrompt (batch)} " --tool gemini --mode write --id ${ fixedId} ` , { run_in_background: true })
314381 }
315- // Fallback: 全局 executionMethod 映射
316- const method = executionContext? .executionMethod || ' Auto'
317- if (method === ' Agent' ) return ' agent'
318- if (method === ' Codex' ) return ' codex'
319- // Auto: 根据复杂度
320- return planObject .complexity === ' Low' ? ' agent' : ' codex'
321- }
322-
323- // 按 executor 分组任务
324- function groupTasksByExecutor (tasks ) {
325- const groups = { gemini: [], codex: [], agent: [] }
326- tasks .forEach (task => {
327- const executor = getTaskExecutor (task)
328- groups[executor].push (task)
329- })
330- return groups
331382}
332383` ` `
333384
385+ **并行执行原则**:
386+ - 每个 batch 对应一个独立的 CLI 实例或 Agent 调用
387+ - 并行 = 多个 Bash(run_in_background=true) 或多个 Task() 同时发出
388+ - 绝不将多个独立任务合并到同一个 CLI prompt 中
389+ - Agent 任务不可后台执行(run_in_background=false),但多个 Agent 任务可通过单条消息中的多个 Task() 调用并发
390+
334391**Execution Flow**: Parallel batches concurrently → Sequential batches in order
335392` ` ` javascript
336393const parallel = executionCalls .filter (c => c .executionType === " parallel" )
@@ -659,8 +716,8 @@ console.log(`✓ Development index: [${category}] ${entry.title}`)
659716## Best Practices
660717
661718**Input Modes**: In-memory (lite-plan), prompt (standalone), file (JSON/text)
662- **Task Grouping**: Based on explicit depends_on only; independent tasks run in single parallel batch
663- **Execution**: All independent tasks launch concurrently via single Claude message with multiple tool calls
719+ **Task Grouping**: Based on explicit depends_on only; independent tasks split by executor, each batch runs as separate CLI instance
720+ **Execution**: Independent task batches launch concurrently via single Claude message with multiple tool calls (one tool call per batch)
664721
665722## Error Handling
666723
0 commit comments