@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.
Files changed (77) hide show
  1. package/AGENTS.md +49 -0
  2. package/README.md +69 -448
  3. package/README.zh-CN.md +69 -459
  4. package/docs/loopx/design/loopx-skill-suite-v1-design.md +80 -0
  5. package/docs/loopx/plans/loopx-skill-suite-v1-implementation.md +81 -0
  6. package/package.json +7 -3
  7. package/plugins/loopx/.codex-plugin/plugin.json +4 -4
  8. package/plugins/loopx/skills/clarify/SKILL.md +38 -311
  9. package/plugins/loopx/skills/debug/SKILL.md +1 -1
  10. package/plugins/loopx/skills/exec/SKILL.md +71 -0
  11. package/plugins/loopx/skills/finish/SKILL.md +349 -0
  12. package/plugins/loopx/skills/fix-review/SKILL.md +216 -0
  13. package/plugins/loopx/skills/go-style/SKILL.md +2 -2
  14. package/plugins/loopx/skills/kratos/SKILL.md +1 -1
  15. package/plugins/loopx/skills/plan/SKILL.md +138 -271
  16. package/plugins/loopx/skills/refactor-plan/SKILL.md +71 -0
  17. package/plugins/loopx/skills/review/SKILL.md +72 -105
  18. package/plugins/loopx/skills/review/code-reviewer.md +168 -0
  19. package/plugins/loopx/skills/spec/DESIGN_SPEC_TEMPLATE.md +323 -0
  20. package/plugins/loopx/skills/spec/SKILL.md +76 -0
  21. package/plugins/loopx/skills/subagent-exec/SKILL.md +282 -0
  22. package/plugins/loopx/skills/subagent-exec/agents/openai.yaml +3 -0
  23. package/plugins/loopx/skills/subagent-exec/code-quality-reviewer-prompt.md +25 -0
  24. package/plugins/loopx/skills/subagent-exec/codex-subagents.md +37 -0
  25. package/plugins/loopx/skills/subagent-exec/implementer-prompt.md +113 -0
  26. package/plugins/loopx/skills/subagent-exec/spec-reviewer-prompt.md +61 -0
  27. package/plugins/loopx/skills/tdd/SKILL.md +1 -1
  28. package/plugins/loopx/skills/verify/SKILL.md +1 -1
  29. package/scripts/claude-workflow-hook.mjs +109 -0
  30. package/scripts/codex-workflow-hook.mjs +2 -5
  31. package/scripts/install-skills.mjs +3 -3
  32. package/scripts/verify-skills.mjs +34 -2
  33. package/skills/RESOLVER.md +22 -17
  34. package/skills/clarify/SKILL.md +38 -311
  35. package/skills/debug/SKILL.md +1 -1
  36. package/skills/exec/SKILL.md +71 -0
  37. package/skills/finish/SKILL.md +349 -0
  38. package/skills/fix-review/SKILL.md +216 -0
  39. package/skills/go-style/SKILL.md +2 -2
  40. package/skills/kratos/SKILL.md +1 -1
  41. package/skills/plan/SKILL.md +138 -271
  42. package/skills/refactor-plan/SKILL.md +71 -0
  43. package/skills/review/SKILL.md +72 -105
  44. package/skills/review/code-reviewer.md +168 -0
  45. package/skills/spec/DESIGN_SPEC_TEMPLATE.md +323 -0
  46. package/skills/spec/SKILL.md +76 -0
  47. package/skills/subagent-exec/SKILL.md +282 -0
  48. package/skills/subagent-exec/agents/openai.yaml +3 -0
  49. package/skills/subagent-exec/code-quality-reviewer-prompt.md +25 -0
  50. package/skills/subagent-exec/codex-subagents.md +37 -0
  51. package/skills/subagent-exec/implementer-prompt.md +113 -0
  52. package/skills/subagent-exec/spec-reviewer-prompt.md +61 -0
  53. package/skills/tdd/SKILL.md +1 -1
  54. package/skills/verify/SKILL.md +1 -1
  55. package/src/autopilot-runtime.mjs +1 -1
  56. package/src/cli.mjs +78 -7
  57. package/src/context-manifest.mjs +2 -2
  58. package/src/html-views.mjs +129 -195
  59. package/src/install-discovery.mjs +210 -6
  60. package/src/next-skill.mjs +2 -4
  61. package/src/plan-runtime.mjs +219 -93
  62. package/src/runtime-maintenance.mjs +5 -2
  63. package/src/workflow.mjs +749 -71
  64. package/templates/architecture.md +58 -16
  65. package/templates/development-plan.md +42 -12
  66. package/plugins/loopx/scripts/plugin-install.test.mjs +0 -125
  67. package/plugins/loopx/skills/archive/SKILL.md +0 -55
  68. package/plugins/loopx/skills/autopilot/SKILL.md +0 -93
  69. package/plugins/loopx/skills/build/SKILL.md +0 -228
  70. package/skills/ai-slop-cleaner/SKILL.md +0 -114
  71. package/skills/archive/SKILL.md +0 -55
  72. package/skills/autopilot/SKILL.md +0 -93
  73. package/skills/autoresearch/SKILL.md +0 -68
  74. package/skills/build/SKILL.md +0 -228
  75. package/skills/deep-interview/SKILL.md +0 -461
  76. package/skills/ralph/SKILL.md +0 -271
  77. package/skills/ralplan/SKILL.md +0 -49
@@ -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
- '如果 docs 只是附加产物,plan 完成判定会再次与文档契约脱节。',
70
- '如果 status 只显示 plan_package_status,规划阶段故障仍然无法诊断。',
86
+ '如果架构边界没有明确外部依赖和副作用控制,build 阶段容易把 mock 范围误做成真实集成。',
87
+ '如果开发切片只按模块拆分而不是按可验收行为拆分,后续实现容易出现页面、接口和数据闭环脱节。',
88
+ '如果测试计划没有覆盖核心验收样例、异常路径和人工确认点,review 阶段无法判断是否满足需求。',
71
89
  ]
72
90
  : [];
73
91
 
74
92
  return {
75
93
  principles: [
76
- '运行时行为必须与已发布的 planning contract 对齐。',
77
- '规划阶段只做规划,不自动进入执行阶段。',
78
- '完成判定必须机器可检查。',
79
- '中文 docs 是阻塞产物,不是附加导出。',
80
- '保持变更范围集中在 plan runtime、status 和测试。',
94
+ '计划必须完整承接已澄清需求,不把核心范围压缩成泛化条目。',
95
+ '架构、详细设计、开发计划和测试计划必须分别回答不同问题,避免 build 阶段自行补设计。',
96
+ '涉及人工确认、外部系统、资金资产、交易、权限或通知的边界必须显式写清。',
97
+ '每个可交付切片都必须有可验证信号和人工确认点。',
98
+ '计划阶段只输出方案和执行输入,不自动进入实现。',
81
99
  ],
82
100
  decisionDrivers: [
83
- '当前 skill contract 与 runtime truth 不一致。',
84
- 'approved plan 与中文 docs 都是必需产物。',
85
- '现有 clarify/build/review 行为需要保持稳定。',
101
+ summary.intent,
102
+ summary.outcome,
103
+ '后续 build 需要可直接执行的模块边界、数据结构、接口契约和验收矩阵。',
86
104
  ],
87
105
  options: [
88
106
  {
89
- name: ' plan runtime 内嵌编排',
90
- pros: ['runtime 真相与产品契约保持一致', '由同一个状态机管理 gate 与工件'],
91
- cons: ['需要额外的 adapter 边界来支撑确定性测试'],
107
+ name: '统一平台底座 + 场景扩展',
108
+ pros: ['共享状态、日志、异常和验收闭环', '减少重复实现', '便于后续接入真实 adapter'],
109
+ cons: ['需要严格控制扩展字段和状态机边界'],
92
110
  },
93
111
  {
94
- name: '在轻量 plan 外包一层共识流程',
95
- pros: ['短期 diff 更小'],
96
- cons: ['wrapper 与 runtime 的事实仍然分裂', 'status 与调试信息继续碎片化'],
112
+ name: '按场景分别实现',
113
+ pros: ['单个场景领域表达更直接'],
114
+ cons: ['重复状态机、接口、日志和页面结构,首期交付和回归成本高'],
97
115
  },
98
116
  ],
99
117
  planText: [
100
- `# loopx 计划: ${slug}`,
118
+ `# 计划:${slug}`,
101
119
  '',
102
120
  '## 需求摘要',
103
121
  '',
104
122
  `- ${summary.intent}`,
105
123
  `- ${summary.outcome}`,
106
124
  '',
107
- '## 交付物',
125
+ '## 交付范围',
108
126
  '',
109
- ...summary.acceptance.map((item, index) => `${index + 1}. ${item}`),
127
+ ...inScope.map((item) => `- ${item}`),
110
128
  '',
111
- '## 实施步骤',
129
+ '## 方案选择',
112
130
  '',
113
- '1. 为 planner、architect、critic 增加 plan orchestration adapter。',
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
- ...(executionInputs.length > 0 ? executionInputs.map((item) => `- ${item}`) : ['- TBD: execution inputs not yet mapped to concrete sources.']),
140
+ '## 验收目标',
141
+ '',
142
+ ...acceptance.map((item) => `- ${item}`),
122
143
  '',
123
144
  '## 风险',
124
145
  '',
125
- ...summary.constraints.map((item) => `- ${item}`),
146
+ ...constraints.map((item) => `- ${item}`),
126
147
  '',
127
- '## 验证',
148
+ '## 执行输入',
128
149
  '',
129
- '- 运行 workflow tests',
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
- `# 架构文档: ${slug}`,
153
+ `# 架构方案:${slug}`,
135
154
  '',
136
- '## 目标',
155
+ '## 文档定位',
137
156
  '',
138
- `- ${summary.intent}`,
157
+ '架构方案是本阶段的架构文档,回答系统边界、模块职责、数据/状态流、接口边界、架构决策和质量属性,不负责逐文件排期或字段级默认值。',
158
+ '',
159
+ '## 架构目标与非目标',
160
+ '',
161
+ `目标:${summary.intent}`,
139
162
  '',
140
- '## 边界',
163
+ '非目标:',
141
164
  '',
142
- ...summary.decisions.map((item) => `- ${item}`),
165
+ ...summary.nonGoals.map((item) => `- ${item}`),
143
166
  '',
144
- '## 选定方案',
167
+ '## 上下文与系统边界',
145
168
  '',
146
- '- plan runtime 负责 planner -> architect -> critic 闭环',
147
- '- 通过专用 adapter 隔离生产编排与确定性测试',
148
- '- canonical plan artifacts 保持写入 `.loopx/plans/`',
149
- '- `.loopx/workflows/<slug>/` 下的主规划工件直接作为中文产物',
169
+ ...decisions.map((item) => `- ${item}`),
150
170
  '',
151
- '## 备选方案',
171
+ '## 组件与职责',
152
172
  '',
153
- '- 保持 plan 轻量并在外层包一层流程',
154
- '- 推迟 runtime 对齐,仅把 skill contract 当目标状态',
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
- `# 开发计划: ${slug}`,
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
- '1. 扩展 plan state schema 与 status 输出。',
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
- '- owner: plan runtime',
169
- '- reviewer: architect and critic',
170
- '- downstream execution: 仅在后续显式批准后进入',
223
+ '## 验证计划',
171
224
  '',
172
- '## 时序要求',
225
+ ...acceptance.map((item) => `- 验证:${item}`),
173
226
  '',
174
- '- architect 未完成前不得运行 critic',
175
- '- plan blockers 清除前不得批准 build',
176
- '- plan 阶段不得自动启动执行',
227
+ '## 完成定义',
228
+ '',
229
+ '- 所有源需求都有实现证据或明确非目标说明。',
230
+ '- 自动化测试、构建和人工验收信号与开发切片一一对应。',
231
+ '- 未完成、风险和回滚路径在交付说明中明确记录。',
177
232
  ].join('\n'),
178
233
  testPlanText: [
179
- `# 测试计划: ${slug}`,
234
+ `# 测试计划:${slug}`,
235
+ '',
236
+ '## 需求到测试矩阵',
237
+ '',
238
+ ...acceptance.map((item) => `- ${item}`),
180
239
  '',
181
- '## 单元测试',
240
+ '## 自动化测试',
182
241
  '',
183
- '- plan consensus mode 的 state 初始化',
184
- '- 中文主规划工件的 blocking checks',
185
- '- planner/architect/critic review artifact 记录',
242
+ '- 状态机合法/非法转换。',
243
+ '- 数据去重、持久化、查询和日志写入。',
244
+ '- API 参数、权限、错误和响应结构。',
245
+ '- 前端构建和关键页面渲染。',
186
246
  '',
187
- '## 集成测试',
247
+ '## 人工验收',
188
248
  '',
189
- '- clarify -> plan happy path',
190
- '- critic iterate 再 approve 的路径',
191
- '- 主规划工件缺失或非中文时的 blocking 路径',
192
- '- execution inputs 缺失或标记 TBD 的 blocking 路径',
249
+ '- 页面/流程是否符合原型和产品文档。',
250
+ '- 人工确认动作是否清晰且不可被系统自动跳过。',
251
+ '- mock/真实边界是否符合非目标。',
193
252
  '',
194
- '## 可观测性',
253
+ '## 回归门禁',
195
254
  '',
196
- '- status 输出 iteration、architect review status、critic verdict 与 execution input blockers',
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
- return createRealPlanAdapter();
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 reviewable: use clear Chinese section headings, concise tables for coverage/options/risks, and explicit approval-ready handoff sections. Avoid generic filler.',
368
- 'Treat the source requirements/PRD as the source of truth. Explicitly cover every named event, field, processing mode, table row, and acceptance item that appears in the source, or clearly mark it out of scope with rationale.',
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
- return runCodexExecJson({
379
- cwd: context.cwd,
380
- prompt,
381
- outputPath,
382
- model,
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
- return runCodexExecJson({
413
- cwd: context.cwd,
414
- prompt,
415
- outputPath,
416
- model,
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
- return runCodexExecJson({
450
- cwd: context.cwd,
451
- prompt,
452
- outputPath,
453
- model,
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', `prd-${slug}.md`);
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) ? canonicalPlanPath : join(workflowRoot, 'plan.md'),
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 || {}),