@fkqfkq123/opencode-autopilot 0.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.
- package/README.md +462 -0
- package/README.zh-CN.md +464 -0
- package/dist/packages/adapters/opencode/src/opencode-session-client.d.ts +188 -0
- package/dist/packages/adapters/opencode/src/opencode-session-client.js +382 -0
- package/dist/packages/core/src/artifacts/artifact-evaluator.d.ts +17 -0
- package/dist/packages/core/src/artifacts/artifact-evaluator.js +1 -0
- package/dist/packages/core/src/artifacts/artifact.d.ts +7 -0
- package/dist/packages/core/src/artifacts/artifact.js +1 -0
- package/dist/packages/core/src/human-actions/human-action-record.d.ts +10 -0
- package/dist/packages/core/src/human-actions/human-action-record.js +1 -0
- package/dist/packages/core/src/human-actions/human-action.d.ts +13 -0
- package/dist/packages/core/src/human-actions/human-action.js +1 -0
- package/dist/packages/core/src/human-actions/question.d.ts +8 -0
- package/dist/packages/core/src/human-actions/question.js +1 -0
- package/dist/packages/core/src/state/phase.d.ts +2 -0
- package/dist/packages/core/src/state/phase.js +1 -0
- package/dist/packages/core/src/state/workflow-runtime-state.d.ts +14 -0
- package/dist/packages/core/src/state/workflow-runtime-state.js +1 -0
- package/dist/packages/core/src/state/workflow-state.d.ts +13 -0
- package/dist/packages/core/src/state/workflow-state.js +1 -0
- package/dist/packages/core/src/transitions/default-phase-transition.d.ts +5 -0
- package/dist/packages/core/src/transitions/default-phase-transition.js +195 -0
- package/dist/packages/core/src/transitions/phase-transition.d.ts +22 -0
- package/dist/packages/core/src/transitions/phase-transition.js +1 -0
- package/dist/packages/core/src/transitions/transition-action.d.ts +20 -0
- package/dist/packages/core/src/transitions/transition-action.js +1 -0
- package/dist/packages/runtime/src/artifacts/file-system-artifact-evaluator.d.ts +36 -0
- package/dist/packages/runtime/src/artifacts/file-system-artifact-evaluator.js +1213 -0
- package/dist/packages/runtime/src/attach/attach-service.d.ts +15 -0
- package/dist/packages/runtime/src/attach/attach-service.js +31 -0
- package/dist/packages/runtime/src/bootstrap/create-harness.d.ts +33 -0
- package/dist/packages/runtime/src/bootstrap/create-harness.js +79 -0
- package/dist/packages/runtime/src/bootstrap/initialize-workflow.d.ts +8 -0
- package/dist/packages/runtime/src/bootstrap/initialize-workflow.js +33 -0
- package/dist/packages/runtime/src/commands/create-opencode-workflow-commands.d.ts +12 -0
- package/dist/packages/runtime/src/commands/create-opencode-workflow-commands.js +24 -0
- package/dist/packages/runtime/src/commands/default-workflow-command-runner.d.ts +4 -0
- package/dist/packages/runtime/src/commands/default-workflow-command-runner.js +343 -0
- package/dist/packages/runtime/src/commands/opencode-plugin-command-adapter.d.ts +20 -0
- package/dist/packages/runtime/src/commands/opencode-plugin-command-adapter.js +22 -0
- package/dist/packages/runtime/src/commands/workflow-command-runner.d.ts +19 -0
- package/dist/packages/runtime/src/commands/workflow-command-runner.js +1 -0
- package/dist/packages/runtime/src/commands/workflow-open-request.d.ts +10 -0
- package/dist/packages/runtime/src/commands/workflow-open-request.js +220 -0
- package/dist/packages/runtime/src/config/skill-registry.d.ts +15 -0
- package/dist/packages/runtime/src/config/skill-registry.js +108 -0
- package/dist/packages/runtime/src/config/workflow-config.d.ts +17 -0
- package/dist/packages/runtime/src/config/workflow-config.js +51 -0
- package/dist/packages/runtime/src/diagnostics/workflow-diagnostics-format.d.ts +4 -0
- package/dist/packages/runtime/src/diagnostics/workflow-diagnostics-format.js +70 -0
- package/dist/packages/runtime/src/diagnostics/workflow-doctor.d.ts +23 -0
- package/dist/packages/runtime/src/diagnostics/workflow-doctor.js +120 -0
- package/dist/packages/runtime/src/engine/default-workflow-engine.d.ts +9 -0
- package/dist/packages/runtime/src/engine/default-workflow-engine.js +337 -0
- package/dist/packages/runtime/src/engine/workflow-engine.d.ts +28 -0
- package/dist/packages/runtime/src/engine/workflow-engine.js +1 -0
- package/dist/packages/runtime/src/events/file-system-workflow-event-store.d.ts +8 -0
- package/dist/packages/runtime/src/events/file-system-workflow-event-store.js +28 -0
- package/dist/packages/runtime/src/events/workflow-event-store.d.ts +10 -0
- package/dist/packages/runtime/src/events/workflow-event-store.js +1 -0
- package/dist/packages/runtime/src/index.d.ts +4 -0
- package/dist/packages/runtime/src/index.js +4 -0
- package/dist/packages/runtime/src/install/workflow-installer.d.ts +15 -0
- package/dist/packages/runtime/src/install/workflow-installer.js +111 -0
- package/dist/packages/runtime/src/plugin/workflow-plugin-entry.d.ts +167 -0
- package/dist/packages/runtime/src/plugin/workflow-plugin-entry.js +340 -0
- package/dist/packages/runtime/src/presentation/human-action-renderer.d.ts +13 -0
- package/dist/packages/runtime/src/presentation/human-action-renderer.js +161 -0
- package/dist/packages/runtime/src/presentation/watch-renderer.d.ts +12 -0
- package/dist/packages/runtime/src/presentation/watch-renderer.js +17 -0
- package/dist/packages/runtime/src/recovery/basic-recovery-classifier.d.ts +4 -0
- package/dist/packages/runtime/src/recovery/basic-recovery-classifier.js +12 -0
- package/dist/packages/runtime/src/recovery/recovery-classifier.d.ts +4 -0
- package/dist/packages/runtime/src/recovery/recovery-classifier.js +1 -0
- package/dist/packages/runtime/src/scheduling/immediate-tick-scheduler.d.ts +9 -0
- package/dist/packages/runtime/src/scheduling/immediate-tick-scheduler.js +28 -0
- package/dist/packages/runtime/src/scheduling/tick-scheduler.d.ts +3 -0
- package/dist/packages/runtime/src/scheduling/tick-scheduler.js +1 -0
- package/dist/packages/runtime/src/sessions/file-system-session-coordinator.d.ts +19 -0
- package/dist/packages/runtime/src/sessions/file-system-session-coordinator.js +132 -0
- package/dist/packages/runtime/src/sessions/session-activity-monitor.d.ts +22 -0
- package/dist/packages/runtime/src/sessions/session-activity-monitor.js +112 -0
- package/dist/packages/runtime/src/sessions/session-coordinator.d.ts +24 -0
- package/dist/packages/runtime/src/sessions/session-coordinator.js +1 -0
- package/dist/packages/runtime/src/shared/json-file.d.ts +2 -0
- package/dist/packages/runtime/src/shared/json-file.js +19 -0
- package/dist/packages/runtime/src/state/file-system-human-action-store.d.ts +15 -0
- package/dist/packages/runtime/src/state/file-system-human-action-store.js +69 -0
- package/dist/packages/runtime/src/state/file-system-workflow-state-store.d.ts +15 -0
- package/dist/packages/runtime/src/state/file-system-workflow-state-store.js +59 -0
- package/dist/packages/runtime/src/state/human-action-service.d.ts +21 -0
- package/dist/packages/runtime/src/state/human-action-service.js +80 -0
- package/dist/packages/runtime/src/state/human-action-store.d.ts +9 -0
- package/dist/packages/runtime/src/state/human-action-store.js +1 -0
- package/dist/packages/runtime/src/state/workflow-state-store.d.ts +11 -0
- package/dist/packages/runtime/src/state/workflow-state-store.js +1 -0
- package/dist/packages/runtime/src/subtasks/noop-subtask-tracker.d.ts +4 -0
- package/dist/packages/runtime/src/subtasks/noop-subtask-tracker.js +5 -0
- package/dist/packages/runtime/src/subtasks/subtask-tracker.d.ts +3 -0
- package/dist/packages/runtime/src/subtasks/subtask-tracker.js +1 -0
- package/dist/packages/runtime/src/workspace/workflow-workspace.d.ts +31 -0
- package/dist/packages/runtime/src/workspace/workflow-workspace.js +43 -0
- package/dist/plugin.d.ts +1 -0
- package/dist/plugin.js +1 -0
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +175 -0
- package/package.json +56 -0
|
@@ -0,0 +1,1213 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { readJsonFile, writeJsonFile } from "../shared/json-file";
|
|
3
|
+
const DEFAULT_USER_REQUEST = "新增 workflow harness MVP。";
|
|
4
|
+
const SPEC_REFINEMENT_QUESTION_BLUEPRINTS = [
|
|
5
|
+
{
|
|
6
|
+
id: "q_original_summary",
|
|
7
|
+
heading: "## 原始需求摘要",
|
|
8
|
+
priority: "required",
|
|
9
|
+
text: "请补充本次需求的原始需求摘要。",
|
|
10
|
+
canAutoResolve: true,
|
|
11
|
+
suggestedAnswer: ({ initialRequest }) => initialRequest,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: "q_requirements_clarification",
|
|
15
|
+
heading: "## 需求澄清",
|
|
16
|
+
priority: "required",
|
|
17
|
+
text: "请补充本次需求的需求澄清,明确范围边界、关键行为或例外场景。",
|
|
18
|
+
canAutoResolve: false,
|
|
19
|
+
suggestedAnswer: () => "请补充范围边界、关键行为、例外场景或默认策略。",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "q_technical_constraints",
|
|
23
|
+
heading: "## 技术约束",
|
|
24
|
+
priority: "required",
|
|
25
|
+
text: "请补充本次需求的技术约束。",
|
|
26
|
+
canAutoResolve: true,
|
|
27
|
+
suggestedAnswer: () => "遵循现有仓库技术栈、代码风格、宿主可插拔约束与现有 workflow runtime 模式。",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "q_acceptance_criteria",
|
|
31
|
+
heading: "## 验收标准",
|
|
32
|
+
priority: "required",
|
|
33
|
+
text: "请确认本次需求的具体验收标准或完成定义。",
|
|
34
|
+
canAutoResolve: false,
|
|
35
|
+
suggestedAnswer: () => "请补充可验证的验收标准,例如页面行为、交互结果或完成定义。",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "q_open_questions",
|
|
39
|
+
heading: "## 疑问清单",
|
|
40
|
+
priority: "recommended",
|
|
41
|
+
text: "请补充当前仍待确认的疑问;若无疑问请填写“无”。",
|
|
42
|
+
canAutoResolve: true,
|
|
43
|
+
suggestedAnswer: () => "无",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "q_report_language",
|
|
47
|
+
heading: "## 报告语言",
|
|
48
|
+
priority: "optional",
|
|
49
|
+
text: "请确认 refinement 报告语言。",
|
|
50
|
+
canAutoResolve: true,
|
|
51
|
+
suggestedAnswer: () => "中文",
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
const unresolvedMarkers = ["待确认", "待补充", "待判定", "待 ai", "tbd", "todo", "unknown", "自行补充", "按文档内容", "按照文档内容", "参考文档"];
|
|
55
|
+
const SECTION_RULES = {
|
|
56
|
+
spec_refinement: {
|
|
57
|
+
title: "# 规格精炼报告",
|
|
58
|
+
sections: [
|
|
59
|
+
"## 原始需求摘要",
|
|
60
|
+
"## 需求澄清",
|
|
61
|
+
"## 技术约束",
|
|
62
|
+
"## 验收标准",
|
|
63
|
+
"## 疑问清单",
|
|
64
|
+
"## 准入结论",
|
|
65
|
+
"## 报告语言",
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
plan: {
|
|
69
|
+
title: "# 开发计划",
|
|
70
|
+
sections: [
|
|
71
|
+
"## 需求摘要",
|
|
72
|
+
"## 影响范围",
|
|
73
|
+
"## 核心修改文件",
|
|
74
|
+
"## Cascade Files",
|
|
75
|
+
"## 实现方案",
|
|
76
|
+
"## i18n 变更",
|
|
77
|
+
"## API / Route 变更",
|
|
78
|
+
"## 组件复用决策",
|
|
79
|
+
"## Section 验收映射(多 section Figma 页面必填)",
|
|
80
|
+
"## Key Visual Elements(复杂 Figma 页面必填)",
|
|
81
|
+
"## 疑问清单",
|
|
82
|
+
"## 风险评估",
|
|
83
|
+
"## 报告语言",
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
develop: {
|
|
87
|
+
title: "# 开发报告",
|
|
88
|
+
sections: [
|
|
89
|
+
"## 状态",
|
|
90
|
+
"## 修改文件",
|
|
91
|
+
"## 配套修改",
|
|
92
|
+
"## 自检结果",
|
|
93
|
+
"## 备注",
|
|
94
|
+
"## 报告语言",
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
review: {
|
|
98
|
+
title: "# 审查报告",
|
|
99
|
+
sections: [
|
|
100
|
+
"## 状态",
|
|
101
|
+
"## 轮次",
|
|
102
|
+
"## 检查范围",
|
|
103
|
+
"## 组件复用验收结果(如适用)",
|
|
104
|
+
"## Section 验收映射检查结果(如适用)",
|
|
105
|
+
"## 发现的问题",
|
|
106
|
+
"## 问题严重度汇总",
|
|
107
|
+
"## 历史遗留观察项(非阻塞,可选)",
|
|
108
|
+
"## Regression 风险评估",
|
|
109
|
+
"## 结论",
|
|
110
|
+
"## 报告语言",
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
test: {
|
|
114
|
+
title: "# 测试报告",
|
|
115
|
+
sections: [
|
|
116
|
+
"## 状态",
|
|
117
|
+
"## 轮次",
|
|
118
|
+
"## 测试策略",
|
|
119
|
+
"## 验证范围",
|
|
120
|
+
"## 测试概要",
|
|
121
|
+
"## 新增页面专项验证(如适用)",
|
|
122
|
+
"## Figma 高保真验证(如适用)",
|
|
123
|
+
"## Key Visual Elements 验证(如适用)",
|
|
124
|
+
"## 失败项",
|
|
125
|
+
"## 历史遗留观察项(非阻塞,可选)",
|
|
126
|
+
"## Regression 验证",
|
|
127
|
+
"## 覆盖范围",
|
|
128
|
+
"## 开发者决策建议",
|
|
129
|
+
"## 结论",
|
|
130
|
+
"## 报告语言",
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
const stripComments = (content) => content.replace(/<!--([\s\S]*?)-->/g, "").trim();
|
|
135
|
+
const extractSectionBody = (content, heading, allHeadings) => {
|
|
136
|
+
const start = content.indexOf(heading);
|
|
137
|
+
if (start === -1) {
|
|
138
|
+
return "";
|
|
139
|
+
}
|
|
140
|
+
const afterHeading = start + heading.length;
|
|
141
|
+
const nextHeadingIndexes = allHeadings
|
|
142
|
+
.map((nextHeading) => content.indexOf(nextHeading, afterHeading))
|
|
143
|
+
.filter((index) => index !== -1);
|
|
144
|
+
const end = nextHeadingIndexes.length > 0 ? Math.min(...nextHeadingIndexes) : content.length;
|
|
145
|
+
return stripComments(content.slice(afterHeading, end));
|
|
146
|
+
};
|
|
147
|
+
const sectionHasContent = (content, heading, allHeadings) => extractSectionBody(content, heading, allHeadings).length > 0;
|
|
148
|
+
const sanitizeSummaryBody = (body) => body
|
|
149
|
+
.split("\n")
|
|
150
|
+
.map((line) => line.trim())
|
|
151
|
+
.filter((line) => line.length > 0)
|
|
152
|
+
.filter((line) => line !== "[USER_PROMPT]")
|
|
153
|
+
.filter((line) => !/^(?:文档[::]?)?(?:\.?\.?\/|\/).+\.(?:md|markdown|txt|rst|adoc|pdf|docx)$/i.test(line))
|
|
154
|
+
.join("\n");
|
|
155
|
+
const questionChecklistResolved = (content, sections) => {
|
|
156
|
+
const body = extractSectionBody(content, "## 疑问清单", sections);
|
|
157
|
+
const normalized = body.trim();
|
|
158
|
+
if (!normalized) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
if (["无", "none", "n/a", "na", "no questions", "not applicable"].includes(normalized.toLowerCase())) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
return normalized
|
|
165
|
+
.split("\n")
|
|
166
|
+
.map((line) => line.trim())
|
|
167
|
+
.filter(Boolean)
|
|
168
|
+
.every((line) => {
|
|
169
|
+
const lower = line.toLowerCase();
|
|
170
|
+
return (lower.includes("resolved")
|
|
171
|
+
|| lower.includes("answered")
|
|
172
|
+
|| lower.includes("confirmed")
|
|
173
|
+
|| lower.includes("all answered")
|
|
174
|
+
|| lower.includes("已解决")
|
|
175
|
+
|| lower.includes("已确认")
|
|
176
|
+
|| lower.includes("全部解决")
|
|
177
|
+
|| line.startsWith("✅")
|
|
178
|
+
|| line.startsWith("- ✅")
|
|
179
|
+
|| line.startsWith("- [x]")
|
|
180
|
+
|| line.startsWith("[x]"));
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
const hasReadyForPlanConclusion = (content, sections) => {
|
|
184
|
+
const body = extractSectionBody(content, "## 准入结论", sections);
|
|
185
|
+
return body
|
|
186
|
+
.split("\n")
|
|
187
|
+
.map((line) => line.replace(/[*`>#-]/g, "").trim())
|
|
188
|
+
.some((line) => line === "READY_FOR_PLAN" || line.includes("READY_FOR_PLAN"));
|
|
189
|
+
};
|
|
190
|
+
const containsUnresolvedMarker = (body) => {
|
|
191
|
+
const normalized = body.trim().toLowerCase();
|
|
192
|
+
return unresolvedMarkers.some((marker) => normalized.includes(marker));
|
|
193
|
+
};
|
|
194
|
+
const hasCompletedStatus = (content, sections) => {
|
|
195
|
+
const body = extractSectionBody(content, "## 状态", sections).toLowerCase();
|
|
196
|
+
return ["通过", "pass", "passed", "completed", "完成", "done"].some((token) => body.includes(token));
|
|
197
|
+
};
|
|
198
|
+
const firstMeaningfulLine = (body) => body
|
|
199
|
+
.split("\n")
|
|
200
|
+
.map((line) => line.trim())
|
|
201
|
+
.find(Boolean) ?? "";
|
|
202
|
+
const getReportStatus = (content) => {
|
|
203
|
+
const rawConclusion = extractSectionBody(content, "## 结论", ["## 结论", "## Conclusion"]);
|
|
204
|
+
const conclusion = firstMeaningfulLine(rawConclusion).toLowerCase();
|
|
205
|
+
if (rawConclusion.trim()) {
|
|
206
|
+
if (["未通过", "失败", "fail", "failed", "blocked"].some((token) => conclusion.includes(token))) {
|
|
207
|
+
return "fail";
|
|
208
|
+
}
|
|
209
|
+
if (["通过", "pass", "passed"].some((token) => conclusion.includes(token))) {
|
|
210
|
+
return "pass";
|
|
211
|
+
}
|
|
212
|
+
return "unknown";
|
|
213
|
+
}
|
|
214
|
+
const firstLine = firstMeaningfulLine(extractSectionBody(content, "## 状态", ["## 状态", "## Status"]).toLowerCase());
|
|
215
|
+
if (!firstLine) {
|
|
216
|
+
return "unknown";
|
|
217
|
+
}
|
|
218
|
+
if (firstLine.includes("待判定") || firstLine.includes("待执行") || firstLine.includes("pending") || firstLine.includes("unknown")) {
|
|
219
|
+
return "unknown";
|
|
220
|
+
}
|
|
221
|
+
if (["未通过", "失败", "fail", "failed", "blocked"].some((token) => firstLine.includes(token))) {
|
|
222
|
+
return "fail";
|
|
223
|
+
}
|
|
224
|
+
if (["通过", "pass", "passed", "completed", "完成", "done"].some((token) => firstLine.includes(token))) {
|
|
225
|
+
return "pass";
|
|
226
|
+
}
|
|
227
|
+
return "unknown";
|
|
228
|
+
};
|
|
229
|
+
const hasBlockingSeverity = (content) => content.split("\n").some((line) => {
|
|
230
|
+
const normalized = line.trim().toLowerCase();
|
|
231
|
+
if (/^#{0,6}\s*\[(?:severity:\s*)?(blocker|critical)\](?:\s|$)/i.test(normalized)) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
const summaryMatch = normalized.match(/^(?:[-*]\s*)?(blocker|critical)\s*[::]\s*(\d+)/i);
|
|
235
|
+
return summaryMatch ? Number(summaryMatch[2]) > 0 : false;
|
|
236
|
+
});
|
|
237
|
+
const hasSufficientTestEvidence = (content, sections) => {
|
|
238
|
+
const criticalSections = [
|
|
239
|
+
"## 测试概要",
|
|
240
|
+
"## Regression 验证",
|
|
241
|
+
"## 覆盖范围",
|
|
242
|
+
"## 开发者决策建议",
|
|
243
|
+
"## 结论",
|
|
244
|
+
];
|
|
245
|
+
for (const section of criticalSections) {
|
|
246
|
+
const body = extractSectionBody(content, section, sections);
|
|
247
|
+
if (!body || containsUnresolvedMarker(body)) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
const normalized = body.toLowerCase();
|
|
251
|
+
if (normalized.includes("人工走查")
|
|
252
|
+
|| normalized.includes("手动打开")
|
|
253
|
+
|| normalized.includes("未自动执行")
|
|
254
|
+
|| normalized.includes("建议在浏览器中做一次")) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return true;
|
|
259
|
+
};
|
|
260
|
+
export class FileSystemArtifactEvaluator {
|
|
261
|
+
workspace;
|
|
262
|
+
constructor(workspace) {
|
|
263
|
+
this.workspace = workspace;
|
|
264
|
+
}
|
|
265
|
+
normalizeUserRequest(userRequest) {
|
|
266
|
+
const normalized = userRequest?.trim();
|
|
267
|
+
return normalized && normalized.length > 0 ? normalized : DEFAULT_USER_REQUEST;
|
|
268
|
+
}
|
|
269
|
+
buildRefinementClarification(userRequest) {
|
|
270
|
+
return `当前主体需求:${userRequest}\n\n需要在进入 plan 前进一步确认范围边界、例外场景与交付口径。`;
|
|
271
|
+
}
|
|
272
|
+
buildRefinementConstraints() {
|
|
273
|
+
return "遵循现有仓库技术栈、代码风格、宿主可插拔约束与现有 workflow runtime 模式。";
|
|
274
|
+
}
|
|
275
|
+
buildQuestionChecklist(questions, answers = {}) {
|
|
276
|
+
if (questions.length === 0) {
|
|
277
|
+
return "无";
|
|
278
|
+
}
|
|
279
|
+
return questions.map((question) => {
|
|
280
|
+
const answer = answers[question.id]?.trim();
|
|
281
|
+
if (answer && !containsUnresolvedMarker(answer)) {
|
|
282
|
+
return `- [x] ${question.text} 已确认:${answer}`;
|
|
283
|
+
}
|
|
284
|
+
return `- [ ] ${question.text}`;
|
|
285
|
+
}).join("\n");
|
|
286
|
+
}
|
|
287
|
+
defaultSpecSectionBodies(userRequest) {
|
|
288
|
+
const initialRequest = this.normalizeUserRequest(userRequest);
|
|
289
|
+
return {
|
|
290
|
+
"## 原始需求摘要": initialRequest,
|
|
291
|
+
"## 需求澄清": this.buildRefinementClarification(initialRequest),
|
|
292
|
+
"## 技术约束": this.buildRefinementConstraints(),
|
|
293
|
+
"## 验收标准": "待确认:请补充本次需求的可验证验收标准或完成定义。",
|
|
294
|
+
"## 疑问清单": "无",
|
|
295
|
+
"## 准入结论": "NOT_READY_FOR_PLAN",
|
|
296
|
+
"## 报告语言": "中文",
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
readSpecSectionBodies(content, userRequest) {
|
|
300
|
+
const defaults = this.defaultSpecSectionBodies(userRequest);
|
|
301
|
+
return Object.fromEntries(SECTION_RULES.spec_refinement.sections.map((heading) => {
|
|
302
|
+
const body = extractSectionBody(content, heading, SECTION_RULES.spec_refinement.sections);
|
|
303
|
+
return [heading, body || defaults[heading] || ""];
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
buildSpecRefinementQuestionsFromSections(args) {
|
|
307
|
+
const { sectionBodies, initialRequest } = args;
|
|
308
|
+
return SPEC_REFINEMENT_QUESTION_BLUEPRINTS
|
|
309
|
+
.filter((blueprint) => {
|
|
310
|
+
const body = sectionBodies[blueprint.heading]?.trim() ?? "";
|
|
311
|
+
return !body || containsUnresolvedMarker(body);
|
|
312
|
+
})
|
|
313
|
+
.map((blueprint) => ({
|
|
314
|
+
id: blueprint.id,
|
|
315
|
+
priority: blueprint.priority,
|
|
316
|
+
text: blueprint.text,
|
|
317
|
+
canAutoResolve: blueprint.canAutoResolve,
|
|
318
|
+
suggestedAnswer: blueprint.suggestedAnswer({ initialRequest }),
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
finalizeSpecSectionBodies(args) {
|
|
322
|
+
const { sectionBodies, initialRequest, answers = {} } = args;
|
|
323
|
+
const questions = this.buildSpecRefinementQuestionsFromSections({
|
|
324
|
+
sectionBodies,
|
|
325
|
+
initialRequest,
|
|
326
|
+
});
|
|
327
|
+
const unresolvedQuestions = questions.filter((question) => {
|
|
328
|
+
const answer = answers[question.id]?.trim();
|
|
329
|
+
return !answer || containsUnresolvedMarker(answer);
|
|
330
|
+
});
|
|
331
|
+
return {
|
|
332
|
+
...sectionBodies,
|
|
333
|
+
"## 疑问清单": this.buildQuestionChecklist(unresolvedQuestions, answers),
|
|
334
|
+
"## 准入结论": unresolvedQuestions.length === 0 ? "READY_FOR_PLAN" : "NOT_READY_FOR_PLAN",
|
|
335
|
+
"## 报告语言": sectionBodies["## 报告语言"]?.trim() || "中文",
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
buildRefinementArtifactFromSections(sectionBodies) {
|
|
339
|
+
return [
|
|
340
|
+
"# 规格精炼报告",
|
|
341
|
+
"",
|
|
342
|
+
...SECTION_RULES.spec_refinement.sections.flatMap((heading) => [heading, sectionBodies[heading] ?? "", ""]),
|
|
343
|
+
].slice(0, -1).join("\n");
|
|
344
|
+
}
|
|
345
|
+
autoFillSpecRefinementContent(content, initialRequest) {
|
|
346
|
+
const sectionBodies = this.readSpecSectionBodies(content, initialRequest);
|
|
347
|
+
for (const blueprint of SPEC_REFINEMENT_QUESTION_BLUEPRINTS) {
|
|
348
|
+
if (!blueprint.canAutoResolve) {
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
const currentBody = sectionBodies[blueprint.heading]?.trim() ?? "";
|
|
352
|
+
if (currentBody && !containsUnresolvedMarker(currentBody)) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
sectionBodies[blueprint.heading] = blueprint.suggestedAnswer({ initialRequest });
|
|
356
|
+
}
|
|
357
|
+
const finalBodies = this.finalizeSpecSectionBodies({
|
|
358
|
+
sectionBodies,
|
|
359
|
+
initialRequest,
|
|
360
|
+
});
|
|
361
|
+
return this.buildRefinementArtifactFromSections(finalBodies);
|
|
362
|
+
}
|
|
363
|
+
buildRefinementArtifact(args) {
|
|
364
|
+
const userRequest = this.normalizeUserRequest(args.userRequest);
|
|
365
|
+
const acceptanceAnswer = args.acceptanceAnswer?.trim();
|
|
366
|
+
const sectionBodies = this.defaultSpecSectionBodies(userRequest);
|
|
367
|
+
if (acceptanceAnswer) {
|
|
368
|
+
sectionBodies["## 验收标准"] = acceptanceAnswer;
|
|
369
|
+
}
|
|
370
|
+
const finalBodies = this.finalizeSpecSectionBodies({
|
|
371
|
+
sectionBodies,
|
|
372
|
+
initialRequest: userRequest,
|
|
373
|
+
...(acceptanceAnswer ? { answers: { q_acceptance_criteria: acceptanceAnswer } } : {}),
|
|
374
|
+
});
|
|
375
|
+
return this.buildRefinementArtifactFromSections(finalBodies);
|
|
376
|
+
}
|
|
377
|
+
buildPlanArtifactFromRefinementContent(refinementContent) {
|
|
378
|
+
const summary = sanitizeSummaryBody(extractSectionBody(refinementContent, "## 原始需求摘要", SECTION_RULES.spec_refinement.sections).trim())
|
|
379
|
+
|| this.normalizeUserRequest(undefined);
|
|
380
|
+
const constraints = extractSectionBody(refinementContent, "## 技术约束", SECTION_RULES.spec_refinement.sections).trim()
|
|
381
|
+
|| "待结合仓库现状补充技术约束。";
|
|
382
|
+
const acceptance = extractSectionBody(refinementContent, "## 验收标准", SECTION_RULES.spec_refinement.sections).trim()
|
|
383
|
+
|| "待补充可验证验收标准。";
|
|
384
|
+
const questions = extractSectionBody(refinementContent, "## 疑问清单", SECTION_RULES.spec_refinement.sections).trim()
|
|
385
|
+
|| "无";
|
|
386
|
+
return [
|
|
387
|
+
"# 开发计划",
|
|
388
|
+
"",
|
|
389
|
+
"## 需求摘要",
|
|
390
|
+
summary,
|
|
391
|
+
"",
|
|
392
|
+
"## 影响范围",
|
|
393
|
+
"待 AI 结合仓库结构与需求范围补充。",
|
|
394
|
+
"",
|
|
395
|
+
"## 核心修改文件",
|
|
396
|
+
"待 AI 结合代码探索结果补充。",
|
|
397
|
+
"",
|
|
398
|
+
"## Cascade Files",
|
|
399
|
+
"待 AI 结合依赖链补充。",
|
|
400
|
+
"",
|
|
401
|
+
"## 实现方案",
|
|
402
|
+
"待 AI 输出分步骤实现方案,并明确为什么这样做。",
|
|
403
|
+
"",
|
|
404
|
+
"## i18n 变更",
|
|
405
|
+
"待 AI 判定是否涉及。",
|
|
406
|
+
"",
|
|
407
|
+
"## API / Route 变更",
|
|
408
|
+
"待 AI 判定是否涉及。",
|
|
409
|
+
"",
|
|
410
|
+
"## 组件复用决策",
|
|
411
|
+
"待 AI 说明复用/不复用理由。",
|
|
412
|
+
"",
|
|
413
|
+
"## Section 验收映射(多 section Figma 页面必填)",
|
|
414
|
+
"不适用或待 AI 结合设计上下文补充。",
|
|
415
|
+
"",
|
|
416
|
+
"## Key Visual Elements(复杂 Figma 页面必填)",
|
|
417
|
+
"不适用或待 AI 结合设计上下文补充。",
|
|
418
|
+
"",
|
|
419
|
+
"## 疑问清单",
|
|
420
|
+
questions,
|
|
421
|
+
"",
|
|
422
|
+
"## 风险评估",
|
|
423
|
+
`技术约束:${constraints}\n\n验收标准:${acceptance}\n\n待 AI 进一步识别风险、受影响旧功能与回归验证建议。`,
|
|
424
|
+
"",
|
|
425
|
+
"## 报告语言",
|
|
426
|
+
"中文",
|
|
427
|
+
].join("\n");
|
|
428
|
+
}
|
|
429
|
+
buildDevelopArtifactFromPlanContent(planContent) {
|
|
430
|
+
const summary = extractSectionBody(planContent, "## 需求摘要", SECTION_RULES.plan.sections).trim()
|
|
431
|
+
|| "待结合计划补充需求摘要。";
|
|
432
|
+
const coreFiles = extractSectionBody(planContent, "## 核心修改文件", SECTION_RULES.plan.sections).trim()
|
|
433
|
+
|| "待根据开发计划补充。";
|
|
434
|
+
const implementation = extractSectionBody(planContent, "## 实现方案", SECTION_RULES.plan.sections).trim()
|
|
435
|
+
|| "待根据开发计划补充。";
|
|
436
|
+
const risk = extractSectionBody(planContent, "## 风险评估", SECTION_RULES.plan.sections).trim()
|
|
437
|
+
|| "待根据开发计划补充。";
|
|
438
|
+
return [
|
|
439
|
+
"# 开发报告",
|
|
440
|
+
"",
|
|
441
|
+
"## 状态",
|
|
442
|
+
"进行中",
|
|
443
|
+
"",
|
|
444
|
+
"## 修改文件",
|
|
445
|
+
coreFiles,
|
|
446
|
+
"",
|
|
447
|
+
"## 配套修改",
|
|
448
|
+
`需求摘要:${summary}\n\n实现方案:${implementation}`,
|
|
449
|
+
"",
|
|
450
|
+
"## 自检结果",
|
|
451
|
+
"待 AI 在完成实现后补充测试、构建、自检证据,以及受影响旧功能的回归检查结果。",
|
|
452
|
+
"",
|
|
453
|
+
"## 备注",
|
|
454
|
+
`风险提示:${risk}`,
|
|
455
|
+
"",
|
|
456
|
+
"## 报告语言",
|
|
457
|
+
"中文",
|
|
458
|
+
].join("\n");
|
|
459
|
+
}
|
|
460
|
+
buildReviewArtifactFromDevelopContent(developContent) {
|
|
461
|
+
const modifiedFiles = extractSectionBody(developContent, "## 修改文件", SECTION_RULES.develop.sections).trim()
|
|
462
|
+
|| "待根据开发结果补充。";
|
|
463
|
+
const pairedChanges = extractSectionBody(developContent, "## 配套修改", SECTION_RULES.develop.sections).trim()
|
|
464
|
+
|| "待根据开发结果补充。";
|
|
465
|
+
const selfCheck = extractSectionBody(developContent, "## 自检结果", SECTION_RULES.develop.sections).trim()
|
|
466
|
+
|| "待根据开发结果补充。";
|
|
467
|
+
const notes = extractSectionBody(developContent, "## 备注", SECTION_RULES.develop.sections).trim()
|
|
468
|
+
|| "无";
|
|
469
|
+
return [
|
|
470
|
+
"# 审查报告",
|
|
471
|
+
"",
|
|
472
|
+
"## 状态",
|
|
473
|
+
"待判定",
|
|
474
|
+
"",
|
|
475
|
+
"## 轮次",
|
|
476
|
+
"第 1 轮",
|
|
477
|
+
"",
|
|
478
|
+
"## 检查范围",
|
|
479
|
+
"待 AI 根据开发输出与计划补充。",
|
|
480
|
+
"",
|
|
481
|
+
"## 组件复用验收结果(如适用)",
|
|
482
|
+
"不适用或待 AI 判定。",
|
|
483
|
+
"",
|
|
484
|
+
"## Section 验收映射检查结果(如适用)",
|
|
485
|
+
"不适用或待 AI 判定。",
|
|
486
|
+
"",
|
|
487
|
+
"## 发现的问题",
|
|
488
|
+
"待 AI 审查开发产物后补充。",
|
|
489
|
+
"",
|
|
490
|
+
"## 问题严重度汇总",
|
|
491
|
+
"blocker: 0",
|
|
492
|
+
"",
|
|
493
|
+
"## 历史遗留观察项(非阻塞,可选)",
|
|
494
|
+
notes,
|
|
495
|
+
"",
|
|
496
|
+
"## Regression 风险评估",
|
|
497
|
+
`修改文件:${modifiedFiles}\n\n配套修改:${pairedChanges}\n\n自检结果:${selfCheck}\n\n请重点关注是否影响既有功能并补充回归风险判断。`,
|
|
498
|
+
"",
|
|
499
|
+
"## 结论",
|
|
500
|
+
"待判定",
|
|
501
|
+
"",
|
|
502
|
+
"## 报告语言",
|
|
503
|
+
"中文",
|
|
504
|
+
].join("\n");
|
|
505
|
+
}
|
|
506
|
+
buildTestArtifactFromReviewContent(reviewContent) {
|
|
507
|
+
const scope = extractSectionBody(reviewContent, "## 检查范围", SECTION_RULES.review.sections).trim()
|
|
508
|
+
|| "待根据审查结果补充。";
|
|
509
|
+
const findings = extractSectionBody(reviewContent, "## 发现的问题", SECTION_RULES.review.sections).trim()
|
|
510
|
+
|| "无";
|
|
511
|
+
const severity = extractSectionBody(reviewContent, "## 问题严重度汇总", SECTION_RULES.review.sections).trim()
|
|
512
|
+
|| "blocker: 0";
|
|
513
|
+
const regressionRisk = extractSectionBody(reviewContent, "## Regression 风险评估", SECTION_RULES.review.sections).trim()
|
|
514
|
+
|| "待根据审查结果补充。";
|
|
515
|
+
return [
|
|
516
|
+
"# 测试报告",
|
|
517
|
+
"",
|
|
518
|
+
"## 状态",
|
|
519
|
+
"待判定",
|
|
520
|
+
"",
|
|
521
|
+
"## 轮次",
|
|
522
|
+
"第 1 轮",
|
|
523
|
+
"",
|
|
524
|
+
"## 测试策略",
|
|
525
|
+
"待 AI 根据 review 结果与实现风险补充测试策略,覆盖新增功能与受影响旧功能的回归验证。",
|
|
526
|
+
"",
|
|
527
|
+
"## 验证范围",
|
|
528
|
+
scope,
|
|
529
|
+
"",
|
|
530
|
+
"## 测试概要",
|
|
531
|
+
"待 AI 执行验证后补充。",
|
|
532
|
+
"",
|
|
533
|
+
"## 新增页面专项验证(如适用)",
|
|
534
|
+
"不适用或待 AI 判定。",
|
|
535
|
+
"",
|
|
536
|
+
"## Figma 高保真验证(如适用)",
|
|
537
|
+
"不适用或待 AI 判定。",
|
|
538
|
+
"",
|
|
539
|
+
"## Key Visual Elements 验证(如适用)",
|
|
540
|
+
"不适用或待 AI 判定。",
|
|
541
|
+
"",
|
|
542
|
+
"## 失败项",
|
|
543
|
+
findings,
|
|
544
|
+
"",
|
|
545
|
+
"## 历史遗留观察项(非阻塞,可选)",
|
|
546
|
+
"无",
|
|
547
|
+
"",
|
|
548
|
+
"## Regression 验证",
|
|
549
|
+
`${regressionRisk}\n\n待 AI 明确哪些旧功能需要回归验证以及验证结果。`,
|
|
550
|
+
"",
|
|
551
|
+
"## 覆盖范围",
|
|
552
|
+
severity,
|
|
553
|
+
"",
|
|
554
|
+
"## 开发者决策建议",
|
|
555
|
+
"待 AI 根据测试结果补充。",
|
|
556
|
+
"",
|
|
557
|
+
"## 结论",
|
|
558
|
+
"待判定",
|
|
559
|
+
"",
|
|
560
|
+
"## 报告语言",
|
|
561
|
+
"中文",
|
|
562
|
+
].join("\n");
|
|
563
|
+
}
|
|
564
|
+
parseAnswerFromArtifact(content, heading) {
|
|
565
|
+
const body = extractSectionBody(content, heading, SECTION_RULES.spec_refinement.sections).trim();
|
|
566
|
+
if (!body || containsUnresolvedMarker(body)) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
return body;
|
|
570
|
+
}
|
|
571
|
+
async prepareForPhase(workflowId, phase, _previousPhase) {
|
|
572
|
+
if (phase === "plan") {
|
|
573
|
+
const refinementContent = await this.readPhaseArtifact(workflowId, "spec_refinement");
|
|
574
|
+
await this.updatePhaseState(workflowId, "plan", {
|
|
575
|
+
valid: true,
|
|
576
|
+
readyForNextPhase: false,
|
|
577
|
+
requiresApproval: true,
|
|
578
|
+
summary: "Plan drafted and awaiting approval",
|
|
579
|
+
});
|
|
580
|
+
await this.writePhaseArtifact(workflowId, "plan", this.buildPlanArtifactFromRefinementContent(refinementContent));
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (phase === "develop") {
|
|
584
|
+
const planContent = await this.readPhaseArtifact(workflowId, "plan");
|
|
585
|
+
await this.updatePhaseState(workflowId, "develop", {
|
|
586
|
+
valid: false,
|
|
587
|
+
readyForNextPhase: false,
|
|
588
|
+
summary: "Development work is not complete yet",
|
|
589
|
+
});
|
|
590
|
+
await this.writePhaseArtifact(workflowId, "develop", this.buildDevelopArtifactFromPlanContent(planContent));
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (phase === "review") {
|
|
594
|
+
const developContent = await this.readPhaseArtifact(workflowId, "develop");
|
|
595
|
+
await this.updatePhaseState(workflowId, "review", {
|
|
596
|
+
valid: true,
|
|
597
|
+
readyForNextPhase: false,
|
|
598
|
+
summary: "Review report pending routing",
|
|
599
|
+
reportStatus: "unknown",
|
|
600
|
+
hasBlockingSeverity: false,
|
|
601
|
+
});
|
|
602
|
+
await this.writePhaseArtifact(workflowId, "review", this.buildReviewArtifactFromDevelopContent(developContent));
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
if (phase === "test") {
|
|
606
|
+
const reviewContent = await this.readPhaseArtifact(workflowId, "review");
|
|
607
|
+
await this.updatePhaseState(workflowId, "test", {
|
|
608
|
+
valid: true,
|
|
609
|
+
readyForNextPhase: false,
|
|
610
|
+
summary: "Test report pending routing",
|
|
611
|
+
reportStatus: "unknown",
|
|
612
|
+
hasBlockingSeverity: false,
|
|
613
|
+
});
|
|
614
|
+
await this.writePhaseArtifact(workflowId, "test", this.buildTestArtifactFromReviewContent(reviewContent));
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
async ensureDefault(workflowId, userRequest) {
|
|
619
|
+
const existing = await readJsonFile(this.workspace.artifactStateFile(workflowId));
|
|
620
|
+
if (existing) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
const normalizedUserRequest = this.normalizeUserRequest(userRequest);
|
|
624
|
+
const refinementQuestions = this.buildSpecRefinementQuestionsFromSections({
|
|
625
|
+
sectionBodies: this.defaultSpecSectionBodies(normalizedUserRequest),
|
|
626
|
+
initialRequest: normalizedUserRequest,
|
|
627
|
+
});
|
|
628
|
+
const defaults = {
|
|
629
|
+
spec_refinement: {
|
|
630
|
+
valid: false,
|
|
631
|
+
readyForNextPhase: false,
|
|
632
|
+
summary: "Need clarification before planning",
|
|
633
|
+
questions: refinementQuestions,
|
|
634
|
+
initialRequest: normalizedUserRequest,
|
|
635
|
+
},
|
|
636
|
+
plan: {
|
|
637
|
+
valid: true,
|
|
638
|
+
readyForNextPhase: false,
|
|
639
|
+
requiresApproval: true,
|
|
640
|
+
summary: "Plan drafted and awaiting approval",
|
|
641
|
+
},
|
|
642
|
+
develop: {
|
|
643
|
+
valid: false,
|
|
644
|
+
readyForNextPhase: false,
|
|
645
|
+
summary: "Development work is not complete yet",
|
|
646
|
+
},
|
|
647
|
+
review: {
|
|
648
|
+
valid: true,
|
|
649
|
+
readyForNextPhase: false,
|
|
650
|
+
summary: "Review report pending routing",
|
|
651
|
+
reportStatus: "unknown",
|
|
652
|
+
hasBlockingSeverity: false,
|
|
653
|
+
},
|
|
654
|
+
test: {
|
|
655
|
+
valid: true,
|
|
656
|
+
readyForNextPhase: false,
|
|
657
|
+
summary: "Test report pending routing",
|
|
658
|
+
reportStatus: "unknown",
|
|
659
|
+
hasBlockingSeverity: false,
|
|
660
|
+
},
|
|
661
|
+
};
|
|
662
|
+
await writeJsonFile(this.workspace.artifactStateFile(workflowId), defaults);
|
|
663
|
+
await this.writePhaseArtifact(workflowId, "spec_refinement", this.buildRefinementArtifact({ userRequest: normalizedUserRequest }));
|
|
664
|
+
await this.writePhaseArtifact(workflowId, "plan", [
|
|
665
|
+
"# 开发计划",
|
|
666
|
+
"",
|
|
667
|
+
"## 需求摘要",
|
|
668
|
+
"构建 workflow harness MVP。",
|
|
669
|
+
"",
|
|
670
|
+
"## 影响范围",
|
|
671
|
+
"state/runtime/adapter。",
|
|
672
|
+
"",
|
|
673
|
+
"## 核心修改文件",
|
|
674
|
+
"packages/runtime/src/engine/default-workflow-engine.ts",
|
|
675
|
+
"",
|
|
676
|
+
"## Cascade Files",
|
|
677
|
+
"src/cli.ts",
|
|
678
|
+
"",
|
|
679
|
+
"## 实现方案",
|
|
680
|
+
"PhaseTransition + TickScheduler。",
|
|
681
|
+
"",
|
|
682
|
+
"## i18n 变更",
|
|
683
|
+
"无",
|
|
684
|
+
"",
|
|
685
|
+
"## API / Route 变更",
|
|
686
|
+
"无",
|
|
687
|
+
"",
|
|
688
|
+
"## 组件复用决策",
|
|
689
|
+
"无 UI 组件",
|
|
690
|
+
"",
|
|
691
|
+
"## Section 验收映射(多 section Figma 页面必填)",
|
|
692
|
+
"不适用",
|
|
693
|
+
"",
|
|
694
|
+
"## Key Visual Elements(复杂 Figma 页面必填)",
|
|
695
|
+
"不适用",
|
|
696
|
+
"",
|
|
697
|
+
"## 疑问清单",
|
|
698
|
+
"无",
|
|
699
|
+
"",
|
|
700
|
+
"## 风险评估",
|
|
701
|
+
"session / workflow 状态不同步",
|
|
702
|
+
"",
|
|
703
|
+
"## 报告语言",
|
|
704
|
+
"中文",
|
|
705
|
+
].join("\n"));
|
|
706
|
+
await this.writePhaseArtifact(workflowId, "develop", [
|
|
707
|
+
"# 开发报告",
|
|
708
|
+
"",
|
|
709
|
+
"## Status",
|
|
710
|
+
"进行中",
|
|
711
|
+
"",
|
|
712
|
+
"## 修改文件",
|
|
713
|
+
"packages/runtime/src/bootstrap/create-harness.ts",
|
|
714
|
+
"",
|
|
715
|
+
"## 配套修改",
|
|
716
|
+
"新增 session client adapter",
|
|
717
|
+
"",
|
|
718
|
+
"## 自检结果",
|
|
719
|
+
"本地测试通过",
|
|
720
|
+
"",
|
|
721
|
+
"## 备注",
|
|
722
|
+
"等待最终完成",
|
|
723
|
+
"",
|
|
724
|
+
"## 报告语言",
|
|
725
|
+
"中文",
|
|
726
|
+
].join("\n"));
|
|
727
|
+
await this.writePhaseArtifact(workflowId, "review", [
|
|
728
|
+
"# 审查报告",
|
|
729
|
+
"",
|
|
730
|
+
"## 状态",
|
|
731
|
+
"待判定",
|
|
732
|
+
"",
|
|
733
|
+
"## 轮次",
|
|
734
|
+
"第 1 轮",
|
|
735
|
+
"",
|
|
736
|
+
"## 检查范围",
|
|
737
|
+
"workflow harness MVP",
|
|
738
|
+
"",
|
|
739
|
+
"## 组件复用验收结果(如适用)",
|
|
740
|
+
"不适用",
|
|
741
|
+
"",
|
|
742
|
+
"## Section 验收映射检查结果(如适用)",
|
|
743
|
+
"不适用",
|
|
744
|
+
"",
|
|
745
|
+
"## 发现的问题",
|
|
746
|
+
"待审查",
|
|
747
|
+
"",
|
|
748
|
+
"## 问题严重度汇总",
|
|
749
|
+
"blocker: 0",
|
|
750
|
+
"",
|
|
751
|
+
"## 历史遗留观察项(非阻塞,可选)",
|
|
752
|
+
"无",
|
|
753
|
+
"",
|
|
754
|
+
"## Regression 风险评估",
|
|
755
|
+
"待判定",
|
|
756
|
+
"",
|
|
757
|
+
"## 结论",
|
|
758
|
+
"待判定",
|
|
759
|
+
"",
|
|
760
|
+
"## 报告语言",
|
|
761
|
+
"中文",
|
|
762
|
+
].join("\n"));
|
|
763
|
+
await this.writePhaseArtifact(workflowId, "test", [
|
|
764
|
+
"# 测试报告",
|
|
765
|
+
"",
|
|
766
|
+
"## 状态",
|
|
767
|
+
"待判定",
|
|
768
|
+
"",
|
|
769
|
+
"## 轮次",
|
|
770
|
+
"第 1 轮",
|
|
771
|
+
"",
|
|
772
|
+
"## 测试策略",
|
|
773
|
+
"以自动化和最小人工检查为主",
|
|
774
|
+
"",
|
|
775
|
+
"## 验证范围",
|
|
776
|
+
"workflow harness 主链",
|
|
777
|
+
"",
|
|
778
|
+
"## 测试概要",
|
|
779
|
+
"待执行",
|
|
780
|
+
"",
|
|
781
|
+
"## 新增页面专项验证(如适用)",
|
|
782
|
+
"不适用",
|
|
783
|
+
"",
|
|
784
|
+
"## Figma 高保真验证(如适用)",
|
|
785
|
+
"不适用",
|
|
786
|
+
"",
|
|
787
|
+
"## Key Visual Elements 验证(如适用)",
|
|
788
|
+
"不适用",
|
|
789
|
+
"",
|
|
790
|
+
"## 失败项",
|
|
791
|
+
"待测试",
|
|
792
|
+
"",
|
|
793
|
+
"## 历史遗留观察项(非阻塞,可选)",
|
|
794
|
+
"无",
|
|
795
|
+
"",
|
|
796
|
+
"## Regression 验证",
|
|
797
|
+
"待判定",
|
|
798
|
+
"",
|
|
799
|
+
"## 覆盖范围",
|
|
800
|
+
"phase transition / answer / approve / session idle",
|
|
801
|
+
"",
|
|
802
|
+
"## 开发者决策建议",
|
|
803
|
+
"待判定",
|
|
804
|
+
"",
|
|
805
|
+
"## 结论",
|
|
806
|
+
"待判定",
|
|
807
|
+
"",
|
|
808
|
+
"## 报告语言",
|
|
809
|
+
"中文",
|
|
810
|
+
].join("\n"));
|
|
811
|
+
}
|
|
812
|
+
async evaluate(state) {
|
|
813
|
+
const current = await this.getPhaseState(state.workflowId, state.phase);
|
|
814
|
+
if (!current) {
|
|
815
|
+
return {
|
|
816
|
+
valid: false,
|
|
817
|
+
readyForNextPhase: false,
|
|
818
|
+
missing: ["artifact state missing"],
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
let content = await this.readPhaseArtifact(state.workflowId, state.phase);
|
|
822
|
+
let fromWorkspace = this.evaluatePhaseContent(state.phase, content);
|
|
823
|
+
if (state.phase === "spec_refinement" && fromWorkspace.questions?.some((question) => question.canAutoResolve)) {
|
|
824
|
+
const initialRequest = current.initialRequest ?? this.normalizeUserRequest(this.parseAnswerFromArtifact(content, "## 原始需求摘要") ?? undefined);
|
|
825
|
+
const autoFilledContent = this.autoFillSpecRefinementContent(content, initialRequest);
|
|
826
|
+
if (autoFilledContent !== content) {
|
|
827
|
+
await this.writePhaseArtifact(state.workflowId, "spec_refinement", autoFilledContent);
|
|
828
|
+
content = autoFilledContent;
|
|
829
|
+
fromWorkspace = this.evaluatePhaseContent(state.phase, content);
|
|
830
|
+
const phasePatch = {
|
|
831
|
+
valid: fromWorkspace.valid ?? false,
|
|
832
|
+
readyForNextPhase: fromWorkspace.readyForNextPhase ?? false,
|
|
833
|
+
missing: fromWorkspace.missing,
|
|
834
|
+
initialRequest,
|
|
835
|
+
};
|
|
836
|
+
if (fromWorkspace.summary) {
|
|
837
|
+
phasePatch.summary = fromWorkspace.summary;
|
|
838
|
+
}
|
|
839
|
+
if (fromWorkspace.questions) {
|
|
840
|
+
phasePatch.questions = fromWorkspace.questions;
|
|
841
|
+
}
|
|
842
|
+
await this.updatePhaseState(state.workflowId, "spec_refinement", {
|
|
843
|
+
...phasePatch,
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
const result = {
|
|
848
|
+
valid: fromWorkspace.valid ?? current.valid,
|
|
849
|
+
readyForNextPhase: fromWorkspace.readyForNextPhase ?? current.readyForNextPhase,
|
|
850
|
+
missing: fromWorkspace.missing.length > 0 ? fromWorkspace.missing : current.missing ?? [],
|
|
851
|
+
};
|
|
852
|
+
if (fromWorkspace.summary) {
|
|
853
|
+
result.summary = fromWorkspace.summary;
|
|
854
|
+
}
|
|
855
|
+
else if (current.summary) {
|
|
856
|
+
result.summary = current.summary;
|
|
857
|
+
}
|
|
858
|
+
if (fromWorkspace.questions) {
|
|
859
|
+
result.questions = fromWorkspace.questions;
|
|
860
|
+
}
|
|
861
|
+
else if (current.questions) {
|
|
862
|
+
result.questions = current.questions;
|
|
863
|
+
}
|
|
864
|
+
if (typeof fromWorkspace.requiresApproval === "boolean") {
|
|
865
|
+
result.requiresApproval = fromWorkspace.requiresApproval;
|
|
866
|
+
}
|
|
867
|
+
else if (typeof current.requiresApproval === "boolean") {
|
|
868
|
+
result.requiresApproval = current.requiresApproval;
|
|
869
|
+
}
|
|
870
|
+
if (typeof fromWorkspace.reportStatus === "string") {
|
|
871
|
+
result.reportStatus = fromWorkspace.reportStatus;
|
|
872
|
+
}
|
|
873
|
+
else if (typeof current.reportStatus === "string") {
|
|
874
|
+
result.reportStatus = current.reportStatus;
|
|
875
|
+
}
|
|
876
|
+
if (typeof fromWorkspace.hasBlockingSeverity === "boolean") {
|
|
877
|
+
result.hasBlockingSeverity = fromWorkspace.hasBlockingSeverity;
|
|
878
|
+
}
|
|
879
|
+
else if (typeof current.hasBlockingSeverity === "boolean") {
|
|
880
|
+
result.hasBlockingSeverity = current.hasBlockingSeverity;
|
|
881
|
+
}
|
|
882
|
+
return result;
|
|
883
|
+
}
|
|
884
|
+
async answerQuestions(workflowId, answers) {
|
|
885
|
+
const current = await this.readPhaseArtifact(workflowId, "spec_refinement");
|
|
886
|
+
const currentState = await this.getPhaseState(workflowId, "spec_refinement");
|
|
887
|
+
const initialRequest = currentState?.initialRequest ?? this.normalizeUserRequest(this.parseAnswerFromArtifact(current, "## 原始需求摘要") ?? undefined);
|
|
888
|
+
const sectionBodies = this.readSpecSectionBodies(current, initialRequest);
|
|
889
|
+
for (const blueprint of SPEC_REFINEMENT_QUESTION_BLUEPRINTS) {
|
|
890
|
+
const answer = answers[blueprint.id]?.trim();
|
|
891
|
+
if (answer) {
|
|
892
|
+
sectionBodies[blueprint.heading] = answer;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
const finalBodies = this.finalizeSpecSectionBodies({
|
|
896
|
+
sectionBodies,
|
|
897
|
+
initialRequest,
|
|
898
|
+
answers,
|
|
899
|
+
});
|
|
900
|
+
const nextContent = this.buildRefinementArtifactFromSections(finalBodies);
|
|
901
|
+
const evaluation = this.evaluatePhaseContent("spec_refinement", nextContent);
|
|
902
|
+
await this.updatePhaseState(workflowId, "spec_refinement", {
|
|
903
|
+
valid: evaluation.valid ?? false,
|
|
904
|
+
readyForNextPhase: evaluation.readyForNextPhase ?? false,
|
|
905
|
+
summary: evaluation.summary ?? "Specification updated after answers",
|
|
906
|
+
questions: evaluation.questions ?? [],
|
|
907
|
+
missing: evaluation.missing,
|
|
908
|
+
initialRequest,
|
|
909
|
+
});
|
|
910
|
+
await this.writePhaseArtifact(workflowId, "spec_refinement", nextContent);
|
|
911
|
+
}
|
|
912
|
+
async markDevelopmentComplete(workflowId) {
|
|
913
|
+
await this.updatePhaseState(workflowId, "develop", {
|
|
914
|
+
valid: true,
|
|
915
|
+
readyForNextPhase: true,
|
|
916
|
+
summary: "Development complete",
|
|
917
|
+
});
|
|
918
|
+
await this.writePhaseArtifact(workflowId, "develop", [
|
|
919
|
+
"# 开发报告",
|
|
920
|
+
"",
|
|
921
|
+
"## 状态",
|
|
922
|
+
"COMPLETED",
|
|
923
|
+
"",
|
|
924
|
+
"## 修改文件",
|
|
925
|
+
"packages/runtime/src/engine/default-workflow-engine.ts",
|
|
926
|
+
"",
|
|
927
|
+
"## 配套修改",
|
|
928
|
+
"session activity monitor",
|
|
929
|
+
"",
|
|
930
|
+
"## 自检结果",
|
|
931
|
+
"tests/typecheck/build 全通过",
|
|
932
|
+
"",
|
|
933
|
+
"## 备注",
|
|
934
|
+
"无",
|
|
935
|
+
"",
|
|
936
|
+
"## 报告语言",
|
|
937
|
+
"中文",
|
|
938
|
+
].join("\n"));
|
|
939
|
+
}
|
|
940
|
+
async setReviewReport(workflowId, status, blocking = false) {
|
|
941
|
+
await this.updatePhaseState(workflowId, "review", {
|
|
942
|
+
valid: true,
|
|
943
|
+
readyForNextPhase: false,
|
|
944
|
+
summary: status === "pass" ? "Review report passed" : "Review report failed",
|
|
945
|
+
reportStatus: status,
|
|
946
|
+
hasBlockingSeverity: blocking,
|
|
947
|
+
});
|
|
948
|
+
const conclusionText = status === "pass" ? "通过" : "失败";
|
|
949
|
+
const severitySummary = blocking ? "[severity:blocker] blocking issue found" : "blocker: 0";
|
|
950
|
+
await this.writePhaseArtifact(workflowId, "review", [
|
|
951
|
+
"# 审查报告",
|
|
952
|
+
"",
|
|
953
|
+
"## 状态",
|
|
954
|
+
"COMPLETED",
|
|
955
|
+
"",
|
|
956
|
+
"## 轮次",
|
|
957
|
+
"第 1 轮",
|
|
958
|
+
"",
|
|
959
|
+
"## 检查范围",
|
|
960
|
+
"workflow harness MVP",
|
|
961
|
+
"",
|
|
962
|
+
"## 组件复用验收结果(如适用)",
|
|
963
|
+
"不适用",
|
|
964
|
+
"",
|
|
965
|
+
"## Section 验收映射检查结果(如适用)",
|
|
966
|
+
"不适用",
|
|
967
|
+
"",
|
|
968
|
+
"## 发现的问题",
|
|
969
|
+
status === "fail" ? "存在待处理问题" : "无",
|
|
970
|
+
"",
|
|
971
|
+
"## 问题严重度汇总",
|
|
972
|
+
severitySummary,
|
|
973
|
+
"",
|
|
974
|
+
"## 历史遗留观察项(非阻塞,可选)",
|
|
975
|
+
"无",
|
|
976
|
+
"",
|
|
977
|
+
"## Regression 风险评估",
|
|
978
|
+
blocking ? "高" : "低",
|
|
979
|
+
"",
|
|
980
|
+
"## 结论",
|
|
981
|
+
conclusionText,
|
|
982
|
+
"",
|
|
983
|
+
"## 报告语言",
|
|
984
|
+
"中文",
|
|
985
|
+
].join("\n"));
|
|
986
|
+
}
|
|
987
|
+
async setTestReport(workflowId, status) {
|
|
988
|
+
await this.updatePhaseState(workflowId, "test", {
|
|
989
|
+
valid: true,
|
|
990
|
+
readyForNextPhase: false,
|
|
991
|
+
summary: status === "pass" ? "Test report passed" : "Test report failed",
|
|
992
|
+
reportStatus: status,
|
|
993
|
+
hasBlockingSeverity: false,
|
|
994
|
+
});
|
|
995
|
+
const conclusionText = status === "pass" ? "通过" : "失败";
|
|
996
|
+
await this.writePhaseArtifact(workflowId, "test", [
|
|
997
|
+
"# 测试报告",
|
|
998
|
+
"",
|
|
999
|
+
"## 状态",
|
|
1000
|
+
"COMPLETED",
|
|
1001
|
+
"",
|
|
1002
|
+
"## 轮次",
|
|
1003
|
+
"第 1 轮",
|
|
1004
|
+
"",
|
|
1005
|
+
"## 测试策略",
|
|
1006
|
+
"以自动化和最小人工检查为主",
|
|
1007
|
+
"",
|
|
1008
|
+
"## 验证范围",
|
|
1009
|
+
"workflow harness 主链",
|
|
1010
|
+
"",
|
|
1011
|
+
"## 测试概要",
|
|
1012
|
+
status === "pass" ? "主链通过" : "测试存在失败项",
|
|
1013
|
+
"",
|
|
1014
|
+
"## 新增页面专项验证(如适用)",
|
|
1015
|
+
"不适用",
|
|
1016
|
+
"",
|
|
1017
|
+
"## Figma 高保真验证(如适用)",
|
|
1018
|
+
"不适用",
|
|
1019
|
+
"",
|
|
1020
|
+
"## Key Visual Elements 验证(如适用)",
|
|
1021
|
+
"不适用",
|
|
1022
|
+
"",
|
|
1023
|
+
"## 失败项",
|
|
1024
|
+
status === "pass" ? "无" : "存在失败项,需要人工决策",
|
|
1025
|
+
"",
|
|
1026
|
+
"## 历史遗留观察项(非阻塞,可选)",
|
|
1027
|
+
"无",
|
|
1028
|
+
"",
|
|
1029
|
+
"## Regression 验证",
|
|
1030
|
+
status === "pass" ? "通过" : "未通过",
|
|
1031
|
+
"",
|
|
1032
|
+
"## 覆盖范围",
|
|
1033
|
+
"phase transition / answer / approve / session idle",
|
|
1034
|
+
"",
|
|
1035
|
+
"## 开发者决策建议",
|
|
1036
|
+
status === "pass" ? "可继续推进" : "建议人工决定是否回到 develop",
|
|
1037
|
+
"",
|
|
1038
|
+
"## 结论",
|
|
1039
|
+
conclusionText,
|
|
1040
|
+
"",
|
|
1041
|
+
"## 报告语言",
|
|
1042
|
+
"中文",
|
|
1043
|
+
].join("\n"));
|
|
1044
|
+
}
|
|
1045
|
+
async getPhaseState(workflowId, phase) {
|
|
1046
|
+
const file = await readJsonFile(this.workspace.artifactStateFile(workflowId));
|
|
1047
|
+
return file?.[phase] ?? null;
|
|
1048
|
+
}
|
|
1049
|
+
async updatePhaseState(workflowId, phase, patch) {
|
|
1050
|
+
const current = (await readJsonFile(this.workspace.artifactStateFile(workflowId))) ?? {};
|
|
1051
|
+
const existing = current[phase] ?? {
|
|
1052
|
+
valid: false,
|
|
1053
|
+
readyForNextPhase: false,
|
|
1054
|
+
};
|
|
1055
|
+
current[phase] = {
|
|
1056
|
+
...existing,
|
|
1057
|
+
...patch,
|
|
1058
|
+
};
|
|
1059
|
+
await writeJsonFile(this.workspace.artifactStateFile(workflowId), current);
|
|
1060
|
+
}
|
|
1061
|
+
async readPhaseArtifact(workflowId, phase) {
|
|
1062
|
+
try {
|
|
1063
|
+
return await readFile(this.workspace.phaseArtifactFile(workflowId, phase), "utf8");
|
|
1064
|
+
}
|
|
1065
|
+
catch (error) {
|
|
1066
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1067
|
+
if (message.includes("ENOENT")) {
|
|
1068
|
+
return "";
|
|
1069
|
+
}
|
|
1070
|
+
throw error;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
async writePhaseArtifact(workflowId, phase, content) {
|
|
1074
|
+
await writeFile(this.workspace.phaseArtifactFile(workflowId, phase), `${content}\n`, "utf8");
|
|
1075
|
+
}
|
|
1076
|
+
evaluatePhaseContent(phase, content) {
|
|
1077
|
+
if (!content.trim()) {
|
|
1078
|
+
return { valid: false, readyForNextPhase: false, missing: ["artifact file is empty"] };
|
|
1079
|
+
}
|
|
1080
|
+
const sectionRule = phase === "spec_refinement"
|
|
1081
|
+
|| phase === "plan"
|
|
1082
|
+
|| phase === "develop"
|
|
1083
|
+
|| phase === "review"
|
|
1084
|
+
|| phase === "test"
|
|
1085
|
+
? SECTION_RULES[phase]
|
|
1086
|
+
: null;
|
|
1087
|
+
if (sectionRule) {
|
|
1088
|
+
const missing = [];
|
|
1089
|
+
if (!content.includes(sectionRule.title)) {
|
|
1090
|
+
missing.push(sectionRule.title);
|
|
1091
|
+
}
|
|
1092
|
+
for (const section of sectionRule.sections) {
|
|
1093
|
+
if (!content.includes(section) || !sectionHasContent(content, section, sectionRule.sections)) {
|
|
1094
|
+
missing.push(section);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
if (phase === "spec_refinement") {
|
|
1098
|
+
if (!hasReadyForPlanConclusion(content, sectionRule.sections)) {
|
|
1099
|
+
missing.push("## 准入结论: READY_FOR_PLAN");
|
|
1100
|
+
}
|
|
1101
|
+
if (!questionChecklistResolved(content, sectionRule.sections)) {
|
|
1102
|
+
missing.push("## 疑问清单: ALL_RESOLVED");
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
if (phase === "develop" && !hasCompletedStatus(content, sectionRule.sections)) {
|
|
1106
|
+
missing.push("## 状态: COMPLETED");
|
|
1107
|
+
}
|
|
1108
|
+
if (phase === "test" && getReportStatus(content) === "pass" && !hasSufficientTestEvidence(content, sectionRule.sections)) {
|
|
1109
|
+
missing.push("## 测试证据: SUFFICIENT");
|
|
1110
|
+
}
|
|
1111
|
+
if (missing.length > 0) {
|
|
1112
|
+
if (phase === "spec_refinement") {
|
|
1113
|
+
const currentBodies = this.readSpecSectionBodies(content);
|
|
1114
|
+
const summaryBody = currentBodies["## 原始需求摘要"] ?? "";
|
|
1115
|
+
const initialRequest = this.normalizeUserRequest(!containsUnresolvedMarker(summaryBody) ? summaryBody : undefined);
|
|
1116
|
+
const questions = this.buildSpecRefinementQuestionsFromSections({
|
|
1117
|
+
sectionBodies: currentBodies,
|
|
1118
|
+
initialRequest,
|
|
1119
|
+
});
|
|
1120
|
+
return {
|
|
1121
|
+
valid: false,
|
|
1122
|
+
readyForNextPhase: false,
|
|
1123
|
+
missing,
|
|
1124
|
+
summary: "规格精炼报告未满足进入 plan 的要求",
|
|
1125
|
+
...(questions.length > 0 ? { questions } : {}),
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
if (phase === "plan") {
|
|
1129
|
+
return {
|
|
1130
|
+
valid: false,
|
|
1131
|
+
readyForNextPhase: false,
|
|
1132
|
+
missing,
|
|
1133
|
+
requiresApproval: false,
|
|
1134
|
+
summary: "开发计划结构不完整,暂不能进入审批",
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
if (phase === "review") {
|
|
1138
|
+
return {
|
|
1139
|
+
valid: false,
|
|
1140
|
+
readyForNextPhase: false,
|
|
1141
|
+
missing,
|
|
1142
|
+
summary: "审查报告结构不完整,暂不能决定 pass/fail",
|
|
1143
|
+
reportStatus: "unknown",
|
|
1144
|
+
hasBlockingSeverity: false,
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
if (phase === "test") {
|
|
1148
|
+
return {
|
|
1149
|
+
valid: false,
|
|
1150
|
+
readyForNextPhase: false,
|
|
1151
|
+
missing,
|
|
1152
|
+
summary: "测试报告证据不足,暂不能决定 pass/fail",
|
|
1153
|
+
reportStatus: "unknown",
|
|
1154
|
+
hasBlockingSeverity: false,
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
return {
|
|
1158
|
+
valid: false,
|
|
1159
|
+
readyForNextPhase: false,
|
|
1160
|
+
missing,
|
|
1161
|
+
summary: "开发报告未满足完成条件",
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (phase === "spec_refinement") {
|
|
1166
|
+
return {
|
|
1167
|
+
valid: true,
|
|
1168
|
+
readyForNextPhase: true,
|
|
1169
|
+
missing: [],
|
|
1170
|
+
summary: "Spec refinement is ready for planning",
|
|
1171
|
+
questions: [],
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
if (phase === "plan") {
|
|
1175
|
+
return {
|
|
1176
|
+
valid: true,
|
|
1177
|
+
readyForNextPhase: false,
|
|
1178
|
+
missing: [],
|
|
1179
|
+
requiresApproval: true,
|
|
1180
|
+
summary: "Plan drafted and awaiting approval",
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
if (phase === "develop") {
|
|
1184
|
+
return {
|
|
1185
|
+
valid: true,
|
|
1186
|
+
readyForNextPhase: true,
|
|
1187
|
+
missing: [],
|
|
1188
|
+
summary: "Development complete",
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
if (phase === "review") {
|
|
1192
|
+
return {
|
|
1193
|
+
valid: true,
|
|
1194
|
+
readyForNextPhase: false,
|
|
1195
|
+
missing: [],
|
|
1196
|
+
summary: "Review report is ready for routing",
|
|
1197
|
+
reportStatus: getReportStatus(content),
|
|
1198
|
+
hasBlockingSeverity: hasBlockingSeverity(content),
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
if (phase === "test") {
|
|
1202
|
+
return {
|
|
1203
|
+
valid: true,
|
|
1204
|
+
readyForNextPhase: false,
|
|
1205
|
+
missing: [],
|
|
1206
|
+
summary: "Test report is ready for routing",
|
|
1207
|
+
reportStatus: getReportStatus(content),
|
|
1208
|
+
hasBlockingSeverity: false,
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
return { valid: false, readyForNextPhase: false, missing: [] };
|
|
1212
|
+
}
|
|
1213
|
+
}
|