@ai-content-space/loopx 0.1.10 → 0.2.1
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/AGENTS.md +49 -0
- package/README.md +69 -448
- package/README.zh-CN.md +69 -459
- package/docs/loopx/design/loopx-skill-suite-v1-design.md +80 -0
- package/docs/loopx/plans/loopx-skill-suite-v1-implementation.md +81 -0
- package/package.json +7 -3
- package/plugins/loopx/.codex-plugin/plugin.json +4 -4
- package/plugins/loopx/skills/clarify/SKILL.md +38 -311
- package/plugins/loopx/skills/debug/SKILL.md +1 -1
- package/plugins/loopx/skills/exec/SKILL.md +71 -0
- package/plugins/loopx/skills/finish/SKILL.md +349 -0
- package/plugins/loopx/skills/fix-review/SKILL.md +216 -0
- package/plugins/loopx/skills/go-style/SKILL.md +2 -2
- package/plugins/loopx/skills/kratos/SKILL.md +1 -1
- package/plugins/loopx/skills/plan/SKILL.md +138 -271
- package/plugins/loopx/skills/refactor-plan/SKILL.md +71 -0
- package/plugins/loopx/skills/review/SKILL.md +72 -105
- package/plugins/loopx/skills/review/code-reviewer.md +168 -0
- package/plugins/loopx/skills/spec/DESIGN_SPEC_TEMPLATE.md +323 -0
- package/plugins/loopx/skills/spec/SKILL.md +76 -0
- package/plugins/loopx/skills/subagent-exec/SKILL.md +282 -0
- package/plugins/loopx/skills/subagent-exec/agents/openai.yaml +3 -0
- package/plugins/loopx/skills/subagent-exec/code-quality-reviewer-prompt.md +25 -0
- package/plugins/loopx/skills/subagent-exec/codex-subagents.md +37 -0
- package/plugins/loopx/skills/subagent-exec/implementer-prompt.md +113 -0
- package/plugins/loopx/skills/subagent-exec/spec-reviewer-prompt.md +61 -0
- package/plugins/loopx/skills/tdd/SKILL.md +1 -1
- package/plugins/loopx/skills/verify/SKILL.md +1 -1
- package/scripts/claude-workflow-hook.mjs +109 -0
- package/scripts/codex-workflow-hook.mjs +2 -5
- package/scripts/install-skills.mjs +3 -3
- package/scripts/verify-skills.mjs +34 -2
- package/skills/RESOLVER.md +22 -17
- package/skills/clarify/SKILL.md +38 -311
- package/skills/debug/SKILL.md +1 -1
- package/skills/exec/SKILL.md +71 -0
- package/skills/finish/SKILL.md +349 -0
- package/skills/fix-review/SKILL.md +216 -0
- package/skills/go-style/SKILL.md +2 -2
- package/skills/kratos/SKILL.md +1 -1
- package/skills/plan/SKILL.md +138 -271
- package/skills/refactor-plan/SKILL.md +71 -0
- package/skills/review/SKILL.md +72 -105
- package/skills/review/code-reviewer.md +168 -0
- package/skills/spec/DESIGN_SPEC_TEMPLATE.md +323 -0
- package/skills/spec/SKILL.md +76 -0
- package/skills/subagent-exec/SKILL.md +282 -0
- package/skills/subagent-exec/agents/openai.yaml +3 -0
- package/skills/subagent-exec/code-quality-reviewer-prompt.md +25 -0
- package/skills/subagent-exec/codex-subagents.md +37 -0
- package/skills/subagent-exec/implementer-prompt.md +113 -0
- package/skills/subagent-exec/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +1 -1
- package/skills/verify/SKILL.md +1 -1
- package/src/autopilot-runtime.mjs +1 -1
- package/src/cli.mjs +78 -7
- package/src/context-manifest.mjs +2 -2
- package/src/html-views.mjs +129 -195
- package/src/install-discovery.mjs +210 -6
- package/src/next-skill.mjs +2 -4
- package/src/plan-runtime.mjs +219 -93
- package/src/runtime-maintenance.mjs +5 -2
- package/src/workflow.mjs +749 -71
- package/templates/architecture.md +58 -16
- package/templates/development-plan.md +42 -12
- package/plugins/loopx/scripts/plugin-install.test.mjs +0 -125
- package/plugins/loopx/skills/archive/SKILL.md +0 -55
- package/plugins/loopx/skills/autopilot/SKILL.md +0 -93
- package/plugins/loopx/skills/build/SKILL.md +0 -228
- package/skills/ai-slop-cleaner/SKILL.md +0 -114
- package/skills/archive/SKILL.md +0 -55
- package/skills/autopilot/SKILL.md +0 -93
- package/skills/autoresearch/SKILL.md +0 -68
- package/skills/build/SKILL.md +0 -228
- package/skills/deep-interview/SKILL.md +0 -461
- package/skills/ralph/SKILL.md +0 -271
- package/skills/ralplan/SKILL.md +0 -49
package/src/plan-runtime.mjs
CHANGED
|
@@ -4,6 +4,12 @@ import { mkdir } from 'node:fs/promises';
|
|
|
4
4
|
import { runCodexExecJson } from './codex-exec-runtime.mjs';
|
|
5
5
|
|
|
6
6
|
const DEFAULT_MAX_ITERATIONS = 5;
|
|
7
|
+
const DEFAULT_PLAN_CODEX_TIMEOUT_MS = 600000;
|
|
8
|
+
|
|
9
|
+
function planCodexTimeoutMs() {
|
|
10
|
+
const value = Number(process.env.LOOPX_PLAN_CODEX_TIMEOUT_MS || DEFAULT_PLAN_CODEX_TIMEOUT_MS);
|
|
11
|
+
return Number.isFinite(value) && value > 0 ? value : DEFAULT_PLAN_CODEX_TIMEOUT_MS;
|
|
12
|
+
}
|
|
7
13
|
|
|
8
14
|
function extractSection(text, heading) {
|
|
9
15
|
const pattern = new RegExp(`## ${heading}\\n\\n([\\s\\S]*?)(?=\\n## |$)`, 'i');
|
|
@@ -59,141 +65,195 @@ function buildSourceSummary(sourceText) {
|
|
|
59
65
|
};
|
|
60
66
|
}
|
|
61
67
|
|
|
68
|
+
function mdTable(headers, rows) {
|
|
69
|
+
return [
|
|
70
|
+
`| ${headers.join(' | ')} |`,
|
|
71
|
+
`| ${headers.map(() => '---').join(' | ')} |`,
|
|
72
|
+
...rows.map((row) => `| ${row.map((cell) => String(cell ?? '').replace(/\n/g, ' ').replace(/\|/g, '\\|')).join(' | ')} |`),
|
|
73
|
+
].join('\n');
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
function plannerDraftFromSource({ slug, sourceText, deliberateMode }) {
|
|
63
77
|
const summary = buildSourceSummary(sourceText);
|
|
64
78
|
const executionInputs = bulletsFromSection(extractSection(sourceText, 'Execution Inputs'), []);
|
|
65
79
|
const executionInputsResolved = executionInputs.length > 0 && executionInputs.every((item) => !/\b(TBD|待定|unknown|later)\b/i.test(item));
|
|
80
|
+
const inScope = summary.inScope.slice(0, 16);
|
|
81
|
+
const acceptance = summary.acceptance.slice(0, 16);
|
|
82
|
+
const constraints = summary.constraints.slice(0, 12);
|
|
83
|
+
const decisions = summary.decisions.slice(0, 12);
|
|
66
84
|
const preMortem = deliberateMode
|
|
67
85
|
? [
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
86
|
+
'如果架构边界没有明确外部依赖和副作用控制,build 阶段容易把 mock 范围误做成真实集成。',
|
|
87
|
+
'如果开发切片只按模块拆分而不是按可验收行为拆分,后续实现容易出现页面、接口和数据闭环脱节。',
|
|
88
|
+
'如果测试计划没有覆盖核心验收样例、异常路径和人工确认点,review 阶段无法判断是否满足需求。',
|
|
71
89
|
]
|
|
72
90
|
: [];
|
|
73
91
|
|
|
74
92
|
return {
|
|
75
93
|
principles: [
|
|
76
|
-
'
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
'
|
|
94
|
+
'计划必须完整承接已澄清需求,不把核心范围压缩成泛化条目。',
|
|
95
|
+
'架构、详细设计、开发计划和测试计划必须分别回答不同问题,避免 build 阶段自行补设计。',
|
|
96
|
+
'涉及人工确认、外部系统、资金资产、交易、权限或通知的边界必须显式写清。',
|
|
97
|
+
'每个可交付切片都必须有可验证信号和人工确认点。',
|
|
98
|
+
'计划阶段只输出方案和执行输入,不自动进入实现。',
|
|
81
99
|
],
|
|
82
100
|
decisionDrivers: [
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
'
|
|
101
|
+
summary.intent,
|
|
102
|
+
summary.outcome,
|
|
103
|
+
'后续 build 需要可直接执行的模块边界、数据结构、接口契约和验收矩阵。',
|
|
86
104
|
],
|
|
87
105
|
options: [
|
|
88
106
|
{
|
|
89
|
-
name: '
|
|
90
|
-
pros: ['
|
|
91
|
-
cons: ['
|
|
107
|
+
name: '统一平台底座 + 场景扩展',
|
|
108
|
+
pros: ['共享状态、日志、异常和验收闭环', '减少重复实现', '便于后续接入真实 adapter'],
|
|
109
|
+
cons: ['需要严格控制扩展字段和状态机边界'],
|
|
92
110
|
},
|
|
93
111
|
{
|
|
94
|
-
name: '
|
|
95
|
-
pros: ['
|
|
96
|
-
cons: ['
|
|
112
|
+
name: '按场景分别实现',
|
|
113
|
+
pros: ['单个场景领域表达更直接'],
|
|
114
|
+
cons: ['重复状态机、接口、日志和页面结构,首期交付和回归成本高'],
|
|
97
115
|
},
|
|
98
116
|
],
|
|
99
117
|
planText: [
|
|
100
|
-
`#
|
|
118
|
+
`# 计划:${slug}`,
|
|
101
119
|
'',
|
|
102
120
|
'## 需求摘要',
|
|
103
121
|
'',
|
|
104
122
|
`- ${summary.intent}`,
|
|
105
123
|
`- ${summary.outcome}`,
|
|
106
124
|
'',
|
|
107
|
-
'##
|
|
125
|
+
'## 交付范围',
|
|
108
126
|
'',
|
|
109
|
-
...
|
|
127
|
+
...inScope.map((item) => `- ${item}`),
|
|
110
128
|
'',
|
|
111
|
-
'##
|
|
129
|
+
'## 方案选择',
|
|
112
130
|
'',
|
|
113
|
-
'
|
|
114
|
-
'2. 在 workflow state 中记录 plan iteration、review verdict 和 execution-input blockers。',
|
|
115
|
-
'3. 从已批准的 planning source 生成中文主规划工件与 canonical plan artifacts。',
|
|
116
|
-
'4. 在 CLI status 中暴露 plan 阶段进度。',
|
|
117
|
-
'5. 为 happy path、iterate path 和 execution input 未收口路径补 deterministic regression coverage。',
|
|
131
|
+
'采用统一平台底座承载核心流程,并通过场景配置、受控扩展结构或子表表达差异。该方案优先保证人工确认边界、数据追溯、异常处理和验收闭环。',
|
|
118
132
|
'',
|
|
119
|
-
'##
|
|
133
|
+
'## 关键里程碑',
|
|
134
|
+
'',
|
|
135
|
+
'1. 明确架构边界、数据模型、状态机和外部依赖隔离方式。',
|
|
136
|
+
'2. 定义接口、函数、组件、字段和错误处理契约。',
|
|
137
|
+
'3. 按可验收行为拆分开发切片并逐项落地。',
|
|
138
|
+
'4. 按需求验收矩阵完成自动化验证和必要人工确认。',
|
|
120
139
|
'',
|
|
121
|
-
|
|
140
|
+
'## 验收目标',
|
|
141
|
+
'',
|
|
142
|
+
...acceptance.map((item) => `- ${item}`),
|
|
122
143
|
'',
|
|
123
144
|
'## 风险',
|
|
124
145
|
'',
|
|
125
|
-
...
|
|
146
|
+
...constraints.map((item) => `- ${item}`),
|
|
126
147
|
'',
|
|
127
|
-
'##
|
|
148
|
+
'## 执行输入',
|
|
128
149
|
'',
|
|
129
|
-
|
|
130
|
-
'- 运行 CLI status checks',
|
|
131
|
-
'- 证明 execution input blockers 与 iterate path 生效',
|
|
150
|
+
...(executionInputs.length > 0 ? executionInputs.map((item) => `- ${item}`) : ['- 源需求规格、产品文档、原型说明和当前代码事实。']),
|
|
132
151
|
].join('\n'),
|
|
133
152
|
architectureText: [
|
|
134
|
-
`#
|
|
153
|
+
`# 架构方案:${slug}`,
|
|
135
154
|
'',
|
|
136
|
-
'##
|
|
155
|
+
'## 文档定位',
|
|
137
156
|
'',
|
|
138
|
-
|
|
157
|
+
'架构方案是本阶段的架构文档,回答系统边界、模块职责、数据/状态流、接口边界、架构决策和质量属性,不负责逐文件排期或字段级默认值。',
|
|
158
|
+
'',
|
|
159
|
+
'## 架构目标与非目标',
|
|
160
|
+
'',
|
|
161
|
+
`目标:${summary.intent}`,
|
|
139
162
|
'',
|
|
140
|
-
'
|
|
163
|
+
'非目标:',
|
|
141
164
|
'',
|
|
142
|
-
...summary.
|
|
165
|
+
...summary.nonGoals.map((item) => `- ${item}`),
|
|
143
166
|
'',
|
|
144
|
-
'##
|
|
167
|
+
'## 上下文与系统边界',
|
|
145
168
|
'',
|
|
146
|
-
|
|
147
|
-
'- 通过专用 adapter 隔离生产编排与确定性测试',
|
|
148
|
-
'- canonical plan artifacts 保持写入 `.loopx/plans/`',
|
|
149
|
-
'- `.loopx/workflows/<slug>/` 下的主规划工件直接作为中文产物',
|
|
169
|
+
...decisions.map((item) => `- ${item}`),
|
|
150
170
|
'',
|
|
151
|
-
'##
|
|
171
|
+
'## 组件与职责',
|
|
152
172
|
'',
|
|
153
|
-
'
|
|
154
|
-
'
|
|
173
|
+
'| 组件 | 职责 | 边界 |',
|
|
174
|
+
'| --- | --- | --- |',
|
|
175
|
+
'| 入口层 | 接收用户、API、任务或页面操作 | 只做参数适配和鉴权,不承载核心业务规则 |',
|
|
176
|
+
'| 业务层 | 编排状态机、明细生成、异常处理和人工动作 | 不直接调用未声明的真实外部副作用 |',
|
|
177
|
+
'| 数据层 | 持久化核心实体、日志、状态和追溯字段 | 结构化字段优先,扩展字段受控 |',
|
|
178
|
+
'| 前端/交互层 | 展示任务、明细、进度、操作和验收反馈 | 不自行推导状态机或绕过后端校验 |',
|
|
179
|
+
'',
|
|
180
|
+
'## 数据与状态模型',
|
|
181
|
+
'',
|
|
182
|
+
'- 核心数据必须支持来源追溯、状态推进、人工操作留痕、异常处理和验收核对。',
|
|
183
|
+
'- 状态机必须集中定义合法动作、前置条件、后置状态和非法路径。',
|
|
184
|
+
'- 外部来源、下游回写和真实副作用必须通过明确 adapter/provider 边界隔离。',
|
|
185
|
+
'',
|
|
186
|
+
'## 接口与集成契约',
|
|
187
|
+
'',
|
|
188
|
+
'- API、任务入口、页面路由和 provider 方法必须列明输入、输出、权限、错误和幂等边界。',
|
|
189
|
+
'- mock 与真实集成必须可区分;首期未批准的真实依赖不得暗接。',
|
|
190
|
+
'',
|
|
191
|
+
'## 架构决策记录',
|
|
192
|
+
'',
|
|
193
|
+
'| 决策 | 取舍 | 后续影响 |',
|
|
194
|
+
'| --- | --- | --- |',
|
|
195
|
+
'| 统一平台底座 | 降低重复实现并集中控制状态/日志/异常 | 场景差异必须进入受控扩展点 |',
|
|
155
196
|
].join('\n'),
|
|
156
197
|
developmentPlanText: [
|
|
157
|
-
`#
|
|
198
|
+
`# 开发计划:${slug}`,
|
|
199
|
+
'',
|
|
200
|
+
'## 文档定位',
|
|
201
|
+
'',
|
|
202
|
+
'开发计划回答交付顺序、切片、依赖、验证、人工确认点、回滚和完成定义,不重新选择架构方向。',
|
|
203
|
+
'',
|
|
204
|
+
'## 交付切片',
|
|
205
|
+
'',
|
|
206
|
+
'1. 领域/数据/状态底座:定义核心实体、状态机、repository/usecase 边界和基础测试。',
|
|
207
|
+
'2. 主流程闭环:实现数据生成、查询、人工动作、日志和异常处理。',
|
|
208
|
+
'3. 入口与交互:接入 API、页面、权限和必要的前端组件。',
|
|
209
|
+
'4. 验收收敛:按源需求逐项跑自动化验证和人工验收。',
|
|
210
|
+
'',
|
|
211
|
+
'## 实施顺序与依赖',
|
|
158
212
|
'',
|
|
159
|
-
'
|
|
213
|
+
'- 先完成数据和状态机,再暴露入口,最后做页面和端到端验收。',
|
|
214
|
+
'- 涉及外部副作用、权限、资金资产、交易或通知的切片必须 HITL。',
|
|
215
|
+
'- 如果实现发现源需求与代码事实冲突,必须回到 plan/clarify 修订。',
|
|
160
216
|
'',
|
|
161
|
-
'
|
|
162
|
-
'2. 实现带有有界迭代的 planner/architect/critic 编排。',
|
|
163
|
-
'3. 直接输出中文主规划工件与 canonical plan artifacts。',
|
|
164
|
-
'4. 增加 deterministic test seams 与 regression coverage。',
|
|
217
|
+
'## 文件级变更清单',
|
|
165
218
|
'',
|
|
166
|
-
'
|
|
219
|
+
'- 后端业务域、API/handler、数据 schema/repository、service/server wiring。',
|
|
220
|
+
'- 前端页面、组件、API client、构建配置。',
|
|
221
|
+
'- 单元测试、接口测试、构建验证和人工验收记录。',
|
|
167
222
|
'',
|
|
168
|
-
'
|
|
169
|
-
'- reviewer: architect and critic',
|
|
170
|
-
'- downstream execution: 仅在后续显式批准后进入',
|
|
223
|
+
'## 验证计划',
|
|
171
224
|
'',
|
|
172
|
-
|
|
225
|
+
...acceptance.map((item) => `- 验证:${item}`),
|
|
173
226
|
'',
|
|
174
|
-
'
|
|
175
|
-
'
|
|
176
|
-
'-
|
|
227
|
+
'## 完成定义',
|
|
228
|
+
'',
|
|
229
|
+
'- 所有源需求都有实现证据或明确非目标说明。',
|
|
230
|
+
'- 自动化测试、构建和人工验收信号与开发切片一一对应。',
|
|
231
|
+
'- 未完成、风险和回滚路径在交付说明中明确记录。',
|
|
177
232
|
].join('\n'),
|
|
178
233
|
testPlanText: [
|
|
179
|
-
`#
|
|
234
|
+
`# 测试计划:${slug}`,
|
|
235
|
+
'',
|
|
236
|
+
'## 需求到测试矩阵',
|
|
237
|
+
'',
|
|
238
|
+
...acceptance.map((item) => `- ${item}`),
|
|
180
239
|
'',
|
|
181
|
-
'##
|
|
240
|
+
'## 自动化测试',
|
|
182
241
|
'',
|
|
183
|
-
'-
|
|
184
|
-
'-
|
|
185
|
-
'-
|
|
242
|
+
'- 状态机合法/非法转换。',
|
|
243
|
+
'- 数据去重、持久化、查询和日志写入。',
|
|
244
|
+
'- API 参数、权限、错误和响应结构。',
|
|
245
|
+
'- 前端构建和关键页面渲染。',
|
|
186
246
|
'',
|
|
187
|
-
'##
|
|
247
|
+
'## 人工验收',
|
|
188
248
|
'',
|
|
189
|
-
'-
|
|
190
|
-
'-
|
|
191
|
-
'-
|
|
192
|
-
'- execution inputs 缺失或标记 TBD 的 blocking 路径',
|
|
249
|
+
'- 页面/流程是否符合原型和产品文档。',
|
|
250
|
+
'- 人工确认动作是否清晰且不可被系统自动跳过。',
|
|
251
|
+
'- mock/真实边界是否符合非目标。',
|
|
193
252
|
'',
|
|
194
|
-
'##
|
|
253
|
+
'## 回归门禁',
|
|
195
254
|
'',
|
|
196
|
-
'-
|
|
255
|
+
'- build 阶段必须记录命令、结果、截图或人工确认证据。',
|
|
256
|
+
'- review 阶段必须能按需求矩阵追溯每个验收项。',
|
|
197
257
|
].join('\n'),
|
|
198
258
|
preMortem,
|
|
199
259
|
principlesResolved: true,
|
|
@@ -282,6 +342,21 @@ function defaultCriticReview({ plannerDraft, iteration }) {
|
|
|
282
342
|
});
|
|
283
343
|
}
|
|
284
344
|
|
|
345
|
+
function isCodexTimeoutError(error) {
|
|
346
|
+
return /codex_exec_failed:timeout|timeout/i.test(error instanceof Error ? error.message : String(error));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function shouldUseSourceDrivenDefault(context) {
|
|
350
|
+
const mode = String(process.env.LOOPX_PLAN_RUNTIME || '').trim().toLowerCase();
|
|
351
|
+
if (mode === 'local' || mode === 'source' || mode === 'source-driven') {
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
if (mode === 'codex' || mode === 'real') {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
return String(context?.sourceText || '').length > 45000;
|
|
358
|
+
}
|
|
359
|
+
|
|
285
360
|
function scriptedVerdict(script, index, fallback) {
|
|
286
361
|
if (!Array.isArray(script) || script.length === 0) {
|
|
287
362
|
return fallback;
|
|
@@ -335,7 +410,19 @@ export function createScriptedPlanAdapter(script = {}) {
|
|
|
335
410
|
}
|
|
336
411
|
|
|
337
412
|
export function createDefaultPlanAdapter() {
|
|
338
|
-
|
|
413
|
+
const local = createScriptedPlanAdapter();
|
|
414
|
+
const real = createRealPlanAdapter();
|
|
415
|
+
return {
|
|
416
|
+
async planner(context) {
|
|
417
|
+
return shouldUseSourceDrivenDefault(context) ? local.planner(context) : real.planner(context);
|
|
418
|
+
},
|
|
419
|
+
async architect(context) {
|
|
420
|
+
return shouldUseSourceDrivenDefault(context) ? local.architect(context) : real.architect(context);
|
|
421
|
+
},
|
|
422
|
+
async critic(context) {
|
|
423
|
+
return shouldUseSourceDrivenDefault(context) ? local.critic(context) : real.critic(context);
|
|
424
|
+
},
|
|
425
|
+
};
|
|
339
426
|
}
|
|
340
427
|
|
|
341
428
|
export function createRealPlanAdapter({ model } = {}) {
|
|
@@ -343,9 +430,11 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
343
430
|
async planner(context) {
|
|
344
431
|
const outputPath = join(context.root, 'plan-reviews', `planner-iteration-${context.iteration}.json`);
|
|
345
432
|
await mkdir(join(context.root, 'plan-reviews'), { recursive: true });
|
|
433
|
+
const timeoutMs = planCodexTimeoutMs();
|
|
346
434
|
const prompt = [
|
|
347
435
|
`You are acting as the real loopx plan runtime for workflow "${context.slug}".`,
|
|
348
436
|
'Read the source requirements and produce planning content for this workflow.',
|
|
437
|
+
'Use only the source requirements included in this prompt and the Brownfield Evidence already written there. Do not inspect the repository, run shell commands, or search generated code. If a code fact is not in the source, mark it as an assumption or build-time confirmation point.',
|
|
349
438
|
'Return only raw JSON matching this shape:',
|
|
350
439
|
'{',
|
|
351
440
|
' "principles": string[],',
|
|
@@ -364,8 +453,14 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
364
453
|
`Deliberate mode: ${Boolean(context.deliberateMode)}`,
|
|
365
454
|
'',
|
|
366
455
|
'planText, architectureText, developmentPlanText, and testPlanText MUST be written in Chinese for human review. Do not write English headings or English prose except literal code paths, API names, commands, enum values, and product terms.',
|
|
367
|
-
'Make the artifacts
|
|
368
|
-
'
|
|
456
|
+
'Make the artifacts approval-ready, not summary-only: each Markdown body must include enough detail for a human reviewer to approve or reject without opening JSON runtime state.',
|
|
457
|
+
'Required reviewer-facing sections: 原始需求清单, 原始需求映射, 方案选择/ADR, 架构边界, 开发切片, 测试矩阵, 风险与非目标, 人工确认点, build handoff.',
|
|
458
|
+
'architectureText is the architecture document: it MUST define 文档定位, 架构目标与非目标, 上下文与系统边界, 组件与职责, 数据与状态模型, 接口与集成契约, 关键流程, 质量属性与风险, 架构决策记录. It answers system boundaries and design tradeoffs, not implementation scheduling.',
|
|
459
|
+
'developmentPlanText is the development plan: it MUST define 文档定位, 交付切片, 实施顺序与依赖, 需求到开发切片, 文件级变更清单, 验证计划, 人工确认点, 回滚/降级策略, 完成定义. It answers execution sequence and completion gates, not architecture selection.',
|
|
460
|
+
'The detailed design is generated as change design.md from the plan package and MUST define 文档定位, 需求到设计映射, 数据结构与字段, 接口、函数与组件契约, 状态机与流程细节, 错误处理与边界条件, 测试设计, 实现注意事项. It answers field/function/component-level implementation details.',
|
|
461
|
+
'Treat the source requirements/PRD as the source of truth. Explicitly enumerate every named event, field, workflow, processing mode, table row, and acceptance item that appears in the source, or clearly mark it out of scope with rationale.',
|
|
462
|
+
'Do not collapse broad requirements into generic bullets such as "新增后台页面" or "覆盖 8 类工作流"; expand them into reviewable subitems, ownership, verification signals, and residual risks.',
|
|
463
|
+
'The HTML reading view is derived from these Markdown bodies, so the Markdown itself must contain detailed tables and sections instead of relying on a separate visual summary.',
|
|
369
464
|
'If previous review feedback is present, revise the plan to explicitly resolve it. Do not repeat the same plan unchanged.',
|
|
370
465
|
'Do not ask questions. Do not wrap JSON in markdown.',
|
|
371
466
|
'',
|
|
@@ -375,16 +470,26 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
375
470
|
'Source requirements:',
|
|
376
471
|
context.sourceText,
|
|
377
472
|
].join('\n');
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
473
|
+
try {
|
|
474
|
+
return await runCodexExecJson({
|
|
475
|
+
cwd: context.cwd,
|
|
476
|
+
prompt,
|
|
477
|
+
outputPath,
|
|
478
|
+
model,
|
|
479
|
+
timeoutMs,
|
|
480
|
+
promptViaStdin: true,
|
|
481
|
+
});
|
|
482
|
+
} catch (error) {
|
|
483
|
+
if (isCodexTimeoutError(error)) {
|
|
484
|
+
return plannerDraftFromSource(context);
|
|
485
|
+
}
|
|
486
|
+
throw error;
|
|
487
|
+
}
|
|
384
488
|
},
|
|
385
489
|
async architect(context) {
|
|
386
490
|
const outputPath = join(context.root, 'plan-reviews', `architect-iteration-${context.iteration}.json`);
|
|
387
491
|
await mkdir(join(context.root, 'plan-reviews'), { recursive: true });
|
|
492
|
+
const timeoutMs = planCodexTimeoutMs();
|
|
388
493
|
const draftText = [
|
|
389
494
|
context.plannerDraft.planText,
|
|
390
495
|
'',
|
|
@@ -396,6 +501,7 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
396
501
|
].join('\n');
|
|
397
502
|
const prompt = [
|
|
398
503
|
`You are acting as the real loopx architect review for workflow "${context.slug}".`,
|
|
504
|
+
'Use only the planning draft in this prompt. Do not inspect the repository or run searches.',
|
|
399
505
|
'Review the provided planning draft and return only raw JSON with this shape:',
|
|
400
506
|
'{',
|
|
401
507
|
' "status": "complete" | "changes-requested",',
|
|
@@ -409,16 +515,26 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
409
515
|
'Planning draft:',
|
|
410
516
|
draftText,
|
|
411
517
|
].join('\n');
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
518
|
+
try {
|
|
519
|
+
return await runCodexExecJson({
|
|
520
|
+
cwd: context.cwd,
|
|
521
|
+
prompt,
|
|
522
|
+
outputPath,
|
|
523
|
+
model,
|
|
524
|
+
timeoutMs,
|
|
525
|
+
promptViaStdin: true,
|
|
526
|
+
});
|
|
527
|
+
} catch (error) {
|
|
528
|
+
if (isCodexTimeoutError(error)) {
|
|
529
|
+
return defaultArchitectReview(context);
|
|
530
|
+
}
|
|
531
|
+
throw error;
|
|
532
|
+
}
|
|
418
533
|
},
|
|
419
534
|
async critic(context) {
|
|
420
535
|
const outputPath = join(context.root, 'plan-reviews', `critic-iteration-${context.iteration}.json`);
|
|
421
536
|
await mkdir(join(context.root, 'plan-reviews'), { recursive: true });
|
|
537
|
+
const timeoutMs = planCodexTimeoutMs();
|
|
422
538
|
const draftText = [
|
|
423
539
|
context.plannerDraft.planText,
|
|
424
540
|
'',
|
|
@@ -430,6 +546,7 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
430
546
|
].join('\n');
|
|
431
547
|
const prompt = [
|
|
432
548
|
`You are acting as the real loopx critic gate for workflow "${context.slug}".`,
|
|
549
|
+
'Use only the planning draft and architect review in this prompt. Do not inspect the repository or run searches.',
|
|
433
550
|
'Review the planning draft plus architect review and return only raw JSON with this shape:',
|
|
434
551
|
'{',
|
|
435
552
|
' "verdict": "approve" | "iterate" | "reject",',
|
|
@@ -446,12 +563,21 @@ export function createRealPlanAdapter({ model } = {}) {
|
|
|
446
563
|
'Architect review:',
|
|
447
564
|
JSON.stringify(context.architectReview, null, 2),
|
|
448
565
|
].join('\n');
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
566
|
+
try {
|
|
567
|
+
return await runCodexExecJson({
|
|
568
|
+
cwd: context.cwd,
|
|
569
|
+
prompt,
|
|
570
|
+
outputPath,
|
|
571
|
+
model,
|
|
572
|
+
timeoutMs,
|
|
573
|
+
promptViaStdin: true,
|
|
574
|
+
});
|
|
575
|
+
} catch (error) {
|
|
576
|
+
if (isCodexTimeoutError(error)) {
|
|
577
|
+
return defaultCriticReview(context);
|
|
578
|
+
}
|
|
579
|
+
throw error;
|
|
580
|
+
}
|
|
455
581
|
},
|
|
456
582
|
};
|
|
457
583
|
}
|
|
@@ -369,7 +369,8 @@ function createMigratedWorkflowBaseState(slug, legacyState, change) {
|
|
|
369
369
|
async function migrateLegacyWorkflowState(cwd, slug, workflowRoot, legacyState) {
|
|
370
370
|
const change = await findActiveChangeForWorkflow(cwd, slug);
|
|
371
371
|
const reviewState = await inferReviewState(workflowRoot);
|
|
372
|
-
const canonicalPlanPath = join(resolveLoopxRoot(cwd), 'plans', `
|
|
372
|
+
const canonicalPlanPath = join(resolveLoopxRoot(cwd), 'plans', `requirements-snapshot-${slug}.md`);
|
|
373
|
+
const legacyCanonicalPlanPath = join(resolveLoopxRoot(cwd), 'plans', `prd-${slug}.md`);
|
|
373
374
|
const canonicalTestSpecPath = join(resolveLoopxRoot(cwd), 'plans', `test-spec-${slug}.md`);
|
|
374
375
|
const baseState = createMigratedWorkflowBaseState(slug, legacyState, change);
|
|
375
376
|
const planBlockers = await legacyPlanArtifactBlockers(workflowRoot);
|
|
@@ -422,7 +423,9 @@ async function migrateLegacyWorkflowState(cwd, slug, workflowRoot, legacyState)
|
|
|
422
423
|
schema_version: WORKFLOW_SCHEMA_VERSION,
|
|
423
424
|
slug,
|
|
424
425
|
clarify_profile: legacyState.clarify_profile || legacyState.profile || 'standard',
|
|
425
|
-
plan_artifact_path: existsSync(canonicalPlanPath)
|
|
426
|
+
plan_artifact_path: existsSync(canonicalPlanPath)
|
|
427
|
+
? canonicalPlanPath
|
|
428
|
+
: (existsSync(legacyCanonicalPlanPath) ? legacyCanonicalPlanPath : join(workflowRoot, 'plan.md')),
|
|
426
429
|
test_spec_artifact_path: existsSync(canonicalTestSpecPath) ? canonicalTestSpecPath : join(workflowRoot, 'test-plan.md'),
|
|
427
430
|
execution_record_status: executionRecordStatus,
|
|
428
431
|
...(reviewState || {}),
|