@openprd/cli 0.1.9 → 0.1.11

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.
Files changed (138) hide show
  1. package/.openprd/benchmarks/index.md +16 -0
  2. package/.openprd/benchmarks/sources.yaml +53 -1
  3. package/.openprd/config.yaml +10 -0
  4. package/.openprd/design/README.md +47 -0
  5. package/.openprd/design/README_EN.md +25 -0
  6. package/.openprd/design/active/asset-spec.md +15 -0
  7. package/.openprd/design/active/direction-plan.md +7 -0
  8. package/.openprd/design/active/facts-sheet.md +13 -0
  9. package/.openprd/design/active/image-preflight.md +9 -0
  10. package/.openprd/design/active/selected-direction.md +11 -0
  11. package/.openprd/design/anti-slop.md +16 -0
  12. package/.openprd/design/assets/dark-mesh.svg +14 -0
  13. package/.openprd/design/assets/paper-grid.svg +14 -0
  14. package/.openprd/design/assets/surface-presets.md +21 -0
  15. package/.openprd/design/assets/workbench-grid.svg +16 -0
  16. package/.openprd/design/checklists/ui-quality-gate.md +22 -0
  17. package/.openprd/design/components/component-catalog.md +42 -0
  18. package/.openprd/design/layouts/layout-catalog.json +45 -0
  19. package/.openprd/design/lenses/frontend-lenses.md +31 -0
  20. package/.openprd/design/recipes/content-experience.md +26 -0
  21. package/.openprd/design/recipes/operational-tool.md +25 -0
  22. package/.openprd/design/recipes/product-launch.md +26 -0
  23. package/.openprd/design/templates/README.md +36 -0
  24. package/.openprd/design/templates/README_EN.md +31 -0
  25. package/.openprd/design/templates/content-home.html +415 -0
  26. package/.openprd/design/templates/ops-dashboard.html +340 -0
  27. package/.openprd/design/templates/product-launch.html +360 -0
  28. package/.openprd/design/themes/theme-catalog.json +77 -0
  29. package/.openprd/engagements/active/prd.md +111 -100
  30. package/.openprd/schema/prd.schema.yaml +25 -0
  31. package/.openprd/templates/base/intake.md +17 -0
  32. package/.openprd/templates/base/prd.md +31 -6
  33. package/AGENTS.md +10 -7
  34. package/README.md +26 -28
  35. package/README_EN.md +3 -3
  36. package/package.json +41 -2
  37. package/scripts/openprd-codex-isolated-worker.mjs +799 -0
  38. package/skills/openprd-benchmark-router/SKILL.md +5 -0
  39. package/skills/openprd-frontend-design/SKILL.md +161 -0
  40. package/skills/openprd-frontend-design/references/design-asset-contract.md +34 -0
  41. package/skills/openprd-frontend-design/references/direction-engine.md +46 -0
  42. package/skills/openprd-harness/SKILL.md +37 -20
  43. package/skills/openprd-learning-review/SKILL.md +2 -1
  44. package/skills/openprd-learning-review/references/style-packs/xianxia-cultivation.prompt.md +1 -1
  45. package/skills/openprd-quality/SKILL.md +5 -3
  46. package/skills/openprd-requirement-intake/SKILL.md +27 -5
  47. package/skills/openprd-requirement-intake/references/prd-template-lenses.md +6 -3
  48. package/skills/openprd-requirement-intake/references/routing-rubric.md +9 -5
  49. package/skills/openprd-requirement-intake/references/startup-validation-lens.md +108 -0
  50. package/skills/openprd-router/SKILL.md +13 -5
  51. package/skills/openprd-shared/SKILL.md +38 -25
  52. package/src/agent-integration.js +120 -69
  53. package/src/brainstorm-artifacts.js +1805 -0
  54. package/src/brainstorm-presentation.js +233 -0
  55. package/src/brainstorm.js +766 -0
  56. package/src/cli/args.js +51 -3
  57. package/src/cli/doctor-print.js +34 -8
  58. package/src/cli/print.js +6 -0
  59. package/src/cli/quality-print.js +79 -3
  60. package/src/cli/run-print.js +43 -1
  61. package/src/cli/shared-print.js +33 -2
  62. package/src/cli/workflow-print.js +21 -0
  63. package/src/codex-hook-runner-template.mjs +838 -58
  64. package/src/design-starter-support.js +462 -0
  65. package/src/design-starter.js +1207 -0
  66. package/src/diagram-core.js +384 -151
  67. package/src/html-artifacts.js +403 -50
  68. package/src/knowledge.js +291 -32
  69. package/src/learning-review.js +3 -3
  70. package/src/loop.js +839 -101
  71. package/src/openprd.js +86 -1
  72. package/src/openspec/change-validate.js +1 -0
  73. package/src/prd-core.js +119 -2
  74. package/src/quality-html-artifact.js +30 -5
  75. package/src/quality-learning.js +50 -6
  76. package/src/quality.js +22 -12
  77. package/src/run-harness.js +191 -29
  78. package/src/self-update.js +301 -4
  79. package/src/standards.js +46 -5
  80. package/src/visual-prepare.js +940 -0
  81. package/src/workspace-core.js +175 -0
  82. package/src/workspace-workflow.js +307 -7
  83. package/.openprd/benchmarks/evidence/milvus-io-ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-code.md +0 -14
  84. package/.openprd/benchmarks/evidence/nolanlawson-com-using-ai-to-write-better-code-more-slowly.md +0 -14
  85. package/.openprd/discovery/config.json +0 -35
  86. package/.openprd/engagements/active/flows.md +0 -35
  87. package/.openprd/engagements/active/handoff.md +0 -16
  88. package/.openprd/engagements/active/review.html +0 -61
  89. package/.openprd/engagements/active/roles.md +0 -22
  90. package/.openprd/engagements/work-units/wu-20260524015648-6d33ded7.json +0 -23
  91. package/.openprd/engagements/work-units/wu-20260602113956-a99b5b88.json +0 -18
  92. package/.openprd/engagements/work-units/wu-20260602122244-78656aaf.json +0 -18
  93. package/.openprd/engagements/work-units/wu-20260602122442-e96489e2.json +0 -18
  94. package/.openprd/engagements/work-units/wu-20260602132835-695429e8.json +0 -18
  95. package/.openprd/exports/.gitkeep +0 -0
  96. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/candidate.json +0 -78
  97. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/diagnostic-report.json +0 -129
  98. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/root-cause-candidates.json +0 -41
  99. package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/timeline.json +0 -14
  100. package/.openprd/knowledge/drafts/openprd-experience-diagnostic-candidate-turn-1780116203372-5f266a79e968c758/SKILL.md +0 -49
  101. package/.openprd/knowledge/index.json +0 -47
  102. package/.openprd/reviews/v0001.html +0 -1322
  103. package/.openprd/reviews/v0002.html +0 -1150
  104. package/.openprd/reviews/v0003.html +0 -1150
  105. package/.openprd/reviews/v0004.html +0 -1150
  106. package/.openprd/reviews/v0005.html +0 -1150
  107. package/.openprd/sessions/.gitkeep +0 -0
  108. package/.openprd/state/changes.json +0 -27
  109. package/.openprd/state/current.json +0 -505
  110. package/.openprd/state/release-ledger.json +0 -387
  111. package/.openprd/state/version-index.json +0 -67
  112. package/.openprd/state/versions/.gitkeep +0 -0
  113. package/.openprd/state/versions/v0001.json +0 -121
  114. package/.openprd/state/versions/v0001.md +0 -161
  115. package/.openprd/state/versions/v0002.json +0 -264
  116. package/.openprd/state/versions/v0002.md +0 -183
  117. package/.openprd/state/versions/v0003.json +0 -269
  118. package/.openprd/state/versions/v0003.md +0 -188
  119. package/.openprd/state/versions/v0004.json +0 -274
  120. package/.openprd/state/versions/v0004.md +0 -193
  121. package/.openprd/state/versions/v0005.json +0 -299
  122. package/.openprd/state/versions/v0005.md +0 -189
  123. package/docs/assets/openprd-capability-overview-en.png +0 -0
  124. package/docs/assets/openprd-capability-overview-zh.png +0 -0
  125. package/docs/assets/openprd-learning-html.png +0 -0
  126. package/docs/assets/openprd-quality-html.png +0 -0
  127. package/docs/assets/openprd-requirement-routing-en.png +0 -0
  128. package/docs/assets/openprd-requirement-routing-en.svg +0 -102
  129. package/docs/assets/openprd-requirement-routing-zh-refined.png +0 -0
  130. package/docs/assets/openprd-requirement-routing-zh.png +0 -0
  131. package/docs/assets/openprd-requirement-routing-zh.svg +0 -102
  132. package/docs/assets/openprd-review-html.png +0 -0
  133. package/docs/assets/openprd-scenario-overview.png +0 -0
  134. package/docs/assets/openprd-scenario-overview.svg +0 -114
  135. package/docs/assets/openprd-self-evolving-mechanisms-en.png +0 -0
  136. package/docs/assets/openprd-self-evolving-mechanisms-zh.png +0 -0
  137. package/docs/assets/openprd-visual-compare-case-study-en.png +0 -0
  138. package/docs/assets/openprd-visual-compare-case-study-zh.png +0 -0
@@ -11,12 +11,16 @@ import { assertReviewPresentationReady, getReviewPresentationGate } from './revi
11
11
  import { syncSessionBindingFromReview, syncSessionBindingFromSnapshot } from './session-binding.js';
12
12
  import { timestamp } from './time.js';
13
13
  import { generateWorkUnitId, normalizeWorkUnitId, readWorkUnitBinding, resolveTargetRoot, writeWorkUnitBinding } from './work-unit.js';
14
- import { appendDecision, appendOpenQuestions, appendProgress, appendWorkflowEvent, buildClarificationPlan, buildClarificationState, buildCurrentStateSnapshot, buildWorkflowTaskGraph, CAPTURE_SOURCES, coerceCapturedValue, deriveGateLabels, detectWorkspaceScenario, extractMarkdownSection, FIELD_PATH_TO_STATE_KEY, isSupportedProductType, loadCurrentLaneSnapshot, loadLatestVersionSnapshot, loadWorkspace, normalizeVersionId, persistWorkspaceCurrentState, readActiveRequirementLane, readVersionIndex, readVersionSnapshot, renderFlowDoc, renderHandoffDoc, renderRolesDoc, resolveActiveTemplatePack, resolveCurrentProductType, validateWorkspace, writeVersionIndex, writeVersionSnapshot } from './workspace-core.js';
14
+ import { appendDecision, appendOpenQuestions, appendProgress, appendWorkflowEvent, buildClarificationPlan, buildClarificationState, buildCurrentStateSnapshot, buildWorkflowTaskGraph, CAPTURE_SOURCES, coerceCapturedValue, deriveGateLabels, detectWorkspaceScenario, extractMarkdownSection, FIELD_PATH_TO_STATE_KEY, isSupportedProductType, loadCurrentLaneSnapshot, loadLatestVersionSnapshot, loadWorkspace, normalizeVersionId, persistWorkspaceCurrentState, readActiveRequirementLane, readVersionIndex, readVersionSnapshot, renderFlowDoc, renderHandoffDoc, renderRolesDoc, resolveActiveTemplatePack, resolveCurrentProductType, USER_CLARIFICATION_PATHS, validateWorkspace, writeVersionIndex, writeVersionSnapshot } from './workspace-core.js';
15
15
 
16
16
  function requirementGatePath(projectRoot) {
17
17
  return path.join(projectRoot, '.openprd', 'harness', 'requirement-gate.json');
18
18
  }
19
19
 
20
+ function normalizedText(value) {
21
+ return String(value ?? '').replace(/\s+/g, ' ').trim();
22
+ }
23
+
20
24
  const PRD_REVIEW_STATUSES = ['pending-confirmation', 'confirmed', 'needs-revision'];
21
25
  const CURRENT_SNAPSHOT_CACHE_KEYS = [
22
26
  'versionId',
@@ -30,6 +34,7 @@ const CURRENT_SNAPSHOT_CACHE_KEYS = [
30
34
  const REVIEW_PRESENTATION_RELEVANT_FIELD_PREFIXES = [
31
35
  'problem.',
32
36
  'users.',
37
+ 'validation.',
33
38
  'goals.',
34
39
  'scope.',
35
40
  'scenarios.',
@@ -56,6 +61,28 @@ const REVIEW_PRESENTATION_RELEVANT_OVERRIDE_KEYS = new Set([
56
61
  'primaryUsers',
57
62
  'secondaryUsers',
58
63
  'stakeholders',
64
+ 'community',
65
+ 'seedUsers',
66
+ 'communityFit',
67
+ 'currentAlternative',
68
+ 'painEvidence',
69
+ 'manualPath',
70
+ 'manualPlaybook',
71
+ 'commitmentSignals',
72
+ 'firstValidationStep',
73
+ 'defaultAlivePlan',
74
+ 'paymentProof',
75
+ 'mvpSlice',
76
+ 'weekendTest',
77
+ 'smallestExecution',
78
+ 'productizeGate',
79
+ 'firstCustomerPath',
80
+ 'pricingHypothesis',
81
+ 'customerOneProfitability',
82
+ 'growthDiscipline',
83
+ 'reversibility',
84
+ 'customerTruth',
85
+ 'valuesFit',
59
86
  'goals',
60
87
  'successMetrics',
61
88
  'acceptanceGoals',
@@ -109,6 +136,28 @@ const SYNTHESIZE_CONTENT_OVERRIDE_KEYS = new Set([
109
136
  'primaryUsers',
110
137
  'secondaryUsers',
111
138
  'stakeholders',
139
+ 'community',
140
+ 'seedUsers',
141
+ 'communityFit',
142
+ 'currentAlternative',
143
+ 'painEvidence',
144
+ 'manualPath',
145
+ 'manualPlaybook',
146
+ 'commitmentSignals',
147
+ 'firstValidationStep',
148
+ 'defaultAlivePlan',
149
+ 'paymentProof',
150
+ 'mvpSlice',
151
+ 'weekendTest',
152
+ 'smallestExecution',
153
+ 'productizeGate',
154
+ 'firstCustomerPath',
155
+ 'pricingHypothesis',
156
+ 'customerOneProfitability',
157
+ 'growthDiscipline',
158
+ 'reversibility',
159
+ 'customerTruth',
160
+ 'valuesFit',
112
161
  'goals',
113
162
  'successMetrics',
114
163
  'acceptanceGoals',
@@ -157,6 +206,29 @@ const SYNTHESIZE_CONTENT_OVERRIDE_KEYS = new Set([
157
206
  'evalPlan',
158
207
  ]);
159
208
 
209
+ function buildBrainstormSuggestion({ currentState, analysis, clarification, requirementGate }) {
210
+ const promptPreview = String(requirementGate?.promptPreview ?? '');
211
+ const explicit = /(脑暴|brainstorm|帮忙梳理|梳理一下|梳理下|先想清楚)/iu.test(promptPreview);
212
+ const missingRequiredFields = Number(analysis?.missingRequiredFields ?? 0);
213
+ const noProblemStatement = !normalizedText(currentState?.problemStatement);
214
+ const noPrimaryUsers = !Array.isArray(currentState?.primaryUsers) || currentState.primaryUsers.length === 0;
215
+ const shouldSuggest = explicit || (clarification?.shouldAskUser && missingRequiredFields >= 4) || (noProblemStatement && noPrimaryUsers && missingRequiredFields >= 3);
216
+ if (!shouldSuggest) {
217
+ return { recommended: false };
218
+ }
219
+ const reasons = [];
220
+ if (explicit) reasons.push('用户当前更像是在请求先梳理业务方向');
221
+ if (missingRequiredFields >= 4) reasons.push(`还有 ${missingRequiredFields} 个关键信息未确认`);
222
+ if (noProblemStatement) reasons.push('问题定义还不够稳定');
223
+ if (noPrimaryUsers) reasons.push('核心用户和场景还没锁定');
224
+ return {
225
+ recommended: true,
226
+ explicitTrigger: explicit,
227
+ reason: reasons.join(';'),
228
+ suggestedCommand: 'openprd brainstorm . --open',
229
+ };
230
+ }
231
+
160
232
  function normalizePrdReviewStatus(status) {
161
233
  return PRD_REVIEW_STATUSES.includes(status) ? status : 'pending-confirmation';
162
234
  }
@@ -192,6 +264,41 @@ function requirementGateReferenceTimestamp(gate) {
192
264
  return gate?.confirmedAt ?? gate?.updatedAt ?? gate?.openedAt ?? null;
193
265
  }
194
266
 
267
+ function requirementNextActionBlocksSynthesize(nextAction) {
268
+ return nextAction === 'clarify-user' || nextAction === 'classify' || nextAction === 'interview';
269
+ }
270
+
271
+ function gateHasClarificationConfirmation(gate) {
272
+ return Boolean(gate?.clarificationConfirmedAt || gate?.status === 'clarification-confirmed');
273
+ }
274
+
275
+ function latestUserConfirmedCaptureTimestamp(currentState, paths = null) {
276
+ const timestamps = Object.entries(currentState?.captureMeta ?? {})
277
+ .filter(([field, entry]) => (!paths || paths.has(field)) && entry?.source === 'user-confirmed')
278
+ .map(([, entry]) => entry?.capturedAt)
279
+ .filter(Boolean)
280
+ .map(String);
281
+ return timestamps.length > 0 ? timestamps.sort().at(-1) : null;
282
+ }
283
+
284
+ function latestConfirmedClarificationCaptureTimestamp(currentState) {
285
+ return latestUserConfirmedCaptureTimestamp(currentState, USER_CLARIFICATION_PATHS)
286
+ || latestUserConfirmedCaptureTimestamp(currentState);
287
+ }
288
+
289
+ function hasDerivedClarificationCaptureSince(currentState, gateAt) {
290
+ const reference = String(gateAt || '');
291
+ return Object.entries(currentState?.captureMeta ?? {}).some(([field, entry]) => {
292
+ if (!USER_CLARIFICATION_PATHS.has(field)) {
293
+ return false;
294
+ }
295
+ if (!entry?.capturedAt || String(entry.capturedAt) < reference) {
296
+ return false;
297
+ }
298
+ return Boolean(entry.source) && entry.source !== 'user-confirmed' && !NON_SEMANTIC_CAPTURE_SOURCES.has(entry.source);
299
+ });
300
+ }
301
+
195
302
  function gateQuestionLimit(gate, fallback) {
196
303
  const raw = Number(gate?.approvalPolicy?.maxClarificationQuestions);
197
304
  if (!Number.isFinite(raw) || raw <= 0) {
@@ -210,7 +317,23 @@ function ensureFreshRequirementStateForSynthesize({ gate, currentState, override
210
317
  }
211
318
  const capturedAt = latestCaptureTimestamp(currentState);
212
319
  if (capturedAt && String(capturedAt) >= String(gateAt)) {
213
- return;
320
+ if (!gateHasClarificationConfirmation(gate)) {
321
+ return;
322
+ }
323
+ const confirmedCaptureAt = latestConfirmedClarificationCaptureTimestamp(currentState);
324
+ const hasDerivedCapture = hasDerivedClarificationCaptureSince(currentState, gateAt);
325
+ if (confirmedCaptureAt && String(confirmedCaptureAt) >= String(gateAt) && !hasDerivedCapture) {
326
+ return;
327
+ }
328
+ throw new Error([
329
+ 'OpenPrd 已阻止 synthesize:当前需求摘要虽已确认,但 current.json 还没有把本轮确认内容稳定写回。',
330
+ hasDerivedCapture
331
+ ? '检测到本轮仍有 user clarification 字段被写成 agent-inferred / project-derived;请先改用 openprd capture 按 canonical 字段路径并以 user-confirmed 写回,再继续 synthesize。'
332
+ : '请先用 openprd capture 按 canonical 字段路径并以 user-confirmed 写回本轮确认事实,再继续 synthesize。',
333
+ hasSynthesizeContentOverrides(overrides)
334
+ ? 'partial override 不能替代这一步 requirement write-back。'
335
+ : null,
336
+ ].filter(Boolean).join(' '));
214
337
  }
215
338
  throw new Error([
216
339
  'OpenPrd 已阻止 synthesize:当前有新的需求入口,但 current.json 还没有记录本轮确认答案。',
@@ -220,6 +343,27 @@ function ensureFreshRequirementStateForSynthesize({ gate, currentState, override
220
343
  ].join(' '));
221
344
  }
222
345
 
346
+ async function ensureRequirementLaneReadyForSynthesize(ws) {
347
+ const guidance = await computeWorkspaceGuidance(ws, { questionLimit: 5 });
348
+ const missingRequiredFields = Number(guidance.analysis?.missingRequiredFields ?? 0);
349
+ const lacksProductType = !guidance.analysis?.productType;
350
+ const blockedByStructuredGap = guidance.nextAction === 'classify'
351
+ || guidance.nextAction === 'interview'
352
+ || (guidance.nextAction === 'clarify-user' && (lacksProductType || missingRequiredFields > 0));
353
+ if (!blockedByStructuredGap) {
354
+ return;
355
+ }
356
+ throw new Error([
357
+ 'OpenPrd 已阻止 synthesize:当前 requirement lane 还没有离开需求补齐阶段。',
358
+ `当前下一步仍是 ${guidance.nextAction}。`,
359
+ guidance.reason ? `原因: ${guidance.reason}` : null,
360
+ guidance.suggestedCommand ? `建议先执行: ${guidance.suggestedCommand}。` : null,
361
+ Array.isArray(guidance.suggestedQuestions) && guidance.suggestedQuestions.length > 0
362
+ ? `优先补齐: ${guidance.suggestedQuestions.slice(0, 2).join(';')}。`
363
+ : null,
364
+ ].filter(Boolean).join(' '));
365
+ }
366
+
223
367
  function resolveReviewPaths(ws, snapshot) {
224
368
  const canonicalReview = canonicalReviewPath(ws, snapshot.versionId);
225
369
  const activeReviewEntry = defaultReviewArtifactPath(ws);
@@ -383,7 +527,6 @@ function detectRequirementIntakeComplexity(gate) {
383
527
  ];
384
528
  const simpleConcretePatterns = [
385
529
  /按钮|文案|颜色|圆角|位置|间距|字号|图标|标题|空格|标点|错别字|拼写|label|copy/i,
386
- /红圈|描边|边框|卡片|平铺|去掉|去除|移除|隐藏|对齐|留白|背景/,
387
530
  /从.+(改到|移到|移动到|换到|变成|改成|改为).+/,
388
531
  ];
389
532
  const reasons = [];
@@ -505,6 +648,67 @@ function collectProjectRiskProbes(gate, snapshot) {
505
648
  return probes;
506
649
  }
507
650
 
651
+ function buildValidationFraming(sections = {}) {
652
+ const validation = sections.validation ?? {};
653
+ return {
654
+ community: shortList(validation.community, '需要先确认第一批最容易触达的 1 到 3 个社区、渠道或人群。'),
655
+ seedUsers: shortList(validation.seedUsers, '需要先确认至少 3 到 10 个具体先找谁聊、先服务谁。'),
656
+ communityFit: shortList(validation.communityFit, '需要先确认你为什么算这个社区里的自己人、他们在哪里聚集、为什么现在就能触达。'),
657
+ currentAlternative: shortList(validation.currentAlternative, '需要先确认他们现在主要靠什么替代方案在解决。'),
658
+ painEvidence: shortList(validation.painEvidence, '需要先确认这个问题到底有多痛、用户是否已经在为更差的办法花时间或花钱。'),
659
+ manualPath: shortList(validation.manualPath, '需要先确认不做完整产品时先怎么手工交付价值。'),
660
+ manualPlaybook: shortList(validation.manualPlaybook, '需要先确认手工交付的触发条件、步骤、工具、耗时和交接点。'),
661
+ commitmentSignals: shortList(validation.commitmentSignals, '需要先确认什么真实承诺能证明不是口头兴趣。'),
662
+ firstValidationStep: shortList(validation.firstValidationStep, '需要先确认最低成本先做哪一步验证。'),
663
+ defaultAlivePlan: shortList(validation.defaultAlivePlan, '需要先确认验证阶段怎样先活下来。'),
664
+ paymentProof: shortList(validation.paymentProof, '需要先确认有没有 10 个潜在用户样本、3/10 付费意愿或更强交易信号。'),
665
+ mvpSlice: shortList(validation.mvpSlice, '需要先确认第一版到底只做哪一件事。'),
666
+ weekendTest: shortList(validation.weekendTest, '需要先确认能不能压成周末级 MVP 或更轻的试跑。'),
667
+ smallestExecution: shortList(validation.smallestExecution, '需要先确认能否先用 spreadsheet、表单或 no-code 工具把价值跑出来。'),
668
+ productizeGate: shortList(validation.productizeGate, '需要先确认达到什么条件才允许继续产品化或加功能。'),
669
+ firstCustomerPath: shortList(validation.firstCustomerPath, '需要先确认第一批客户最现实的触达顺序。'),
670
+ pricingHypothesis: shortList(validation.pricingHypothesis, '需要先确认从第一个客户开始准备怎么收费。'),
671
+ customerOneProfitability: shortList(validation.customerOneProfitability, '需要先确认第一个客户如何覆盖时间和交付成本。'),
672
+ growthDiscipline: shortList(validation.growthDiscipline, '需要先确认销售、launch 和增长阶段准备守哪些纪律。'),
673
+ reversibility: shortList(validation.reversibility, '需要先确认验证结果一般时这条路是否容易回退,是否会逼出重招聘、长期绑定或重平台化。'),
674
+ customerTruth: shortList(validation.customerTruth, '需要先确认这更像在解决客户真问题还是满足内部冲动。'),
675
+ valuesFit: shortList(validation.valuesFit, '需要先确认这条路是否符合团队想坚持的价值观,以及它是不是你愿意长期住进去的业务形态。'),
676
+ };
677
+ }
678
+
679
+ function hasSatisfiedConfirmedClarificationWriteback({ gate, currentState, analysis, hasProductType }) {
680
+ if (!gateHasClarificationConfirmation(gate)) {
681
+ return false;
682
+ }
683
+ if (!hasProductType || Number(analysis?.missingRequiredFields ?? 0) > 0) {
684
+ return false;
685
+ }
686
+ const gateAt = requirementGateReferenceTimestamp(gate);
687
+ if (!gateAt) {
688
+ return false;
689
+ }
690
+ const confirmedCaptureAt = latestConfirmedClarificationCaptureTimestamp(currentState);
691
+ if (!confirmedCaptureAt || String(confirmedCaptureAt) < String(gateAt)) {
692
+ return false;
693
+ }
694
+ return !hasDerivedClarificationCaptureSince(currentState, gateAt);
695
+ }
696
+
697
+ function hasSatisfiedFreshRequirementWriteback({ gate, currentState, analysis, hasProductType }) {
698
+ if (!gate || gateHasClarificationConfirmation(gate)) {
699
+ return false;
700
+ }
701
+ if (!hasProductType || Number(analysis?.missingRequiredFields ?? 0) > 0) {
702
+ return false;
703
+ }
704
+ const gateAt = requirementGateReferenceTimestamp(gate);
705
+ if (!gateAt) {
706
+ return false;
707
+ }
708
+ const capturedAt = latestCaptureTimestamp(currentState);
709
+ return Boolean(capturedAt && String(capturedAt) >= String(gateAt));
710
+ }
711
+
508
712
  function buildProjectFraming({ gate, snapshot, scenario, productType }) {
509
713
  const sections = snapshot.sections ?? {};
510
714
  const guardrailHints = [
@@ -512,6 +716,7 @@ function buildProjectFraming({ gate, snapshot, scenario, productType }) {
512
716
  ...normalizeTextList(sections.constraints?.dependencies),
513
717
  ];
514
718
  const riskProbes = collectProjectRiskProbes(gate, snapshot);
719
+ const validationFraming = buildValidationFraming(sections);
515
720
  return {
516
721
  audience: shortList(
517
722
  sections.users?.primaryUsers,
@@ -524,6 +729,7 @@ function buildProjectFraming({ gate, snapshot, scenario, productType }) {
524
729
  nonGoals: shortList(sections.scope?.outOfScope, '需要先确认本轮先不做什么。'),
525
730
  guardrails: shortList(guardrailHints, '需要先确认哪些现有能力、数据、流程或体验不能被破坏。'),
526
731
  architectureSignals: summarizeArchitectureSignals(gate, snapshot),
732
+ validationFraming,
527
733
  riskProbes,
528
734
  riskProbeSummary: shortList(riskProbes, '当前没有明显命中额外风险探针。'),
529
735
  };
@@ -553,7 +759,7 @@ function describeProductLensFocus(productType) {
553
759
  function requirementTypeDisplay(gate) {
554
760
  const tier = String(gate?.intent?.requirementTier ?? '').toLowerCase();
555
761
  if (tier === 'l0') {
556
- return '快速修正(L0)';
762
+ return '直接处理(L0)';
557
763
  }
558
764
  if (tier === 'l1') {
559
765
  return '现有功能优化(L1)';
@@ -800,6 +1006,7 @@ function buildInlineClarification({ clarification, reflection, presentation }) {
800
1006
  const prompt = reflection?.promptPreview || '本轮需求';
801
1007
  const projectContext = reflection?.projectContext ?? {};
802
1008
  const projectFraming = projectContext.projectFraming ?? {};
1009
+ const validationFraming = projectFraming.validationFraming ?? {};
803
1010
  const productType = projectContext.productType;
804
1011
  const primaryQuestion = clarification.mustAskUser[0] ?? null;
805
1012
  const followUpQuestions = clarification.mustAskUser.slice(1, presentation.mode === 'inline' ? 2 : 3);
@@ -830,6 +1037,27 @@ function buildInlineClarification({ clarification, reflection, presentation }) {
830
1037
  `- 第一版先让用户做到:${projectFraming.firstSlice ?? '待确认'}。`,
831
1038
  `- 这轮先不碰:${projectFraming.nonGoals ?? '待确认'}。`,
832
1039
  `- 必须守住:${projectFraming.guardrails ?? '待确认'}。`,
1040
+ `- 第一批最容易触达:${validationFraming.community ?? '待确认'}。`,
1041
+ `- 社区契合与触达依据:${validationFraming.communityFit ?? '待确认'}。`,
1042
+ `- 当前主要替代:${validationFraming.currentAlternative ?? '待确认'}。`,
1043
+ `- 痛点与替代证据:${validationFraming.painEvidence ?? '待确认'}。`,
1044
+ `- 先怎么手工交付:${validationFraming.manualPath ?? '待确认'}。`,
1045
+ `- 手工作战卡:${validationFraming.manualPlaybook ?? '待确认'}。`,
1046
+ `- 什么承诺才算真需求:${validationFraming.commitmentSignals ?? '待确认'}。`,
1047
+ `- 最低成本先验证:${validationFraming.firstValidationStep ?? '待确认'}。`,
1048
+ `- 怎么先活下来:${validationFraming.defaultAlivePlan ?? '待确认'}。`,
1049
+ `- 付费验证信号:${validationFraming.paymentProof ?? '待确认'}。`,
1050
+ `- 第一版只做一件事:${validationFraming.mvpSlice ?? '待确认'}。`,
1051
+ `- 周末级验证长什么样:${validationFraming.weekendTest ?? '待确认'}。`,
1052
+ `- 最小工具桥接:${validationFraming.smallestExecution ?? '待确认'}。`,
1053
+ `- 产品化门槛:${validationFraming.productizeGate ?? '待确认'}。`,
1054
+ `- 第一批客户路径:${validationFraming.firstCustomerPath ?? '待确认'}。`,
1055
+ `- 初始收费假设:${validationFraming.pricingHypothesis ?? '待确认'}。`,
1056
+ `- 客户 1 盈利路径:${validationFraming.customerOneProfitability ?? '待确认'}。`,
1057
+ `- 销售与增长纪律:${validationFraming.growthDiscipline ?? '待确认'}。`,
1058
+ `- 结果一般是否容易回退:${validationFraming.reversibility ?? '待确认'}。`,
1059
+ `- 更像客户真问题还是自嗨:${validationFraming.customerTruth ?? '待确认'}。`,
1060
+ `- 是否符合团队价值观:${validationFraming.valuesFit ?? '待确认'}。`,
833
1061
  ];
834
1062
  if (projectFraming.architectureSignals) {
835
1063
  lines.push(`- 这次更可能会影响:${projectFraming.architectureSignals}。`);
@@ -950,6 +1178,21 @@ async function buildRequirementIntakeReflection({ projectRoot, ws, snapshot, ana
950
1178
  productType: snapshot.productType ?? resolveCurrentProductType(ws),
951
1179
  });
952
1180
  const lensQuestion = buildLensReflectionQuestion(snapshot.productType ?? resolveCurrentProductType(ws));
1181
+ const validationLoopQuestion = reflectionQuestion(
1182
+ 'validation-loop',
1183
+ '验证与创业闭环',
1184
+ '请确认第一批最容易触达的社区或用户是谁、你为什么算这个社区里的自己人、他们现在靠什么替代、问题到底有多痛、如果先不做完整产品怎么手工交付、手工作战卡怎么写、什么真实承诺最能证明值得继续,以及最低成本验证和先活下来底线是什么。'
1185
+ );
1186
+ const mvpAndSalesQuestion = reflectionQuestion(
1187
+ 'mvp-sales',
1188
+ '最小 MVP 与首批成交',
1189
+ '请确认第一版到底只做哪一件事、能不能压成周末级 MVP 或更轻试跑、能不能先用 spreadsheet 或 no-code 工具跑起来、第一批客户最现实的触达顺序是什么,以及从第一个客户开始准备怎么收费、有没有 10 个潜在用户样本和更强付费信号、达到什么条件才允许产品化。'
1190
+ );
1191
+ const minimalistReviewQuestion = reflectionQuestion(
1192
+ 'minimalist-review',
1193
+ '可逆性 / 客户真问题 / 价值观',
1194
+ '请确认如果验证结果一般,这条路有多可逆,是否会逼出重招聘、长期绑定或重平台化;这更像在解决客户真问题还是满足内部技术冲动;以及它是否符合团队现在想坚持的价值观、是不是你愿意长期住进去的业务形态。'
1195
+ );
953
1196
  const needsDeliveryShapeQuestion = !needsInterfaceSketch
954
1197
  && (
955
1198
  projectFraming.riskProbes.length > 0
@@ -959,6 +1202,9 @@ async function buildRequirementIntakeReflection({ projectRoot, ws, snapshot, ana
959
1202
  ? [
960
1203
  reflectionQuestion('intent', '意图与目标', '请确认我理解得对不对:这次主要是谁在什么场景下遇到什么问题,第一版最想先改善什么结果?'),
961
1204
  lensQuestion,
1205
+ validationLoopQuestion,
1206
+ mvpAndSalesQuestion,
1207
+ minimalistReviewQuestion,
962
1208
  reflectionQuestion('project-context', '项目影响范围', '结合当前项目,请确认第一版最小可用切片是什么;哪些已有模块、入口、流程或历史需求必须复用,哪些可以调整?'),
963
1209
  reflectionQuestion('scope-quality', '范围与验收', '请确认这次先做到哪一步就算有价值;哪些这轮先不动;哪些老用户习惯、现有业务结果或交付节奏不能被影响?'),
964
1210
  needsInterfaceSketch
@@ -1011,6 +1257,8 @@ async function buildRequirementIntakeReflection({ projectRoot, ws, snapshot, ana
1011
1257
  `当前产品:${productName};当前产品场景:${formatProductTypeDisplay(productType, { fallback: '待确认' })};已记录问题:${currentProblem}`,
1012
1258
  `当前范围线索:${currentScope}`,
1013
1259
  `首轮画像:用户群体=${projectFraming.audience};产品形态=${projectFraming.productShape};第一版先做=${projectFraming.firstSlice}`,
1260
+ `验证闭环:可触达人群=${projectFraming.validationFraming?.community};社区契合=${projectFraming.validationFraming?.communityFit};当前替代=${projectFraming.validationFraming?.currentAlternative};痛点证据=${projectFraming.validationFraming?.painEvidence};手工作战卡=${projectFraming.validationFraming?.manualPlaybook}`,
1261
+ `最小成交链:一件事 MVP=${projectFraming.validationFraming?.mvpSlice};周末级验证=${projectFraming.validationFraming?.weekendTest};最小工具桥接=${projectFraming.validationFraming?.smallestExecution};产品化门槛=${projectFraming.validationFraming?.productizeGate}`,
1014
1262
  activeChange ? `仍有 active change:${activeChange.activeChange}(${activeChange.status}),需要和本轮需求分开评估。` : '当前没有检测到 active change 冲突。',
1015
1263
  ],
1016
1264
  },
@@ -1020,6 +1268,8 @@ async function buildRequirementIntakeReflection({ projectRoot, ws, snapshot, ana
1020
1268
  findings: [
1021
1269
  `仍需确认的信息:${shortList(missing, '暂无明显缺口')}`,
1022
1270
  `边界与约束:先不做=${projectFraming.nonGoals};不能破坏=${projectFraming.guardrails}`,
1271
+ `商业验证:承诺信号=${projectFraming.validationFraming?.commitmentSignals};低成本验证=${projectFraming.validationFraming?.firstValidationStep};先活下来=${projectFraming.validationFraming?.defaultAlivePlan};付费验证=${projectFraming.validationFraming?.paymentProof};初始收费=${projectFraming.validationFraming?.pricingHypothesis};客户 1 盈利=${projectFraming.validationFraming?.customerOneProfitability};增长纪律=${projectFraming.validationFraming?.growthDiscipline}`,
1272
+ `极简判断:最小工具桥接=${projectFraming.validationFraming?.smallestExecution};产品化门槛=${projectFraming.validationFraming?.productizeGate};可逆性=${projectFraming.validationFraming?.reversibility};客户真问题=${projectFraming.validationFraming?.customerTruth};价值观一致性=${projectFraming.validationFraming?.valuesFit}`,
1023
1273
  needsInterfaceSketch ? '需求看起来涉及界面或流程,需要先给用户确认草图或关键操作路径。' : `影响环节:${projectFraming.architectureSignals}`,
1024
1274
  `业务提醒:${projectFraming.riskProbeSummary}`,
1025
1275
  '进入实现前必须保留范围、非目标、异常路径和验收证据。',
@@ -1058,6 +1308,27 @@ function renderRequirementIntakeReflection(reflection) {
1058
1308
  `| 第一版先做 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.firstSlice ?? '待补充')} |`,
1059
1309
  `| 暂不处理 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.nonGoals ?? '待补充')} |`,
1060
1310
  `| 不能破坏 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.guardrails ?? '待补充')} |`,
1311
+ `| 可触达人群 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.community ?? '待补充')} |`,
1312
+ `| 社区契合与触达依据 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.communityFit ?? '待补充')} |`,
1313
+ `| 当前替代 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.currentAlternative ?? '待补充')} |`,
1314
+ `| 痛点与替代证据 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.painEvidence ?? '待补充')} |`,
1315
+ `| 手工路径 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.manualPath ?? '待补充')} |`,
1316
+ `| 手工作战卡 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.manualPlaybook ?? '待补充')} |`,
1317
+ `| 承诺信号 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.commitmentSignals ?? '待补充')} |`,
1318
+ `| 最低成本验证 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.firstValidationStep ?? '待补充')} |`,
1319
+ `| 先活下来 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.defaultAlivePlan ?? '待补充')} |`,
1320
+ `| 付费验证信号 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.paymentProof ?? '待补充')} |`,
1321
+ `| 第一版只做一件事 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.mvpSlice ?? '待补充')} |`,
1322
+ `| 周末级验证 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.weekendTest ?? '待补充')} |`,
1323
+ `| 最小工具桥接 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.smallestExecution ?? '待补充')} |`,
1324
+ `| 产品化门槛 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.productizeGate ?? '待补充')} |`,
1325
+ `| 第一批客户路径 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.firstCustomerPath ?? '待补充')} |`,
1326
+ `| 初始收费假设 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.pricingHypothesis ?? '待补充')} |`,
1327
+ `| 客户 1 盈利路径 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.customerOneProfitability ?? '待补充')} |`,
1328
+ `| 销售与增长纪律 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.growthDiscipline ?? '待补充')} |`,
1329
+ `| 可逆性判断 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.reversibility ?? '待补充')} |`,
1330
+ `| 客户真问题校验 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.customerTruth ?? '待补充')} |`,
1331
+ `| 价值观一致性 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.validationFraming?.valuesFit ?? '待补充')} |`,
1061
1332
  `| 影响环节 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.architectureSignals ?? '待补充')} |`,
1062
1333
  `| 业务提醒 | ${escapeMarkdownTableCell(reflection.projectContext.projectFraming?.riskProbeSummary ?? '待补充')} |`,
1063
1334
  '',
@@ -1091,6 +1362,9 @@ function buildRequirementIntakeDepth(gate, reflection = null) {
1091
1362
  const fallbackLayers = [
1092
1363
  reflectionQuestion('product-context', '用户 / 产品形态 / 问题', '先确认:这是给谁用的、它更像个人产品 / 团队流程 / Agent 协作中的哪一种、为什么现在值得解决?'),
1093
1364
  reflectionQuestion('product-outcome', '第一版切片 / 目标 / 成功标准', '请确认第一版最小可用切片是什么;解决后用户先能完成什么,用什么业务结果或验收标准判断有效?'),
1365
+ reflectionQuestion('product-validation', '验证闭环 / 承诺信号', '请确认第一批最容易触达的社区或用户是谁、你为什么算这个社区里的自己人、他们现在靠什么替代、问题到底有多痛、如果先不做完整产品怎么手工交付、手工作战卡怎么写、什么真实承诺最能证明值得继续,以及最低成本验证和先活下来底线是什么。'),
1366
+ reflectionQuestion('product-mvp-sales', '最小 MVP / 首批成交', '请确认第一版到底只做哪一件事、能不能压成周末级 MVP 或更轻试跑、第一批客户最现实的触达顺序是什么,以及从第一个客户开始准备怎么收费、怎么验证客户 1 也能打平时间和交付成本。'),
1367
+ reflectionQuestion('product-minimalist-review', '可逆性 / 客户真问题 / 价值观', '请确认如果验证结果一般,这条路有多可逆;这更像在解决客户真问题还是满足内部技术冲动;以及它是否符合团队现在想坚持的价值观、是不是你愿意长期住进去的业务形态。'),
1094
1368
  reflectionQuestion('product-flow', '范围 / 非目标 / 异常路径', '请拆出本轮先做什么、不做什么、哪些既有行为不能被破坏,以及关键失败路径和恢复方式。'),
1095
1369
  reflectionQuestion(
1096
1370
  'product-detail',
@@ -1226,11 +1500,15 @@ async function synthesizeWorkspace(projectRoot, overrides = {}) {
1226
1500
  if (!(await exists(ws.workspaceRoot))) {
1227
1501
  throw new Error(`Missing workspace: ${ws.workspaceRoot}`);
1228
1502
  }
1503
+ const requirementGate = await readActiveRequirementGate(projectRoot);
1229
1504
  ensureFreshRequirementStateForSynthesize({
1230
- gate: await readActiveRequirementGate(projectRoot),
1505
+ gate: requirementGate,
1231
1506
  currentState: ws.data.currentState ?? {},
1232
1507
  overrides,
1233
1508
  });
1509
+ if (requirementGate?.active && gateHasClarificationConfirmation(requirementGate)) {
1510
+ await ensureRequirementLaneReadyForSynthesize(ws);
1511
+ }
1234
1512
 
1235
1513
  const versionIndex = await readVersionIndex(ws);
1236
1514
  const nextVersionNumber = overrides.versionNumber ?? (versionIndex.length > 0
@@ -1911,6 +2189,18 @@ async function computeWorkspaceGuidance(ws, options = {}) {
1911
2189
  const scenario = await detectWorkspaceScenario(ws.projectRoot, ws, versionIndex);
1912
2190
  const requirementGate = await readActiveRequirementGate(ws.projectRoot);
1913
2191
  const intakeSatisfiedByReview = prdReviewState.status === 'confirmed' && analysis.missingRequiredFields === 0;
2192
+ const intakeSatisfiedByConfirmedWriteback = hasSatisfiedConfirmedClarificationWriteback({
2193
+ gate: requirementGate,
2194
+ currentState,
2195
+ analysis,
2196
+ hasProductType,
2197
+ });
2198
+ const intakeSatisfiedByFreshWriteback = hasSatisfiedFreshRequirementWriteback({
2199
+ gate: requirementGate,
2200
+ currentState,
2201
+ analysis,
2202
+ hasProductType,
2203
+ });
1914
2204
  const intakeReflection = await buildRequirementIntakeReflection({
1915
2205
  projectRoot: ws.projectRoot,
1916
2206
  ws,
@@ -1927,7 +2217,9 @@ async function computeWorkspaceGuidance(ws, options = {}) {
1927
2217
  captureMeta: currentState.captureMeta ?? {},
1928
2218
  prdReviewState,
1929
2219
  limit: Number(options.questionLimit ?? 5),
1930
- }), requirementGate, intakeReflection, { satisfied: intakeSatisfiedByReview });
2220
+ }), requirementGate, intakeReflection, {
2221
+ satisfied: intakeSatisfiedByReview || intakeSatisfiedByConfirmedWriteback || intakeSatisfiedByFreshWriteback,
2222
+ });
1931
2223
 
1932
2224
  let nextAction = 'synthesize';
1933
2225
  let reason = 'PRD 可以合成为第一个版本。';
@@ -1987,6 +2279,12 @@ async function computeWorkspaceGuidance(ws, options = {}) {
1987
2279
 
1988
2280
  const taskGraph = buildWorkflowTaskGraph(analysisSnapshot, analysis, { diagramState, prdReviewState, clarificationState: clarification });
1989
2281
  const gates = deriveGateLabels({ nextAction, diagramState, clarification });
2282
+ const brainstormSuggestion = buildBrainstormSuggestion({
2283
+ currentState,
2284
+ analysis,
2285
+ clarification,
2286
+ requirementGate,
2287
+ });
1990
2288
 
1991
2289
  return {
1992
2290
  versionIndex,
@@ -2002,6 +2300,7 @@ async function computeWorkspaceGuidance(ws, options = {}) {
2002
2300
  suggestedCommand,
2003
2301
  suggestedQuestions,
2004
2302
  gates,
2303
+ brainstormSuggestion,
2005
2304
  };
2006
2305
  }
2007
2306
 
@@ -2065,7 +2364,8 @@ async function nextWorkspace(projectRoot) {
2065
2364
  currentGate: gates.currentGate,
2066
2365
  upcomingGate: gates.upcomingGate,
2067
2366
  },
2068
- workflow: ['clarify', 'classify', 'interview', 'synthesize', 'diagram', 'review', 'freeze', 'handoff'],
2367
+ brainstormSuggestion: guidance.brainstormSuggestion,
2368
+ workflow: ['brainstorm', 'clarify', 'classify', 'interview', 'synthesize', 'diagram', 'review', 'freeze', 'handoff'],
2069
2369
  };
2070
2370
  }
2071
2371
 
@@ -1,14 +0,0 @@
1
- # milvus.io/ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-codex-vs-qwen-vs-minimax.md
2
-
3
- - ID: milvus-io-ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-code
4
- - 状态: approved
5
- - 场景: agent-harness, pr-review-harness
6
- - 触发: 设计 Agent harness、长程任务、状态持久化、验证门禁或人工接管;设计 merge 前高风险复核、独立 reviewer 交叉验证、误报过滤、reviewer agreement 或 merge recommendation
7
- - 不适用: 普通 PRD / 产品流程设计;与 CLI 无关的一次性 UI 视觉问题;默认给每个低风险 PR 拉起多 reviewer 并行审查
8
- - 研究方式: official_page_first
9
- - 来源: https://milvus.io/ar/blog/ai-code-review-gets-better-when-models-debate-claude-vs-gemini-vs-codex-vs-qwen-vs-minimax.md
10
-
11
- ## 备注
12
-
13
- AI code review / PR review harness;reviewer agreement、交叉验证、误报过滤、hallucination filter、merge recommendation
14
-
@@ -1,14 +0,0 @@
1
- # nolanlawson.com/using-ai-to-write-better-code-more-slowly
2
-
3
- - ID: nolanlawson-com-using-ai-to-write-better-code-more-slowly
4
- - 状态: approved
5
- - 场景: agent-harness, pr-review-harness
6
- - 触发: 设计 Agent harness、长程任务、状态持久化、验证门禁或人工接管;设计 merge 前高风险复核、独立 reviewer 交叉验证、误报过滤、reviewer agreement 或 merge recommendation
7
- - 不适用: 普通 PRD / 产品流程设计;与 CLI 无关的一次性 UI 视觉问题;默认给每个低风险 PR 拉起多 reviewer 并行审查
8
- - 研究方式: official_page_first
9
- - 来源: https://nolanlawson.com/2026/05/25/using-ai-to-write-better-code-more-slowly/
10
-
11
- ## 备注
12
-
13
- AI code review / PR review harness;独立审查、主代理先不站队、汇总后再验证、critical/high/medium/low 分级、merge recommendation
14
-
@@ -1,35 +0,0 @@
1
- {
2
- "activeChange": "adaptive-project-framing",
3
- "taskSharding": {
4
- "maxItemsPerFile": 25,
5
- "handoffRequired": true,
6
- "firstFile": "tasks.md",
7
- "nextFilePattern": "tasks-###.md"
8
- },
9
- "taskMetadata": {
10
- "stableIdPattern": "T###.##",
11
- "required": [
12
- "done",
13
- "verify"
14
- ],
15
- "optional": [
16
- "deps",
17
- "type",
18
- "test-layer",
19
- "test-size",
20
- "test-scope",
21
- "evidence",
22
- "evidence-plan",
23
- "upgrade-reason",
24
- "waiver",
25
- "waiver-reason",
26
- "execution-mode",
27
- "parallel-group",
28
- "write-scope",
29
- "owner-role",
30
- "local-verify",
31
- "integration-owner"
32
- ],
33
- "dependencyOrder": "dependencies must appear before dependents"
34
- }
35
- }
@@ -1,35 +0,0 @@
1
- # 流程
2
-
3
- ## 主流程
4
-
5
- - 用户第一次提需求时,OpenPrd 先输出一页项目画像:用户群体、产品形态、第一版先做、先不做、不能破坏、技术落点和风险探针
6
- - 如果用户表达已经足够清楚,Agent 直接用系统归纳的画像摘要请用户确认
7
- - 如果用户表达模糊,Agent 先追问少量高价值问题,必要时给 2 到 3 个方向或行业原型供用户选择
8
- - 画像确认后再进入后续 PRD、review、change 和 tasks 流程
9
-
10
- ## Mermaid 流程图
11
-
12
- ```mermaid
13
- flowchart LR
14
- entry["入口触发<br/>用户第一次提需求时,OpenPrd 先输出一页项目画像:用户群体、产品形态、第一版先做、先不做、不能破坏、技术落点和风险探针"]
15
- experience["产品内步骤<br/>如果用户表达已经足够清楚,Agent 直接用系统归纳的画像摘要请用户确认"]
16
- decision{"决策点<br/>用户一开始只有一句模糊想法,需要先给候选方向而不是硬追问技术细节"}
17
- success(["成功结果<br/>首轮 clarify 能输出结构化的项目画像摘要,并包含用户群体、产品形态、第一版先做、先不做和不能破坏项"])
18
- failure[["失败与恢复<br/>入口过重,导致 Vibe Coding 用户在第一次提需时无法回答"]]
19
- entry -->|"用户第一次提需求时,OpenPrd 先输出一页项目画像:用户群体、产品形态、第一版…"| experience
20
- experience -->|"如果用户表达已经足够清楚,Agent 直接用系统归纳的画像摘要请用户确认"| decision
21
- decision -->|"在第一次提需时先形成一页轻量项目画像,而不是直接跳进局部需求修改"| success
22
- decision -.->|"入口过重,导致 Vibe Coding 用户在第一次提需时无法回答"| failure
23
- ```
24
-
25
- ## 边界情况
26
-
27
- - 用户一开始只有一句模糊想法,需要先给候选方向而不是硬追问技术细节
28
- - 已有项目上下文时应优先沿用已知事实,而不是把所有画像问题重问一遍
29
- - 局部 copy、样式或小交互修正不应被错误升级成重画像流程
30
-
31
- ## 失败模式
32
-
33
- - 入口过重,导致 Vibe Coding 用户在第一次提需时无法回答
34
- - 入口过轻,导致 Agent 没有先确认项目边界就直接落地
35
- - 错误复用旧 active change 或旧 PRD 上下文,导致实现偏题
@@ -1,16 +0,0 @@
1
- # 交接
2
-
3
- - 版本: v0005
4
- - 产品类型: agent
5
- - 模板包: agent
6
- - Digest: 7f0e2d2730982a2ae96647d2377f6204e54f8adadf1b18b05f4bc7167046c575
7
- - 负责人: Codex
8
- - 下一步: 生成新的 change 和 tasks,并按首轮项目画像流程修改 workflow、模板、文档和测试。
9
- - 目标系统: OpenPrd
10
-
11
- ## 变化摘要
12
-
13
- - 新增:clarify 生成的 intake-reflection 应包含首轮项目画像和风险探针
14
- - 新增:inline clarification 应显式展示适用对象、产品形态、第一版先做、先不做、不能破坏和技术落点
15
- - 新增:冷启动 kickoff 问题改成画像导向问题,而不是抽象的通用提问
16
- - 新增:类型专项 intake 模板改成更贴近产品与业务语言的提问方式