@hupan56/wlkj 3.4.2 → 3.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,622 +1,625 @@
1
- ---
2
- name: wl-task
3
- description: "任务管理 + 禅道同步。把 PRD 落成任务、看我的活、改状态、记工时(本地+禅道双写)。动作词: 建任务/我的任务/开始/完成/归档。自然语言问'我的Bug''看#301'直连禅道回答。"
4
- argument-hint: "[动作] [任务名或ID] 动作: create/list/show/start/finish/archive/rank/plan"
5
- auto-approve: true
6
- allowed-tools: [Read, Glob, Grep, Bash, Write, Edit]
7
- ---
8
-
9
- # /wl-task - 任务管理(产品的排期站)
10
-
11
- User input: $ARGUMENTS
12
-
13
- > **小而美 · 7 站之一。** 产品在这里把 PRD 落成可执行的任务,排好优先级和依赖,
14
- > 然后交接给设计/开发工位。
15
- > **模块契约**(输入/输出/校验):`.qoder/contracts/task.md`
16
- > **第一性原理:又快又准。** = 并行评估 RICE + 一次取全任务列表;准 = PRD 衔接不丢字段 + 三道检查。
17
-
18
- ## 🔧 环境自检(QoderWork 桌面端 vs Qoder IDE/CLI)
19
-
20
- **先确定仓库根 R**(QoderWork 桌面端工作目录不是仓库根,相对路径会失效):
21
- ```bash
22
- R=$(python ~/.qoderwork/repo_root.py 2>/dev/null || python3 ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
23
- PY=$(python --version >/dev/null 2>&1 && echo python || echo python3)
24
- ```
25
- > 后续脚本统一用 `$PY "$R/.qoder/scripts/orchestration/wlkj.py" <命令>`。
26
-
27
- ---
28
-
29
- ## 🔌 禅道模式判定(先于路由,决定数据走向)
30
-
31
- **禅道为唯一事实源**(已确认架构)。`/wl-task` 的 create/start/finish/reassign 优先走禅道 MCP;
32
- 本地 task.json 降级为离线缓存 + 增强字段层(RICE/依赖甘特这些禅道没有的本地能力全部保留,叠加在禅道任务上)。
33
-
34
- ### ⚠️ 归属安全(防改错队友任务,必须遵守)
35
-
36
- 禅道是**每人一个账号**(凭据在 `workspace/members/{我}/.private/secrets/zentao.env`,gitignore),归属靠 member.json 的 `zentao_account` 映射 + 工具默认行为兜底:
37
-
38
- - **看任务**:`my_workbench` / `list_my_tasks` / `list_execution_tasks` **默认只返回指派给我的**(assignedTo=我),不会看到队友。要看全队排期才传 `all=true`。本地流最快,优先走它。
39
- - **看我的活**:本地 `qw_mcp_call("my_workbench")` **优先**(快);本地不可得(无凭据/进程没起)再降级平台 `cap.mcp.call("get_my_zentao_panel")`(凭据走平台 per-user 配置,云防火墙时 `get_zentao_creds` 取凭据本机直连)。
40
- - **Bug→需求反查**:`cap.mcp.call("bug_trace", {"bug_id": X})` 查"这个 Bug 是哪个需求引入的"。
41
- - **空账号直接拒绝**:member.json 没配 `zentao_account` 时,"看我的"工具返回 `🚫 未识别禅道身份`(不再静默降级返回全队)。原样转述,引导 `/wl-init`。
42
- - **改任务前必校验**:start/finish/assign/update 拿到 task_id 后,**先 `get_task(id)` 看 assignedTo**:
43
- - `assignedTo == 我` → 正常执行;
44
- - `assignedTo == 别人` → **停下来问用户**"这是指派给 X 的任务,确认要改吗?",**不静默改**。
45
- - **建任务**:`create_task` `assignedTo` **默认指派给当前开发者**(从 member.json 的 zentao_account),显式传参才派别人。
46
- - 隔离红线见 `contracts/isolation.md`「禅道同步」。
47
-
48
- ### 0 步:按操作类型决定要不要探测禅道
49
-
50
- 禅道工具自带内网/凭据门(不通返回友好提示)。**读类操作直接调,省掉探测往返**:
51
-
52
- | 操作类型 | 探测? | 走法 |
53
- |---------|:----:|------|
54
- | **读**(看我的活/列任务/看详情/Bug状态) | ❌ 不探测 | 直接 `my_workbench`/`list_my_tasks`/`get_*`,门会兜底 |
55
- | **写**(create/start/finish/assign/resolve/改/删) | 探测 | `check_zentao_status`,或写前 `get_*` 确认归属时顺手探,避免写一半断网 |
56
- | 用户明说"离线/先存本地" | — | 即使可达也走本地模式 |
57
-
58
- - **✅ 可达** 走**禅道模式**(下方各动作的「禅道走法」)
59
- - **❌ 不可达 / 未配置** 走**本地模式**(下方各动作的原有 `task.py` 走法,离线兜底,不报错不阻塞)
60
-
61
- > 这套判定让禅道有就同步、没内网就静默回退 —— 符合 AGENTS.md「本地数据是唯一事实源、连接器是可选增强、断网不阻塞」原则。
62
- > 高频的"看我的活"现在是 **1 次调用直达**(旧版 probe + workbench 两次),思考链更短。
63
-
64
- ### 列表勾选式交互(禅道模式的统一范式)
65
-
66
- 禅道所有关联/选择类操作都用这个范式 —— **PM/组长回个数字就完事,零学习成本**:
67
-
68
- ```
69
- 用户说一句话 AI 调 list_* 工具搜候选 → 列 5-8 个带编号(附状态/数量)
70
- → 用户回编号(支持 1,2 多选) → AI 调 create/link/resolve 执行
71
- → 反馈 ✅ + 关联结果 → (可选)追问下一步
72
- ```
73
-
74
- **剧本示例(组长拆任务,跨执行多选):**
75
- ```
76
- 组长: 把 R-231 报价单导出拆成任务
77
- AI: 匹配到需求 #761 报价单导出 ✅
78
- 要拆到哪个执行?(最近 5 个):
79
- [1] 执行#73 车险v2后端 进行中 · 14任务
80
- [2] 执行#75 小程序改版 进行中 · 6任务
81
- [3] 执行#71 理赔优化 已完成
82
- 组长> 1,2 ← 支持多选 = 跨执行建任务
83
- AI: 建议: ①后端导出接口(6h) ②前端按钮(3h) ③测试用例(2h)
84
- 拆哪几个? (回 1,3 "全部")
85
- 组长> 全部
86
- AI: 6 个任务已建(2执行×3任务), 均挂 story=#761
87
- ```
88
-
89
- ---
90
-
91
- ## ⚠️ STEP 0: 动作路由(看 $ARGUMENTS 第一个词)
92
-
93
- **判断 `$ARGUMENTS` 第一个词,立刻决定走哪条线。没有动作词 → 语义自动归档。**
94
- > ⚠️ 边界:本命令管的是**本地任务工序**(建/查/排/归档)。禅道的**自然查询**("我的禅道任务""看下#301""Bug状态")
95
- > 不走本命令 —— AI 直接用禅道 MCP 工具自然回答(见 AGENTS.md「禅道自然查询」),别把随口问拉进工作流。
96
- > 禅道的**显式工序**(PRD 发禅道建需求、记工时、改任务状态)走下表 zentao 动作。
97
-
98
- | 用户说 / 第一个词 | 动作 | 跑什么 |
99
- |------------------|------|--------|
100
- | `create` / "建任务" / "加个任务" | **create** | `task.py create` |
101
- | `list` / "看任务" / "有哪些任务" | **list** | `task.py list` |
102
- | `show` / "看下 XXX" / "XXX 怎么样了" | **show** | `task.py show <name>` |
103
- | `start` / "开始做" / "启动 XXX" | **start** | `task.py start <name>` |
104
- | `finish` / "做完了" / "完成了" | **finish** | `task.py finish` |
105
- | `archive` / "归档" | **archive** | `task.py archive <name>` |
106
- | `rank` / "排优先级" / "排一下" | **rank** | RICE 评估(见下) |
107
- | `plan` / "排期" / "甘特图" / "排个日期" | **plan** | `task.py gantt` + set-due |
108
- | `sync` / "同步到禅道" | **sync** | 本地缓存 禅道(见 STEP 2) |
109
- | `zentao` / "PRD发禅道" / "记工时" / "改任务状态" | **zentao** | 显式禅道工序(见 STEP 2 工序剧本) |
110
- | **(无动作词)** | **语义自动归档** | 见下 |
111
-
112
- ### 语义自动归档(无动作词时,AI 按这句话的意图判断)
113
-
114
- - "PRD 写完了,建个任务吧" / "把刚才那个需求建成任务" → **create**(带 PRD 衔接)
115
- - "现在要做哪些?" / "接下来干啥" → **list --ready**(只看能立刻开始的)
116
- - "卡住的有哪些?" → **list --blocked**
117
- - "PRD 发禅道" / "记下今天 3 小时工时" → **zentao**(显式工序)
118
- - 拿不准意图**先 list 给全貌,再问用户要操作哪个**
119
- - ⚠️ "我的任务" / "看下我的禅道" / "Bug 状态" 这类**纯查询**——不归档进工作流,AI 直接自然回答
120
-
121
- ---
122
-
123
- ## STEP 1: 三道前置检查(任何动作前先做,保证健壮)
124
-
125
- 这三道是"鲁棒"的保障,防止脚本崩在半路、空数据吓到用户。
126
-
127
- > ①②相互独立 → **同一条消息并发探测**(一次回合发两个,别逐个等);③是执行 task.py 后才看退出码,归 STEP 2。
128
-
129
- ### 检查 ①|身份在不在(与②并发)
130
- ```bash
131
- python -c "import sys; sys.path.insert(0,r'$R/.qoder/scripts'); from foundation.core.paths import get_developer, get_repo_root; d=get_developer(get_repo_root()); print(d or 'MISSING')"
132
- ```
133
- - 有名字 → 继续。
134
- - `MISSING` **停下,提示**:"还没初始化身份,先跑 `/wl-init 你的名字 产品`"。不要继续往下跑(create 会把 assignee 写成空)。
135
-
136
- ### 检查 ②|tasks 目录在不在(与①并发)
137
- - 不存在 / 空 create 直接建;list/show/rank 等读操作 **友好提示** "还没有任务,要建一个吗?" 而不是报错。
138
-
139
- ### 检查 ③|脚本退出码(STEP 2 执行 task.py 后看)
140
- - `task.py` 任何子命令返回**非 0**,**先读 stderr 原样告诉用户**,不吞错。
141
- - 常见非 0:`4` = 权限不足(零信任 ACL,只有 creator/assignee/admin 能改任务);`1` = 任务不存在 / task.json 缺失。
142
- - 权限不足时提示:"这个任务不是你建的,找 `{assignee}` 或管理员操作。" 不要自己绕过 ACL。
143
-
144
- ---
145
-
146
- ## STEP 2: 各动作执行
147
-
148
- ### create —— 把需求落成任务(产品最高频)
149
-
150
- **A. PRD 衔接(准的保障:不丢字段)**
151
-
152
- 如果刚跑完 `/wl-prd`(或用户说"把刚才那个需求建成任务"),先确认 PRD 在哪:
153
- ```bash
154
- # 找最近的 PRD 草稿(当前开发者的)
155
- ls workspace/members/<developer>/drafts/REQ-*.md 2>/dev/null
156
- # 或已发布的
157
- ls data/docs/prd/REQ-*.md 2>/dev/null
158
- ```
159
- - 有 PRD → 从 PRD 提取**标题、模块、验收点**填进任务,**别让用户重述一遍**。
160
- PRD REQ-ID 记到 task.json 的 `tags` 里(如 `["REQ-2026-042"]`),方便双向追溯。
161
- - 无 PRD → 用 $ARGUMENTS 里用户给的一句话当标题。
162
-
163
- **B. 建任务**
164
-
165
- **禅道走法(禅道可达时优先):** 需求建在禅道、任务也拆在禅道,本地只缓存一份。
166
- ```bash
167
- # 1. 先确认需求在禅道(从 PRD 发布时已建, 或现在建):
168
- # create_story(product_id=<PID>, title=<PRD标题>, spec=<描述>)
169
- # ★ 建完需求后, 主动问 PM "要关联到哪个版本?(本周/未来)", 用
170
- # list_builds(project_id=<项目ID>) 列候选 link_story_to_build 关联
171
- # (详见下方 story 域「建需求后主动问版本」剧本)
172
- # 2. 组长拆任务(列表勾选式):
173
- # 先 list_executions() 列候选执行让用户选create_task(execution_id=<EID>,
174
- # name=<任务名>, assignedTo=<账号>, type=devel/test/design, estimate=<h>, story=<需求ID>)
175
- # 3. 同步一份到本地缓存(增强字段层用):
176
- # $PY "$R/.qoder/scripts/domain/task/task.py" create "<标题>" --priority <P0|P1|P2|P3>
177
- ```
178
-
179
- **本地走法(禅道不可达 / 用户要离线时):**
180
- ```bash
181
- $PY "$R/.qoder/scripts/domain/task/task.py" create "<标题>" --priority <P0|P1|P2|P3>
182
- ```
183
- - `--assignee` 不填则默认当前开发者(产品建的常指派给开发,可加 `--assignee 老李`)。
184
- - `--slug` 不填则自动从标题生成(中文标题也能生成,但建议给个英文 slug `--slug login-export`)。
185
- - 同名撞库时脚本会自动加后缀(creator 短标记),**不阻塞**,告诉用户最终名字即可。
186
- - **禅道恢复后**:用 `sync` 动作把本地缓存推回禅道(见 STEP 2 sync)。
187
-
188
- **C. 建完必做**
189
- 1. 告诉用户任务路径:`workspace/tasks/{MM-DD-slug}/`,里面有 `prd.md` 模板(若已有 PRD,可把 PRD 内容贴进去或引用)。
190
- 2. **待排期池 >3 个任务时**,主动建议:"要不要我跑个 rank 排下优先级?"
191
- 3. 自动同步(用户永不碰 git):
192
- ```bash
193
- $PY "$R/.qoder/scripts/domain/task/team_sync.py" push
194
- ```
195
-
196
- ### list —— 看任务(一次取全,别串行)
197
-
198
- **禅道走法(QoderWork 第一宿主,丝滑首选):**
199
- ```
200
- qw_mcp_call("my_workbench", {}) ← ★一行看全(任务+需求+Bug), 跨执行/产品聚合
201
- qw_mcp_call("list_my_tasks", {"status":"wait"}) ← 只看我未开始的任务
202
- qw_mcp_call("list_my_tasks", {"status":"doing"}) ← 只看我进行中的
203
- ```
204
- > QoderWork 用 `qw_mcp_call(toolName, arguments)` 直接调(工具已起进程,省去 list/get 两步元调用)。
205
- > **禁逐个 list_execution_tasks**(会漏早期执行里的任务,且每次都要 execution_id)。"我的全部" my_workbench/list_my_tasks。
206
-
207
- **本地走法(离线缓存 / 禅道不可达):**
208
- ```bash
209
- $PY "$R/.qoder/scripts/domain/task/task.py" list [--mine] [--status <s>] [--ready] [--blocked]
210
- ```
211
-
212
- | flag | 用途 |
213
- |------|------|
214
- | `--mine` | 只看我建/指派给我的(产品排自己负责的) |
215
- | `--status planning` / `in_progress` / `completed` | 按状态过滤 |
216
- | `--ready` | ⭐ 只看**能立刻开始的**(无未完成依赖)—— 回答"接下来做啥" |
217
- | `--blocked` | 只看**卡住的**(有未完成依赖)—— 排查阻塞链 |
218
-
219
- 输出渲染成表格(当前任务标 `*`):
220
- ```
221
- | * | 优先级 | 状态 | 任务 | 负责人 | 标题 | 备注(due/blocked) |
222
- ```
223
- **空数据兜底**:`No tasks found` 时别干巴巴贴脚本输出,补一句人话:
224
- - 全空 "还没有任何任务。刚写完 PRD 的话,说'建任务'我帮你落地。"
225
- - `--ready` 空 → "当前没有能立刻开始的任务,都在等依赖。要看卡住的吗?(--blocked)"
226
-
227
- ### show —— 看单个任务详情
228
-
229
- **禅道走法(QoderWork):** `qw_mcp_call("get_task", {"task_id": <禅道ID>})` ← 含工时预计/消耗/剩余/关联需求
230
- **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" show <任务名或目录>`
231
-
232
- 任务名支持模糊匹配(脚本内部 `resolve_task_dir` 会解析)。读完把关键字段(状态/优先级/assignee/due/依赖/prd路径)整理给用户。
233
-
234
- ### start —— 启动任务(移交开发工位的信号)
235
-
236
- **禅道走法(QoderWork,优先):** `qw_mcp_call("start_task", {"task_id": <禅道ID>, "left": <剩余工时h>})`
237
- **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" start <任务名>`
238
- - 状态 `planning → in_progress`,成为活跃任务(`.current-task`)。
239
- - 这是产品→开发的交接点:start 后开发就能 `/wl-code` 了。
240
- - **ACL**:只有 creator/assignee/admin start。被拒(返回 4)→ 提示找对应人。
241
- - **禅道+本地双写**:先 qw_mcp_call start_task 同步禅道,再 task.py start 同步本地缓存(双源一致)。
242
-
243
- ### reassign —— 改派任务负责人(多角色协作必需)
244
-
245
- **禅道走法(QoderWork):** `qw_mcp_call("assign_task", {"task_id": <禅道ID>, "account": <禅道账号>})`
246
- - 账号先用 `qw_mcp_call("list_users", {})` 列候选让用户选(**列表勾选式**)。
247
- **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" reassign <任务名> <新负责人>`
248
- - 场景:PM 建任务(assignee 默认是自己)后,要转给开发老李做。
249
- - 改派后新负责人才能 `start`/`finish`(零信任 ACL 按 assignee 判定)。
250
- - **ACL**:只有 **creator/admin** 能改派(assignee 自己不能转手甩锅)。被拒(返回 4)→ 提示找 creator。
251
-
252
- ### finish —— 完成当前任务
253
-
254
- **禅道走法(QoderWork,优先):** `qw_mcp_call("finish_task", {"task_id": <禅道ID>, "consumed": <本次消耗h>, "left": 0})`
255
- - 禅道要求填消耗工时,**先问用户"这次花了多少小时?"** 再调。
256
- **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" finish` → 然后 `team_sync.py push`
257
- - 活跃任务状态 `completed`,活跃指针清除。
258
- - 完成后提示:要不要 `archive` 归档?要不要看下个 `--ready` 的任务?
259
-
260
- ### archive —— 归档(移到 .qoder/archive/{YYYY-MM}/})
261
-
262
- **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" archive <任务名>`
263
- - 事务化:先 move 成功再改 status,move 失败原任务完好。
264
- - **自动清理孤儿依赖**:归档后,其它任务里对它的 `blocked_by/blocks/children/parent` 引用会被扫除(防止僵尸任务永远 blocked)。
265
- - 提示用户:归档后任务不再出现在 list 里,但归档目录可翻历史。
266
-
267
- ### zentao —— 禅道全域(需求/任务/Bug/规划工时,一站式)
268
-
269
- 禅道是完整的项目管理域(63 工具)。`/wl-task` 是唯一入口,按自然语言判断属于哪个域,直接调对应禅道 MCP 工具。
270
-
271
- **先按这句话的意图判断属于哪个域:**
272
-
273
- | 用户说的关键词 | 域 | 典型动作 |
274
- |---------------|-----|---------|
275
- | 需求/PRD/story/发布/计划/版本 | **需求(story)** | 建需求/看需求/改状态/关联 |
276
- | 任务/task/拆任务/开始/完成/工时 | **任务(task)** | 建/看/开始/完成/记工时 |
277
- | Bug/缺陷/问题/bug/解决/激活 | **Bug** | 提bug/看/确认/解决/关闭 |
278
- | 产品/项目/执行/迭代/发布/计划/工时统计 | **规划+工时** | 建产品/执行/版本, 看工时 |
279
-
280
- #### 域路由表(自然语言 工具调用,低 AI 调用:每句直达一个工具)
281
-
282
- **① 需求(story)域**
283
- ```
284
- 用户说 → 工具
285
- "PRD 发禅道"/"建需求" → create_story(product_id=?, title=<PRD标题>, spec=<描述>)
286
- (不知道产品先 list_products() 让用户选)
287
- "看下需求"/"有哪些需求" list_stories(product_id=?)
288
- "看需求#761详情" get_story_detail(story_id=761)
289
- "需求改状态"(draft→reviewing等) → change_story_status(story_id=?, status=?)
290
- "需求关联到计划/版本/执行" link_story_to_plan / link_story_to_build / link_story_to_execution
291
- "看需求的平台开发spec" find_specs_by_zentao(zentao_id=<story号>) ← 反查平台 spec(这需求已有开发规格?避免重复设计)
292
- ```
293
-
294
- #### 建需求后主动问版本(★ 你们的版本=项目级 build,PM 最高频关联动作)
295
-
296
- > 你们的「版本」是**项目级 build**(项目#9 下按周 `V+日期`,如 `V20260625`),不是产品级 plan/release。
297
- > 一个需求可能**同时进当前版本 + 未来版本**(一次发部分、下次发另一部分),所以**显式让 PM 多选**,不自动关联。
298
-
299
- **剧本(建需求/评审通过后,AI 主动列 build PM 勾选):**
300
- ```
301
- PM: 需求#761建好了
302
- AI: 要关联到哪个版本?(最近 6 build):
303
- [1] #22 V20260625 (本周, 已关联 9 个 bug)
304
- [2] #21 V20260618 (上周)
305
- [3] #16 V20260604
306
- [4] #11 V20260429 (已含 25 个需求)
307
- ...
308
- PM> 1,3 支持多选 = 一个需求进当前版本(本周发)+未来版本
309
- AI: 需求#761 已关联 build#22(本周) + #16(未来)
310
- ```
311
- - 候选用 `list_builds(project_id=<项目ID>)`(项目维度,最直观)或 `list_builds(product_id=<产品ID>)` 取最近 6 个,按时间倒序编号。
312
- - 关联调 `link_story_to_build(build_id=<选中>, stories=[<需求ID>])`,**逐个 build 调一次**(每个版本独立关联)。
313
- - **PM 也可主动触发**:"把#761 关联到本周版本" → AI 查本周 build 再关联。
314
- - 不知道项目/产品 ID 时先 `list_projects()` / `list_products()` 让用户勾选。
315
-
316
- **② 任务(task)域**(详见前面 create/start/finish 段,这里补查询)
317
- ```
318
- "看我的任务"/"这周干啥"/"我手头有什么活" → ★my_workbench() 或 list_my_tasks()
319
- 跨所有执行全量聚合, 不漏! 别用 list_execution_tasks 逐个查
320
- "看我未开始的" / "看进行中的" → list_my_tasks(status='wait') / list_my_tasks(status='doing')
321
- "看某执行的任务"(已知执行ID) list_execution_tasks(execution_id=?) ← 默认只看我的
322
- "看全队任务" → list_execution_tasks(execution_id=?, all=true)
323
- "拆需求为任务"/"建任务" create_task(execution_id=?, name=?, ...) ← 不填 assignedTo 默认指派给我
324
- "看任务#301" get_task(task_id=301) ← 含工时预计/消耗/剩余
325
- "任务改指派/改工时/改截止" update_task(task_id=?, ...) 或 assign_task(task_id=?, account=?)
326
- "记工时"/"今天干了3小时" log_effort(task_id=?, consumed=3, left=?)
327
- "看任务工时记录" list_effort(task_id=?)
328
- ```
329
-
330
- > **🚫 铁律: 问啥查啥, 禁止跨域冒充 (v3.1.5)**
331
- > 用户问「任务」就查 task 域(my_workbench/list_my_tasks/get_task), 问「需求」查 story 域, 问「Bug」查 bug 域。
332
- > **查不到必须如实说**「没有指派给你的任务」+ 贴查询证据, **绝不拿需求/Bug 顶替任务来回答**(用户实测踩坑:
333
- > 问任务却收到需求列表)。归属过滤空账号时工具会拒绝并提示 /wl-init, 把拒绝原样转述, 不绕过。
334
-
335
- **③ Bug 域**
336
- ```
337
- "提个Bug"/"发现个问题" → create_bug(product_id=?, title=?, openedBuild=?, severity=1-4)
338
- "看我的Bug"/"我的缺陷" → ★list_my_bugs() ← 跨所有产品全量, 不漏! 别逐产品查
339
- "看某产品的Bug"(已知产品ID) → list_bugs(product_id=?) ← 默认只看指派给我的
340
- "看全队的Bug" list_bugs(product_id=?, all=true)
341
- "Bug#101" get_bug(bug_id=101)
342
- "Bug解决了"/"修好了" resolve_bug(bug_id=?, resolution='fixed', resolvedBuild=?)
343
- "确认Bug"/"认领Bug" confirm_bug(bug_id=?)
344
- "Bug没改好,重开" activate_bug(bug_id=?)
345
- "关闭Bug" close_bug(bug_id=?)
346
- ```
347
-
348
- **④ 规划+工时域**
349
- ```
350
- "有哪些产品/项目/执行" → list_products() / list_projects() / list_executions()
351
- "建产品/项目/执行/版本/计划" → create_product / create_project / create_execution / create_build / create_plan
352
- "看执行#73" → get_execution(execution_id=73)
353
- "需求/Bug 关联计划/版本" link_story_to_plan / link_bug_to_plan / link_*_to_build
354
- ```
355
-
356
- #### 通用规则(所有禅道操作)
357
- - **不知道 ID 时**:先调 list_* 让用户勾选(列表勾选式,别让用户背 ID)。
358
- - **写操作默认归属校验**:工具层已强制——改任务/Bug 前自动校验 assignedTo==我,不是我的会拒绝并提示归属人。
359
- - **建任务/Bug 不填 assignedTo**:默认指派给当前开发者自己(防共享账号误派)。
360
- - **禅道不可达**:提示"内网未连,看本地缓存用 `/wl-task list`",不报错。
361
- - **🚫 跨域不冒充**:问任务查任务、问需求查需求。查不到如实说"没有"+贴证据,绝不拿别的域顶替。
362
-
363
- #### 🔌 宿主路由:禅道怎么调(显式工序操作用)
364
-
365
- > 仅适用于本文件的**显式工序操作**(建需求/建任务/改状态/记工时)。
366
- > 自然查询("我的任务""看下 #301")不走本表 —— AI 直接用禅道 MCP 工具自然回答,不拉入 /wl-task。
367
-
368
- | 宿主 | 调用入口 |
369
- |------|---------|
370
- | **QoderWork** | `qw_mcp_call("工具名", {参数})`(复用已起的 zentao 进程;工具名确定时直接 call,省 qw_mcp_list/get 两步) |
371
- | **Qoder IDE / Quest** | `mcp__qoder-zentao__工具名(参数)` |
372
- | **CLI / 其他** | `cap.mcp.call("工具名", {参数})` |
373
-
374
- > **★ cap.mcp.call 已进程复用**(`_REGISTRY` 单例 + `_pool` 缓存 transport,per server 不重复 spawn;实测同 server 第2次调用 3.07s→0.03s,100x)。QoderWork 下 `qw_mcp_call` 与 `cap.mcp.call` 等价(都复用进程),**统一推荐 `cap.mcp.call`**(跨宿主一致,零感知)。上表 qw_mcp_call 是 QoderWork 原生可选优化。
375
-
376
- #### 🎯 工序操作剧本(显式建/改,非查询)
377
-
378
- > 下面是 /wl-task 的**工序动作**(建需求/建任务/改状态/记工时),需要流程保障。
379
- > 查询类(看我的活、看进度)见 AGENTS.md「禅道自然查询」,直接用 MCP 工具回答,不在这里。
380
-
381
- **① 发需求到禅道(PM 产出动作 · 完整工序,别发完就停)**
382
- ```
383
- 触发: "PRD 发禅道"/"建需求"/"发布到禅道"/"把这个需求发上去"
384
-
385
- ★这是 PM 最重要的产出动作, 必须走完整工序(7步), 发完一句"✅已创建"就停 = 失败。
386
-
387
- AI 步骤:
388
- 1. PRD: 把本地 PRD.md 全文读出来 (背景/目标/功能详细/验收标准), 别只取标题
389
- 2. 确认产品: 不知道就 qw_mcp_call("list_products", {}) 勾选 (你们活跃: #1 ICS2.0/#12 车辆作业/#13 车辆异常)
390
- 3. ★建需求(spec 必填全文, 不是一句摘要):
391
- qw_mcp_call("create_story", {
392
- "product_id": <选>,
393
- "title": <PRD标题>,
394
- "spec": <★PRD全文: 背景+目标+功能详细说明+验收标准, 用 markdown>,
395
- "pri": <1-4, 默认3>,
396
- "category": "feature"
397
- })
398
- 拿到 story_id
399
-
400
- 4. ★提评审 (不跳过! draft 不评审等于没发):
401
- 告诉用户: "需求 #X 已建(draft), 建议提交评审让相关人过目"
402
- qw_mcp_call("change_story_status", {"story_id": <X>, "status": "reviewing"})
403
- 状态 draft→reviewing, 评审人会收到通知
404
-
405
- 5. ★关联计划 (你们版本=项目级 plan, 不关联计划的需求等于孤儿):
406
- qw_mcp_call("list_plans", {"product_id": <选>}) 取最近计划 列给用户:
407
- [1] #15 2026年6月迭代 [2] #14 2026年7月迭代
408
- 用户选 qw_mcp_call("link_story_to_plan", {"plan_id": <选>, "stories": [<story_id>]})
409
-
410
- 6. 关联版本 (build, 可选, 发布管理用):
411
- qw_mcp_call("list_builds", {"project_id": 9}) 取最近 build → 问用户要不要关联
412
- 要 → qw_mcp_call("link_story_to_build", {"build_id": <选>, "stories": [<story_id>]})
413
-
414
- 7. ★原型图 (有则处理, 无则跳过):
415
- 有本地原型文件 → qw_mcp_call("upload_file", {"file_path":"<原型路径>","object_type":"story","object_id":<X>})
416
- - 上传成功: 告诉用户附件已挂
417
- - 上传失败(禅道REST限制): 给降级方案(手动上传/截图嵌入), 并保留本地预览路径
418
- 无原型: 跳过此步
419
-
420
- 8. ★富反馈 (告诉用户做了什么, 不是一句"✅"):
421
- 汇总输出:
422
- ┌──────────────────────────────────┐
423
- 需求已发布到禅道 │
424
- │ 标题: <PRD标题> │
425
- │ ID: #<X> 地址: <URL>/story-view-<X>.html │
426
- 产品: <选的产品>
427
- 状态: reviewing(已提评审)
428
- 关联: 计划 #<P> 版本 #<B>(若有)
429
- 原型: 已上传/降级(见上)
430
- spec: 已填全文(<N>字)
431
- └──────────────────────────────────┘
432
- 追问: "评审过了要我关联到执行让研发拆任务吗? (list_executions 勾选)"
433
- ```
434
- ⚠️ 铁律: spec 必须是 PRD 全文(背景/目标/功能/验收), 不是一句话摘要。发完"✅已创建"就停、
435
- 不提评审不关联计划 = 半成品发布, 用户感受不到价值(实测踩坑)
436
-
437
-
438
- **② 拆任务/改状态/记工时(组长·研发工序)**
439
- ```
440
- 触发: "拆任务"/"把需求拆成任务"/"开始做#301"/"完成了"/"今天干了3小时"
441
- AI: 拆任务(列表勾选式, 跨执行多选):
442
- 1. qw_mcp_call("list_executions", {}) 列候选执行 → 用户选 [1,2]
443
- 2. AI 建议任务拆分模板(基于需求): "建议: ①后端接口(6h) ②前端(3h) ③测试(2h), 拆哪几个?"
444
- 3. 用户回"全部" → qw_mcp_call("create_task", {"execution_id":<eid>,"name":<名>,"assignedTo":<我>,"type":"devel","estimate":6,"story":<sid>}) 逐个建, ✅ 汇总
445
- 改状态(QoderWork qw_mcp_call):
446
- "开始做#301" qw_mcp_call("start_task", {"task_id":301,"left":6}) + 追问"要同步本地 task.py start 吗?"
447
- "完成了" → 先问"花了多少小时?" → qw_mcp_call("finish_task", {"task_id":301,"consumed":X,"left":0})
448
- "记工时" → qw_mcp_call("log_effort", {"task_id":301,"consumed":3,"left":<剩余>})
449
- ```
450
-
451
- ---
452
-
453
- ### sync —— 本地缓存同步回禅道(离线恢复后用)
454
-
455
- > v3.1.3 起有**独立脚本** `zentao_sync.py`,安全优先(不拉错/不覆盖队友)。
456
- > **隔离红线**见 `contracts/isolation.md`「禅道同步」。
457
-
458
- 离线时本地建的任务,禅道恢复后用脚本推上去。**默认 dry-run 预览,确认无误再加 `--apply`:**
459
-
460
- ```bash
461
- # 看同步状态(只读:本地哪些任务关联了禅道,哪些待推)
462
- $PY "$R/.qoder/scripts/domain/task/zentao_sync.py" status
463
-
464
- # 推送本地任务到禅道(先 dry-run 预览)
465
- $PY "$R/.qoder/scripts/domain/task/zentao_sync.py" push --execution_id <执行ID> # 预览
466
- $PY "$R/.qoder/scripts/domain/task/zentao_sync.py" push --execution_id <执行ID> --apply # 真推
467
-
468
- # 拉取禅道任务到本地(仅 assignedTo=我,队友的拉不进来)
469
- $PY "$R/.qoder/scripts/domain/task/zentao_sync.py" pull <执行ID> --apply
470
- ```
471
-
472
- **安全保证(脚本强制,不靠 AI 自觉):**
473
- - **幂等**:本地已有 `zentao_id` 的任务跳过(不重复建)。
474
- - **归属硬过滤**:拉取只取 `assignedTo == 我的禅道账号`;推送只推 `creator == 我` 的本地任务。
475
- - **防误改队友**:推送时若禅道同名任务的 `assignedTo` ≠ 我 → **拒绝更新**。
476
- - **归属映射**:本地 developer → 禅道 account 靠 `member.json` 的 `zentao_account` 字段,没配则拒绝同步(宁可不同步也不猜)。
477
- - **禅道不可达**:提示"内网未连,看本地缓存用 `/wl-task list`",退出码 5 不报错。
478
-
479
- > 提示:首次用前确认 `member.json` 配了 `zentao_account`(如 hupan56→hupan),见 `contracts/isolation.md`。
480
-
481
- ---
482
-
483
- ## STEP 3: RICE 排序(rank 动作 —— 产品的优先级决策)
484
-
485
- **RICE = Reach × Impact × Confidence ÷ Effort**
486
-
487
- ### 一次取全(别逐个问)
488
- ```bash
489
- $PY "$R/.qoder/scripts/domain/task/task.py" list --status planning
490
- ```
491
- 拿到所有待排任务,**一次性**评估,不要一个任务问四轮 R/I/C/E。
492
-
493
- ### 评估口径(让 AI 自己先估,再让用户校准)
494
- | 维度 | 怎么估 | 分值参考 |
495
- |------|--------|----------|
496
- | **R 触达** | 这个功能影响多少用户/订单?看 PRD 的用户画像 | 1=少 / 10=全员 |
497
- | **I 影响** | 对核心目标的贡献度?看 PRD 的目标指标 | 0.5=弱 / 3=强 |
498
- | **C 信心** | 有数据/竞品支撑?还是拍脑袋? | 50%=猜 / 100%=有据 |
499
- | **E 工作量** | 大概几个人周?看 PRD 的功能点数量 | 1=小 / 5=大 |
500
-
501
- ### 流程
502
- 1. **任务 >5 个或依赖复杂时,先 Planning 拆任务依赖树**(宿主 Planning Agent 模式):列出谁 block 谁、哪些能并行、哪些是关键路径,再排 RICE。无 Planning → 手动画依赖图(STEP 4 的 block 关系)降级,**不跳过依赖梳理**。
503
- 2. AI 对每个任务**先给一组 R/I/C/E 估算**(基于 PRD,标注信心来源)
504
- 3. 算出 RICE 分,排序,展示给用户:
505
- ```
506
- | 排名 | 任务 | R | I | C | E | RICE | 信心来源 |
507
- ```
508
- 4. 问:"要调整哪个的打分?" —— 用户改完,**写回每个 task.json 的 `rice` 字段**(Edit 工具),并据此重排优先级(`--priority`)。
509
- 5. push 同步。
510
-
511
- ### 排不动时的兜底
512
- - 任务都没有 PRD / PRD 太空 → 诚实说:"这几个缺 PRD 背景,R/I 估不准。要不先补 PRD,或直接用优先级(P0-P3)手动排?"
513
- - 只有 1 个任务 → 不用 RICE,直接确认优先级即可。
514
-
515
- ---
516
-
517
- ## STEP 4: 排期与依赖(plan 动作 —— 产品的甘特图)
518
-
519
- ### 设置截止日期
520
- ```bash
521
- $PY "$R/.qoder/scripts/orchestration/wlkj.py" task set-due <任务名> <YYYY-MM-DD>
522
- ```
523
-
524
- ### 依赖关系(谁挡谁)
525
- ```bash
526
- # A 被 B 阻塞(要等 B 完成)
527
- $PY "$R/.qoder/scripts/orchestration/wlkj.py" task block <A任务名> <B任务名>
528
- # 解除
529
- $PY "$R/.qoder/scripts/orchestration/wlkj.py" task unblock <A任务名> <B任务名>
530
- ```
531
- - **循环依赖自动检测**:脚本会 BFS 检查,发现环就拒绝(返回 1)。
532
- - 反向 `blocks` 关系自动维护(A B 阻塞时,B 的 blocks 自动加 A)。
533
-
534
- ### 看甘特图
535
- ```bash
536
- $PY "$R/.qoder/scripts/domain/task/team_sync.py" push
537
- $PY "$R/.qoder/scripts/orchestration/wlkj.py" task gantt [--weeks 4]
538
- ```
539
- - 只排有 `start_date`/`due_date` 的任务。
540
- - 没日期的任务 提示:"用 `set-due` 给任务加截止日期,甘特图才会显示。"
541
- - 自动标记逾期(⚠️)。
542
-
543
- ---
544
-
545
- ## STEP 5: 子任务(大需求拆解)
546
-
547
- ```bash
548
- # 把大任务 A 拆出子任务 B
549
- $PY "$R/.qoder/scripts/orchestration/wlkj.py" task add-subtask <A任务名> <B任务名>
550
- $PY "$R/.qoder/scripts/orchestration/wlkj.py" task remove-subtask <A任务名> <B任务名>
551
- ```
552
- - 父任务记录 `children`,子任务记录 `parent`。
553
- - 产品用:把一个大需求拆成"前端/后端/数据"几个子任务,各自指派。
554
-
555
- ---
556
-
557
- ## 🔒 三道质量检查(create 时全跑,其它动作跳过)
558
-
559
- 对齐 `/wl-prd` 的质量锁理念,保证任务不"飘":
560
-
561
- | 检查 | 查什么 | 不过怎么办 |
562
- |------|--------|-----------|
563
- | **检查① 衔接锁** | 有 PRD 的需求,task 的 tags 里有没有记 REQ-ID?标题有没有对上 PRD? | 补上 REQ-ID 关联,提示"可从 PRD 复制验收点到 task 的 prd.md" |
564
- | **检查② 归属锁** | assignee 是谁?产品建的任务指派给开发了吗? | assignee 为空 → 问"指派给谁?",或默认当前开发者但显式提示 |
565
- | **检查③ 阻塞锁** | 这个任务有没有依赖?依赖建对了吗?(产品常忘标依赖) | 列出可能的依赖任务,问"要不要 block 上?" |
566
-
567
- > 这三道不是阻塞门(任务照样能建),而是**建完后顺手提醒**,降低"建了任务但没人接/卡死/孤儿"的概率。
568
-
569
- ---
570
-
571
- ## Storage(存储规则)
572
-
573
- ```
574
- 任务目录 → workspace/tasks/{MM-DD-slug}/
575
- ├── task.json 元数据(状态/优先级/RICE/依赖/assignee)
576
- ├── prd.md PRD 模板(或引用已有 PRD)
577
- ├── implement.jsonl 实现上下文(开发填)
578
- └── check.jsonl 检查上下文(测试填)
579
- 归档 → .qoder/archive/{YYYY-MM}/{MM-DD-slug}/
580
- 活跃指针 .qoder/.current-task
581
- ```
582
-
583
- ---
584
-
585
- ## 与上下游的衔接(7 站流水线)
586
-
587
- ```
588
- prd ──create(带REQ-ID)──→ task ──start──→ code/test ──finish/archive──→ 归档
589
- ↑ ↓ rank ↓
590
- PRD 的验收点 → task.prd.md RICE 排序 开发接 /wl-code
591
- ```
592
-
593
- | 方向 | 衔接点 |
594
- |------|--------|
595
- | 上游 prd | create 时把 REQ-ID 记进 tags,PRD 验收点贴进 task 的 prd.md |
596
- | 下游 code/test | start 后,开发从 task 目录的 prd.md/spec 开始 `/wl-code` |
597
- | 自身 | finish/archive 后任务出 list,但留在归档可追溯 |
598
-
599
- ---
600
-
601
- ## 路由小结
602
-
603
- ```
604
- /wl-task ← list 给全貌(语义:看任务)
605
- /wl-task create <标题> ← 建任务(最高频)禅道可达时同步建禅道
606
- /wl-task list --ready ← 看能立刻做的
607
- /wl-task list --blocked ← 看卡住的
608
- /wl-task show <名> 看详情
609
- /wl-task start <名> 启动(移交开发)禅道可达时同步 start_task
610
- /wl-task reassign <名> <开发> 改派任务负责人(PM 建完转给开发,多角色协作)
611
- /wl-task finish 完成(禅道可达时同步 finish_task + 记工时)
612
- /wl-task archive <名> 归档
613
- /wl-task rank RICE 排优先级
614
- /wl-task plan 甘特图/排期/依赖
615
- /wl-task zentao 查禅道侧任务/需求(禅道模式专属)
616
- /wl-task sync本地缓存 → 禅道(离线恢复后用)
617
- ```
618
-
619
- > 完整执行规则见 `.qoder/skills/wl-task/SKILL.md`(Quest/QoderWork 自然语言入口)。
1
+ ---
2
+ n## 🚨 MCP工具优先,禁跑本地脚本
3
+ 所有知识查询走 mcp__qoder-knowledge-graph__ 工具(云平台),绝不跑 wlkj.py kg(本地kg空)。
4
+
5
+ name: wl-task
6
+ description: "任务管理 + 禅道同步。把 PRD 落成任务、看我的活、改状态、记工时(本地+禅道双写)。动作词: 建任务/我的任务/开始/完成/归档。自然语言问'我的Bug''看#301'直连禅道回答。"
7
+ argument-hint: "[动作] [任务名或ID] 动作: create/list/show/start/finish/archive/rank/plan"
8
+ auto-approve: true
9
+ allowed-tools: [Read, Glob, Grep, Bash, Write, Edit]
10
+ ---
11
+
12
+ # /wl-task - 任务管理(产品的排期站)
13
+
14
+ User input: $ARGUMENTS
15
+
16
+ > **小而美 · 7 站之一。** 产品在这里把 PRD 落成可执行的任务,排好优先级和依赖,
17
+ > 然后交接给设计/开发工位。
18
+ > **模块契约**(输入/输出/校验):`.qoder/contracts/task.md`
19
+ > **第一性原理:又快又准。** 快 = 并行评估 RICE + 一次取全任务列表;准 = PRD 衔接不丢字段 + 三道检查。
20
+
21
+ ## 🔧 环境自检(QoderWork 桌面端 vs Qoder IDE/CLI)
22
+
23
+ **先确定仓库根 R**(QoderWork 桌面端工作目录不是仓库根,相对路径会失效):
24
+ ```bash
25
+ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null || python3 ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
26
+ PY=$(python --version >/dev/null 2>&1 && echo python || echo python3)
27
+ ```
28
+ > 后续脚本统一用 `$PY "$R/.qoder/scripts/orchestration/wlkj.py" <命令>`。
29
+
30
+ ---
31
+
32
+ ## 🔌 禅道模式判定(先于路由,决定数据走向)
33
+
34
+ **禅道为唯一事实源**(已确认架构)。`/wl-task` create/start/finish/reassign 优先走禅道 MCP;
35
+ 本地 task.json 降级为离线缓存 + 增强字段层(RICE/依赖甘特这些禅道没有的本地能力全部保留,叠加在禅道任务上)。
36
+
37
+ ### ⚠️ 归属安全(防改错队友任务,必须遵守)
38
+
39
+ 禅道是**每人一个账号**(凭据在 `workspace/members/{我}/.private/secrets/zentao.env`,gitignore),归属靠 member.json `zentao_account` 映射 + 工具默认行为兜底:
40
+
41
+ - **看任务**:`my_workbench` / `list_my_tasks` / `list_execution_tasks` **默认只返回指派给我的**(assignedTo=我),不会看到队友。要看全队排期才传 `all=true`。本地流最快,优先走它。
42
+ - **看我的活**:本地 `qw_mcp_call("my_workbench")` **优先**(快);本地不可得(无凭据/进程没起)再降级平台 `cap.mcp.call("get_my_zentao_panel")`(凭据走平台 per-user 配置,云防火墙时 `get_zentao_creds` 取凭据本机直连)。
43
+ - **Bug→需求反查**:`cap.mcp.call("bug_trace", {"bug_id": X})` 查"这个 Bug 是哪个需求引入的"。
44
+ - **空账号直接拒绝**:member.json 没配 `zentao_account` 时,"看我的"工具返回 `🚫 未识别禅道身份`(不再静默降级返回全队)。原样转述,引导 `/wl-init`。
45
+ - **改任务前必校验**:start/finish/assign/update 拿到 task_id 后,**先 `get_task(id)` assignedTo**:
46
+ - `assignedTo == 我` → 正常执行;
47
+ - `assignedTo == 别人` → **停下来问用户**"这是指派给 X 的任务,确认要改吗?",**不静默改**。
48
+ - **建任务**:`create_task` `assignedTo` **默认指派给当前开发者**(从 member.json 的 zentao_account),显式传参才派别人。
49
+ - 隔离红线见 `contracts/isolation.md`「禅道同步」。
50
+
51
+ ### 第 0 步:按操作类型决定要不要探测禅道
52
+
53
+ 禅道工具自带内网/凭据门(不通返回友好提示)。**读类操作直接调,省掉探测往返**:
54
+
55
+ | 操作类型 | 探测? | 走法 |
56
+ |---------|:----:|------|
57
+ | **读**(看我的活/列任务/看详情/Bug状态) | ❌ 不探测 | 直接 `my_workbench`/`list_my_tasks`/`get_*`,门会兜底 |
58
+ | **写**(create/start/finish/assign/resolve/改/删) | 探测 | 先 `check_zentao_status`,或写前 `get_*` 确认归属时顺手探,避免写一半断网 |
59
+ | 用户明说"离线/先存本地" | | 即使可达也走本地模式 |
60
+
61
+ - **✅ 可达** 走**禅道模式**(下方各动作的「禅道走法」)
62
+ - **❌ 不可达 / 未配置** 走**本地模式**(下方各动作的原有 `task.py` 走法,离线兜底,不报错不阻塞)
63
+
64
+ > 这套判定让禅道有就同步、没内网就静默回退 —— 符合 AGENTS.md「本地数据是唯一事实源、连接器是可选增强、断网不阻塞」原则。
65
+ > ★ 高频的"看我的活"现在是 **1 次调用直达**(旧版 probe + workbench 两次),思考链更短。
66
+
67
+ ### 列表勾选式交互(禅道模式的统一范式)
68
+
69
+ 禅道所有关联/选择类操作都用这个范式 —— **PM/组长回个数字就完事,零学习成本**:
70
+
71
+ ```
72
+ 用户说一句话 → AI 调 list_* 工具搜候选 → 列 5-8 个带编号(附状态/数量)
73
+ → 用户回编号(支持 1,2 多选) → AI 调 create/link/resolve 执行
74
+ → 反馈 ✅ + 关联结果 → (可选)追问下一步
75
+ ```
76
+
77
+ **剧本示例(组长拆任务,跨执行多选):**
78
+ ```
79
+ 组长: R-231 报价单导出拆成任务
80
+ AI: 匹配到需求 #761 报价单导出
81
+ 要拆到哪个执行?(最近 5 个):
82
+ [1] 执行#73 车险v2后端 进行中 · 14任务
83
+ [2] 执行#75 小程序改版 进行中 · 6任务
84
+ [3] 执行#71 理赔优化 已完成
85
+ 组长> 1,2 ← 支持多选 = 跨执行建任务
86
+ AI: 建议: ①后端导出接口(6h) ②前端按钮(3h) ③测试用例(2h)
87
+ 拆哪几个? (回 1,3 或 "全部")
88
+ 组长> 全部
89
+ AI: ✅ 6 个任务已建(2执行×3任务), 均挂 story=#761
90
+ ```
91
+
92
+ ---
93
+
94
+ ## ⚠️ STEP 0: 动作路由(看 $ARGUMENTS 第一个词)
95
+
96
+ **判断 `$ARGUMENTS` 第一个词,立刻决定走哪条线。没有动作词 语义自动归档。**
97
+ > ⚠️ 边界:本命令管的是**本地任务工序**(建/查/排/归档)。禅道的**自然查询**("我的禅道任务""看下#301""Bug状态")
98
+ > 不走本命令 —— AI 直接用禅道 MCP 工具自然回答(见 AGENTS.md「禅道自然查询」),别把随口问拉进工作流。
99
+ > 禅道的**显式工序**(PRD 发禅道建需求、记工时、改任务状态)走下表 zentao 动作。
100
+
101
+ | 用户说 / 第一个词 | 动作 | 跑什么 |
102
+ |------------------|------|--------|
103
+ | `create` / "建任务" / "加个任务" | **create** | `task.py create` |
104
+ | `list` / "看任务" / "有哪些任务" | **list** | `task.py list` |
105
+ | `show` / "看下 XXX" / "XXX 怎么样了" | **show** | `task.py show <name>` |
106
+ | `start` / "开始做" / "启动 XXX" | **start** | `task.py start <name>` |
107
+ | `finish` / "做完了" / "完成了" | **finish** | `task.py finish` |
108
+ | `archive` / "归档" | **archive** | `task.py archive <name>` |
109
+ | `rank` / "排优先级" / "排一下" | **rank** | RICE 评估(见下) |
110
+ | `plan` / "排期" / "甘特图" / "排个日期" | **plan** | `task.py gantt` + set-due |
111
+ | `sync` / "同步到禅道" | **sync** | 本地缓存 → 禅道(见 STEP 2) |
112
+ | `zentao` / "PRD发禅道" / "记工时" / "改任务状态" | **zentao** | 显式禅道工序(见 STEP 2 工序剧本) |
113
+ | **(无动作词)** | **语义自动归档** | 见下 |
114
+
115
+ ### 语义自动归档(无动作词时,AI 按这句话的意图判断)
116
+
117
+ - "PRD 写完了,建个任务吧" / "把刚才那个需求建成任务" → **create**(带 PRD 衔接)
118
+ - "现在要做哪些?" / "接下来干啥" **list --ready**(只看能立刻开始的)
119
+ - "卡住的有哪些?" **list --blocked**
120
+ - "PRD 发禅道" / "记下今天 3 小时工时" → **zentao**(显式工序)
121
+ - 拿不准意图 → **先 list 给全貌,再问用户要操作哪个**
122
+ - ⚠️ "我的任务" / "看下我的禅道" / "Bug 状态" 这类**纯查询**——不归档进工作流,AI 直接自然回答
123
+
124
+ ---
125
+
126
+ ## STEP 1: 三道前置检查(任何动作前先做,保证健壮)
127
+
128
+ 这三道是"鲁棒"的保障,防止脚本崩在半路、空数据吓到用户。
129
+
130
+ > ①②相互独立 → **同一条消息并发探测**(一次回合发两个,别逐个等);③是执行 task.py 后才看退出码,归 STEP 2。
131
+
132
+ ### 检查 ①|身份在不在(与②并发)
133
+ ```bash
134
+ python -c "import sys; sys.path.insert(0,r'$R/.qoder/scripts'); from foundation.core.paths import get_developer, get_repo_root; d=get_developer(get_repo_root()); print(d or 'MISSING')"
135
+ ```
136
+ - 有名字 继续。
137
+ - `MISSING`**停下,提示**:"还没初始化身份,先跑 `/wl-init 你的名字 产品`"。不要继续往下跑(create 会把 assignee 写成空)。
138
+
139
+ ### 检查 ②|tasks 目录在不在(与①并发)
140
+ - 不存在 / create 直接建;list/show/rank 等读操作 → **友好提示** "还没有任务,要建一个吗?" 而不是报错。
141
+
142
+ ### 检查 ③|脚本退出码(STEP 2 执行 task.py 后看)
143
+ - `task.py` 任何子命令返回**非 0**,**先读 stderr 原样告诉用户**,不吞错。
144
+ - 常见非 0:`4` = 权限不足(零信任 ACL,只有 creator/assignee/admin 能改任务);`1` = 任务不存在 / task.json 缺失。
145
+ - 权限不足时提示:"这个任务不是你建的,找 `{assignee}` 或管理员操作。" 不要自己绕过 ACL。
146
+
147
+ ---
148
+
149
+ ## STEP 2: 各动作执行
150
+
151
+ ### create —— 把需求落成任务(产品最高频)
152
+
153
+ **A. PRD 衔接(准的保障:不丢字段)**
154
+
155
+ 如果刚跑完 `/wl-prd`(或用户说"把刚才那个需求建成任务"),先确认 PRD 在哪:
156
+ ```bash
157
+ # 找最近的 PRD 草稿(当前开发者的)
158
+ ls workspace/members/<developer>/drafts/REQ-*.md 2>/dev/null
159
+ # 或已发布的
160
+ ls data/docs/prd/REQ-*.md 2>/dev/null
161
+ ```
162
+ - 有 PRD → 从 PRD 提取**标题、模块、验收点**填进任务,**别让用户重述一遍**。
163
+ PRD 的 REQ-ID 记到 task.json 的 `tags` 里(如 `["REQ-2026-042"]`),方便双向追溯。
164
+ - 无 PRD → 用 $ARGUMENTS 里用户给的一句话当标题。
165
+
166
+ **B. 建任务**
167
+
168
+ **禅道走法(禅道可达时优先):** 需求建在禅道、任务也拆在禅道,本地只缓存一份。
169
+ ```bash
170
+ # 1. 先确认需求在禅道( PRD 发布时已建, 或现在建):
171
+ # create_story(product_id=<PID>, title=<PRD标题>, spec=<描述>)
172
+ # 建完需求后, 主动问 PM "要关联到哪个版本?(本周/未来)", 用
173
+ # list_builds(project_id=<项目ID>) 列候选link_story_to_build 关联
174
+ # (详见下方 story 域「建需求后主动问版本」剧本)
175
+ # 2. 组长拆任务(列表勾选式):
176
+ # list_executions() 列候选执行让用户选 create_task(execution_id=<EID>,
177
+ # name=<任务名>, assignedTo=<账号>, type=devel/test/design, estimate=<h>, story=<需求ID>)
178
+ # 3. 同步一份到本地缓存(增强字段层用):
179
+ # $PY "$R/.qoder/scripts/domain/task/task.py" create "<标题>" --priority <P0|P1|P2|P3>
180
+ ```
181
+
182
+ **本地走法(禅道不可达 / 用户要离线时):**
183
+ ```bash
184
+ $PY "$R/.qoder/scripts/domain/task/task.py" create "<标题>" --priority <P0|P1|P2|P3>
185
+ ```
186
+ - `--assignee` 不填则默认当前开发者(产品建的常指派给开发,可加 `--assignee 老李`)。
187
+ - `--slug` 不填则自动从标题生成(中文标题也能生成,但建议给个英文 slug 如 `--slug login-export`)。
188
+ - 同名撞库时脚本会自动加后缀(creator 短标记),**不阻塞**,告诉用户最终名字即可。
189
+ - **禅道恢复后**:用 `sync` 动作把本地缓存推回禅道(见 STEP 2 sync)。
190
+
191
+ **C. 建完必做**
192
+ 1. 告诉用户任务路径:`workspace/tasks/{MM-DD-slug}/`,里面有 `prd.md` 模板(若已有 PRD,可把 PRD 内容贴进去或引用)。
193
+ 2. **待排期池 >3 个任务时**,主动建议:"要不要我跑个 rank 排下优先级?"
194
+ 3. 自动同步(用户永不碰 git):
195
+ ```bash
196
+ $PY "$R/.qoder/scripts/domain/task/team_sync.py" push
197
+ ```
198
+
199
+ ### list —— 看任务(一次取全,别串行)
200
+
201
+ **禅道走法(QoderWork 第一宿主,丝滑首选):**
202
+ ```
203
+ qw_mcp_call("my_workbench", {}) ← ★一行看全(任务+需求+Bug), 跨执行/产品聚合
204
+ qw_mcp_call("list_my_tasks", {"status":"wait"}) 只看我未开始的任务
205
+ qw_mcp_call("list_my_tasks", {"status":"doing"}) ← 只看我进行中的
206
+ ```
207
+ > QoderWork 用 `qw_mcp_call(toolName, arguments)` 直接调(工具已起进程,省去 list/get 两步元调用)。
208
+ > **禁逐个 list_execution_tasks**(会漏早期执行里的任务,且每次都要 execution_id)。"我的全部"用 my_workbench/list_my_tasks。
209
+
210
+ **本地走法(离线缓存 / 禅道不可达):**
211
+ ```bash
212
+ $PY "$R/.qoder/scripts/domain/task/task.py" list [--mine] [--status <s>] [--ready] [--blocked]
213
+ ```
214
+
215
+ | flag | 用途 |
216
+ |------|------|
217
+ | `--mine` | 只看我建/指派给我的(产品排自己负责的) |
218
+ | `--status planning` / `in_progress` / `completed` | 按状态过滤 |
219
+ | `--ready` | ⭐ 只看**能立刻开始的**(无未完成依赖)—— 回答"接下来做啥" |
220
+ | `--blocked` | 只看**卡住的**(有未完成依赖)—— 排查阻塞链 |
221
+
222
+ 输出渲染成表格(当前任务标 `*`):
223
+ ```
224
+ | * | 优先级 | 状态 | 任务 | 负责人 | 标题 | 备注(due/blocked) |
225
+ ```
226
+ **空数据兜底**:`No tasks found` 时别干巴巴贴脚本输出,补一句人话:
227
+ - 全空 "还没有任何任务。刚写完 PRD 的话,说'建任务'我帮你落地。"
228
+ - `--ready` 空 → "当前没有能立刻开始的任务,都在等依赖。要看卡住的吗?(--blocked)"
229
+
230
+ ### show —— 看单个任务详情
231
+
232
+ **禅道走法(QoderWork):** `qw_mcp_call("get_task", {"task_id": <禅道ID>})` ← 含工时预计/消耗/剩余/关联需求
233
+ **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" show <任务名或目录>`
234
+
235
+ 任务名支持模糊匹配(脚本内部 `resolve_task_dir` 会解析)。读完把关键字段(状态/优先级/assignee/due/依赖/prd路径)整理给用户。
236
+
237
+ ### start —— 启动任务(移交开发工位的信号)
238
+
239
+ **禅道走法(QoderWork,优先):** `qw_mcp_call("start_task", {"task_id": <禅道ID>, "left": <剩余工时h>})`
240
+ **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" start <任务名>`
241
+ - 状态 `planning in_progress`,成为活跃任务(`.current-task`)。
242
+ - 这是产品→开发的交接点:start 后开发就能 `/wl-code` 了。
243
+ - **ACL**:只有 creator/assignee/admin 能 start。被拒(返回 4)→ 提示找对应人。
244
+ - **禅道+本地双写**:先 qw_mcp_call start_task 同步禅道,再 task.py start 同步本地缓存(双源一致)。
245
+
246
+ ### reassign —— 改派任务负责人(多角色协作必需)
247
+
248
+ **禅道走法(QoderWork):** `qw_mcp_call("assign_task", {"task_id": <禅道ID>, "account": <禅道账号>})`
249
+ - 账号先用 `qw_mcp_call("list_users", {})` 列候选让用户选(**列表勾选式**)。
250
+ **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" reassign <任务名> <新负责人>`
251
+ - 场景:PM 建任务(assignee 默认是自己)后,要转给开发老李做。
252
+ - 改派后新负责人才能 `start`/`finish`(零信任 ACL 按 assignee 判定)。
253
+ - **ACL**:只有 **creator/admin** 能改派(assignee 自己不能转手甩锅)。被拒(返回 4)→ 提示找 creator。
254
+
255
+ ### finish —— 完成当前任务
256
+
257
+ **禅道走法(QoderWork,优先):** `qw_mcp_call("finish_task", {"task_id": <禅道ID>, "consumed": <本次消耗h>, "left": 0})`
258
+ - 禅道要求填消耗工时,**先问用户"这次花了多少小时?"** 再调。
259
+ **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" finish` → 然后 `team_sync.py push`
260
+ - 活跃任务状态 `completed`,活跃指针清除。
261
+ - 完成后提示:要不要 `archive` 归档?要不要看下个 `--ready` 的任务?
262
+
263
+ ### archive —— 归档(移到 .qoder/archive/{YYYY-MM}/})
264
+
265
+ **本地走法:** `$PY "$R/.qoder/scripts/domain/task/task.py" archive <任务名>`
266
+ - 事务化:先 move 成功再改 status,move 失败原任务完好。
267
+ - **自动清理孤儿依赖**:归档后,其它任务里对它的 `blocked_by/blocks/children/parent` 引用会被扫除(防止僵尸任务永远 blocked)。
268
+ - 提示用户:归档后任务不再出现在 list 里,但归档目录可翻历史。
269
+
270
+ ### zentao —— 禅道全域(需求/任务/Bug/规划工时,一站式)
271
+
272
+ 禅道是完整的项目管理域(63 工具)。`/wl-task` 是唯一入口,按自然语言判断属于哪个域,直接调对应禅道 MCP 工具。
273
+
274
+ **先按这句话的意图判断属于哪个域:**
275
+
276
+ | 用户说的关键词 | | 典型动作 |
277
+ |---------------|-----|---------|
278
+ | 需求/PRD/story/发布/计划/版本 | **需求(story)** | 建需求/看需求/改状态/关联 |
279
+ | 任务/task/拆任务/开始/完成/工时 | **任务(task)** | 建/看/开始/完成/记工时 |
280
+ | Bug/缺陷/问题/bug/解决/激活 | **Bug** | 提bug/看/确认/解决/关闭 |
281
+ | 产品/项目/执行/迭代/发布/计划/工时统计 | **规划+工时** | 建产品/执行/版本, 看工时 |
282
+
283
+ #### 域路由表(自然语言 → 工具调用,低 AI 调用:每句直达一个工具)
284
+
285
+ **① 需求(story)域**
286
+ ```
287
+ 用户说 工具
288
+ "PRD 发禅道"/"建需求" create_story(product_id=?, title=<PRD标题>, spec=<描述>)
289
+ (不知道产品先 list_products() 让用户选)
290
+ "看下需求"/"有哪些需求" list_stories(product_id=?)
291
+ "看需求#761详情" get_story_detail(story_id=761)
292
+ "需求改状态"(draft→reviewing等) → change_story_status(story_id=?, status=?)
293
+ "需求关联到计划/版本/执行" → link_story_to_plan / link_story_to_build / link_story_to_execution
294
+ "看需求的平台开发spec" → find_specs_by_zentao(zentao_id=<story号>) ← 反查平台 spec(这需求已有开发规格?避免重复设计)
295
+ ```
296
+
297
+ #### 建需求后主动问版本(★ 你们的版本=项目级 build,PM 最高频关联动作)
298
+
299
+ > 你们的「版本」是**项目级 build**(项目#9 下按周 `V+日期`,如 `V20260625`),不是产品级 plan/release。
300
+ > 一个需求可能**同时进当前版本 + 未来版本**(一次发部分、下次发另一部分),所以**显式让 PM 多选**,不自动关联。
301
+
302
+ **剧本(建需求/评审通过后,AI 主动列 build PM 勾选):**
303
+ ```
304
+ PM: 需求#761建好了
305
+ AI: 要关联到哪个版本?(最近 6 个 build):
306
+ [1] #22 V20260625 (本周, 已关联 9 个 bug)
307
+ [2] #21 V20260618 (上周)
308
+ [3] #16 V20260604
309
+ [4] #11 V20260429 (已含 25 个需求)
310
+ ...
311
+ PM> 1,3 ← 支持多选 = 一个需求进当前版本(本周发)+未来版本
312
+ AI: 需求#761 已关联 build#22(本周) + #16(未来)
313
+ ```
314
+ - 候选用 `list_builds(project_id=<项目ID>)`(项目维度,最直观)或 `list_builds(product_id=<产品ID>)` 取最近 6 个,按时间倒序编号。
315
+ - 关联调 `link_story_to_build(build_id=<选中>, stories=[<需求ID>])`,**逐个 build 调一次**(每个版本独立关联)。
316
+ - **PM 也可主动触发**:"把#761 关联到本周版本" → AI 查本周 build 再关联。
317
+ - 不知道项目/产品 ID 时先 `list_projects()` / `list_products()` 让用户勾选。
318
+
319
+ **② 任务(task)域**(详见前面 create/start/finish 段,这里补查询)
320
+ ```
321
+ "看我的任务"/"这周干啥"/"我手头有什么活" → ★my_workbench() 或 list_my_tasks()
322
+ 跨所有执行全量聚合, 不漏! 别用 list_execution_tasks 逐个查
323
+ "看我未开始的" / "看进行中的" list_my_tasks(status='wait') / list_my_tasks(status='doing')
324
+ "看某执行的任务"(已知执行ID) list_execution_tasks(execution_id=?) ← 默认只看我的
325
+ "看全队任务" list_execution_tasks(execution_id=?, all=true)
326
+ "拆需求为任务"/"建任务" create_task(execution_id=?, name=?, ...) ← 不填 assignedTo 默认指派给我
327
+ "看任务#301" get_task(task_id=301) ← 含工时预计/消耗/剩余
328
+ "任务改指派/改工时/改截止" → update_task(task_id=?, ...) 或 assign_task(task_id=?, account=?)
329
+ "记工时"/"今天干了3小时" → log_effort(task_id=?, consumed=3, left=?)
330
+ "看任务工时记录" → list_effort(task_id=?)
331
+ ```
332
+
333
+ > **🚫 铁律: 问啥查啥, 禁止跨域冒充 (v3.1.5)**
334
+ > 用户问「任务」就查 task 域(my_workbench/list_my_tasks/get_task), 问「需求」查 story 域, 问「Bug」查 bug 域。
335
+ > **查不到必须如实说**「没有指派给你的任务」+ 贴查询证据, **绝不拿需求/Bug 顶替任务来回答**(用户实测踩坑:
336
+ > 问任务却收到需求列表)。归属过滤空账号时工具会拒绝并提示 /wl-init, 把拒绝原样转述, 不绕过。
337
+
338
+ **③ Bug 域**
339
+ ```
340
+ "提个Bug"/"发现个问题" create_bug(product_id=?, title=?, openedBuild=?, severity=1-4)
341
+ "看我的Bug"/"我的缺陷" ★list_my_bugs() ← 跨所有产品全量, 不漏! 别逐产品查
342
+ "看某产品的Bug"(已知产品ID) list_bugs(product_id=?) ← 默认只看指派给我的
343
+ "看全队的Bug" list_bugs(product_id=?, all=true)
344
+ "Bug#101" get_bug(bug_id=101)
345
+ "Bug解决了"/"修好了" resolve_bug(bug_id=?, resolution='fixed', resolvedBuild=?)
346
+ "确认Bug"/"认领Bug" → confirm_bug(bug_id=?)
347
+ "Bug没改好,重开" → activate_bug(bug_id=?)
348
+ "关闭Bug" → close_bug(bug_id=?)
349
+ ```
350
+
351
+ **④ 规划+工时域**
352
+ ```
353
+ "有哪些产品/项目/执行" list_products() / list_projects() / list_executions()
354
+ "建产品/项目/执行/版本/计划" → create_product / create_project / create_execution / create_build / create_plan
355
+ "看执行#73" → get_execution(execution_id=73)
356
+ "需求/Bug 关联计划/版本" → link_story_to_plan / link_bug_to_plan / link_*_to_build
357
+ ```
358
+
359
+ #### 通用规则(所有禅道操作)
360
+ - **不知道 ID 时**:先调 list_* 让用户勾选(列表勾选式,别让用户背 ID)。
361
+ - **写操作默认归属校验**:工具层已强制——改任务/Bug 前自动校验 assignedTo==我,不是我的会拒绝并提示归属人。
362
+ - **建任务/Bug 不填 assignedTo**:默认指派给当前开发者自己(防共享账号误派)。
363
+ - **禅道不可达**:提示"内网未连,看本地缓存用 `/wl-task list`",不报错。
364
+ - **🚫 跨域不冒充**:问任务查任务、问需求查需求。查不到如实说"没有"+贴证据,绝不拿别的域顶替。
365
+
366
+ #### 🔌 宿主路由:禅道怎么调(显式工序操作用)
367
+
368
+ > 仅适用于本文件的**显式工序操作**(建需求/建任务/改状态/记工时)。
369
+ > 自然查询("我的任务""看下 #301")不走本表 —— AI 直接用禅道 MCP 工具自然回答,不拉入 /wl-task。
370
+
371
+ | 宿主 | 调用入口 |
372
+ |------|---------|
373
+ | **QoderWork** | `qw_mcp_call("工具名", {参数})`(复用已起的 zentao 进程;工具名确定时直接 call,省 qw_mcp_list/get 两步) |
374
+ | **Qoder IDE / Quest** | `mcp__qoder-zentao__工具名(参数)` |
375
+ | **CLI / 其他** | `cap.mcp.call("工具名", {参数})` |
376
+
377
+ > **★ cap.mcp.call 已进程复用**(`_REGISTRY` 单例 + `_pool` 缓存 transport,per server 不重复 spawn;实测同 server 第2次调用 3.07s→0.03s,100x)。QoderWork 下 `qw_mcp_call` 与 `cap.mcp.call` 等价(都复用进程),**统一推荐 `cap.mcp.call`**(跨宿主一致,零感知)。上表 qw_mcp_call 是 QoderWork 原生可选优化。
378
+
379
+ #### 🎯 工序操作剧本(显式建/改,非查询)
380
+
381
+ > 下面是 /wl-task 的**工序动作**(建需求/建任务/改状态/记工时),需要流程保障。
382
+ > 查询类(看我的活、看进度)见 AGENTS.md「禅道自然查询」,直接用 MCP 工具回答,不在这里。
383
+
384
+ **① 发需求到禅道(PM 产出动作 · 完整工序,别发完就停)**
385
+ ```
386
+ 触发: "PRD 发禅道"/"建需求"/"发布到禅道"/"把这个需求发上去"
387
+
388
+ ★这是 PM 最重要的产出动作, 必须走完整工序(7步), 发完一句"✅已创建"就停 = 失败。
389
+
390
+ AI 步骤:
391
+ 1. 读 PRD: 把本地 PRD.md 全文读出来 (背景/目标/功能详细/验收标准), 别只取标题
392
+ 2. 确认产品: 不知道就 qw_mcp_call("list_products", {}) 勾选 (你们活跃: #1 ICS2.0/#12 车辆作业/#13 车辆异常)
393
+ 3. ★建需求(spec 必填全文, 不是一句摘要):
394
+ qw_mcp_call("create_story", {
395
+ "product_id": <选>,
396
+ "title": <PRD标题>,
397
+ "spec": <★PRD全文: 背景+目标+功能详细说明+验收标准, 用 markdown>,
398
+ "pri": <1-4, 默认3>,
399
+ "category": "feature"
400
+ })
401
+ 拿到 story_id
402
+
403
+ 4. ★提评审 (不跳过! draft 不评审等于没发):
404
+ 告诉用户: "需求 #X 已建(draft), 建议提交评审让相关人过目"
405
+ qw_mcp_call("change_story_status", {"story_id": <X>, "status": "reviewing"})
406
+ 状态 draftreviewing, 评审人会收到通知
407
+
408
+ 5. ★关联计划 (你们版本=项目级 plan, 不关联计划的需求等于孤儿):
409
+ qw_mcp_call("list_plans", {"product_id": <选>}) 取最近计划 → 列给用户:
410
+ [1] #15 2026年6月迭代 [2] #14 2026年7月迭代
411
+ 用户选 → qw_mcp_call("link_story_to_plan", {"plan_id": <选>, "stories": [<story_id>]})
412
+
413
+ 6. 关联版本 (build, 可选, 发布管理用):
414
+ qw_mcp_call("list_builds", {"project_id": 9}) 取最近 build → 问用户要不要关联
415
+ → qw_mcp_call("link_story_to_build", {"build_id": <选>, "stories": [<story_id>]})
416
+
417
+ 7. ★原型图 (有则处理, 无则跳过):
418
+ 有本地原型文件 → qw_mcp_call("upload_file", {"file_path":"<原型路径>","object_type":"story","object_id":<X>})
419
+ - 上传成功: 告诉用户附件已挂
420
+ - 上传失败(禅道REST限制): 给降级方案(手动上传/截图嵌入), 并保留本地预览路径
421
+ 无原型: 跳过此步
422
+
423
+ 8. ★富反馈 (告诉用户做了什么, 不是一句""):
424
+ 汇总输出:
425
+ ┌──────────────────────────────────┐
426
+ 需求已发布到禅道
427
+ 标题: <PRD标题>
428
+ ID: #<X> 地址: <URL>/story-view-<X>.html
429
+ 产品: <选的产品>
430
+ 状态: reviewing(已提评审)
431
+ │ 关联: 计划 #<P> 版本 #<B>(若有) │
432
+ 原型: 已上传/降级(见上)
433
+ │ spec: 已填全文(<N>字) │
434
+ └──────────────────────────────────┘
435
+ 追问: "评审过了要我关联到执行让研发拆任务吗? (list_executions 勾选)"
436
+ ```
437
+ ⚠️ 铁律: spec 必须是 PRD 全文(背景/目标/功能/验收), 不是一句话摘要。发完"✅已创建"就停、
438
+ 不提评审不关联计划 = 半成品发布, 用户感受不到价值(实测踩坑)。
439
+
440
+
441
+ **② 拆任务/改状态/记工时(组长·研发工序)**
442
+ ```
443
+ 触发: "拆任务"/"把需求拆成任务"/"开始做#301"/"完成了"/"今天干了3小时"
444
+ AI: 拆任务(列表勾选式, 跨执行多选):
445
+ 1. qw_mcp_call("list_executions", {}) 列候选执行 → 用户选 [1,2]
446
+ 2. AI 建议任务拆分模板(基于需求): "建议: ①后端接口(6h) ②前端(3h) ③测试(2h), 拆哪几个?"
447
+ 3. 用户回"全部" → qw_mcp_call("create_task", {"execution_id":<eid>,"name":<名>,"assignedTo":<我>,"type":"devel","estimate":6,"story":<sid>}) 逐个建, ✅ 汇总
448
+ 改状态(QoderWork qw_mcp_call):
449
+ "开始做#301" → qw_mcp_call("start_task", {"task_id":301,"left":6}) → ✅ + 追问"要同步本地 task.py start 吗?"
450
+ "完成了" → 先问"花了多少小时?" → qw_mcp_call("finish_task", {"task_id":301,"consumed":X,"left":0})
451
+ "记工时" → qw_mcp_call("log_effort", {"task_id":301,"consumed":3,"left":<剩余>})
452
+ ```
453
+
454
+ ---
455
+
456
+ ### sync —— 本地缓存同步回禅道(离线恢复后用)
457
+
458
+ > v3.1.3 起有**独立脚本** `zentao_sync.py`,安全优先(不拉错/不覆盖队友)。
459
+ > **隔离红线**见 `contracts/isolation.md`「禅道同步」。
460
+
461
+ 离线时本地建的任务,禅道恢复后用脚本推上去。**默认 dry-run 预览,确认无误再加 `--apply`:**
462
+
463
+ ```bash
464
+ # 看同步状态(只读:本地哪些任务关联了禅道,哪些待推)
465
+ $PY "$R/.qoder/scripts/domain/task/zentao_sync.py" status
466
+
467
+ # 推送本地任务到禅道(先 dry-run 预览)
468
+ $PY "$R/.qoder/scripts/domain/task/zentao_sync.py" push --execution_id <执行ID> # 预览
469
+ $PY "$R/.qoder/scripts/domain/task/zentao_sync.py" push --execution_id <执行ID> --apply # 真推
470
+
471
+ # 拉取禅道任务到本地(仅 assignedTo=我,队友的拉不进来)
472
+ $PY "$R/.qoder/scripts/domain/task/zentao_sync.py" pull <执行ID> --apply
473
+ ```
474
+
475
+ **安全保证(脚本强制,不靠 AI 自觉):**
476
+ - **幂等**:本地已有 `zentao_id` 的任务跳过(不重复建)。
477
+ - **归属硬过滤**:拉取只取 `assignedTo == 我的禅道账号`;推送只推 `creator == 我` 的本地任务。
478
+ - **防误改队友**:推送时若禅道同名任务的 `assignedTo` ≠ 我 → **拒绝更新**。
479
+ - **归属映射**:本地 developer → 禅道 account 靠 `member.json` `zentao_account` 字段,没配则拒绝同步(宁可不同步也不猜)。
480
+ - **禅道不可达**:提示"内网未连,看本地缓存用 `/wl-task list`",退出码 5 不报错。
481
+
482
+ > 提示:首次用前确认 `member.json` 配了 `zentao_account`(如 hupan56→hupan),见 `contracts/isolation.md`。
483
+
484
+ ---
485
+
486
+ ## STEP 3: RICE 排序(rank 动作 —— 产品的优先级决策)
487
+
488
+ **RICE = Reach × Impact × Confidence ÷ Effort**
489
+
490
+ ### 一次取全(别逐个问)
491
+ ```bash
492
+ $PY "$R/.qoder/scripts/domain/task/task.py" list --status planning
493
+ ```
494
+ 拿到所有待排任务,**一次性**评估,不要一个任务问四轮 R/I/C/E。
495
+
496
+ ### 评估口径(让 AI 自己先估,再让用户校准)
497
+ | 维度 | 怎么估 | 分值参考 |
498
+ |------|--------|----------|
499
+ | **R 触达** | 这个功能影响多少用户/订单?看 PRD 的用户画像 | 1=少 / 10=全员 |
500
+ | **I 影响** | 对核心目标的贡献度?看 PRD 的目标指标 | 0.5=弱 / 3=强 |
501
+ | **C 信心** | 有数据/竞品支撑?还是拍脑袋? | 50%=猜 / 100%=有据 |
502
+ | **E 工作量** | 大概几个人周?看 PRD 的功能点数量 | 1=小 / 5=大 |
503
+
504
+ ### 流程
505
+ 1. ★ **任务 >5 个或依赖复杂时,先 Planning 拆任务依赖树**(宿主 Planning Agent 模式):列出谁 block 谁、哪些能并行、哪些是关键路径,再排 RICE。无 Planning → 手动画依赖图(STEP 4 的 block 关系)降级,**不跳过依赖梳理**。
506
+ 2. AI 对每个任务**先给一组 R/I/C/E 估算**(基于 PRD,标注信心来源)
507
+ 3. 算出 RICE 分,排序,展示给用户:
508
+ ```
509
+ | 排名 | 任务 | R | I | C | E | RICE | 信心来源 |
510
+ ```
511
+ 4. 问:"要调整哪个的打分?" —— 用户改完,**写回每个 task.json 的 `rice` 字段**(Edit 工具),并据此重排优先级(`--priority`)。
512
+ 5. push 同步。
513
+
514
+ ### 排不动时的兜底
515
+ - 任务都没有 PRD / PRD 太空 → 诚实说:"这几个缺 PRD 背景,R/I 估不准。要不先补 PRD,或直接用优先级(P0-P3)手动排?"
516
+ - 只有 1 个任务 → 不用 RICE,直接确认优先级即可。
517
+
518
+ ---
519
+
520
+ ## STEP 4: 排期与依赖(plan 动作 —— 产品的甘特图)
521
+
522
+ ### 设置截止日期
523
+ ```bash
524
+ $PY "$R/.qoder/scripts/orchestration/wlkj.py" task set-due <任务名> <YYYY-MM-DD>
525
+ ```
526
+
527
+ ### 依赖关系(谁挡谁)
528
+ ```bash
529
+ # A B 阻塞(要等 B 完成)
530
+ $PY "$R/.qoder/scripts/orchestration/wlkj.py" task block <A任务名> <B任务名>
531
+ # 解除
532
+ $PY "$R/.qoder/scripts/orchestration/wlkj.py" task unblock <A任务名> <B任务名>
533
+ ```
534
+ - **循环依赖自动检测**:脚本会 BFS 检查,发现环就拒绝(返回 1)。
535
+ - 反向 `blocks` 关系自动维护(A 被 B 阻塞时,B 的 blocks 自动加 A)。
536
+
537
+ ### 看甘特图
538
+ ```bash
539
+ $PY "$R/.qoder/scripts/domain/task/team_sync.py" push
540
+ $PY "$R/.qoder/scripts/orchestration/wlkj.py" task gantt [--weeks 4]
541
+ ```
542
+ - 只排有 `start_date`/`due_date` 的任务。
543
+ - 没日期的任务 → 提示:"用 `set-due` 给任务加截止日期,甘特图才会显示。"
544
+ - 自动标记逾期(⚠️)。
545
+
546
+ ---
547
+
548
+ ## STEP 5: 子任务(大需求拆解)
549
+
550
+ ```bash
551
+ # 把大任务 A 拆出子任务 B
552
+ $PY "$R/.qoder/scripts/orchestration/wlkj.py" task add-subtask <A任务名> <B任务名>
553
+ $PY "$R/.qoder/scripts/orchestration/wlkj.py" task remove-subtask <A任务名> <B任务名>
554
+ ```
555
+ - 父任务记录 `children`,子任务记录 `parent`。
556
+ - 产品用:把一个大需求拆成"前端/后端/数据"几个子任务,各自指派。
557
+
558
+ ---
559
+
560
+ ## 🔒 三道质量检查(create 时全跑,其它动作跳过)
561
+
562
+ 对齐 `/wl-prd` 的质量锁理念,保证任务不"飘":
563
+
564
+ | 检查 | 查什么 | 不过怎么办 |
565
+ |------|--------|-----------|
566
+ | **检查① 衔接锁** | 有 PRD 的需求,task 的 tags 里有没有记 REQ-ID?标题有没有对上 PRD? | 补上 REQ-ID 关联,提示"可从 PRD 复制验收点到 task 的 prd.md" |
567
+ | **检查② 归属锁** | assignee 是谁?产品建的任务指派给开发了吗? | assignee 为空 → 问"指派给谁?",或默认当前开发者但显式提示 |
568
+ | **检查③ 阻塞锁** | 这个任务有没有依赖?依赖建对了吗?(产品常忘标依赖) | 列出可能的依赖任务,问"要不要 block 上?" |
569
+
570
+ > 这三道不是阻塞门(任务照样能建),而是**建完后顺手提醒**,降低"建了任务但没人接/卡死/孤儿"的概率。
571
+
572
+ ---
573
+
574
+ ## Storage(存储规则)
575
+
576
+ ```
577
+ 任务目录 workspace/tasks/{MM-DD-slug}/
578
+ ├── task.json 元数据(状态/优先级/RICE/依赖/assignee)
579
+ ├── prd.md PRD 模板(或引用已有 PRD)
580
+ ├── implement.jsonl 实现上下文(开发填)
581
+ └── check.jsonl 检查上下文(测试填)
582
+ 归档 → .qoder/archive/{YYYY-MM}/{MM-DD-slug}/
583
+ 活跃指针 → .qoder/.current-task
584
+ ```
585
+
586
+ ---
587
+
588
+ ## 与上下游的衔接(7 站流水线)
589
+
590
+ ```
591
+ prd ──create(带REQ-ID)──→ task ──start──→ code/test ──finish/archive──→ 归档
592
+ ↑ ↓ rank ↓
593
+ PRD 的验收点 task.prd.md RICE 排序 开发接 /wl-code
594
+ ```
595
+
596
+ | 方向 | 衔接点 |
597
+ |------|--------|
598
+ | 上游 prd | create 时把 REQ-ID 记进 tags,PRD 验收点贴进 task 的 prd.md |
599
+ | 下游 code/test | start 后,开发从 task 目录的 prd.md/spec 开始 `/wl-code` |
600
+ | 自身 | finish/archive 后任务出 list,但留在归档可追溯 |
601
+
602
+ ---
603
+
604
+ ## 路由小结
605
+
606
+ ```
607
+ /wl-task list 给全貌(语义:看任务)
608
+ /wl-task create <标题> 建任务(最高频)禅道可达时同步建禅道
609
+ /wl-task list --ready 看能立刻做的
610
+ /wl-task list --blocked 看卡住的
611
+ /wl-task show <名> 看详情
612
+ /wl-task start <名> 启动(移交开发)禅道可达时同步 start_task
613
+ /wl-task reassign <名> <开发> 改派任务负责人(PM 建完转给开发,多角色协作)
614
+ /wl-task finish 完成(禅道可达时同步 finish_task + 记工时)
615
+ /wl-task archive <名> 归档
616
+ /wl-task rankRICE 排优先级
617
+ /wl-task plan ← 甘特图/排期/依赖
618
+ /wl-task zentao ← 查禅道侧任务/需求(禅道模式专属)
619
+ /wl-task sync ← 本地缓存 → 禅道(离线恢复后用)
620
+ ```
621
+
622
+ > 完整执行规则见 `.qoder/skills/wl-task/SKILL.md`(Quest/QoderWork 自然语言入口)。
620
623
 
621
624
 
622
625
  ## 🚨 接地铁律(防幻觉/保真)