@prd-improve/cli 1.0.0 → 1.1.0

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.
@@ -0,0 +1,514 @@
1
+ # =============================================================================
2
+ # 范例 PRD:批量导入历史 PRD 文件
3
+ # -----------------------------------------------------------------------------
4
+ # 这是一份完整填写的 PRD 样例,对应 templates/prd.yaml (schema_version v0.2)。
5
+ #
6
+ # 场景:PRD Improve 团队自身的 dogfood —— PM 把散落在 Notion / 飞书 / Wiki
7
+ # 的旧 PRD 一次性迁入 .prd/features/<slug>/ 目录,不必逐个手工重写。
8
+ #
9
+ # 本范例用途:
10
+ # 1. 给 Skill 作为"完整 PRD 长什么样"的金标准
11
+ # 2. 给 PM 看一份参考,知道每个字段如何填写
12
+ # 3. 反向检验 templates/prd.yaml 的字段是否够用、好用
13
+ #
14
+ # 使用须知(AI 和 PM 都要注意):
15
+ # - 本范例是"满填示例",展示所有模块都能填出实质内容的状态。
16
+ # 真实 PRD 若规模较小,可按需裁剪(例如纯后台功能可 ui_interaction: []、
17
+ # scenarios 少写几条、data_and_api 子类可留空),不必强行填满。
18
+ # - 具体人名 / 邮箱 / 日期 / 样例数据(如 pm-alice@example.com、
19
+ # 2026-04-29、具体 slug 正则等)仅作演示。AI 生成新 PRD 时务必用真实值
20
+ # 替换占位,不要照搬本文件的具体值。
21
+ # - [CLI 管] 标记的字段(如 meta.lifecycle.updated_at)本范例按规定留空,
22
+ # 由 CLI 在写操作时自动填充。PM 不要手填。
23
+ #
24
+ # 注:完整填写后的 PRD 可以删除大部分 [必填] / [PM 填] 等标记注释,
25
+ # 仅保留 "-- 中文名" 便于读者识别 ID 前缀含义。本范例就是这种状态。
26
+ # =============================================================================
27
+
28
+
29
+ schema_version: "v0.2"
30
+
31
+
32
+ meta:
33
+ team: "prd-improve"
34
+ project: "prd-improve"
35
+ feature: "import-history-prds"
36
+ title: "批量导入历史 PRD 文件"
37
+ summary: "支持 PM 一次性上传 ≤ 50 份 MD/TXT 格式旧 PRD,自动建立 feature 目录并生成 working 版 prd.yaml"
38
+ owner: "pm-alice@example.com"
39
+ tags: ["import", "migration", "onboarding"]
40
+ created_at: "2026-04-22"
41
+ source_materials:
42
+ - type: "meeting_notes"
43
+ title: "2026-04 季度 PM 工具对齐会"
44
+ url: "https://meet.example.com/docs/2026-04-quarterly-pm-sync"
45
+ excerpt: "PM 共识:迁移到 PRD Improve 的最大阻力是历史 PRD 数量大,需要能一次性批量上传"
46
+ captured_at: "2026-04-15"
47
+ - type: "ai_dialog"
48
+ title: "Claude 对话 · 批量迁移场景拆解"
49
+ url: ""
50
+ excerpt: "聊到了格式识别、重复 slug 冲突、失败回报等关键边界问题"
51
+ captured_at: "2026-04-18"
52
+
53
+ lifecycle:
54
+ version: "working"
55
+ status: "draft"
56
+ updated_at: "" # CLI 首次写入时填充;PM 不手填
57
+
58
+
59
+ background_and_goals:
60
+ background: |
61
+ PRD Improve 要让 PM 把需求规范化、版本化、可追溯。但真实的 PM 往往已经在 Notion / 飞书 /
62
+ Wiki / 本地 Markdown 中积累了大量旧 PRD。如果只能从空白页开始,早期价值很弱;如果要求 PM
63
+ 逐个手工重写,迁移成本足以劝退。因此需要一个"批量导入"能力,让 PM 一次性把历史 PRD 拖进
64
+ 来,系统自动识别 feature 并建立目录结构。本期只做 MD / TXT 两种最通用的文本格式,为 MVP
65
+ 试水。
66
+ goals:
67
+ - "PM 能一次性导入 ≤ 50 份 MD/TXT 格式的旧 PRD,5 分钟内完成,支持部分失败隔离"
68
+ - "每份旧 PRD 自动生成对应 .prd/features/<slug>/working/prd.yaml 骨架,至少填入 meta 和 background"
69
+ - "失败文件能被精准定位(原因 + 原文件名 + 可下载 CSV)供 PM 后续处理"
70
+
71
+
72
+ users_and_roles:
73
+ - role: "产品经理"
74
+ description: "本 workspace 的 PM,拥有 features 的写权限。多数情况下是本功能的发起者"
75
+ typical_goals:
76
+ - "把历史 PRD 一次性迁入新系统,不做大量重复劳动"
77
+ - "清楚知道哪些文件导入成功、哪些失败、失败原因"
78
+ - "失败的文件能方便地修复后重导"
79
+ - role: "工作空间管理员"
80
+ description: "workspace 级管理者,能查看所有项目、所有成员的导入任务,负责异常处理与审计"
81
+ typical_goals:
82
+ - "监控 workspace 内异常的导入任务(长时间卡顿、失败率过高)"
83
+ - "排查特定用户的导入问题"
84
+
85
+
86
+ in_scope:
87
+ - id: "S-1" # -- 范围项
88
+ name: "批量上传入口"
89
+ summary: "PM 可通过 workspace 首页的'导入历史 PRD'按钮,打开上传面板,支持拖拽 / 多选 ≤ 50 份文件"
90
+ - id: "S-2"
91
+ name: "MD / TXT 格式解析"
92
+ summary: "系统能识别 .md / .markdown / .txt 三种扩展名,读取文件前 N 行作为 title / summary,生成符合 schema v0.2 的 prd.yaml 骨架"
93
+ - id: "S-3"
94
+ name: "slug 自动生成"
95
+ summary: "从文件名或内容首个标题生成 lowercase-hyphen 格式的 feature slug,确保符合路径规范与正则要求"
96
+ - id: "S-4"
97
+ name: "冲突检测与提示"
98
+ summary: "若生成的 slug 与已有 feature 冲突,或同批次内 slug 重复,在任务详情中标注并让用户后续处理"
99
+ - id: "S-5"
100
+ name: "失败明细与下载"
101
+ summary: "任务完成后显示失败文件清单(原文件名 + 错误码 + 原因),支持一键下载 CSV"
102
+
103
+
104
+ out_of_scope:
105
+ - item: "Word (.docx) / PDF 格式支持"
106
+ reason: "本期聚焦 MVP 验证。Word 样式和嵌入对象解析复杂,PDF 需要 OCR,都不在本期承诺内"
107
+ - item: "从 Notion / 飞书 / Confluence 等第三方服务 API 直接抓取"
108
+ reason: "需要配置 OAuth、处理第三方权限、应对 rate limit,单独立项更合适"
109
+ - item: "AI 深度解析旧 PRD 内容,自动填充 scenarios / edge_cases / business_rules"
110
+ reason: "本期只填 meta + background,其他模块由 PM 后续用 Skill 逐步补齐。否则 AI 幻觉风险大"
111
+ - item: "追加模式(向已存在的 feature 追加或合并内容)"
112
+ reason: "追加语义复杂(替换?合并?变更单?)需独立设计,本期只做全新导入"
113
+ - item: "导入后自动冻结或上传后端"
114
+ reason: "这些由 CLI 另行提供,本功能只负责建立 working 草稿"
115
+
116
+
117
+ entities:
118
+ - id: "EN-1" # -- 实体
119
+ name: "导入任务 (ImportTask)"
120
+ description: "一次批量导入的整体记录,追踪从提交到完成的全生命周期。一次用户操作对应一个 ImportTask"
121
+ # 仅列业务关键字段。id / created_at / updated_at / completed_at 等通用技术字段
122
+ # 由开发按 convention 补,不在 PRD 中重复声明。
123
+ fields:
124
+ - name: "initiator"
125
+ type: "reference<用户>"
126
+ required: true
127
+ notes: "发起者;state_permissions 与 SEC-2 的可见性判定基于此字段"
128
+ - name: "workspace_id"
129
+ type: "reference<workspace>"
130
+ required: true
131
+ notes: "任务所属 workspace,跨 workspace 不可见"
132
+ - name: "file_count"
133
+ type: "int"
134
+ required: true
135
+ notes: "本次提交的文件总数,创建后不变(用于幂等键和进度展示)"
136
+ - name: "succeeded_count"
137
+ type: "int"
138
+ required: true
139
+ notes: "成功文件数,动态更新;终态用于决定 partial_succeeded vs succeeded"
140
+ - name: "failed_count"
141
+ type: "int"
142
+ required: true
143
+ notes: "失败文件数,动态更新;终态用于决定 partial_succeeded vs failed"
144
+ relations:
145
+ - "一个 ImportTask 包含多个 ImportFileRecord"
146
+ - "属于一个 workspace"
147
+ lifecycle_states: ["pending", "parsing", "succeeded", "partial_succeeded", "failed", "canceled"]
148
+
149
+ - id: "EN-2"
150
+ name: "导入文件记录 (ImportFileRecord)"
151
+ description: "批次内每个文件的独立处理记录,便于单条重试、失败诊断、审计追溯"
152
+ # 仅列业务关键 / 有特殊约束的字段。id / task_id / 时间戳等由开发按 convention 补。
153
+ fields:
154
+ - name: "original_filename"
155
+ type: "string"
156
+ required: true
157
+ notes: "用户上传时的原始文件名,含扩展名;失败清单展示用"
158
+ - name: "generated_slug"
159
+ type: "string"
160
+ required: false
161
+ notes: "系统生成的 feature slug;须匹配 DC-5 的正则;失败时可能为空"
162
+ - name: "status"
163
+ type: "enum"
164
+ required: true
165
+ notes: "pending / parsing / succeeded / failed / skipped(slug 冲突被跳过)"
166
+ - name: "error_code"
167
+ type: "string"
168
+ required: false
169
+ notes: "失败码枚举:FILE_TOO_LARGE / ENCODING_ERROR / SLUG_CONFLICT_IN_BATCH / SLUG_CONFLICT_EXISTING / UNSUPPORTED_FORMAT / PARSE_ERROR"
170
+ - name: "error_message"
171
+ type: "string"
172
+ required: false
173
+ notes: "人类可读的错误信息;失败清单 CSV 直接消费"
174
+ - name: "created_feature_path"
175
+ type: "string"
176
+ required: false
177
+ notes: "成功时返回的 .prd/features/<slug>/ 路径,供前端跳转"
178
+ relations:
179
+ - "属于一个 ImportTask"
180
+ - "成功时关联一个新建的 feature"
181
+ lifecycle_states: ["pending", "parsing", "succeeded", "failed", "skipped"]
182
+
183
+
184
+ main_flow:
185
+ - step: 1
186
+ actor: "产品经理"
187
+ trigger: "在 workspace 首页点击'导入历史 PRD'按钮"
188
+ action: "打开上传面板,拖拽或多选 ≤ 50 份 .md / .markdown / .txt 文件"
189
+ outcome: "前端校验文件数量和大小,展示待上传列表"
190
+ - step: 2
191
+ actor: "产品经理"
192
+ trigger: "点击面板内的'开始导入'"
193
+ action: "前端把文件提交到后端,创建 ImportTask (status = pending)"
194
+ outcome: "任务进入 pending 状态,页面跳转到任务详情"
195
+ - step: 3
196
+ actor: "系统"
197
+ trigger: "任务进入 worker 队列"
198
+ action: "ImportTask 状态变为 parsing,后台 worker 逐文件处理,每文件生成 ImportFileRecord"
199
+ outcome: "文件记录随处理过程流转状态"
200
+ - step: 4
201
+ actor: "系统"
202
+ trigger: "单个文件处理完成"
203
+ action: "若成功,按 schema v0.2 生成 .prd/features/<slug>/working/prd.yaml 骨架(填 meta + background);若失败,记录错误码和消息"
204
+ outcome: "ImportFileRecord 状态终结,任务详情页实时刷新进度"
205
+ - step: 5
206
+ actor: "系统"
207
+ trigger: "全部文件处理结束"
208
+ action: "汇总 succeeded / failed 数量,更新 ImportTask 最终状态(succeeded / partial_succeeded / failed)"
209
+ outcome: "任务详情展示最终结果;发起者收到站内消息通知。后续失败文件处理(下载 / 重导 / 放弃)由 ui_interaction 和 state_permissions 覆盖,不属于本主流程"
210
+
211
+
212
+ # state_permissions 只写状态差异化的操作。跨状态通用的"查看任务详情 / 实时进度"由
213
+ # data_and_api.security_constraints (SEC-2) 统一声明:PM 看自己的、管理员看 workspace 全部。
214
+ state_permissions:
215
+ - entity_id: "EN-1" # -- 实体外键:导入任务
216
+ state: "pending"
217
+ allowed_actions:
218
+ - action: "取消任务"
219
+ roles: ["产品经理", "工作空间管理员"]
220
+ notes: "pending 状态取消等于直接丢弃,无副作用"
221
+ - entity_id: "EN-1"
222
+ state: "parsing"
223
+ allowed_actions:
224
+ - action: "取消任务"
225
+ roles: ["产品经理", "工作空间管理员"]
226
+ notes: "已处理完的文件保留;未处理的丢弃;正在处理的文件记录标 canceled"
227
+ - entity_id: "EN-1"
228
+ state: "succeeded"
229
+ allowed_actions:
230
+ - action: "下载失败清单"
231
+ roles: ["产品经理", "工作空间管理员"]
232
+ notes: "succeeded 状态下清单为空,按钮仍可点击(下载空 CSV)"
233
+ - entity_id: "EN-1"
234
+ state: "partial_succeeded"
235
+ allowed_actions:
236
+ - action: "下载失败清单"
237
+ roles: ["产品经理", "工作空间管理员"]
238
+ notes: "CSV 列:原文件名 / 错误码 / 错误消息 / 任务 ID"
239
+ - entity_id: "EN-1"
240
+ state: "failed"
241
+ allowed_actions:
242
+ - action: "下载失败清单"
243
+ roles: ["产品经理", "工作空间管理员"]
244
+ - entity_id: "EN-1"
245
+ state: "canceled"
246
+ allowed_actions: []
247
+ notes: "终态只读,无差异化操作。查看权限走 SEC-2;不能在原任务上恢复,需重新发起新任务"
248
+
249
+
250
+ business_rules:
251
+ - id: "R-1" # -- 业务规则
252
+ rule: "feature slug 在 workspace 内必须唯一。新导入文件生成的 slug 若与已有 feature 冲突,该文件标为 skipped,不覆盖原有内容"
253
+ rationale: "避免 PM 误操作覆盖他人已冻结或正在编辑的需求。冻结后被覆盖属于严重数据事故"
254
+ applies_to: ["EN-1", "EN-2", "S-4"]
255
+ - id: "R-2"
256
+ rule: "同一批次内若多个文件生成了相同的 slug,保留第一个,后续全部标 skipped"
257
+ rationale: "批次内冲突客观存在,按顺序处理避免不确定性,同时给 PM 明确提示去修改源文件"
258
+ applies_to: ["EN-2", "S-4"]
259
+ - id: "R-3"
260
+ rule: "部分成功允许。单个文件失败不阻断其他文件处理"
261
+ rationale: "批量导入 50 个文件时若一个失败就全量回滚,用户体验极差。分条隔离是更合理的策略"
262
+ applies_to: ["EN-1"]
263
+ - id: "R-4"
264
+ rule: "slug 自动生成规则:优先取文件名(去后缀、转小写、空格和下划线改连字符);若结果冲突,追加 -2、-3 递增后缀。本期仅覆盖英文文件名;中文文件名的处理策略见 Q-2"
265
+ rationale: "多数 PM 的文件名本身就接近 slug 语义,自动生成能减少用户负担"
266
+ applies_to: ["EN-2", "S-3"]
267
+ - id: "R-5"
268
+ rule: "失败文件的原始内容保留 7 天后自动清理;失败清单 CSV 的元信息永久保留在任务详情中"
269
+ rationale: "7 天给 PM 充分时间修复重导;CSV 只存元信息,存储成本低,便于长期审计"
270
+ applies_to: ["EN-2"]
271
+
272
+
273
+ scenarios:
274
+ - id: "SC-1" # -- 场景
275
+ condition: "用户一次上传 10 份 .md 文件,文件名各不相同,内容格式正常"
276
+ expected_behavior: "全部 10 个文件解析成功,任务状态 succeeded,每文件对应一个新 feature 目录"
277
+ input: "10 份 .md,每份 ≤ 1MB"
278
+ output: "succeeded_count=10, failed_count=0,生成 10 个 .prd/features/<slug>/working/prd.yaml"
279
+ refers_to: ["S-1", "S-2", "S-3", "R-4"]
280
+ - id: "SC-2"
281
+ condition: "10 份文件中 8 份是 .md,2 份是 .docx"
282
+ expected_behavior: ".md 全部成功;.docx 标失败(error_code=UNSUPPORTED_FORMAT),任务状态 partial_succeeded"
283
+ input: "8 份 .md + 2 份 .docx"
284
+ output: "succeeded_count=8, failed_count=2,失败清单列出两个 docx 文件名"
285
+ refers_to: ["S-2", "S-5", "R-3"]
286
+ - id: "SC-3"
287
+ condition: "批次内 2 份文件经过 slug 生成规则得到相同 slug(如 batch-import.md 和 Batch Import.txt 都生成 batch-import)"
288
+ expected_behavior: "首份成功,第二份标 skipped(error_code=SLUG_CONFLICT_IN_BATCH),失败清单说明冲突来源"
289
+ input: "2 份文件:batch-import.md (12KB) + Batch Import.txt (8KB),内容不同但文件名生成同一 slug"
290
+ output: "succeeded_count=1(batch-import.md → feature 建立),failed_count=1(Batch Import.txt status=skipped,error_message='该 slug 在同批次内已被 batch-import.md 占用')"
291
+ refers_to: ["R-2", "S-4", "S-5"]
292
+ - id: "SC-4"
293
+ condition: "某文件生成的 slug 与 workspace 内已存在的 feature 重名"
294
+ expected_behavior: "该文件标 skipped(error_code=SLUG_CONFLICT_EXISTING);任务最终状态取决于其他文件结果"
295
+ input: "1 份文件 approval-flow.md,workspace 内已存在 features/approval-flow/"
296
+ output: "succeeded_count=0, failed_count=1,error_code=SLUG_CONFLICT_EXISTING,error_message='workspace 内已存在 feature approval-flow,请重命名文件或先处理已有功能'"
297
+ refers_to: ["R-1", "S-4", "S-5"]
298
+ - id: "SC-5"
299
+ condition: "导入成功后,PM 跳转到 features 列表"
300
+ expected_behavior: "新导入的 feature 立即出现在列表,版本标记 working,PM 可进入开始补充其他模块"
301
+ input: "SC-1 完成后,PM 点击任务详情页的 feature 链接"
302
+ output: "跳转到 .prd/features/<slug>/ 页面,页面展示生成的 prd.yaml 含 meta + background,status=draft,version=working"
303
+ refers_to: ["S-1", "S-2", "S-3"]
304
+
305
+
306
+ edge_cases:
307
+ - id: "E-1" # -- 异常/边界
308
+ situation: "用户上传单个文件大小超过 5MB"
309
+ expected_behavior: "前端立即拒绝该文件(不加入上传列表),提示'单文件不得超过 5MB,请拆分后重试'"
310
+ mitigation: "前端选文件时就做大小检查;后端作为兜底再次校验"
311
+ - id: "E-2"
312
+ situation: "用户一次选择超过 50 个文件"
313
+ expected_behavior: "前端不允许提交,提示'单次最多 50 个文件,本次选择了 N 个,请分批'"
314
+ mitigation: "给出'分批指引'链接,说明限制原因"
315
+ - id: "E-3"
316
+ situation: "文件编码不是 UTF-8(如 GBK)"
317
+ expected_behavior: "该文件标失败(error_code=ENCODING_ERROR),消息提示'检测到非 UTF-8 编码,请转换后重试'"
318
+ mitigation: "失败清单附转换工具或步骤建议"
319
+ - id: "E-4"
320
+ situation: "用户在任务 parsing 状态下点击'取消'"
321
+ expected_behavior: "任务状态转 canceled;已完成的 ImportFileRecord 保留成功状态;已生成的 feature 保留;未处理的文件标 canceled"
322
+ mitigation: "取消前弹确认框,明确'已导入的文件会保留,未导入的会放弃'"
323
+ - id: "E-5"
324
+ situation: "用户在 parsing 过程中关闭浏览器,稍后重新登录"
325
+ expected_behavior: "任务在服务端继续运行;用户回来后在'我的任务'列表能看到该任务,点击进入看当前进度或最终结果"
326
+ mitigation: "任务状态持久化到数据库,不依赖前端会话"
327
+ - id: "E-6"
328
+ situation: "AI 解析服务异常(外部依赖超时 / 429 / 502)"
329
+ expected_behavior: "受影响的文件标失败(error_code=PARSE_ERROR),消息含排查用的 request_id;其他文件不受影响"
330
+ mitigation: "worker 对单文件失败最多重试 2 次后即标失败,不阻塞后续文件;错误消息含 request_id"
331
+ - id: "E-7"
332
+ situation: "用户快速连续点击'开始导入'两次"
333
+ expected_behavior: "后端幂等处理,只创建一个 ImportTask"
334
+ mitigation: "前端按钮点击后立即置灰;后端用 idempotency_key(基于 initiator + 文件哈希 + 60s 时间窗口)去重"
335
+
336
+
337
+ ui_interaction:
338
+ - id: "UI-1" # -- 页面
339
+ page: "批量导入面板(弹窗或侧边抽屉)"
340
+ purpose: "让 PM 选择文件并发起导入任务"
341
+ key_elements:
342
+ - "拖拽上传区域(提示:支持 .md / .markdown / .txt,单文件 ≤ 5MB,单次 ≤ 50 个)"
343
+ - "已选文件列表(文件名 + 大小 + 移除按钮)"
344
+ - "文件总数与总大小统计"
345
+ - "'开始导入'按钮(文件数 > 0 才可点;>20 时点击弹二次确认)"
346
+ - "'取消'按钮"
347
+ user_actions:
348
+ - "拖拽或点击选择文件"
349
+ - "从已选列表移除某个文件"
350
+ - "点击开始导入"
351
+ - "关闭面板放弃本次操作"
352
+ empty_state: "展示引导文案:'还没添加文件?把 .md / .txt 文件拖进来,或点击此处选择'"
353
+ loading_state: "文件上传中时,每个文件旁显示上传进度条"
354
+ error_state: "单文件校验失败(格式 / 大小)在该文件旁显示红色警告图标 + 原因"
355
+
356
+ - id: "UI-2"
357
+ page: "导入任务详情页"
358
+ purpose: "让 PM 实时看任务进度、查看完成结果、处理失败文件"
359
+ key_elements:
360
+ - "任务状态标签(pending / parsing / succeeded / partial_succeeded / failed / canceled)"
361
+ - "进度条 + 文字:'已处理 M / N · 当前:<文件名>'"
362
+ - "文件表格(列:原文件名 / 生成 slug / 状态 / 错误码 / 错误消息 / 操作)"
363
+ - "'下载失败清单 CSV'按钮"
364
+ - "'取消任务'按钮(仅 pending / parsing 状态显示)"
365
+ user_actions:
366
+ - "查看实时进度"
367
+ - "下载失败清单"
368
+ - "取消任务"
369
+ - "从成功列表点击进入新建的 feature"
370
+ empty_state: "N/A(任务创建即有数据)"
371
+ loading_state: "parsing 时进度条滚动,表格行实时刷新"
372
+ error_state: "failed 状态下顶部展示整体失败横幅 + 原因;表格列出各文件失败详情"
373
+
374
+
375
+ data_and_api:
376
+ data_constraints:
377
+ - id: "DC-1" # -- 数据约束
378
+ text: "单文件大小 ≤ 5MB"
379
+ applies_to: ["EN-2"]
380
+ - id: "DC-2"
381
+ text: "单次批次文件数 ≤ 50"
382
+ applies_to: ["EN-1"]
383
+ - id: "DC-3"
384
+ text: "文件扩展名只支持 .md / .markdown / .txt(大小写不敏感)"
385
+ applies_to: ["EN-2"]
386
+ - id: "DC-4"
387
+ text: "文件必须是 UTF-8 编码"
388
+ applies_to: ["EN-2"]
389
+ - id: "DC-5"
390
+ text: "生成的 feature slug 必须匹配正则 ^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$(仅适用于英文文件名;中文文件名的 slug 策略见 Q-2)"
391
+ applies_to: ["EN-2"]
392
+
393
+ api_constraints:
394
+ - id: "API-1" # -- 接口约束
395
+ text: "创建任务接口幂等:同一 initiator + 相同文件哈希集合 + 60 秒时间窗口内的重复请求返回同一 task_id"
396
+ applies_to: ["EN-1"]
397
+ - id: "API-2"
398
+ text: "任务详情接口返回结构稳定,支持前端轮询获取进度"
399
+ applies_to: ["EN-1"]
400
+ - id: "API-3"
401
+ text: "失败清单 CSV 下载链接需鉴权,有效期 ≤ 10 分钟(具体协议由开发选型)"
402
+ applies_to: ["EN-1"]
403
+
404
+ performance_constraints:
405
+ - id: "PC-1" # -- 性能约束
406
+ text: "50 个 1MB 以下的 .md 文件,整个导入应在 2 分钟内完成(含 AI 解析)"
407
+ - id: "PC-2"
408
+ text: "进度刷新延迟 ≤ 3 秒"
409
+ - id: "PC-3"
410
+ text: "单 workspace 并发导入任务 ≤ 3 个,超过者排队"
411
+
412
+ security_constraints:
413
+ - id: "SEC-1" # -- 安全约束
414
+ text: "失败文件的原始内容仅任务发起者和 workspace 管理员可下载(保留时长见 R-5)"
415
+ applies_to: ["EN-2"]
416
+ - id: "SEC-2"
417
+ text: "PM 只能查看自己发起的导入任务;workspace 管理员可查看所有"
418
+ applies_to: ["EN-1"]
419
+ - id: "SEC-3"
420
+ text: "导入操作写审计日志,记录 initiator / task_id / file_count / result;日志保留 90 天"
421
+ applies_to: ["EN-1"]
422
+ - id: "SEC-4"
423
+ text: "超过 20 个文件的批次在发起前弹二次确认框"
424
+
425
+
426
+ acceptance_criteria:
427
+ - id: "AC-1" # -- 验收标准
428
+ criterion: "上传 10 份格式正常的 .md 文件,全部成功,每文件生成 .prd/features/<slug>/working/prd.yaml,2 分钟内完成"
429
+ priority: "must"
430
+ verification_method: "手工用例 + 自动化 E2E"
431
+ refers_to: ["S-1", "S-2", "SC-1", "PC-1"]
432
+ - id: "AC-2"
433
+ criterion: "上传 8 份 .md + 2 份 .docx,任务最终状态 partial_succeeded;.md 全部成功,.docx 在失败清单中标 UNSUPPORTED_FORMAT"
434
+ priority: "must"
435
+ verification_method: "手工用例"
436
+ refers_to: ["S-5", "SC-2", "R-3"]
437
+ - id: "AC-3"
438
+ criterion: "同批次内 2 份文件生成相同 slug 时,首份成功,次份 skipped 且错误码为 SLUG_CONFLICT_IN_BATCH"
439
+ priority: "must"
440
+ verification_method: "自动化单元测试"
441
+ refers_to: ["R-2", "SC-3"]
442
+ - id: "AC-4"
443
+ criterion: "已存在 feature slug 不被覆盖,冲突文件 skipped 且错误码为 SLUG_CONFLICT_EXISTING"
444
+ priority: "must"
445
+ verification_method: "自动化单元测试"
446
+ refers_to: ["R-1", "SC-4"]
447
+ - id: "AC-5"
448
+ criterion: "parsing 状态下取消任务,已成功的文件保留,未处理的文件丢弃,任务状态变 canceled"
449
+ priority: "must"
450
+ verification_method: "手工用例 + 自动化 E2E"
451
+ refers_to: ["E-4", "S-1"]
452
+ - id: "AC-6"
453
+ criterion: "失败清单 CSV 可下载,列包含:原文件名 / 错误码 / 错误消息 / 任务 ID"
454
+ priority: "must"
455
+ verification_method: "手工用例"
456
+ refers_to: ["S-5"]
457
+ - id: "AC-7"
458
+ criterion: "超过 5MB 的单文件被前端直接拒绝,不进入上传队列"
459
+ priority: "should"
460
+ verification_method: "手工用例"
461
+ refers_to: ["E-1", "DC-1"]
462
+ - id: "AC-8"
463
+ criterion: "超过 50 个文件时前端拒绝提交并给出明确提示"
464
+ priority: "should"
465
+ verification_method: "手工用例"
466
+ refers_to: ["E-2", "DC-2"]
467
+ - id: "AC-9"
468
+ criterion: "浏览器关闭后重新打开,能看到运行中任务的当前进度或最终结果"
469
+ priority: "should"
470
+ verification_method: "手工用例"
471
+ refers_to: ["E-5"]
472
+ - id: "AC-10"
473
+ criterion: "任务完成后发起者收到站内消息,点击可跳转任务详情"
474
+ priority: "could"
475
+ verification_method: "手工用例"
476
+
477
+
478
+ open_questions:
479
+ - id: "Q-1" # -- 待确认问题
480
+ question: "AI 解析用哪个模型?自托管还是调外部 API?预算限制是什么?"
481
+ severity: "blocking"
482
+ deferred_to: ""
483
+ owner: "架构负责人 / CTO"
484
+ due_date: "2026-04-29"
485
+ - id: "Q-2"
486
+ question: "slug 自动生成规则对中文文件名如何处理?(拼音?哈希?要求用户英文命名?)"
487
+ severity: "blocking"
488
+ deferred_to: ""
489
+ owner: "pm-alice"
490
+ due_date: "2026-04-25"
491
+ - id: "Q-3"
492
+ question: "50 / 5MB / 20 这些硬上限是最终值吗?是否需要配置化?"
493
+ severity: "non_blocking"
494
+ deferred_to: "v1.1"
495
+ owner: "pm-alice"
496
+ due_date: ""
497
+ - id: "Q-4"
498
+ question: "是否需要导入前的'预览模式'(解析但不落盘,让用户确认 slug 列表后再提交)?"
499
+ severity: "non_blocking"
500
+ deferred_to: "v1.1"
501
+ owner: "pm-alice"
502
+ due_date: ""
503
+ - id: "Q-5"
504
+ question: "工作空间管理员能否强制取消他人的导入任务?"
505
+ severity: "non_blocking"
506
+ deferred_to: "v1.1"
507
+ owner: "pm-alice"
508
+ due_date: ""
509
+ - id: "Q-6"
510
+ question: "本功能假设 workspace / project / user 能力已就绪(角色模型、权限、用户账号)。这些底层能力在 PRD Improve 自身是否已定义、是否与本功能并行建设、还是需要先验证就绪?"
511
+ severity: "blocking"
512
+ deferred_to: ""
513
+ owner: "架构负责人"
514
+ due_date: "2026-04-25"