mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-26 19:56:37 +08:00
- Delete 21 old team skill directories using CSV-wave pipeline pattern (~100+ files) - Delete old team-lifecycle (v3) and team-planex-v2 - Create generic team-worker.toml and team-supervisor.toml (replacing tlv4-specific TOMLs) - Convert 19 team skills from Claude Code format (Agent/SendMessage/TaskCreate) to Codex format (spawn_agent/wait_agent/tasks.json/request_user_input) - Update team-lifecycle-v4 to use generic agent types (team_worker/team_supervisor) - Convert all coordinator role files: dispatch.md, monitor.md, role.md - Convert all worker role files: remove run_in_background, fix Bash syntax - Convert all specs/pipelines.md references - Final state: 20 team skills, 217 .md files, zero Claude Code API residuals Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
513 lines
18 KiB
Markdown
513 lines
18 KiB
Markdown
# Team Lifecycle v4 — Codex 迁移计划
|
||
|
||
## 目标
|
||
|
||
将 Claude Code 版 `.claude/skills/team-lifecycle-v4` 迁移到 Codex 版 `.codex/skills/team-lifecycle-v4`,采用 **通用 worker + role 文档注入** 模式(与 Claude Code 设计对齐),去掉 `spawn_agents_on_csv`,用 `spawn_agent` + `wait_agent` + `send_input` + `close_agent` 实现同构编排。
|
||
|
||
## 设计原则
|
||
|
||
1. **通用 agent,role 文档区分角色** — 只定义 2 个 TOML(worker + supervisor),角色差异由 `roles/<role>/role.md` 决定
|
||
2. **role 文档直接复用** — `.claude/skills/` 中的 roles/specs/templates 原样迁移
|
||
3. **items 结构化传参** — 取代 message 字符串,分段传递角色分配、任务描述、上游上下文
|
||
4. **JSON 取代 CSV** — `tasks.json` 管理状态,`discoveries/{id}.json` 分文件写入
|
||
5. **两版同构** — Codex 调用原语与 Claude Code 形成 1:1 映射
|
||
|
||
## 平台调用映射
|
||
|
||
| 编排概念 | Claude Code | Codex |
|
||
|---------|------------|-------|
|
||
| Worker spawn | `Agent({ subagent_type: "team-worker", prompt })` | `spawn_agent({ agent_type: "tlv4_worker", items })` |
|
||
| Supervisor spawn | `Agent({ subagent_type: "team-supervisor", prompt })` | `spawn_agent({ agent_type: "tlv4_supervisor", items })` |
|
||
| Supervisor wake | `SendMessage({ recipient: "supervisor", content })` | `send_input({ id: supervisorId, items })` |
|
||
| Supervisor shutdown | `SendMessage({ type: "shutdown_request" })` | `close_agent({ id: supervisorId })` |
|
||
| 等待完成 | 后台回调 -> monitor.md | `wait_agent({ ids, timeout_ms })` |
|
||
| 任务状态 | `TaskCreate` / `TaskUpdate` | `tasks.json` 文件读写 |
|
||
| 团队管理 | `TeamCreate` / `TeamDelete` | session folder init / cleanup |
|
||
| 消息总线 | `mcp__ccw-tools__team_msg` | `discoveries/{id}.json` + `session-state.json` |
|
||
| 用户交互 | `AskUserQuestion` | `request_user_input` |
|
||
| 角色加载 | prompt 中 `@roles/<role>/role.md` | items text 中指示 `Read roles/<role>/role.md` |
|
||
|
||
## 目录结构 (迁移后)
|
||
|
||
```
|
||
.codex/
|
||
├── agents/
|
||
│ ├── tlv4-worker.toml # 通用 worker (NEW)
|
||
│ └── tlv4-supervisor.toml # 驻留 supervisor (NEW)
|
||
└── skills/
|
||
└── team-lifecycle-v4/
|
||
├── SKILL.md # 主编排 (REWRITE)
|
||
├── MIGRATION-PLAN.md # 本文档
|
||
├── roles/ # 从 .claude/ 复制
|
||
│ ├── coordinator/
|
||
│ │ ├── role.md
|
||
│ │ └── commands/
|
||
│ │ ├── analyze.md
|
||
│ │ ├── dispatch.md
|
||
│ │ └── monitor.md
|
||
│ ├── analyst/role.md
|
||
│ ├── writer/role.md
|
||
│ ├── planner/role.md
|
||
│ ├── executor/
|
||
│ │ ├── role.md
|
||
│ │ └── commands/
|
||
│ │ ├── implement.md
|
||
│ │ └── fix.md
|
||
│ ├── tester/role.md
|
||
│ ├── reviewer/
|
||
│ │ ├── role.md
|
||
│ │ └── commands/
|
||
│ │ ├── review-code.md
|
||
│ │ └── review-spec.md
|
||
│ └── supervisor/role.md
|
||
├── specs/ # 从 .claude/ 复制
|
||
│ ├── pipelines.md
|
||
│ ├── quality-gates.md
|
||
│ └── knowledge-transfer.md
|
||
├── templates/ # 从 .claude/ 复制
|
||
│ ├── product-brief.md
|
||
│ ├── requirements.md
|
||
│ ├── architecture.md
|
||
│ └── epics.md
|
||
└── schemas/
|
||
└── tasks-schema.md # REWRITE: CSV -> JSON
|
||
```
|
||
|
||
---
|
||
|
||
## 步骤 1: Agent TOML 定义
|
||
|
||
### `.codex/agents/tlv4-worker.toml`
|
||
|
||
```toml
|
||
name = "tlv4_worker"
|
||
description = "Generic team-lifecycle-v4 worker. Role-specific behavior loaded from role.md at spawn time."
|
||
model = "gpt-5.4"
|
||
model_reasoning_effort = "high"
|
||
sandbox_mode = "workspace-write"
|
||
|
||
developer_instructions = """
|
||
You are a team-lifecycle-v4 worker agent.
|
||
|
||
## Boot Protocol
|
||
1. Read role_spec file path from your task assignment (MUST read first)
|
||
2. Read session state from session path
|
||
3. Execute role-specific Phase 2-4 defined in role.md
|
||
4. Write deliverables to session artifacts directory
|
||
5. Write findings to discoveries/{task_id}.json
|
||
6. Report via report_agent_job_result
|
||
|
||
## Output Schema
|
||
{
|
||
"id": "<task_id>",
|
||
"status": "completed | failed",
|
||
"findings": "<max 500 chars>",
|
||
"quality_score": "<0-100, reviewer only>",
|
||
"supervision_verdict": "",
|
||
"error": ""
|
||
}
|
||
"""
|
||
```
|
||
|
||
### `.codex/agents/tlv4-supervisor.toml`
|
||
|
||
```toml
|
||
name = "tlv4_supervisor"
|
||
description = "Resident supervisor for team-lifecycle-v4. Woken via send_input for checkpoint verification."
|
||
model = "gpt-5.4"
|
||
model_reasoning_effort = "high"
|
||
sandbox_mode = "read-only"
|
||
|
||
developer_instructions = """
|
||
You are a team-lifecycle-v4 supervisor agent (resident pattern).
|
||
|
||
## Boot Protocol
|
||
1. Read role_spec file path from your task assignment (MUST read first)
|
||
2. Load baseline context from session
|
||
3. Report ready, wait for checkpoint requests via send_input
|
||
|
||
## Per Checkpoint
|
||
1. Read artifacts specified in checkpoint request
|
||
2. Verify cross-artifact consistency per role.md definitions
|
||
3. Issue verdict: pass (>= 0.8), warn (0.5-0.79), block (< 0.5)
|
||
4. Write report to artifacts/CHECKPOINT-{id}-report.md
|
||
5. Report findings
|
||
|
||
## Constraints
|
||
- Read-only: never modify artifacts
|
||
- Never issue pass when critical inconsistencies exist
|
||
- Never block for minor style issues
|
||
"""
|
||
```
|
||
|
||
---
|
||
|
||
## 步骤 2: SKILL.md 改写
|
||
|
||
### 核心变更
|
||
|
||
| 区域 | 现状 | 改写后 |
|
||
|------|------|--------|
|
||
| allowed-tools | `spawn_agents_on_csv, spawn_agent, wait, send_input, close_agent, ...` | `spawn_agent, wait_agent, send_input, close_agent, report_agent_job_result, Read, Write, Edit, Bash, Glob, Grep, request_user_input` |
|
||
| 执行模型 | hybrid: CSV wave (primary) + spawn_agent (secondary) | 统一: spawn_agent + wait_agent (all tasks) |
|
||
| 状态管理 | tasks.csv (CSV) | tasks.json (JSON) |
|
||
| 发现板 | discoveries.ndjson (共享追加) | discoveries/{task_id}.json (分文件) |
|
||
| exec_mode 分类 | csv-wave / interactive | 移除 — 所有任务统一用 spawn_agent |
|
||
| wave CSV 构建 | 生成 wave-{N}.csv, spawn_agents_on_csv | 循环 spawn_agent + 批量 wait_agent |
|
||
|
||
### Worker Spawn Template
|
||
|
||
```javascript
|
||
// 对齐 Claude Code 的 Agent({ subagent_type: "team-worker", prompt }) 模式
|
||
spawn_agent({
|
||
agent_type: "tlv4_worker",
|
||
items: [
|
||
// 段 1: 角色分配 (对齐 Claude Code prompt 的 Role Assignment 块)
|
||
{ type: "text", text: `## Role Assignment
|
||
role: ${task.role}
|
||
role_spec: ${skillRoot}/roles/${task.role}/role.md
|
||
session: ${sessionFolder}
|
||
session_id: ${sessionId}
|
||
requirement: ${requirement}
|
||
inner_loop: ${hasInnerLoop(task.role)}` },
|
||
|
||
// 段 2: 读取指示 (核心 — 保持 role 文档引用模式)
|
||
{ type: "text", text: `Read role_spec file (${skillRoot}/roles/${task.role}/role.md) to load Phase 2-4 domain instructions.
|
||
Execute built-in Phase 1 (task discovery) -> role Phase 2-4 -> built-in Phase 5 (report).` },
|
||
|
||
// 段 3: 任务上下文
|
||
{ type: "text", text: `## Task Context
|
||
task_id: ${task.id}
|
||
title: ${task.title}
|
||
description: ${task.description}
|
||
pipeline_phase: ${task.pipeline_phase}` },
|
||
|
||
// 段 4: 上游发现
|
||
{ type: "text", text: `## Upstream Context\n${task.prev_context}` }
|
||
]
|
||
})
|
||
```
|
||
|
||
### Supervisor Spawn Template
|
||
|
||
```javascript
|
||
// Spawn — 一次 (Phase 2 init, 对齐 Claude Code Agent({ subagent_type: "team-supervisor" }))
|
||
const supervisorId = spawn_agent({
|
||
agent_type: "tlv4_supervisor",
|
||
items: [
|
||
{ type: "text", text: `## Role Assignment
|
||
role: supervisor
|
||
role_spec: ${skillRoot}/roles/supervisor/role.md
|
||
session: ${sessionFolder}
|
||
session_id: ${sessionId}
|
||
requirement: ${requirement}
|
||
|
||
Read role_spec file (${skillRoot}/roles/supervisor/role.md) to load checkpoint definitions.
|
||
Init: load baseline context, report ready, go idle.
|
||
Wake cycle: orchestrator sends checkpoint requests via send_input.` }
|
||
]
|
||
})
|
||
```
|
||
|
||
### Supervisor Wake Template
|
||
|
||
```javascript
|
||
// 对齐 Claude Code SendMessage({ recipient: "supervisor", content })
|
||
send_input({
|
||
id: supervisorId,
|
||
items: [
|
||
{ type: "text", text: `## Checkpoint Request
|
||
task_id: ${task.id}
|
||
scope: [${task.deps}]
|
||
pipeline_progress: ${done}/${total} tasks completed` }
|
||
]
|
||
})
|
||
wait_agent({ ids: [supervisorId], timeout_ms: 300000 })
|
||
```
|
||
|
||
### Supervisor Shutdown
|
||
|
||
```javascript
|
||
// 对齐 Claude Code SendMessage({ type: "shutdown_request" })
|
||
close_agent({ id: supervisorId })
|
||
```
|
||
|
||
### Wave 执行引擎
|
||
|
||
```javascript
|
||
for (let wave = 1; wave <= maxWave; wave++) {
|
||
const state = JSON.parse(Read(`${sessionFolder}/tasks.json`))
|
||
const waveTasks = Object.values(state.tasks).filter(t => t.wave === wave && t.status === 'pending')
|
||
|
||
// 跳过依赖失败的任务
|
||
const executableTasks = []
|
||
for (const task of waveTasks) {
|
||
if (task.deps.some(d => ['failed', 'skipped'].includes(state.tasks[d]?.status))) {
|
||
state.tasks[task.id].status = 'skipped'
|
||
state.tasks[task.id].error = 'Dependency failed or skipped'
|
||
continue
|
||
}
|
||
executableTasks.push(task)
|
||
}
|
||
|
||
// 构建 prev_context
|
||
for (const task of executableTasks) {
|
||
const contextParts = task.context_from
|
||
.map(id => {
|
||
const prev = state.tasks[id]
|
||
if (prev?.status === 'completed' && prev.findings) {
|
||
return `[Task ${id}: ${prev.title}] ${prev.findings}`
|
||
}
|
||
try {
|
||
const disc = JSON.parse(Read(`${sessionFolder}/discoveries/${id}.json`))
|
||
return `[Task ${id}] ${disc.findings || JSON.stringify(disc.key_findings || '')}`
|
||
} catch { return null }
|
||
})
|
||
.filter(Boolean)
|
||
task.prev_context = contextParts.join('\n') || 'No previous context available'
|
||
}
|
||
|
||
// 分离普通任务和 CHECKPOINT 任务
|
||
const regularTasks = executableTasks.filter(t => !t.id.startsWith('CHECKPOINT-'))
|
||
const checkpointTasks = executableTasks.filter(t => t.id.startsWith('CHECKPOINT-'))
|
||
|
||
// 1) 并发 spawn 普通任务
|
||
const agentMap = [] // [{ agentId, taskId }]
|
||
for (const task of regularTasks) {
|
||
state.tasks[task.id].status = 'in_progress'
|
||
const agentId = spawn_agent({
|
||
agent_type: "tlv4_worker",
|
||
items: [
|
||
{ type: "text", text: `## Role Assignment
|
||
role: ${task.role}
|
||
role_spec: ${skillRoot}/roles/${task.role}/role.md
|
||
session: ${sessionFolder}
|
||
session_id: ${sessionId}
|
||
requirement: ${requirement}
|
||
inner_loop: ${hasInnerLoop(task.role)}` },
|
||
{ type: "text", text: `Read role_spec file (${skillRoot}/roles/${task.role}/role.md) to load Phase 2-4 domain instructions.
|
||
Execute built-in Phase 1 (task discovery) -> role Phase 2-4 -> built-in Phase 5 (report).` },
|
||
{ type: "text", text: `## Task Context
|
||
task_id: ${task.id}
|
||
title: ${task.title}
|
||
description: ${task.description}
|
||
pipeline_phase: ${task.pipeline_phase}` },
|
||
{ type: "text", text: `## Upstream Context\n${task.prev_context}` }
|
||
]
|
||
})
|
||
agentMap.push({ agentId, taskId: task.id })
|
||
}
|
||
|
||
// 2) 批量等待
|
||
if (agentMap.length > 0) {
|
||
wait_agent({ ids: agentMap.map(a => a.agentId), timeout_ms: 900000 })
|
||
}
|
||
|
||
// 3) 收集结果,合并到 tasks.json
|
||
for (const { agentId, taskId } of agentMap) {
|
||
try {
|
||
const disc = JSON.parse(Read(`${sessionFolder}/discoveries/${taskId}.json`))
|
||
state.tasks[taskId].status = disc.status || 'completed'
|
||
state.tasks[taskId].findings = disc.findings || ''
|
||
state.tasks[taskId].quality_score = disc.quality_score || null
|
||
state.tasks[taskId].error = disc.error || null
|
||
} catch {
|
||
state.tasks[taskId].status = 'failed'
|
||
state.tasks[taskId].error = 'No discovery file produced'
|
||
}
|
||
close_agent({ id: agentId })
|
||
}
|
||
|
||
// 4) 执行 CHECKPOINT 任务 (send_input 唤醒 supervisor)
|
||
for (const task of checkpointTasks) {
|
||
send_input({
|
||
id: supervisorId,
|
||
items: [
|
||
{ type: "text", text: `## Checkpoint Request
|
||
task_id: ${task.id}
|
||
scope: [${task.deps.join(', ')}]
|
||
pipeline_progress: ${completedCount}/${totalCount} tasks completed` }
|
||
]
|
||
})
|
||
wait_agent({ ids: [supervisorId], timeout_ms: 300000 })
|
||
|
||
// 读取 checkpoint 报告
|
||
try {
|
||
const report = Read(`${sessionFolder}/artifacts/${task.id}-report.md`)
|
||
const verdict = parseVerdict(report) // pass | warn | block
|
||
state.tasks[task.id].status = 'completed'
|
||
state.tasks[task.id].findings = `Verdict: ${verdict.decision} (score: ${verdict.score})`
|
||
state.tasks[task.id].supervision_verdict = verdict.decision
|
||
|
||
if (verdict.decision === 'block') {
|
||
const action = request_user_input({
|
||
questions: [{
|
||
question: `Checkpoint ${task.id} BLOCKED (score: ${verdict.score}). Choose action.`,
|
||
header: "Blocked",
|
||
id: "blocked_action",
|
||
options: [
|
||
{ label: "Override", description: "Proceed despite block" },
|
||
{ label: "Revise upstream", description: "Go back and fix issues" },
|
||
{ label: "Abort", description: "Stop pipeline" }
|
||
]
|
||
}]
|
||
})
|
||
// Handle user choice...
|
||
}
|
||
} catch {
|
||
state.tasks[task.id].status = 'failed'
|
||
state.tasks[task.id].error = 'Supervisor report not produced'
|
||
}
|
||
}
|
||
|
||
// 5) 持久化 tasks.json
|
||
Write(`${sessionFolder}/tasks.json`, JSON.stringify(state, null, 2))
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 步骤 3: 复制 Role 文档
|
||
|
||
从 `.claude/skills/team-lifecycle-v4/` 复制以下目录到 `.codex/skills/team-lifecycle-v4/`:
|
||
|
||
| 源 | 目标 | 说明 |
|
||
|----|------|------|
|
||
| `roles/` (全部) | `roles/` | 原样复制,coordinator 中平台相关调用需适配 |
|
||
| `specs/pipelines.md` | `specs/pipelines.md` | 原样复制 |
|
||
| `specs/quality-gates.md` | `specs/quality-gates.md` | 原样复制 |
|
||
| `specs/knowledge-transfer.md` | `specs/knowledge-transfer.md` | 需适配: team_msg -> discoveries/ 文件 |
|
||
| `templates/` (全部) | `templates/` | 原样复制 |
|
||
|
||
### Role 文档适配点
|
||
|
||
**`roles/coordinator/role.md`** — 需改写:
|
||
- Phase 2: `TeamCreate` -> session folder init
|
||
- Phase 3: `TaskCreate` -> tasks.json 写入
|
||
- Phase 4: `Agent(team-worker)` -> `spawn_agent(tlv4_worker)`
|
||
- monitor.md 中 callback 处理 -> `wait_agent` 结果处理
|
||
|
||
**`roles/coordinator/commands/monitor.md`** — 需改写:
|
||
- handleCallback -> wait_agent 结果解析
|
||
- handleSpawnNext -> spawn_agent 循环
|
||
- SendMessage(supervisor) -> send_input(supervisorId)
|
||
|
||
**`specs/knowledge-transfer.md`** — 需适配:
|
||
- `team_msg(operation="get_state")` -> 读 tasks.json
|
||
- `team_msg(type="state_update")` -> 写 discoveries/{id}.json
|
||
- 探索缓存协议保持不变
|
||
|
||
**其余 role 文档** (analyst, writer, planner, executor, tester, reviewer, supervisor):
|
||
- 核心执行逻辑不变
|
||
- `ccw cli` 调用保持不变 (CLI 工具两侧通用)
|
||
- 发现产出改为写 `discoveries/{task_id}.json` (替代 team_msg)
|
||
- `report_agent_job_result` 替代 team_msg state_update
|
||
|
||
---
|
||
|
||
## 步骤 4: Tasks Schema 改写
|
||
|
||
### 现状: tasks.csv
|
||
|
||
```csv
|
||
id,title,description,role,pipeline_phase,deps,context_from,exec_mode,wave,status,findings,quality_score,supervision_verdict,error
|
||
```
|
||
|
||
### 改写后: tasks.json
|
||
|
||
```json
|
||
{
|
||
"session_id": "tlv4-auth-system-20260324",
|
||
"pipeline": "full-lifecycle",
|
||
"requirement": "Design and implement user authentication system",
|
||
"created_at": "2026-03-24T10:00:00+08:00",
|
||
"supervision": true,
|
||
"completed_waves": [],
|
||
"active_agents": {},
|
||
"tasks": {
|
||
"RESEARCH-001": {
|
||
"title": "Domain research",
|
||
"description": "Explore domain, extract structured context...",
|
||
"role": "analyst",
|
||
"pipeline_phase": "research",
|
||
"deps": [],
|
||
"context_from": [],
|
||
"wave": 1,
|
||
"status": "pending",
|
||
"findings": null,
|
||
"quality_score": null,
|
||
"supervision_verdict": null,
|
||
"error": null
|
||
},
|
||
"DRAFT-001": {
|
||
"title": "Product brief",
|
||
"description": "Generate product brief from research context...",
|
||
"role": "writer",
|
||
"pipeline_phase": "product-brief",
|
||
"deps": ["RESEARCH-001"],
|
||
"context_from": ["RESEARCH-001"],
|
||
"wave": 2,
|
||
"status": "pending",
|
||
"findings": null,
|
||
"quality_score": null,
|
||
"supervision_verdict": null,
|
||
"error": null
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 发现文件: discoveries/{task_id}.json
|
||
|
||
```json
|
||
{
|
||
"task_id": "RESEARCH-001",
|
||
"worker": "RESEARCH-001",
|
||
"timestamp": "2026-03-24T10:15:00+08:00",
|
||
"type": "research",
|
||
"status": "completed",
|
||
"findings": "Explored domain: identified OAuth2+RBAC pattern, 5 integration points.",
|
||
"quality_score": null,
|
||
"supervision_verdict": null,
|
||
"error": null,
|
||
"data": {
|
||
"dimension": "domain",
|
||
"findings": ["Auth system needs OAuth2 + RBAC"],
|
||
"constraints": ["Must support SSO"],
|
||
"integration_points": ["User service API"]
|
||
},
|
||
"artifacts_produced": ["spec/discovery-context.json"]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 步骤 5: 删除旧文件
|
||
|
||
迁移完成后,删除 Codex 版中不再需要的文件:
|
||
|
||
| 文件 | 原因 |
|
||
|------|------|
|
||
| `agents/agent-instruction.md` | 角色逻辑在 roles/ 中,通用协议在 TOML developer_instructions 中 |
|
||
| `agents/requirement-clarifier.md` | 需求澄清逻辑合并到 coordinator/role.md Phase 1 |
|
||
| `agents/supervisor.md` | 迁移到 roles/supervisor/role.md |
|
||
| `agents/quality-gate.md` | 迁移到 roles/reviewer/role.md (QUALITY-* 任务处理) |
|
||
| `schemas/tasks-schema.md` (旧版) | 被 JSON schema 版本替代 |
|
||
|
||
---
|
||
|
||
## 实施顺序
|
||
|
||
| 步骤 | 内容 | 依赖 | 复杂度 |
|
||
|------|------|------|--------|
|
||
| **1** | 创建 2 个 TOML agent 定义 | 无 | 低 |
|
||
| **2** | 复制 roles/specs/templates 从 .claude/ | 无 | 低 (纯复制) |
|
||
| **3** | 改写 tasks-schema.md (CSV -> JSON) | 无 | 低 |
|
||
| **4** | 改写 SKILL.md 主编排 | 1, 2, 3 | 高 (核心工作) |
|
||
| **5** | 适配 coordinator role.md + commands/ | 4 | 中 |
|
||
| **6** | 适配 knowledge-transfer.md | 3 | 低 |
|
||
| **7** | 适配 worker role 文档 (发现产出方式) | 3 | 低 |
|
||
| **8** | 删除旧文件,清理 | 全部 | 低 |
|
||
|
||
步骤 1-3 可并行,步骤 4 是关键路径,步骤 5-7 依赖步骤 4 但可并行。
|