prd-workflow-cli 1.1.25

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,794 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const confirm = require('./confirm');
5
+ const dialog = require('./dialog');
6
+
7
+ module.exports = async function (action, type, options = {}) {
8
+ const configPath = path.join(process.cwd(), '.prd-config.json');
9
+
10
+ if (!await fs.pathExists(configPath)) {
11
+ console.log(chalk.red('✗ 当前目录不是一个 PRD 项目'));
12
+ return;
13
+ }
14
+
15
+ const config = await fs.readJSON(configPath);
16
+
17
+ if (action === 'create') {
18
+ await createVersionDoc(type, config, configPath);
19
+ } else if (action === 'freeze') {
20
+ await freezeVersion(config, configPath, options);
21
+ } else {
22
+ console.log(chalk.red('✗ 未知操作'));
23
+ console.log('可用操作: create C0|C1|C2, freeze');
24
+ }
25
+ };
26
+
27
+ async function createVersionDoc(type, config, configPath) {
28
+ if (config.currentIteration === 0) {
29
+ console.log(chalk.red('✗ 请先创建迭代'));
30
+ return;
31
+ }
32
+
33
+ const iterationDir = path.join(
34
+ process.cwd(),
35
+ '02_迭代记录',
36
+ `第${String(config.currentIteration).padStart(2, '0')}轮迭代`
37
+ );
38
+
39
+ // C 类文档必须先有 B3
40
+ const b3Path = path.join(iterationDir, 'B3_规划冻结归档.md');
41
+ if (!await fs.pathExists(b3Path)) {
42
+ console.log(chalk.red('✗ 请先完成规划冻结 (B3)'));
43
+ console.log('运行: prd plan freeze');
44
+ return;
45
+ }
46
+
47
+ const templates = {
48
+ 'C0': getC0Template(),
49
+ 'C1': getC1Template(),
50
+ 'C2': getC2Template()
51
+ };
52
+
53
+ if (!templates[type]) {
54
+ console.log(chalk.red(`✗ 未知的文档类型: ${type}`));
55
+ console.log('可用类型: C0, C1, C2');
56
+ return;
57
+ }
58
+
59
+ const fileName = getFileName(type);
60
+ const filePath = path.join(iterationDir, fileName);
61
+
62
+ if (await fs.pathExists(filePath)) {
63
+ console.log(chalk.yellow(`⚠ 文件已存在: ${fileName}`));
64
+ return;
65
+ }
66
+
67
+ // C1 需要先有 C0
68
+ if (type === 'C1') {
69
+ const c0Path = path.join(iterationDir, 'C0_版本范围声明.md');
70
+ if (!await fs.pathExists(c0Path)) {
71
+ console.log(chalk.red('✗ 请先创建 C0'));
72
+ console.log('运行: prd version create C0');
73
+ return;
74
+ }
75
+ }
76
+
77
+ // 写入文件
78
+ await fs.writeFile(filePath, templates[type]);
79
+
80
+ // 记录文档创建
81
+ await dialog.logDocumentCreation('version', type, filePath);
82
+
83
+ console.log(chalk.green(`✓ ${fileName} 创建成功!`));
84
+ console.log(chalk.cyan(`文件位置: ${filePath}\n`));
85
+
86
+ if (type === 'C0') {
87
+ console.log(chalk.bold('⚠️ 重要提醒:\n'));
88
+ console.log(chalk.yellow('【PM 职责】'));
89
+ console.log('- 对版本承诺负责');
90
+ console.log('- 明确包含/不包含\n');
91
+
92
+ console.log(chalk.cyan('【AI 职责】'));
93
+ console.log('- 将 B3 转译为版本语言');
94
+ console.log('- 检查是否超出规划\n');
95
+
96
+ console.log(chalk.red('【AI 禁止】'));
97
+ console.log('- ❌ 新增版本目标\n');
98
+
99
+ console.log(chalk.bold('下一步:'));
100
+ console.log('1. PM 填写 C0_版本范围声明.md (必须基于 B3)');
101
+ console.log('2. 创建 C1: prd version create C1');
102
+ } else if (type === 'C1') {
103
+ console.log(chalk.bold('⚠️ 重要提醒:\n'));
104
+ console.log(chalk.yellow('【PM 职责】'));
105
+ console.log('- 确认需求是否准确');
106
+ console.log('- 确认需求是否完整\n');
107
+
108
+ console.log(chalk.cyan('【AI 职责】'));
109
+ console.log('- 拆分为清单');
110
+ console.log('- 校验可验证性');
111
+ console.log('- 标注来源关系\n');
112
+
113
+ console.log(chalk.red('【AI 禁止】'));
114
+ console.log('- ❌ 引入规划外需求\n');
115
+
116
+ console.log(chalk.bold('下一步:'));
117
+ console.log('1. PM 填写 C1_版本需求清单.md');
118
+ console.log('2. 执行 R2 审视: prd review r2');
119
+ console.log('');
120
+ console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
121
+ console.log(chalk.bold.red('🚨 关键步骤:C1 填写完成后必须执行 R2 审视'));
122
+ console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
123
+ console.log('');
124
+ console.log(chalk.yellow(' R2 审视将检查:'));
125
+ console.log(' 1. ✅ 一致性检查:C1 是否忠实于 B3/B2/C0');
126
+ console.log(' 2. ✅ 范围检查:是否有超出当前版本的需求');
127
+ console.log(' 3. ✅ 用户视角审查:站在用户角度评估需求');
128
+ console.log(' - 用户感知是否良好?');
129
+ console.log(' - 是否解决了用户真正的问题?');
130
+ console.log(' - 用户使用时会满意吗?');
131
+ console.log('');
132
+ console.log(chalk.gray('提示:没有 R2 审视报告,无法执行 prd version freeze'));
133
+ } else if (type === 'C2') {
134
+ console.log(chalk.bold('⚠️ 重要提醒:\n'));
135
+ console.log(chalk.yellow('【C2 用途】'));
136
+ console.log('- 记录版本冻结后发生的变更');
137
+ console.log('- 保证版本决策可追溯');
138
+ console.log('- 防止版本失控\n');
139
+
140
+ console.log(chalk.cyan('【填写要求】'));
141
+ console.log('- 只记录"已发生的变更"');
142
+ console.log('- 必须说明变更原因');
143
+ console.log('- 评估对版本目标的影响\n');
144
+
145
+ console.log(chalk.red('【注意】'));
146
+ console.log('- ⚠️ 重大变更可能需要重新执行 R2 审视\n');
147
+ }
148
+ }
149
+
150
+ async function freezeVersion(config, configPath, options = {}) {
151
+ if (config.currentIteration === 0) {
152
+ console.log(chalk.red('✗ 请先创建迭代'));
153
+ return;
154
+ }
155
+
156
+ const iterationDir = path.join(
157
+ process.cwd(),
158
+ '02_迭代记录',
159
+ `第${String(config.currentIteration).padStart(2, '0')}轮迭代`
160
+ );
161
+
162
+ // 检查 B3, C0, C1 是否存在
163
+ const b3Path = path.join(iterationDir, 'B3_规划冻结归档.md');
164
+ const c0Path = path.join(iterationDir, 'C0_版本范围声明.md');
165
+ const c1Path = path.join(iterationDir, 'C1_版本需求清单.md');
166
+
167
+ if (!await fs.pathExists(b3Path)) {
168
+ console.log(chalk.red('✗ 请先完成规划冻结 (B3)'));
169
+ return;
170
+ }
171
+
172
+ if (!await fs.pathExists(c0Path) || !await fs.pathExists(c1Path)) {
173
+ console.log(chalk.red('✗ 请先完成 C0 和 C1'));
174
+ return;
175
+ }
176
+
177
+ // 检查 R2 审视是否通过
178
+ const r2ReviewPath = path.join(iterationDir, 'R2_版本审视报告.md');
179
+ if (!await fs.pathExists(r2ReviewPath)) {
180
+ console.log(chalk.red('✗ 请先完成 R2 版本审视'));
181
+ console.log('运行: prd review r2');
182
+ return;
183
+ }
184
+
185
+ // 读取 R2 审视结论
186
+ const r2Content = await fs.readFile(r2ReviewPath, 'utf-8');
187
+ const hasPassed = r2Content.includes('- [x] ✅ 通过') || r2Content.includes('[x] 通过');
188
+
189
+ if (!hasPassed) {
190
+ console.log(chalk.red('✗ R2 审视未通过,不能冻结版本'));
191
+ console.log(chalk.yellow('请修改 C0/C1 后重新执行 R2 审视'));
192
+ return;
193
+ }
194
+
195
+ // ⭐ 支持预确认模式:PM 已在对话中确认并提供签名
196
+ let pmSignature = null;
197
+ if (options.pmConfirmed && options.pmSignature) {
198
+ console.log(chalk.green(`✓ PM 已在对话中确认版本冻结,签名: ${options.pmSignature}`));
199
+ pmSignature = options.pmSignature;
200
+ } else {
201
+ // 交互式确认
202
+ pmSignature = await confirm.confirmC3Freeze();
203
+ }
204
+
205
+ if (!pmSignature) {
206
+ console.log(chalk.yellow('\n根据 PM 决策,未执行冻结'));
207
+ return;
208
+ }
209
+
210
+ // ⭐ 读取 C0、C1、R2 内容,提取关键信息
211
+ console.log(chalk.gray('正在从 C0/C1/R2 提取关键信息...'));
212
+
213
+ const c0Content = await fs.readFile(c0Path, 'utf-8');
214
+ const c1Content = await fs.readFile(c1Path, 'utf-8');
215
+
216
+ // 提取 C0 版本目标
217
+ let c0VersionGoal = extractSection(c0Content, '版本目标') ||
218
+ extractSection(c0Content, '核心问题') ||
219
+ '(请手动填写,未能自动提取)';
220
+
221
+ // 提取 C0 版本范围
222
+ let c0Scope = extractSection(c0Content, '包含范围') ||
223
+ extractSection(c0Content, '版本包含') ||
224
+ '(请手动填写,未能自动提取)';
225
+
226
+ // 统计 C1 需求数量
227
+ const p0Count = (c1Content.match(/优先级[:\s]*P0/gi) || []).length;
228
+ const p1Count = (c1Content.match(/优先级[:\s]*P1/gi) || []).length;
229
+ const p2Count = (c1Content.match(/优先级[:\s]*P2/gi) || []).length;
230
+ const reqCount = (c1Content.match(/需求\s*#\d+|REQ-\d+/gi) || []).length || (p0Count + p1Count + p2Count);
231
+
232
+ // 提取 R2 审视摘要
233
+ let r2Summary = '';
234
+ const r2Sections = ['版本目标一致性', '版本范围偏移检查', '规划覆盖完整性', '需求粒度成熟度', '进入执行准备度'];
235
+ for (const section of r2Sections) {
236
+ const sectionContent = extractSection(r2Content, section);
237
+ if (sectionContent && sectionContent.length > 10) {
238
+ r2Summary += `- ${section}: ${sectionContent.substring(0, 80)}...\n`;
239
+ }
240
+ }
241
+ if (!r2Summary) {
242
+ r2Summary = '(请参考 R2_版本审视报告.md)';
243
+ }
244
+
245
+ // 生成 C3(传入提取的内容)
246
+ const c3Template = getC3Template(pmSignature, {
247
+ c0VersionGoal,
248
+ c0Scope,
249
+ reqCount,
250
+ p0Count,
251
+ p1Count,
252
+ p2Count,
253
+ r2Summary
254
+ });
255
+ const c3Path = path.join(iterationDir, 'C3_版本冻结归档.md');
256
+ await fs.writeFile(c3Path, c3Template);
257
+
258
+ // 记录 PM 决策和文档创建
259
+ await dialog.logPMConfirmation('version', 'freeze_c3', 'approved',
260
+ `PM签名: ${pmSignature}, 版本冻结`
261
+ );
262
+ await dialog.logDocumentCreation('version', 'C3', c3Path);
263
+
264
+ console.log(chalk.green('\n✓ C3_版本冻结归档.md 创建成功!'));
265
+ console.log(chalk.cyan(`文件位置: ${c3Path}\n`));
266
+
267
+ console.log(chalk.bold.green('🎉 版本已冻结!产品需求阶段完成!\n'));
268
+ console.log(chalk.bold('✅ 本轮迭代已完成,可以:'));
269
+ console.log('1. 将冻结的需求交付给研发团队');
270
+ console.log('2. 开始下一轮迭代: prd iteration new');
271
+ console.log('3. 查看项目状态: prd status');
272
+ console.log('');
273
+ }
274
+
275
+ function getFileName(type) {
276
+ const names = {
277
+ 'C0': 'C0_版本范围声明.md',
278
+ 'C1': 'C1_版本需求清单.md',
279
+ 'C2': 'C2_版本变更说明.md'
280
+ };
281
+ return names[type];
282
+ }
283
+
284
+ function getC0Template() {
285
+ return `# C0_版本范围声明
286
+
287
+ **创建时间**: ${new Date().toLocaleString('zh-CN')}
288
+ **文档状态**: 草案
289
+
290
+ ---
291
+
292
+ ## 文档说明
293
+
294
+ **目的**:
295
+ - 明确本版本要交付什么
296
+ - 声明版本边界和约束
297
+ - 对外承诺的依据
298
+
299
+ **填写要求**:
300
+ - 必须基于 B3 冻结的规划
301
+ - 不得超出 B3 范围
302
+ - 必须说明"不包含什么"
303
+
304
+ ---
305
+
306
+ ## 1. 版本目标
307
+
308
+ ### 1.1 版本定位
309
+
310
+ **本版本解决的核心问题**:
311
+ <!-- 引用 B1/B3 中的规划目标 -->
312
+
313
+ **版本编号**: v______
314
+ **计划发布时间**: ______
315
+
316
+ ---
317
+
318
+ ## 2. 版本范围
319
+
320
+ ### 2.1 包含范围
321
+
322
+ **本版本包含的功能**:
323
+ 1.
324
+ 2.
325
+ 3.
326
+
327
+ **对应 B2 中的需求项**:
328
+ - 需求项 #__: ______
329
+ - 需求项 #__: ______
330
+
331
+ ### 2.2 不包含内容
332
+
333
+ **本版本明确不包含**:
334
+ 1.
335
+ 2.
336
+ 3.
337
+
338
+ **不包含的原因**:
339
+ - 延后到后续版本
340
+ - 不在 B3 规划范围
341
+ - 资源/时间限制
342
+
343
+ ---
344
+
345
+ ## 3. 用户价值
346
+
347
+ ### 3.1 目标用户
348
+
349
+ **本版本面向的用户**:
350
+ <!-- 基于 A0/B1 -->
351
+
352
+ ### 3.2 解决的问题
353
+
354
+ **版本发布后用户可以**:
355
+ 1.
356
+ 2.
357
+ 3.
358
+
359
+ ---
360
+
361
+ ## 4. 功能清单(概述)
362
+
363
+ ### 4.1 核心功能
364
+
365
+ **功能1**:
366
+ - 价值:
367
+ - 来源: (引用 B2 需求项)
368
+
369
+ **功能2**:
370
+ <!-- 继续列举 -->
371
+
372
+ ---
373
+
374
+ ## 5. 版本约束
375
+
376
+ ### 5.1 技术约束
377
+
378
+ **已知限制**:
379
+ 1.
380
+ 2.
381
+
382
+ ### 5.2 业务约束
383
+
384
+ **时间约束**:
385
+ **资源约束**:
386
+ **依赖条件**:
387
+
388
+ ---
389
+
390
+ ## 6. 版本边界确认
391
+
392
+ ### 6.1 与 B3 一致性
393
+
394
+ - [ ] 所有功能均来自 B3
395
+ - [ ] 未超出 B3 范围
396
+ - [ ] "不包含"部分已明确
397
+
398
+ ### 6.2 PM 确认
399
+
400
+ **PM 签字**: _____________
401
+ **日期**: _____________
402
+
403
+ ---
404
+
405
+ ## 备注
406
+
407
+ <!-- 其他需要说明的内容 -->
408
+ `;
409
+ }
410
+
411
+ function getC1Template() {
412
+ return `# C1_版本需求清单
413
+
414
+ **创建时间**: ${new Date().toLocaleString('zh-CN')}
415
+ **文档状态**: 需求中
416
+
417
+ ---
418
+
419
+ ## 文档说明
420
+
421
+ **目的**:
422
+ - 详细列出所有版本需求
423
+ - 定义验收标准
424
+ - 作为研发的输入
425
+
426
+ **填写要求**:
427
+ - 每个需求必须可在 B2/C0 中找到来源
428
+ - 必须有明确的验收条件
429
+ - 禁止引入规划外的需求
430
+
431
+ ---
432
+
433
+ ## 1. 需求列表
434
+
435
+ ### 需求 #1
436
+
437
+ **需求标题**:
438
+ **需求编号**: REQ-001
439
+ **来源**: B2 需求项 #__ / C0 功能__
440
+
441
+ **需求描述**:
442
+ <!-- 详细描述需求 -->
443
+
444
+ **业务目标**:
445
+ <!-- 该需求解决什么业务问题 -->
446
+
447
+ **核心规则**:
448
+ 1.
449
+ 2.
450
+ 3.
451
+
452
+ **验收标准**:
453
+ - [ ] 标准1
454
+ - [ ] 标准2
455
+ - [ ] 标准3
456
+
457
+ **优先级**: P0 / P1 / P2
458
+
459
+ ---
460
+
461
+ ### 需求 #2
462
+
463
+ <!-- 继续列举其他需求 -->
464
+
465
+ ---
466
+
467
+ ## 2. 需求关系
468
+
469
+ ### 2.1 依赖关系
470
+
471
+ **需求 #1 依赖**:
472
+ - 依赖需求: REQ-___
473
+ - 依赖功能: (引用 A1)
474
+
475
+ ### 2.2 互斥关系
476
+
477
+ **互斥需求**:
478
+ <!-- 如果某些需求不能同时满足,说明原因 -->
479
+
480
+ ---
481
+
482
+ ## 3. 非功能需求
483
+
484
+ ### 3.1 性能要求
485
+
486
+ **响应时间**:
487
+ **并发量**:
488
+
489
+ ### 3.2 安全要求
490
+
491
+ **权限控制**:
492
+ **数据安全**:
493
+
494
+ ---
495
+
496
+ ## 4. 边界情况
497
+
498
+ ### 4.1 异常处理
499
+
500
+ **异常场景1**:
501
+ - 触发条件:
502
+ - 期望行为:
503
+
504
+ ### 4.2 边界值
505
+
506
+ **边界条件**:
507
+ <!-- 列出关键的边界值和处理方式 -->
508
+
509
+ ---
510
+
511
+ ## 5. 验收总览
512
+
513
+ ### 5.1 需求完整性
514
+
515
+ - [ ] 所有需求均来自 B2/C0
516
+ - [ ] 每个需求都有验收标准
517
+ - [ ] 依赖关系已标注
518
+ - [ ] 边界情况已说明
519
+
520
+ ### 5.2 PM 确认
521
+
522
+ **总需求数**: ______
523
+ **P0 需求数**: ______
524
+ **P1 需求数**: ______
525
+ **P2 需求数**: ______
526
+
527
+ **PM 签字**: _____________
528
+ **日期**: _____________
529
+
530
+ ---
531
+
532
+ ## 备注
533
+
534
+ <!-- 其他需要说明的内容 -->
535
+ `;
536
+ }
537
+
538
+ function getC2Template() {
539
+ return `# C2_版本变更说明
540
+
541
+ **创建时间**: ${new Date().toLocaleString('zh-CN')}
542
+ **文档状态**: 变更记录
543
+
544
+ ---
545
+
546
+ ## 文档说明
547
+
548
+ **目的**:
549
+ - 记录版本冻结后发生的变更
550
+ - 保证版本决策可追溯
551
+ - 防止版本失控
552
+
553
+ **填写要求**:
554
+ - 只记录"已发生的变更",不做评判
555
+ - 必须说明变更原因和影响
556
+ - 每次变更单独记录
557
+
558
+ ---
559
+
560
+ ## 变更记录
561
+
562
+ ### 变更 #1
563
+
564
+ **变更时间**: ____________
565
+ **变更人**: ____________
566
+
567
+ **变更原因**:
568
+ <!-- 说明为什么需要变更 -->
569
+
570
+ **变更内容**:
571
+ <!-- 详细描述变更了什么 -->
572
+
573
+ **对版本目标的影响**:
574
+ - [ ] 无影响
575
+ - [ ] 范围调整(说明:______)
576
+ - [ ] 优先级调整(说明:______)
577
+ - [ ] 需求变更(说明:______)
578
+
579
+ **是否需要重新审视**:
580
+ - [ ] 不需要
581
+ - [ ] 需要重新执行 R2 审视
582
+
583
+ ---
584
+
585
+ ### 变更 #2
586
+
587
+ <!-- 如有更多变更,按相同格式记录 -->
588
+
589
+ ---
590
+
591
+ ## 变更汇总
592
+
593
+ **总变更次数**: ______
594
+ **最后变更时间**: ______
595
+
596
+ **变更类型统计**:
597
+ - 范围调整: ______ 次
598
+ - 优先级调整: ______ 次
599
+ - 需求变更: ______ 次
600
+ - 其他: ______ 次
601
+
602
+ ---
603
+
604
+ ## 变更审批
605
+
606
+ **审批人**: _____________
607
+ **审批日期**: _____________
608
+ **审批意见**:
609
+
610
+ ---
611
+
612
+ ## 备注
613
+
614
+ <!-- 其他需要说明的内容 -->
615
+ `;
616
+ }
617
+
618
+ /**
619
+ * 从文档中提取指定标题下的内容
620
+ */
621
+ function extractSection(content, sectionTitle) {
622
+ const patterns = [
623
+ new RegExp(`\\*\\*${sectionTitle}\\*\\*[:\\s]*([\\s\\S]*?)(?=\\n\\*\\*|\\n##|\\n---|\$)`, 'i'),
624
+ new RegExp(`###?\\s*${sectionTitle}[\\s\\S]*?\\n([\\s\\S]*?)(?=\\n##|\\n---|\$)`, 'i'),
625
+ new RegExp(`${sectionTitle}[:\\s]*\\n([\\s\\S]*?)(?=\\n\\*\\*|\\n##|\\n---|\$)`, 'i')
626
+ ];
627
+
628
+ for (const pattern of patterns) {
629
+ const match = content.match(pattern);
630
+ if (match && match[1]) {
631
+ let extracted = match[1].trim();
632
+ extracted = extracted.replace(/<!--[\s\S]*?-->/g, '').trim();
633
+ extracted = extracted.replace(/_{3,}/g, '').trim();
634
+ if (extracted.length > 5) {
635
+ return extracted;
636
+ }
637
+ }
638
+ }
639
+ return null;
640
+ }
641
+
642
+ function getC3Template(pmSignature, extractedContent = {}) {
643
+ const {
644
+ c0VersionGoal = '(未提供)',
645
+ c0Scope = '(未提供)',
646
+ reqCount = 0,
647
+ p0Count = 0,
648
+ p1Count = 0,
649
+ p2Count = 0,
650
+ r2Summary = '(未提供)'
651
+ } = extractedContent;
652
+
653
+ return `# C3_版本冻结归档
654
+
655
+ **冻结时间**: ${new Date().toLocaleString('zh-CN')}
656
+ **PM 签名**: ${pmSignature}
657
+ **文档状态**: 已冻结 ✅
658
+
659
+ ---
660
+
661
+ ## 冻结声明
662
+
663
+ 本版本需求已通过 R2 审视,正式冻结。
664
+
665
+ **冻结承诺**:
666
+ - 产品需求阶段完成
667
+ - 可以交付给研发团队
668
+ - 冻结后禁止修改需求
669
+
670
+ ---
671
+
672
+ ## 1. 版本总结
673
+
674
+ ### 1.1 版本目标
675
+
676
+ **来自 C0 的版本目标**:
677
+
678
+ ${c0VersionGoal}
679
+
680
+ ### 1.2 版本范围
681
+
682
+ **来自 C0 的范围说明**:
683
+
684
+ ${c0Scope}
685
+
686
+ ### 1.3 需求清单
687
+
688
+ **来自 C1 的需求统计**:
689
+ - 总需求数: ${reqCount || '(请手动统计)'}
690
+ - P0 需求: ${p0Count}
691
+ - P1 需求: ${p1Count}
692
+ - P2 需求: ${p2Count}
693
+
694
+ ---
695
+
696
+ ## 2. R2 审视结论
697
+
698
+ ### 2.1 审视结果
699
+
700
+ **R2 审视状态**: ✅ 通过
701
+
702
+ **通过时间**: ${new Date().toLocaleString('zh-CN')}
703
+
704
+ **审视摘要**:
705
+
706
+ ${r2Summary}
707
+
708
+ ### 2.2 一致性确认
709
+
710
+ **与 B3 规划的一致性**:
711
+ - ✅ 未背叛规划
712
+ - ✅ 未超出 B3 范围
713
+ - ✅ 需求可追溯到 B2
714
+
715
+ ---
716
+
717
+ ## 3. 交付清单
718
+
719
+ ### 3.1 关键文档
720
+
721
+ **基线文档**:
722
+ - A0: 产品基础与范围说明
723
+ - A1: 已上线功能清单
724
+ - A2: 存量反馈汇总
725
+
726
+ **规划文档**:
727
+ - B1: 需求规划草案
728
+ - B2: 规划拆解与范围界定
729
+ - B3: 规划冻结归档
730
+ - R1: 规划审视报告
731
+
732
+ **版本文档**:
733
+ - C0: 版本范围声明
734
+ - C1: 版本需求清单
735
+ - R2: 版本审视报告
736
+
737
+ ### 3.2 交付物
738
+
739
+ **可交付给研发的文档**:
740
+ - ✅ C1_版本需求清单.md (主要依据)
741
+ - ✅ C0_版本范围声明.md (边界参考)
742
+ - ✅ B3_规划冻结归档.md (背景理解)
743
+
744
+ ---
745
+
746
+ ## 4. 冻结管理
747
+
748
+ ### 4.1 修改规则
749
+
750
+ **冻结后禁止**:
751
+ - ❌ 修改需求内容
752
+ - ❌ 新增需求
753
+ - ❌ 调整验收标准
754
+
755
+ **允许补充**:
756
+ - ✅ 技术实现方案的说明
757
+ - ✅ UI/UX 设计细节
758
+ - ✅ 测试用例
759
+
760
+ ### 4.2 变更流程
761
+
762
+ **如需变更需求**:
763
+ 1. 运行 prd change 记录变更
764
+ 2. 创建 C2_版本变更说明.md
765
+ 3. 评估是否需要重新执行 R2 审视
766
+ 4. PM 重新签字确认
767
+
768
+ ---
769
+
770
+ ## 5. 下一步
771
+
772
+ ### 5.1 研发阶段
773
+
774
+ **可以启动**:
775
+ - 技术方案设计
776
+ - 架构评审
777
+ - 开发排期
778
+
779
+ ### 5.2 后续迭代
780
+
781
+ **如需新的迭代**:
782
+ 1. 运行: prd iteration new
783
+ 2. 重新执行 A → R → B → C 流程
784
+ 3. 基于本次迭代的经验优化
785
+
786
+ ---
787
+
788
+ **PM 最终确认**: ${pmSignature}
789
+ **冻结日期**: ${new Date().toLocaleDateString('zh-CN')}
790
+ **状态**: 🔒 已冻结
791
+ **产品需求阶段**: ✅ 完成
792
+ `;
793
+ }
794
+