fix: enable dynamic inner_loop for parallel task execution across 6 team skills

- team-lifecycle-v4: executor inner_loop true→dynamic, add dynamicImplDispatch for PLAN-001 callback
- team-testing: executor inner_loop true→dynamic for comprehensive pipeline parallel TESTRUN
- team-quality-assurance: executor inner_loop true→dynamic for full mode parallel QARUN
- team-perf-opt: optimizer inner_loop true→dynamic for fan-out/independent parallel IMPL branches
- team-arch-opt: refactorer inner_loop true→dynamic for fan-out/independent parallel REFACTOR branches
- team-coordinate: fix dependency_graph schema mismatch, needs_research sequencing,
  handleSpawnNext role→task level check, add output_tag to template, precise inner_loop rules

All handleSpawnNext now read task description InnerLoop: field instead of role-level default,
enabling same-role workers to run in parallel when tasks have no mutual dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
catlog22
2026-03-29 21:37:35 +08:00
parent 885eb18d87
commit 56c0429412
26 changed files with 117 additions and 54 deletions

View File

@@ -78,7 +78,7 @@ Execute built-in Phase 1 (task discovery) -> role Phase 2-4 -> built-in Phase 5
})
```
**Inner Loop roles** (refactorer): Set `inner_loop: true`.
**Inner Loop roles** (refactorer): Set `inner_loop` dynamically — `true` for single mode, `false` for fan-out/independent (parallel branches).
**Single-task roles** (analyzer, designer, validator, reviewer): Set `inner_loop: false`.
## User Commands

View File

@@ -82,7 +82,9 @@ Find ready tasks, spawn workers, STOP.
2. No ready + work in progress -> report waiting, STOP
3. No ready + nothing in progress -> handleComplete
4. Has ready -> for each:
a. Check if inner loop role with active worker -> skip (worker picks up)
a. Check inner_loop: parse task description `InnerLoop:` field (NOT role.md default)
- InnerLoop: true AND same-role worker already active -> skip (worker picks up)
- InnerLoop: false OR no active same-role worker -> spawn new worker
b. TaskUpdate -> in_progress
c. team_msg log -> task_unblocked
d. Spawn team-worker (see SKILL.md Spawn Template):

View File

@@ -1,12 +1,14 @@
---
role: refactorer
prefix: REFACTOR
inner_loop: true
inner_loop: dynamic
message_types: [state_update]
---
# Code Refactorer
> **inner_loop: dynamic** — Dispatch sets per-task: `true` for single mode (one REFACTOR task with iterative fix cycles), `false` for fan-out/independent modes (REFACTOR-B01..N run as separate parallel workers). When false, each branch gets its own worker.
Implement architecture refactoring changes following the design plan. For FIX tasks, apply targeted corrections based on review/validation feedback.
## Modes

View File

@@ -64,10 +64,10 @@
"type": "code_generation",
"description": "Implements architecture refactoring changes following the design plan",
"role_spec": "roles/refactorer/role.md",
"inner_loop": true,
"inner_loop": "dynamic",
"frontmatter": {
"prefix": "REFACTOR",
"inner_loop": true,
"inner_loop": "dynamic",
"additional_prefixes": ["FIX"],
"discuss_rounds": [],
"cli_tools": ["explore"],

View File

@@ -130,9 +130,10 @@ Execute built-in Phase 1 (task discovery) -> role-spec Phase 2-4 -> built-in Pha
})
```
**Inner Loop roles** (role has 2+ serial same-prefix tasks): Set `inner_loop: true`. The team-worker agent handles the loop internally.
**Single-task roles**: Set `inner_loop: false`.
**Inner Loop**: Determined per-task from task description `InnerLoop:` field, not per-role:
- Serial chain (2+ tasks, each blockedBy previous): `inner_loop: true` — single worker loops
- Parallel tasks (no mutual blockedBy): `inner_loop: false` — separate workers per task
- Single-task roles: `inner_loop: false`
---

View File

@@ -130,7 +130,7 @@ For each role, determine frontmatter and generation hints:
| Field | Derivation |
|-------|------------|
| `prefix` | From capability prefix (e.g., RESEARCH, DRAFT, IMPL) |
| `inner_loop` | `true` if role has 2+ serial same-prefix tasks |
| `inner_loop` | `true` if role has 2+ same-prefix tasks AND they form a serial chain (each blockedBy the previous). `false` if tasks are parallel (no mutual blockedBy) or role has only 1 task |
| `CLI tools` | Suggested, not mandatory — coordinator may adjust based on task needs |
| `pattern_hint` | Reference pattern name from role-spec-template (research/document/code/analysis/validation) — guides coordinator's Phase 2-4 composition, NOT a rigid template selector |
| `output_type` | `artifact` (new files in session/artifacts/) / `codebase` (modify existing project files) / `mixed` (both) — determines verification strategy in Behavioral Traits |

View File

@@ -95,9 +95,10 @@ RoleSpec: <session-folder>/role-specs/<role-name>.md
| Condition | InnerLoop |
|-----------|-----------|
| Role has 2+ serial same-prefix tasks | true |
| Role has 2+ same-prefix tasks forming a serial chain (each blockedBy the previous) | true |
| Role has 1 task | false |
| Tasks are parallel (no dependency between them) | false |
| Role has 2+ same-prefix tasks but they are parallel (no mutual blockedBy) | false |
| Mixed: some serial, some parallel within same role | Set per-task: true for serial chain members, false for parallel members |
### Dependency Validation

View File

@@ -144,9 +144,10 @@ Ready tasks found?
+- NONE + work in progress -> report waiting -> STOP
+- NONE + nothing in progress -> PIPELINE_COMPLETE -> handleComplete
+- HAS ready tasks -> for each:
+- Is task owner an Inner Loop role AND that role already has an active_worker?
+- Parse task description `InnerLoop:` field (NOT session.roles[].inner_loop)
+- InnerLoop: true AND same-role worker already in active_workers?
| +- YES -> SKIP spawn (existing worker will pick it up via inner loop)
| +- NO -> normal spawn below
| +- NO -> normal spawn below (InnerLoop: false OR no active same-role worker)
+- TaskUpdate -> in_progress
+- team_msg log -> task_unblocked (session_id=<session-id>)
+- Spawn team-worker (see spawn tool call below)

View File

@@ -207,15 +207,9 @@ Regardless of complexity score or role count, coordinator MUST:
- `project_root` = result of `Bash({ command: "pwd" })`
- `skill_root` = `<project_root>/.claude/skills/team-coordinate`
2. **Check `needs_research` flag** from task-analysis.json:
- If `true`: **Spawn researcher worker first** to gather codebase context
- Wait for researcher callback
- Merge research findings into task context
- Update task-analysis.json with enriched context
2. **Generate session ID**: `TC-<slug>-<date>` (slug from first 3 meaningful words of task)
3. **Generate session ID**: `TC-<slug>-<date>` (slug from first 3 meaningful words of task)
4. **Create session folder structure**:
3. **Create session folder structure**:
```
.workflow/.team/<session-id>/
+-- role-specs/
@@ -226,11 +220,11 @@ Regardless of complexity score or role count, coordinator MUST:
+-- .msg/
```
5. **Call TeamCreate** with team name derived from session ID
4. **Call TeamCreate** with team name derived from session ID
6. **Read `specs/role-spec-template.md`** for Behavioral Traits + Reference Patterns
5. **Read `specs/role-spec-template.md`** for Behavioral Traits + Reference Patterns
7. **For each role in task-analysis.json#roles**:
6. **For each role in task-analysis.json#roles**:
- Fill YAML frontmatter: role, prefix, inner_loop, additional_members, message_types
- **Compose Phase 2-4 content** (NOT copy from template):
- Phase 2: Derive input sources and context loading steps from **task description + upstream dependencies**
@@ -239,14 +233,14 @@ Regardless of complexity score or role count, coordinator MUST:
- Reference Patterns may guide phase structure, but task description determines specific content
- Write generated role-spec to `<session>/role-specs/<role-name>.md`
8. **Register roles** in team-session.json#roles (with `role_spec` path instead of `role_file`)
7. **Register roles** in team-session.json#roles (with `role_spec` path instead of `role_file`)
9. **Initialize shared infrastructure**:
8. **Initialize shared infrastructure**:
- `wisdom/learnings.md`, `wisdom/decisions.md`, `wisdom/issues.md` (empty with headers)
- `explorations/cache-index.json` (`{ "entries": [] }`)
- `discussions/` (empty directory)
10. **Initialize pipeline metadata** via team_msg:
9. **Initialize pipeline metadata** via team_msg:
```typescript
// 使用 team_msg 将 pipeline 元数据写入 .msg/meta.json
// 注意: 此处为动态角色,执行时需将 <placeholders> 替换为 task-analysis.json 中生成的实际角色列表
@@ -265,7 +259,14 @@ mcp__ccw-tools__team_msg({
})
```
11. **Write team-session.json** with: session_id, task_description, status="active", roles, pipeline (empty), active_workers=[], completion_action="interactive", created_at
10. **Write team-session.json** with: session_id, task_description, status="active", roles, pipeline (empty), active_workers=[], completion_action="interactive", created_at
11. **Check `needs_research` flag** from task-analysis.json:
- If `true`: Spawn researcher worker (role-spec now exists from step 6) to gather codebase context
- Wait for researcher callback
- Merge research findings into task context
- Update task-analysis.json with enriched context
- If `false`: Skip, proceed to Phase 3
**Success**: Session created, role-spec files generated, shared infrastructure initialized.

View File

@@ -38,18 +38,20 @@ Phase 3-N: monitor.md
## Dependency Graph Structure
task-analysis.json encodes the pipeline:
task-analysis.json encodes the pipeline as adjacency list (task ID -> blockedBy array):
```json
{
"dependency_graph": {
"RESEARCH-001": { "role": "researcher", "blockedBy": [], "priority": "P0" },
"IMPL-001": { "role": "developer", "blockedBy": ["RESEARCH-001"], "priority": "P1" },
"TEST-001": { "role": "tester", "blockedBy": ["IMPL-001"], "priority": "P2" }
"RESEARCH-001": [],
"IMPL-001": ["RESEARCH-001"],
"TEST-001": ["IMPL-001"]
}
}
```
Role mapping comes from `task-analysis.json#capabilities[].tasks[]`, not from the dependency graph itself.
## Role-Worker Map
Dynamic — loaded from session role-specs at runtime:
@@ -64,6 +66,7 @@ Role-spec files contain YAML frontmatter:
role: <role-name>
prefix: <PREFIX>
inner_loop: <true|false>
output_tag: "[<role-name>]"
message_types:
success: <type>
error: error

View File

@@ -11,6 +11,7 @@ Template used by coordinator to generate lightweight worker role-spec files at r
role: <role_name>
prefix: <PREFIX>
inner_loop: <true|false>
output_tag: "[<role_name>]"
CLI tools: [<CLI tool-names>]
message_types:
success: <prefix>_complete

View File

@@ -40,7 +40,7 @@ Skill(skill="team-lifecycle-v4", args="task description")
| analyst | [roles/analyst/role.md](roles/analyst/role.md) | RESEARCH-* | false |
| writer | [roles/writer/role.md](roles/writer/role.md) | DRAFT-* | true |
| planner | [roles/planner/role.md](roles/planner/role.md) | PLAN-* | true |
| executor | [roles/executor/role.md](roles/executor/role.md) | IMPL-* | true |
| executor | [roles/executor/role.md](roles/executor/role.md) | IMPL-* | dynamic |
| tester | [roles/tester/role.md](roles/tester/role.md) | TEST-* | false |
| reviewer | [roles/reviewer/role.md](roles/reviewer/role.md) | REVIEW-*, QUALITY-*, IMPROVE-* | false |
| supervisor | [roles/supervisor/role.md](roles/supervisor/role.md) | CHECKPOINT-* | false |

View File

@@ -34,8 +34,20 @@ RoleSpec: ~ or <project>/.claude/skills/team-lifecycle-v4/roles/<role>/role.md
## InnerLoop Flag Rules
- true: Role has 2+ serial same-prefix tasks (writer: DRAFT-001->004)
- false: Role has 1 task, or tasks are parallel
- true: Role has 2+ serial same-prefix tasks (writer: DRAFT-001->004) where each blockedBy the previous
- false: Role has 1 task, or tasks are parallel (no mutual blockedBy)
### Parallel Detection for IMPL Tasks
When creating IMPL-{1..N} tasks from PLAN-001 output:
1. Read plan.json → extract `tasks[]` with their `depends_on` fields
2. Build dependency graph among IMPL tasks only
3. Classify:
- **Serial chain**: IMPL-002 blockedBy IMPL-001 → both get `InnerLoop: true`, single worker
- **Parallel set**: IMPL-001..N all blockedBy PLAN-001 only (no mutual deps) → each gets `InnerLoop: false`, separate workers
- **Mixed DAG**: Some parallel, some serial → group into independent chains, each chain gets one worker with `InnerLoop: true`; independent chains spawn in parallel
4. Set `InnerLoop` in each task description accordingly
## CHECKPOINT Task Rules

View File

@@ -36,9 +36,29 @@ Worker completed. Process and advance.
- CHECKPOINT-* with verdict "warn" -> log risks to wisdom, proceed normally
- CHECKPOINT-* with verdict "pass" -> proceed normally
- QUALITY-001 -> display quality gate, pause for user commands
- PLAN-001 -> read plan.json complexity, create dynamic IMPL tasks per specs/pipelines.md routing
- PLAN-001 -> dynamicImplDispatch (see below)
6. -> handleSpawnNext
### dynamicImplDispatch (PLAN-001 callback)
When PLAN-001 completes, coordinator creates IMPL tasks based on complexity:
1. Read `<session>/plan/plan.json` → extract `complexity`, `tasks[]`
2. Route by complexity (per specs/pipelines.md §6):
| Complexity | Action |
|------------|--------|
| Low (1-2 modules) | Create single IMPL-001, blockedBy: [PLAN-001], InnerLoop: true |
| Medium (3-4 modules) | Create IMPL-{1..N}, each blockedBy: [PLAN-001] only, InnerLoop: false |
| High (5+ modules) | Create IMPL-{1..N} with DAG deps from plan.json, InnerLoop per dispatch rules |
3. For each IMPL task: TaskCreate with structured description (dispatch.md template)
4. Set blockedBy:
- **Parallel tasks**: blockedBy: [PLAN-001] (or [CHECKPOINT-003] if supervision enabled)
- **Serial chain within DAG**: blockedBy includes upstream IMPL task IDs
5. Update team-session.json: `pipeline.tasks_total`, `pipeline.impl_topology: "single"|"parallel"|"dag"`
6. Log via team_msg: `{ type: "state_update", data: { impl_count: N, topology: "..." } }`
## handleCheck
Read-only status report, then STOP.
@@ -71,7 +91,9 @@ Find ready tasks, spawn workers, STOP.
2. No ready + work in progress -> report waiting, STOP
3. No ready + nothing in progress -> handleComplete
4. Has ready -> for each:
a. Check if inner loop role with active worker -> skip (worker picks up)
a. Check inner_loop: parse task description `InnerLoop:` field (NOT role.md default)
- InnerLoop: true AND same-role worker already active -> skip (worker picks up next task)
- InnerLoop: false OR no active same-role worker -> spawn new worker
b. **CHECKPOINT-* task** -> wake resident supervisor (see below)
c. Other tasks -> standard spawn:
- TaskUpdate -> in_progress

View File

@@ -1,7 +1,7 @@
---
role: executor
prefix: IMPL
inner_loop: true
inner_loop: dynamic
message_types:
success: impl_complete
progress: impl_progress
@@ -12,6 +12,8 @@ message_types:
Code implementation worker with dual execution modes.
> **inner_loop: dynamic** — Dispatch sets per-task: `true` for serial IMPL chains (IMPL-001→002→003 with inter-dependencies), `false` for parallel IMPL sets (independent modules). When false, each IMPL task gets its own worker.
## Identity
- Tag: [executor] | Prefix: IMPL-*
- Responsibility: Implement code from plan tasks via agent or CLI delegation

View File

@@ -90,7 +90,7 @@ Execute built-in Phase 1 (task discovery) -> role Phase 2-4 -> built-in Phase 5
})
```
**Inner Loop roles** (optimizer): Set `inner_loop: true`.
**Inner Loop roles** (optimizer): Set `inner_loop` dynamically — `true` for single mode, `false` for fan-out/independent (parallel branches).
**Single-task roles** (profiler, strategist, benchmarker, reviewer): Set `inner_loop: false`.
## User Commands

View File

@@ -1,7 +1,7 @@
---
role: optimizer
prefix: IMPL
inner_loop: true
inner_loop: dynamic
additional_prefixes: [FIX]
message_types:
success: impl_complete
@@ -11,6 +11,8 @@ message_types:
# Code Optimizer
> **inner_loop: dynamic** — Dispatch sets per-task: `true` for single mode (one IMPL task with iterative fix cycles), `false` for fan-out/independent modes (IMPL-B01..N run as separate parallel workers). When false, each branch gets its own worker.
Implement optimization changes following the strategy plan. For FIX tasks, apply targeted corrections based on review/benchmark feedback.
## Modes

View File

@@ -65,10 +65,10 @@
"type": "code_generation",
"description": "Implements optimization changes following the strategy plan",
"role_spec": "roles/optimizer/role.md",
"inner_loop": true,
"inner_loop": "dynamic",
"frontmatter": {
"prefix": "IMPL",
"inner_loop": true,
"inner_loop": "dynamic",
"additional_prefixes": ["FIX"],
"discuss_rounds": [],
"delegates_to": [],

View File

@@ -38,7 +38,7 @@ Skill(skill="team-quality-assurance", args="task description")
| scout | [roles/scout/role.md](roles/scout/role.md) | SCOUT-* | false |
| strategist | [roles/strategist/role.md](roles/strategist/role.md) | QASTRAT-* | false |
| generator | [roles/generator/role.md](roles/generator/role.md) | QAGEN-* | false |
| executor | [roles/executor/role.md](roles/executor/role.md) | QARUN-* | true |
| executor | [roles/executor/role.md](roles/executor/role.md) | QARUN-* | dynamic |
| analyst | [roles/analyst/role.md](roles/analyst/role.md) | QAANA-* | false |
## Role Router

View File

@@ -87,8 +87,10 @@ SCOUT-002 (scout): Regression scan after fixes
## InnerLoop Flag Rules
- true: executor roles (run-fix cycles)
- false: scout, strategist, generator, analyst roles
- **executor**: dynamic per pipeline mode:
- Discovery/Testing: `true` (serial layer chain, single executor handles GC loops)
- Full: `false` for parallel QARUN tasks (QARUN-L1-001 and QARUN-L2-001 have independent blockedBy, each gets own worker)
- **scout, strategist, generator, analyst**: always false
## Dependency Validation

View File

@@ -132,10 +132,12 @@ Find ready tasks, spawn workers, STOP.
| SCOUT-* | scout | false |
| QASTRAT-* | strategist | false |
| QAGEN-* | generator | false |
| QARUN-* | executor | true |
| QARUN-* | executor | dynamic |
| QAANA-* | analyst | false |
b. Check if inner loop role with active worker -> skip (worker picks up next task)
b. Check inner_loop: parse task description `InnerLoop:` field (NOT role.md default)
- InnerLoop: true AND same-role worker already active -> skip (worker picks up next task)
- InnerLoop: false OR no active same-role worker -> spawn new worker
c. TaskUpdate -> in_progress
d. team_msg log -> task_unblocked
e. Spawn team-worker:

View File

@@ -1,7 +1,7 @@
---
role: executor
prefix: QARUN
inner_loop: true
inner_loop: dynamic
additional_prefixes: [QARUN-gc]
message_types:
success: tests_passed
@@ -12,6 +12,8 @@ message_types:
# Test Executor
> **inner_loop: dynamic** — Dispatch sets per-task: `true` for serial layer chains (discovery/testing mode), `false` for parallel layer execution (full mode where QARUN-L1-001 and QARUN-L2-001 run independently). When false, each QARUN task gets its own worker.
Run test suites, collect coverage data, and perform automatic fix cycles when tests fail. Implements the execution side of the Generator-Executor (GC) loop.
## Phase 2: Environment Detection

View File

@@ -37,7 +37,7 @@ Skill(skill="team-testing", args="task description")
| coordinator | [roles/coordinator/role.md](roles/coordinator/role.md) | — | — |
| strategist | [roles/strategist/role.md](roles/strategist/role.md) | STRATEGY-* | false |
| generator | [roles/generator/role.md](roles/generator/role.md) | TESTGEN-* | true |
| executor | [roles/executor/role.md](roles/executor/role.md) | TESTRUN-* | true |
| executor | [roles/executor/role.md](roles/executor/role.md) | TESTRUN-* | dynamic |
| analyst | [roles/analyst/role.md](roles/analyst/role.md) | TESTANA-* | false |
## Role Router

View File

@@ -84,8 +84,11 @@ TESTANA-001 (analyst): Defect pattern analysis, quality report
## InnerLoop Flag Rules
- true: generator, executor roles (GC loop iterations)
- false: strategist, analyst roles
- **generator**: always true (GC loop iterations within a single layer)
- **executor**: dynamic per pipeline mode:
- Targeted/Standard: `true` (serial layer chain, single executor handles GC loops)
- Comprehensive: `false` for parallel TESTRUN tasks (TESTRUN-001 and TESTRUN-002 have independent blockedBy, each gets own worker)
- **strategist, analyst**: always false
## Dependency Validation

View File

@@ -27,7 +27,7 @@ Event-driven pipeline coordination. Beat model: coordinator wake -> process -> s
|--------|------|-----------|------------|
| STRATEGY-* | strategist | `~ or <project>/.claude/skills/team-testing/roles/strategist/role.md` | false |
| TESTGEN-* | generator | `~ or <project>/.claude/skills/team-testing/roles/generator/role.md` | true |
| TESTRUN-* | executor | `~ or <project>/.claude/skills/team-testing/roles/executor/role.md` | true |
| TESTRUN-* | executor | `~ or <project>/.claude/skills/team-testing/roles/executor/role.md` | dynamic |
| TESTANA-* | analyst | `~ or <project>/.claude/skills/team-testing/roles/analyst/role.md` | false |
## handleCallback
@@ -136,7 +136,9 @@ Find ready tasks, spawn workers, STOP.
3. No ready + nothing in progress -> handleComplete
4. Has ready -> for each ready task:
a. Determine role from prefix (use Role-Worker Map)
b. Check if inner loop role (generator/executor) with active worker -> skip (worker picks up next task)
b. Check inner_loop: parse task description `InnerLoop:` field (NOT role.md default)
- InnerLoop: true AND same-role worker already active -> skip (worker picks up next task)
- InnerLoop: false OR no active same-role worker -> spawn new worker
c. TaskUpdate -> in_progress
d. team_msg log -> task_unblocked
e. Spawn team-worker:

View File

@@ -1,7 +1,7 @@
---
role: executor
prefix: TESTRUN
inner_loop: true
inner_loop: dynamic
message_types:
success: tests_passed
failure: tests_failed
@@ -11,6 +11,8 @@ message_types:
# Test Executor
> **inner_loop: dynamic** — Dispatch sets per-task: `true` for serial GC loops (single layer pipeline), `false` for parallel layer execution (comprehensive pipeline where TESTRUN-001 and TESTRUN-002 run independently). When false, each TESTRUN task gets its own worker.
Execute tests, collect coverage, attempt auto-fix for failures. Acts as the Critic in the Generator-Critic loop. Reports pass rate and coverage for coordinator GC decisions.
## Phase 2: Context Loading