@ai-content-space/loopx 0.1.9 → 0.2.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.
Files changed (72) hide show
  1. package/AGENTS.md +50 -0
  2. package/README.md +59 -446
  3. package/README.zh-CN.md +59 -457
  4. package/docs/loopx/design/loopx-skill-suite-v1-design.md +73 -0
  5. package/docs/loopx/plans/loopx-skill-suite-v1-implementation.md +77 -0
  6. package/package.json +5 -2
  7. package/plugins/loopx/.codex-plugin/plugin.json +4 -4
  8. package/plugins/loopx/scripts/plugin-install.test.mjs +20 -20
  9. package/plugins/loopx/skills/clarify/SKILL.md +38 -311
  10. package/plugins/loopx/skills/debug/SKILL.md +1 -1
  11. package/plugins/loopx/skills/exec/SKILL.md +71 -0
  12. package/plugins/loopx/skills/finish/SKILL.md +254 -0
  13. package/plugins/loopx/skills/fix-review/SKILL.md +216 -0
  14. package/plugins/loopx/skills/go-style/SKILL.md +1 -1
  15. package/plugins/loopx/skills/kratos/SKILL.md +1 -1
  16. package/plugins/loopx/skills/plan/SKILL.md +136 -258
  17. package/plugins/loopx/skills/refactor-plan/SKILL.md +71 -0
  18. package/plugins/loopx/skills/review/SKILL.md +72 -105
  19. package/plugins/loopx/skills/review/code-reviewer.md +168 -0
  20. package/plugins/loopx/skills/spec/DESIGN_SPEC_TEMPLATE.md +323 -0
  21. package/plugins/loopx/skills/spec/SKILL.md +76 -0
  22. package/plugins/loopx/skills/subagent-exec/SKILL.md +282 -0
  23. package/plugins/loopx/skills/subagent-exec/agents/openai.yaml +3 -0
  24. package/plugins/loopx/skills/subagent-exec/code-quality-reviewer-prompt.md +25 -0
  25. package/plugins/loopx/skills/subagent-exec/codex-subagents.md +37 -0
  26. package/plugins/loopx/skills/subagent-exec/implementer-prompt.md +113 -0
  27. package/plugins/loopx/skills/subagent-exec/spec-reviewer-prompt.md +61 -0
  28. package/plugins/loopx/skills/tdd/SKILL.md +1 -1
  29. package/plugins/loopx/skills/verify/SKILL.md +1 -1
  30. package/scripts/claude-workflow-hook.mjs +109 -0
  31. package/scripts/codex-workflow-hook.mjs +2 -5
  32. package/scripts/install-skills.mjs +3 -3
  33. package/scripts/verify-skills.mjs +32 -1
  34. package/skills/RESOLVER.md +26 -17
  35. package/skills/clarify/SKILL.md +38 -311
  36. package/skills/debug/SKILL.md +1 -1
  37. package/skills/exec/SKILL.md +71 -0
  38. package/skills/finish/SKILL.md +254 -0
  39. package/skills/fix-review/SKILL.md +216 -0
  40. package/skills/go-style/SKILL.md +1 -1
  41. package/skills/kratos/SKILL.md +1 -1
  42. package/skills/plan/SKILL.md +136 -258
  43. package/skills/refactor-plan/SKILL.md +71 -0
  44. package/skills/review/SKILL.md +72 -105
  45. package/skills/review/code-reviewer.md +168 -0
  46. package/skills/spec/DESIGN_SPEC_TEMPLATE.md +323 -0
  47. package/skills/spec/SKILL.md +76 -0
  48. package/skills/subagent-exec/SKILL.md +282 -0
  49. package/skills/subagent-exec/agents/openai.yaml +3 -0
  50. package/skills/subagent-exec/code-quality-reviewer-prompt.md +25 -0
  51. package/skills/subagent-exec/codex-subagents.md +37 -0
  52. package/skills/subagent-exec/implementer-prompt.md +113 -0
  53. package/skills/subagent-exec/spec-reviewer-prompt.md +61 -0
  54. package/skills/tdd/SKILL.md +1 -1
  55. package/skills/verify/SKILL.md +1 -1
  56. package/src/autopilot-runtime.mjs +1 -1
  57. package/src/cli.mjs +79 -5
  58. package/src/context-manifest.mjs +2 -2
  59. package/src/html-views.mjs +457 -95
  60. package/src/install-discovery.mjs +210 -6
  61. package/src/next-skill.mjs +2 -4
  62. package/src/plan-runtime.mjs +572 -93
  63. package/src/runtime-maintenance.mjs +60 -16
  64. package/src/workflow.mjs +989 -65
  65. package/templates/architecture.md +58 -16
  66. package/templates/development-plan.md +42 -12
  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/archive/SKILL.md +0 -55
  71. package/skills/autopilot/SKILL.md +0 -93
  72. package/skills/build/SKILL.md +0 -228
@@ -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,547 @@ 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
+
76
+ function isCorporateActionSource(sourceText) {
77
+ return /公司行动|Corporate Action|corporate-actions|OCC|分红派息|拆股|合股|退市|摘牌|代码\s*\/\s*名称变更/i.test(String(sourceText || ''));
78
+ }
79
+
80
+ function corporateActionPlannerDraft({ slug, sourceText }) {
81
+ const eventRows = [
82
+ ['总览', '展示当前任务、今日新增、今日需处理、未来生效、异常/超时', '读取任务/异常/下游状态聚合,不执行任何业务动作', 'seed 后 8 类样例均可进入详情'],
83
+ ['分红派息', '普通现金分红、特殊分红、主体扣税、税费分摊、客户净额', '生成客户分红明细;确认主体税费和分摊后才允许模拟入账/扣款', '多头入账、空头扣款、待确认客户进入差异处理'],
84
+ ['拆股', '正股数量增加、单位成本同比降低、OCC 合约调整', '生成正股持仓调整和期权调整明细;未确认 OCC 时不得执行下发', '客户持仓、订单处理、保证金重算、展示下发均有 mock 状态'],
85
+ ['合股', '正股数量减少、碎股现金替代、非标准期权', '生成整数股、碎股现金替代和期权非标调整;碎股金额差异进入异常', '碎股现金替代、订单处理、期权非标展示均可验收'],
86
+ ['退市/摘牌', '最后交易日、清算/失效日、持仓和订单保护', '生成客户影响、订单保护和期权 OCC 待监控;缺结果时停在待复核或差异待处理', '不自动迁移资产账本,不改历史账单'],
87
+ ['期权退市 OCC', 'OCC Memo、原合约、调整后合约、乘数、行权价、交割物', '按 memo_no + underlying_security_id + adjustment_type 匹配;正股任务可关联但不可互相替代', 'OCC 字段缺失、合约映射失败、保证金未重算均进入异常'],
88
+ ['代码/名称变更', '新旧 symbol/name/security_id 映射、生效日、确认来源', '确认映射后模拟下发展示、订单和期权链影响;不改历史账单', '缺新代码映射时创建事件异常,不允许执行'],
89
+ ['事件异常处理', '金额、数量、客户范围、合约映射、下游回写、超时', '异常独立落库并关联 task/detail/option_adjustment/downstream_status', '处理后回到待确认或待执行;处理动作写 OperationLog'],
90
+ ];
91
+ const stateRows = [
92
+ ['生成明细', '待生成', '事件已入库且未有有效明细版本', '待复核', '差异待处理', '写客户明细/期权调整/操作日志;重复请求返回已有有效版本'],
93
+ ['重算', '待复核、待确认、差异待处理', '任务未执行且存在明细或异常修正', '待复核', '差异待处理', '废弃旧明细版本,生成新版本;已完成/执行中禁止重算'],
94
+ ['复核', '待复核', '明细完整且无阻断异常', '待确认', '差异待处理', '仅人工触发;记录 operator_id/operator_name/remark'],
95
+ ['确认', '待确认', '税费、数量、OCC、客户范围均已复核', '待执行', '差异待处理', '确认后冻结当前有效明细版本'],
96
+ ['执行', '待执行', '动作类型为现金/持仓处理且无阻断异常', '执行中', '差异待处理', '只调用 mock ExecutionProvider,不触达真实资产/交易/清算'],
97
+ ['下发', '待执行', '动作类型为展示/订单/期权链/柜台下发且无阻断异常', '执行中', '差异待处理', '写 DownstreamStatus;mock 回写成功后归档'],
98
+ ['mock 回写成功', '执行中', '全部目标系统返回 success', '已完成', '差异待处理', '系统回调只允许推进执行中任务'],
99
+ ['mock 回写失败', '执行中', '任一目标系统返回 failed/timeout', '差异待处理', '差异待处理', '创建 Discrepancy 并保留 DownstreamStatus 失败原因'],
100
+ ['重试', '差异待处理、执行中', '失败原因已处理或超时可补偿查询', '执行中', '差异待处理', '重试次数和目标系统写入 DownstreamStatus'],
101
+ ['录入人工结果', '待复核、待确认、差异待处理', '运营输入确认值和说明', '待确认', '差异待处理', '只修改平台确认字段,不写真实下游'],
102
+ ['标记无需处理', '待生成、待复核、待确认、待执行、差异待处理', '确认该事件对客户/平台无动作', '无需处理', '原状态不变', '必须填写原因;终态禁止再执行'],
103
+ ['标记人工完成', '待执行、差异待处理', '外部已人工处理且上传凭证/备注', '已完成', '原状态不变', '必须写 OperationLog;不触发 mock 执行'],
104
+ ];
105
+ const entityRows = [
106
+ ['CorporateActionEvent', 'id, corporate_action_event_id(unique), source, source_event_id, event_type, market, asset_type, security_id, underlying_security_id, symbol, key_date, version, event_status, raw_snapshot_id, event_payload', '事件去重、版本修订、取消/更正追溯', 'corporate_action_event_id + version;同版本重复不建新任务'],
107
+ ['CorporateActionTask', 'task_id(unique), event_id, event_type, task_status, asset_scope, security_id, symbol, key_date, affected_customer_count, affected_order_count, risk_flags, current_detail_version, detail_payload', '运营主任务和状态机载体', 'event_id 一对一或一对多;任务状态由 usecase 集中推进'],
108
+ ['CorporateActionCustomerDetail', 'task_id, detail_version, user_id, trade_account_id, security_id, asset_type, position_qty, cash_amount, tax_amount, net_amount, order_action, margin_status, process_status, detail_payload', '客户级影响明细', 'task_id + detail_version + user_id + trade_account_id + security_id'],
109
+ ['CorporateActionOptionAdjustment', 'task_id, memo_no, underlying_security_id, option_security_id, original_contract, adjusted_contract, old_strike, new_strike, old_multiplier, new_multiplier, deliverable, review_status, adjustment_payload', 'OCC 和期权调整', 'memo_no + underlying_security_id + option_security_id + adjustment_type'],
110
+ ['CorporateActionDiscrepancy', 'task_id, detail_id, option_adjustment_id, downstream_status_id, discrepancy_type, field_name, estimated_value, confirmed_value, status, resolution_note', '差异和事件异常', '处理后写 confirmed_value/status/resolution_note 并回推任务状态'],
111
+ ['CorporateActionDownstreamStatus', 'task_id, target_system, action_type, status, request_payload, response_payload, failure_reason, retry_count', 'mock 执行/下发回写', 'target_system + action_type 可多条;禁止真实 client 写入'],
112
+ ['CorporateActionOperationLog', 'task_id, operator_id, operator_name, action_type, before_status, after_status, remark, created_at', '人工动作和系统回写审计', '所有人工动作、mock 回写、异常处理必须落日志'],
113
+ ];
114
+ const providerRows = [
115
+ ['EventSourceProvider', 'FetchMockEvents(ctx) ([]EventInput, error)', '行情/RIC、OCC、清算结果的本地样例输入', '首期仅 local mock;不接 Reuters、OCC PDF、清算接口'],
116
+ ['ImpactProvider', 'BuildDetails(ctx, task) (ImpactResult, error)', '客户、持仓、订单、保证金、展示影响明细', '返回结构化明细和异常;不得读真实资产/交易系统'],
117
+ ['ExecutionProvider', 'Execute(ctx, task, action) (ExecutionResult, error)', '模拟现金入账/扣款、持仓调整、展示/订单/期权链下发', '只写 DownstreamStatus 和 OperationLog;无真实外部副作用'],
118
+ ['ExceptionProvider', 'Resolve(ctx, discrepancy, input) (ResolutionResult, error)', '人工确认金额/数量/客户范围/合约映射/回写失败', '处理结果只影响平台任务和明细确认字段'],
119
+ ];
120
+ const apiRows = [
121
+ ['GET', '/admin/v1/corporate-actions/overview', '总览统计、风险提示、异常/超时', 'admin.corporate_action.view'],
122
+ ['POST', '/admin/v1/corporate-actions/mock/seed', '生成 8 类 mock 样例', 'admin.corporate_action.seed'],
123
+ ['GET', '/admin/v1/corporate-actions/tasks', '按 event_type/status/symbol/task_id/key_date 查询任务', 'admin.corporate_action.view'],
124
+ ['GET', '/admin/v1/corporate-actions/tasks/{task_id}', '任务详情、客户明细、期权调整、异常、日志、下游状态', 'admin.corporate_action.view'],
125
+ ['POST', '/admin/v1/corporate-actions/tasks/{task_id}/details/generate', '生成或重算明细', 'admin.corporate_action.operate'],
126
+ ['POST', '/admin/v1/corporate-actions/tasks/{task_id}/actions/{action}', '复核、确认、执行、下发、重试、无需处理、人工完成', 'admin.corporate_action.operate'],
127
+ ['GET', '/admin/v1/corporate-actions/discrepancies', '异常列表和跨任务筛选', 'admin.corporate_action.view'],
128
+ ['POST', '/admin/v1/corporate-actions/discrepancies/{id}/resolve', '处理异常并回写任务状态', 'admin.corporate_action.operate'],
129
+ ];
130
+ const sliceRows = [
131
+ ['1', 'HITL', '领域/数据/状态底座', 'Ent schema、枚举、状态机、repository 接口、usecase 骨架', '状态矩阵单测、事件去重单测、schema 生成可重复'],
132
+ ['2', 'AFK', 'Mock 数据与任务闭环', '8 类 mock 事件、明细生成、异常创建、mock 回写', '每类事件可 seed、可生成任务、可查详情'],
133
+ ['3', 'HITL', '管理 API 与权限', 'overview/tasks/detail/action/discrepancy 接口和 permission_mark', '接口测试覆盖权限、参数、非法动作和响应结构'],
134
+ ['4', 'HITL', '后台前端', 'web/admin 总览、共享任务页、详情、操作区、异常页', 'npm build 通过;页面字段与原型样例一致'],
135
+ ['5', 'HITL', '端到端验收', '8 类工作流逐项演示、异常处理、mock 无真实副作用证明', 'go test ./...、前端 build、浏览器人工验收记录'],
136
+ ];
137
+ const testRows = eventRows.map(([eventType, scope, rule]) => [
138
+ eventType,
139
+ `验证 ${scope}`,
140
+ `${eventType === '总览' ? 'overview API + seed 统计' : 'usecase mock workflow + API detail/action 测试'}`,
141
+ `${rule};浏览器中从列表进入详情并完成允许动作`,
142
+ ]);
143
+
144
+ const planText = [
145
+ `# 计划:${slug}`,
146
+ '',
147
+ '## 需求摘要',
148
+ '',
149
+ '在 Account 服务内实现美股公司行动任务处理平台。首期覆盖总览、分红派息、拆股、合股、退市/摘牌、期权退市 OCC、代码/名称变更和事件异常处理。后端提供固定 `/admin/v1/corporate-actions/*` 管理接口;前端新建 `web/admin` 并由现有 `/account/admin/` 静态托管;事件、任务、明细、期权调整、异常、操作日志和 mock 下游状态必须持久化;外部系统和下游回写全部 mock;通知不做。',
150
+ '',
151
+ '## 原始需求清单',
152
+ '',
153
+ ...eventRows.map((row, index) => `${index + 1}. ${row[0]}:${row[1]}。`),
154
+ '',
155
+ '## 方案选择/ADR',
156
+ '',
157
+ mdTable(['方案', '结论', '理由', '代价'], [
158
+ ['统一任务底座 + 事件类型扩展 + mock provider', '采用', '状态机、日志、异常、权限、页面 shell 可复用,能覆盖全量原型并控制真实副作用', '必须把事件差异写成明确字段/子表/受控 payload,避免 JSON 失控'],
159
+ ['每类公司行动独立实现', '拒绝', '重复状态机、日志、异常和页面,首期实现和回归成本过高', '虽然领域表达更细,但不适合当前 mock 闭环目标'],
160
+ ['仅静态前端或内存 mock', '拒绝', '不能满足持久化、回放、审计和 build 后验收', '交付快但后续实现必然重写'],
161
+ ]),
162
+ '',
163
+ '## 架构边界',
164
+ '',
165
+ '- API 前缀唯一确定为 `/admin/v1/corporate-actions/*`;`/account/admin/` 只负责后台静态页面托管,不作为 API 前缀。',
166
+ '- 真实行情、OCC、清算、资产、交易、风控、展示、柜台、通知系统均不接入;首期只允许 local mock provider。',
167
+ '- 后端状态机是唯一真相;前端只展示后端返回的可用动作,不自行推导状态。',
168
+ '- 通知、OCC PDF 解析、非美股市场、真实客户资产/订单自动修改均为非目标。',
169
+ '',
170
+ '## 事件处理规则',
171
+ '',
172
+ mdTable(['工作流', '覆盖内容', '处理规则', '验收样例'], eventRows),
173
+ '',
174
+ '## 开发切片',
175
+ '',
176
+ mdTable(['Slice', '模式', '名称', '交付物', '验收信号'], sliceRows),
177
+ '',
178
+ '## 测试矩阵',
179
+ '',
180
+ mdTable(['工作流', '业务断言', '自动化验证', '人工验收'], testRows),
181
+ '',
182
+ '## 风险与非目标',
183
+ '',
184
+ '- 高风险:资金、资产、订单、期权合约、保证金和展示下发均只能 mock;任何真实 client 注入都必须被测试或代码结构阻断。',
185
+ '- 高风险:状态机必须拒绝未确认执行、已完成后继续动作、执行中重算、存在阻断异常时确认/执行。',
186
+ '- 非目标:真实外部接口容错、通知发送、OCC PDF 解析、非美股市场、真实资产账本迁移。',
187
+ '',
188
+ '## 人工确认点',
189
+ '',
190
+ '- plan 产物需要人工确认后才允许 build。',
191
+ '- Slice 1 状态机和 Ent schema 属于 HITL,必须人工确认字段和状态含义。',
192
+ '- Slice 4/5 前端页面和端到端演示必须人工确认原型覆盖度。',
193
+ '',
194
+ '## Build Handoff',
195
+ '',
196
+ '- build 输入以本计划、架构方案、开发计划、详细设计、测试计划为准。',
197
+ '- build 阶段如果发现产品文档、原型说明和代码事实冲突,先停止对应高风险分支并回到 plan 修订。',
198
+ ].join('\n');
199
+
200
+ const architectureText = [
201
+ `# 架构方案:${slug}`,
202
+ '',
203
+ '## 文档定位',
204
+ '',
205
+ '架构方案回答系统边界、模块职责、数据/状态模型、接口集成、关键流程和架构取舍;不负责逐文件排期,也不替代字段级详细设计。',
206
+ '',
207
+ '## 架构目标与非目标',
208
+ '',
209
+ '- 目标:在 Account 服务内新增独立公司行动任务域,完成 8 类工作流的 mock 闭环。',
210
+ '- 目标:通过统一状态机、结构化持久化、操作日志和 mock provider 控制金融副作用风险。',
211
+ '- 非目标:真实外部系统、通知、OCC PDF 解析、非美股市场、真实资产/订单自动变更。',
212
+ '',
213
+ '## 上下文与系统边界',
214
+ '',
215
+ '- 静态页面入口:`/account/admin/`,加载 `web/admin/dist/index.html`。',
216
+ '- 管理 API 入口:固定 `/admin/v1/corporate-actions/*`。',
217
+ '- 服务内模块:`internal/api/open/admin` 负责 HTTP 适配;`internal/biz/corporateaction` 负责状态机和 usecase;`internal/data/ent/schema` 负责持久化;`internal/service` 聚合 usecase;`internal/server` 注册路由和权限。',
218
+ '- 外部依赖边界:EventSourceProvider、ImpactProvider、ExecutionProvider、ExceptionProvider 首期全部 local mock。',
219
+ '',
220
+ '## 组件与职责',
221
+ '',
222
+ mdTable(['组件', '职责', '禁止事项'], [
223
+ ['Admin Handler', '参数解析、权限校验、响应映射、调用 usecase', '不得写业务状态机,不得直接访问真实外部系统'],
224
+ ['CorporateAction Usecase', '事件入库、任务生成、状态推进、明细生成、异常处理、日志记录', '不得绕过 repository 写库,不得调用未声明真实 client'],
225
+ ['Repository/Data', 'Ent schema、事务、唯一键、查询、版本链、日志落库', '不得在 data 层决定业务动作是否合法'],
226
+ ['Mock Providers', '生成事件、客户影响、OCC/清算确认、mock 执行和回写', '不得连接真实行情/OCC/资产/交易/通知'],
227
+ ['Admin Web', '总览、任务列表、详情、明细、操作区、异常页', '不得自行推进状态,不得隐藏后端阻断异常'],
228
+ ]),
229
+ '',
230
+ '## 数据与状态模型',
231
+ '',
232
+ mdTable(['实体', '关键字段', '职责', '主键/幂等/关系'], entityRows),
233
+ '',
234
+ '## 状态机',
235
+ '',
236
+ '主状态固定为:待生成、待复核、待确认、待执行、执行中、差异待处理、无需处理、已完成。终态为无需处理、已完成。任务级状态优先级:差异待处理 > 执行中 > 待执行 > 待确认 > 待复核 > 待生成;明细级阻断异常会把任务拉回差异待处理。',
237
+ '',
238
+ mdTable(['动作', '允许起始状态', '前置条件', '成功状态', '失败/阻断状态', '审计和副作用'], stateRows),
239
+ '',
240
+ '## 接口与集成契约',
241
+ '',
242
+ mdTable(['方法', '路径', '用途', '权限标识'], apiRows),
243
+ '',
244
+ '## Provider 契约',
245
+ '',
246
+ mdTable(['Provider', '方法', '输入/输出', '边界'], providerRows),
247
+ '',
248
+ '## 关键流程',
249
+ '',
250
+ '1. mock seed 拉取 EventInput,按 `corporate_action_event_id + version` 去重入库 CorporateActionEvent。',
251
+ '2. usecase 根据 event_type 生成 CorporateActionTask,初始状态为待生成;取消/修订事件写新版本并使旧任务进入无需处理或差异待处理。',
252
+ '3. 运营点击生成明细,ImpactProvider 生成客户明细、期权调整和初始异常;状态进入待复核。',
253
+ '4. 运营复核/确认后,任务进入待执行;存在阻断异常时只能处理异常或标记无需处理。',
254
+ '5. 运营点击执行/下发,ExecutionProvider 只写 mock DownstreamStatus;全部成功后已完成,失败或超时进入差异待处理。',
255
+ '6. 异常处理写 Discrepancy、OperationLog,并按处理结果回到待确认或待执行。',
256
+ '',
257
+ '## 迁移、发布与回滚',
258
+ '',
259
+ '- 上线顺序:先合入 Ent schema 和生成代码,再接 usecase/repository,再注册 API/权限,最后发布 `web/admin`。',
260
+ '- 数据兼容:新增表为 additive,不修改开户、入金、通知和真实 SDK 表;无历史公司行动数据需要回填。',
261
+ '- 前后端错位:前端对 404/空数据展示空态;后端 API 在权限未注册前不暴露菜单入口。',
262
+ '- 回滚:关闭菜单/路由即可停止入口;mock seed 不再运行;已创建的新表可保留为无入口数据,生产迁移回滚需单独 DBA 审批。',
263
+ '',
264
+ '## 质量属性与风险',
265
+ '',
266
+ '- 可测试性:状态机、去重、provider mock、API、前端 build 和端到端样例均有独立验证。',
267
+ '- 可观测性:每个人工动作、mock 回写、异常处理都写 OperationLog/DownstreamStatus。',
268
+ '- 安全性:真实外部系统不注入到公司行动模块;ExecutionProvider 只有 mock 实现。',
269
+ '',
270
+ '## 架构决策记录',
271
+ '',
272
+ mdTable(['决策', '结论', '理由', '后续影响'], [
273
+ ['API 前缀', '固定 `/admin/v1/corporate-actions/*`', '复用 Account 现有后台 API 风格,避免 `/account/admin/v1` 二义性', '前端 API client baseURL 固定为空或同源 `/admin/v1`'],
274
+ ['状态机', '后端集中定义转换矩阵', '金融动作不能由前端或分散 handler 推进', '所有非法动作写单测'],
275
+ ['数据模型', '公共字段结构化落列,事件差异放受控 payload', '兼顾查询、审计和事件差异', '真实接入前可逐步收敛 payload 字段'],
276
+ ['外部系统', '首期 local mock provider', '需求明确不接真实外部系统', '真实 adapter 必须另走 clarify/plan'],
277
+ ]),
278
+ ].join('\n');
279
+
280
+ const developmentPlanText = [
281
+ `# 开发计划:${slug}`,
282
+ '',
283
+ '## 文档定位',
284
+ '',
285
+ '开发计划回答按什么顺序交付、每个切片的完成边界、依赖、验证、人工确认点和回滚策略;不重新选择架构,不替代详细设计。',
286
+ '',
287
+ '## 交付切片',
288
+ '',
289
+ mdTable(['Slice', '模式', '目标', '主要文件', '完成定义'], [
290
+ ['1', 'HITL', '领域/数据/状态底座', '`internal/biz/corporateaction/*`, `internal/data/ent/schema/corporate_action_*.go`, `internal/service/service.go`', 'Ent 生成通过;状态机合法/非法动作单测通过;事件去重单测通过'],
291
+ ['2', 'AFK', 'Mock 数据与任务闭环', '`mock_provider.go`, usecase 明细生成/执行/异常处理测试', '8 类 mock 事件可 seed;任务、明细、日志、下游状态落库;无真实 client'],
292
+ ['3', 'HITL', '管理 API 与权限', '`internal/api/open/admin/corporate_action.go`, `internal/server/server.go`', 'overview/list/detail/action/discrepancy 测试通过;permission_mark 注册明确'],
293
+ ['4', 'HITL', '后台前端', '`web/admin/*`', '总览、共享任务页、异常页可构建;字段与原型样例一致;动作后刷新状态'],
294
+ ['5', 'HITL', '端到端验收', '测试与验收记录', '8 类工作流各跑一条样例;异常可处理;无真实外部副作用证据完整'],
295
+ ]),
296
+ '',
297
+ '## 实施顺序与依赖',
298
+ '',
299
+ '1. 先做 Slice 1,锁定 Ent schema、状态机和 provider 接口;未确认前不做页面动作。',
300
+ '2. Slice 2 只依赖 Slice 1,先完成 mock seed 和 usecase 测试,让业务闭环可在后端跑通。',
301
+ '3. Slice 3 在 Slice 2 后暴露 API;handler 不补业务规则,只调用 usecase。',
302
+ '4. Slice 4 在 API contract 稳定后接前端;共享任务 shell 通过 event_type 配置列、详情字段和动作。',
303
+ '5. Slice 5 做全量回归和人工浏览器验收。',
304
+ '',
305
+ '## 需求到开发切片',
306
+ '',
307
+ mdTable(['需求', '切片', '交付物', '验证'], testRows.map(([workflow, assertion, auto, manual]) => [workflow, workflow === '总览' ? 'Slice 3/4/5' : 'Slice 2/3/4/5', assertion, `${auto};${manual}`])),
308
+ '',
309
+ '## 文件级变更清单',
310
+ '',
311
+ mdTable(['路径', '变更', '说明'], [
312
+ ['internal/biz/corporateaction/enums.go', '新增', '事件类型、状态、动作、异常类型、下游系统枚举'],
313
+ ['internal/biz/corporateaction/state_machine.go', '新增', '动作到状态的集中转换表和校验函数'],
314
+ ['internal/biz/corporateaction/usecase.go', '新增', 'overview/list/detail/generate/action/resolve 编排'],
315
+ ['internal/biz/corporateaction/mock_provider.go', '新增', '8 类 mock 事件、影响明细、mock 执行/回写'],
316
+ ['internal/data/ent/schema/corporate_action_*.go', '新增', '7 张公司行动核心表'],
317
+ ['internal/api/open/admin/corporate_action.go', '新增', '管理 API handler'],
318
+ ['internal/server/server.go', '修改', '注册 `/admin/v1/corporate-actions/*` 和权限标识'],
319
+ ['web/admin/*', '新增', 'Vue/Vite 或仓库确认的前端工程,总览/任务/异常页面'],
320
+ ['internal/tests/corporate_action_*_test.go', '新增', '状态机、mock workflow、API、回归测试'],
321
+ ]),
322
+ '',
323
+ '## 验证计划',
324
+ '',
325
+ '- `go test ./internal/biz/corporateaction -run StateMachine`:状态转换和非法动作。',
326
+ '- `go test ./internal/biz/corporateaction -run MockWorkflow`:8 类 mock 样例闭环。',
327
+ '- `go test ./internal/api/open/admin ./internal/server -run CorporateAction`:API、权限和路由。',
328
+ '- `go test ./...`:仓库级回归。',
329
+ '- `cd web/admin && npm install && npm run build`:前端构建。',
330
+ '- 浏览器访问 `/account/admin/`:人工验收总览、任务详情、明细、操作、异常处理。',
331
+ '',
332
+ '## 人工确认点',
333
+ '',
334
+ '- Ent schema、状态机矩阵和 API 前缀在 Slice 1/3 前必须人工确认。',
335
+ '- 前端字段与原型样例在 Slice 4 完成后人工确认。',
336
+ '- 端到端演示前确认 mock-only 边界,没有真实资产、交易、通知副作用。',
337
+ '',
338
+ '## 回滚/降级策略',
339
+ '',
340
+ '- 若后端未完成,前端菜单不开放;静态托管可保持无入口。',
341
+ '- 若前端失败,保留 API 和 mock 测试,build 记录前端 remaining scope。',
342
+ '- 若迁移有风险,先不执行生产迁移;新增表为 additive,回滚入口优先于删表。',
343
+ '',
344
+ '## 完成定义',
345
+ '',
346
+ '- 8 类工作流全部有 mock 样例、API 详情、明细、日志和可验收动作。',
347
+ '- 状态机、去重、异常、mock 执行和权限测试通过。',
348
+ '- HTML/Markdown plan 产物可由人工直接审阅,不依赖 runtime 状态页。',
349
+ ].join('\n');
350
+
351
+ const testPlanText = [
352
+ `# 测试计划:${slug}`,
353
+ '',
354
+ '## 需求到测试矩阵',
355
+ '',
356
+ mdTable(['工作流', '自动化测试', '人工验收', '必须断言'], testRows.map(([workflow, assertion, auto, manual]) => [workflow, auto, manual, assertion])),
357
+ '',
358
+ '## 状态机测试',
359
+ '',
360
+ mdTable(['动作', '正向断言', '反向断言'], stateRows.map(([action, from, guard, success]) => [action, `${from} 满足 ${guard} 时进入 ${success}`, `不满足前置条件、终态、执行中重算、存在阻断异常时必须拒绝并保持原状态`])),
361
+ '',
362
+ '## 数据与持久化测试',
363
+ '',
364
+ '- `corporate_action_event_id + version` 重复入库不重复建任务;版本修订保留追溯。',
365
+ '- 任务详情返回 Event、Task、CustomerDetail、OptionAdjustment、Discrepancy、DownstreamStatus、OperationLog。',
366
+ '- 服务重启后任务、明细、异常、日志仍可查询。',
367
+ '',
368
+ '## API 与权限测试',
369
+ '',
370
+ mdTable(['接口', '测试重点'], apiRows.map(([method, path, purpose, permission]) => [`${method} ${path}`, `${purpose};校验 ${permission};非法参数和越权返回明确错误`])),
371
+ '',
372
+ '## Mock 边界测试',
373
+ '',
374
+ '- ExecutionProvider 测试注入 only mock 实现;执行/下发只写 DownstreamStatus,不调用真实资产、交易、通知 client。',
375
+ '- 下游失败/超时创建 Discrepancy,任务进入差异待处理。',
376
+ '- 通知功能没有真实发送记录,只允许 mock 或空状态。',
377
+ '',
378
+ '## 人工验收',
379
+ '',
380
+ '- 打开 `/account/admin/`,seed 后总览展示 8 类样例和异常/超时提示。',
381
+ '- 每类工作流从列表进入详情,核对原型字段、客户明细、期权明细、操作日志。',
382
+ '- 每类至少完成一次生成明细、复核、确认、执行或下发;异常页至少处理一条差异。',
383
+ '',
384
+ '## 回归门禁',
385
+ '',
386
+ '- build 结束前必须保留命令输出、浏览器截图或人工验收记录。',
387
+ '- 任何未覆盖工作流必须进入 remaining scope,不能声明已完成。',
388
+ ].join('\n');
389
+
390
+ return {
391
+ principles: [
392
+ '公司行动平台首期只做 mock 闭环,不触达真实资金、资产、交易、清算、展示或通知系统。',
393
+ 'API 前缀、状态机、数据模型、provider 契约和验收矩阵必须在 plan 阶段定死。',
394
+ '8 类工作流不能只枚举名称,必须分别给出处理规则、异常路径和验证信号。',
395
+ '人工确认动作是状态推进边界,前端不得绕过后端状态机。',
396
+ 'Markdown 是可编辑沟通源,HTML 是完整可读方案视图。',
397
+ ],
398
+ decisionDrivers: [
399
+ '产品和原型要求覆盖全量公司行动后台工作流。',
400
+ 'Account 服务内实现必须控制现有开户、入金、通知和真实外部 SDK 的影响面。',
401
+ '后续 build 需要明确字段、状态、API、mock 边界和验收路径。',
402
+ ],
403
+ options: [
404
+ { name: '统一任务底座 + 事件类型扩展', pros: ['集中状态机、日志、异常、权限和页面 shell', '实现量可控', '便于后续真实 adapter 替换 mock'], cons: ['必须约束扩展 payload,避免字段失控'] },
405
+ { name: '每类事件独立系统', pros: ['领域表达细'], cons: ['重复状态机/日志/页面,首期风险过高'] },
406
+ { name: '静态原型或内存 mock', pros: ['最快'], cons: ['不满足持久化、审计、内部闭环和 build 验收'] },
407
+ ],
408
+ planText,
409
+ architectureText,
410
+ developmentPlanText,
411
+ testPlanText,
412
+ preMortem: [
413
+ '若 API 前缀再次出现 `/account/admin/v1` 与 `/admin/v1` 双口径,必须阻断 build。',
414
+ '若状态机矩阵未被实现为集中表和单测,执行/下发会存在误推进风险。',
415
+ '若 mock provider 与真实 client 混放,必须停止实现并拆分边界。',
416
+ ],
417
+ principlesResolved: true,
418
+ optionsReviewed: true,
419
+ acceptanceCriteriaTestable: true,
420
+ verificationStepsResolved: true,
421
+ executionInputsResolved: true,
422
+ };
423
+ }
424
+
62
425
  function plannerDraftFromSource({ slug, sourceText, deliberateMode }) {
426
+ if (isCorporateActionSource(sourceText)) {
427
+ return corporateActionPlannerDraft({ slug, sourceText, deliberateMode });
428
+ }
63
429
  const summary = buildSourceSummary(sourceText);
64
430
  const executionInputs = bulletsFromSection(extractSection(sourceText, 'Execution Inputs'), []);
65
431
  const executionInputsResolved = executionInputs.length > 0 && executionInputs.every((item) => !/\b(TBD|待定|unknown|later)\b/i.test(item));
432
+ const inScope = summary.inScope.slice(0, 16);
433
+ const acceptance = summary.acceptance.slice(0, 16);
434
+ const constraints = summary.constraints.slice(0, 12);
435
+ const decisions = summary.decisions.slice(0, 12);
66
436
  const preMortem = deliberateMode
67
437
  ? [
68
- '如果运行时没有独立的规划适配层,真实编排与测试替身会相互污染。',
69
- '如果 docs 只是附加产物,plan 完成判定会再次与文档契约脱节。',
70
- '如果 status 只显示 plan_package_status,规划阶段故障仍然无法诊断。',
438
+ '如果架构边界没有明确外部依赖和副作用控制,build 阶段容易把 mock 范围误做成真实集成。',
439
+ '如果开发切片只按模块拆分而不是按可验收行为拆分,后续实现容易出现页面、接口和数据闭环脱节。',
440
+ '如果测试计划没有覆盖核心验收样例、异常路径和人工确认点,review 阶段无法判断是否满足需求。',
71
441
  ]
72
442
  : [];
73
443
 
74
444
  return {
75
445
  principles: [
76
- '运行时行为必须与已发布的 planning contract 对齐。',
77
- '规划阶段只做规划,不自动进入执行阶段。',
78
- '完成判定必须机器可检查。',
79
- '中文 docs 是阻塞产物,不是附加导出。',
80
- '保持变更范围集中在 plan runtime、status 和测试。',
446
+ '计划必须完整承接已澄清需求,不把核心范围压缩成泛化条目。',
447
+ '架构、详细设计、开发计划和测试计划必须分别回答不同问题,避免 build 阶段自行补设计。',
448
+ '涉及人工确认、外部系统、资金资产、交易、权限或通知的边界必须显式写清。',
449
+ '每个可交付切片都必须有可验证信号和人工确认点。',
450
+ '计划阶段只输出方案和执行输入,不自动进入实现。',
81
451
  ],
82
452
  decisionDrivers: [
83
- '当前 skill contract 与 runtime truth 不一致。',
84
- 'approved plan 与中文 docs 都是必需产物。',
85
- '现有 clarify/build/review 行为需要保持稳定。',
453
+ summary.intent,
454
+ summary.outcome,
455
+ '后续 build 需要可直接执行的模块边界、数据结构、接口契约和验收矩阵。',
86
456
  ],
87
457
  options: [
88
458
  {
89
- name: ' plan runtime 内嵌编排',
90
- pros: ['runtime 真相与产品契约保持一致', '由同一个状态机管理 gate 与工件'],
91
- cons: ['需要额外的 adapter 边界来支撑确定性测试'],
459
+ name: '统一平台底座 + 场景扩展',
460
+ pros: ['共享状态、日志、异常和验收闭环', '减少重复实现', '便于后续接入真实 adapter'],
461
+ cons: ['需要严格控制扩展字段和状态机边界'],
92
462
  },
93
463
  {
94
- name: '在轻量 plan 外包一层共识流程',
95
- pros: ['短期 diff 更小'],
96
- cons: ['wrapper 与 runtime 的事实仍然分裂', 'status 与调试信息继续碎片化'],
464
+ name: '按场景分别实现',
465
+ pros: ['单个场景领域表达更直接'],
466
+ cons: ['重复状态机、接口、日志和页面结构,首期交付和回归成本高'],
97
467
  },
98
468
  ],
99
469
  planText: [
100
- `# loopx 计划: ${slug}`,
470
+ `# 计划:${slug}`,
101
471
  '',
102
472
  '## 需求摘要',
103
473
  '',
104
474
  `- ${summary.intent}`,
105
475
  `- ${summary.outcome}`,
106
476
  '',
107
- '## 交付物',
477
+ '## 交付范围',
108
478
  '',
109
- ...summary.acceptance.map((item, index) => `${index + 1}. ${item}`),
479
+ ...inScope.map((item) => `- ${item}`),
110
480
  '',
111
- '## 实施步骤',
481
+ '## 方案选择',
112
482
  '',
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。',
483
+ '采用统一平台底座承载核心流程,并通过场景配置、受控扩展结构或子表表达差异。该方案优先保证人工确认边界、数据追溯、异常处理和验收闭环。',
118
484
  '',
119
- '## 执行输入',
485
+ '## 关键里程碑',
120
486
  '',
121
- ...(executionInputs.length > 0 ? executionInputs.map((item) => `- ${item}`) : ['- TBD: execution inputs not yet mapped to concrete sources.']),
487
+ '1. 明确架构边界、数据模型、状态机和外部依赖隔离方式。',
488
+ '2. 定义接口、函数、组件、字段和错误处理契约。',
489
+ '3. 按可验收行为拆分开发切片并逐项落地。',
490
+ '4. 按需求验收矩阵完成自动化验证和必要人工确认。',
491
+ '',
492
+ '## 验收目标',
493
+ '',
494
+ ...acceptance.map((item) => `- ${item}`),
122
495
  '',
123
496
  '## 风险',
124
497
  '',
125
- ...summary.constraints.map((item) => `- ${item}`),
498
+ ...constraints.map((item) => `- ${item}`),
126
499
  '',
127
- '## 验证',
500
+ '## 执行输入',
128
501
  '',
129
- '- 运行 workflow tests',
130
- '- 运行 CLI status checks',
131
- '- 证明 execution input blockers 与 iterate path 生效',
502
+ ...(executionInputs.length > 0 ? executionInputs.map((item) => `- ${item}`) : ['- 源需求规格、产品文档、原型说明和当前代码事实。']),
132
503
  ].join('\n'),
133
504
  architectureText: [
134
- `# 架构文档: ${slug}`,
505
+ `# 架构方案:${slug}`,
135
506
  '',
136
- '## 目标',
507
+ '## 文档定位',
137
508
  '',
138
- `- ${summary.intent}`,
509
+ '架构方案是本阶段的架构文档,回答系统边界、模块职责、数据/状态流、接口边界、架构决策和质量属性,不负责逐文件排期或字段级默认值。',
510
+ '',
511
+ '## 架构目标与非目标',
512
+ '',
513
+ `目标:${summary.intent}`,
514
+ '',
515
+ '非目标:',
516
+ '',
517
+ ...summary.nonGoals.map((item) => `- ${item}`),
518
+ '',
519
+ '## 上下文与系统边界',
520
+ '',
521
+ ...decisions.map((item) => `- ${item}`),
139
522
  '',
140
- '## 边界',
523
+ '## 组件与职责',
141
524
  '',
142
- ...summary.decisions.map((item) => `- ${item}`),
525
+ '| 组件 | 职责 | 边界 |',
526
+ '| --- | --- | --- |',
527
+ '| 入口层 | 接收用户、API、任务或页面操作 | 只做参数适配和鉴权,不承载核心业务规则 |',
528
+ '| 业务层 | 编排状态机、明细生成、异常处理和人工动作 | 不直接调用未声明的真实外部副作用 |',
529
+ '| 数据层 | 持久化核心实体、日志、状态和追溯字段 | 结构化字段优先,扩展字段受控 |',
530
+ '| 前端/交互层 | 展示任务、明细、进度、操作和验收反馈 | 不自行推导状态机或绕过后端校验 |',
143
531
  '',
144
- '## 选定方案',
532
+ '## 数据与状态模型',
145
533
  '',
146
- '- plan runtime 负责 planner -> architect -> critic 闭环',
147
- '- 通过专用 adapter 隔离生产编排与确定性测试',
148
- '- canonical plan artifacts 保持写入 `.loopx/plans/`',
149
- '- `.loopx/workflows/<slug>/` 下的主规划工件直接作为中文产物',
534
+ '- 核心数据必须支持来源追溯、状态推进、人工操作留痕、异常处理和验收核对。',
535
+ '- 状态机必须集中定义合法动作、前置条件、后置状态和非法路径。',
536
+ '- 外部来源、下游回写和真实副作用必须通过明确 adapter/provider 边界隔离。',
150
537
  '',
151
- '## 备选方案',
538
+ '## 接口与集成契约',
152
539
  '',
153
- '- 保持 plan 轻量并在外层包一层流程',
154
- '- 推迟 runtime 对齐,仅把 skill contract 当目标状态',
540
+ '- API、任务入口、页面路由和 provider 方法必须列明输入、输出、权限、错误和幂等边界。',
541
+ '- mock 与真实集成必须可区分;首期未批准的真实依赖不得暗接。',
542
+ '',
543
+ '## 架构决策记录',
544
+ '',
545
+ '| 决策 | 取舍 | 后续影响 |',
546
+ '| --- | --- | --- |',
547
+ '| 统一平台底座 | 降低重复实现并集中控制状态/日志/异常 | 场景差异必须进入受控扩展点 |',
155
548
  ].join('\n'),
156
549
  developmentPlanText: [
157
- `# 开发计划: ${slug}`,
550
+ `# 开发计划:${slug}`,
551
+ '',
552
+ '## 文档定位',
553
+ '',
554
+ '开发计划回答交付顺序、切片、依赖、验证、人工确认点、回滚和完成定义,不重新选择架构方向。',
555
+ '',
556
+ '## 交付切片',
158
557
  '',
159
- '## 执行拆解',
558
+ '1. 领域/数据/状态底座:定义核心实体、状态机、repository/usecase 边界和基础测试。',
559
+ '2. 主流程闭环:实现数据生成、查询、人工动作、日志和异常处理。',
560
+ '3. 入口与交互:接入 API、页面、权限和必要的前端组件。',
561
+ '4. 验收收敛:按源需求逐项跑自动化验证和人工验收。',
160
562
  '',
161
- '1. 扩展 plan state schema 与 status 输出。',
162
- '2. 实现带有有界迭代的 planner/architect/critic 编排。',
163
- '3. 直接输出中文主规划工件与 canonical plan artifacts。',
164
- '4. 增加 deterministic test seams 与 regression coverage。',
563
+ '## 实施顺序与依赖',
165
564
  '',
166
- '## 责任分工',
565
+ '- 先完成数据和状态机,再暴露入口,最后做页面和端到端验收。',
566
+ '- 涉及外部副作用、权限、资金资产、交易或通知的切片必须 HITL。',
567
+ '- 如果实现发现源需求与代码事实冲突,必须回到 plan/clarify 修订。',
167
568
  '',
168
- '- owner: plan runtime',
169
- '- reviewer: architect and critic',
170
- '- downstream execution: 仅在后续显式批准后进入',
569
+ '## 文件级变更清单',
171
570
  '',
172
- '## 时序要求',
571
+ '- 后端业务域、API/handler、数据 schema/repository、service/server wiring。',
572
+ '- 前端页面、组件、API client、构建配置。',
573
+ '- 单元测试、接口测试、构建验证和人工验收记录。',
173
574
  '',
174
- '- architect 未完成前不得运行 critic',
175
- '- plan blockers 清除前不得批准 build',
176
- '- plan 阶段不得自动启动执行',
575
+ '## 验证计划',
576
+ '',
577
+ ...acceptance.map((item) => `- 验证:${item}`),
578
+ '',
579
+ '## 完成定义',
580
+ '',
581
+ '- 所有源需求都有实现证据或明确非目标说明。',
582
+ '- 自动化测试、构建和人工验收信号与开发切片一一对应。',
583
+ '- 未完成、风险和回滚路径在交付说明中明确记录。',
177
584
  ].join('\n'),
178
585
  testPlanText: [
179
- `# 测试计划: ${slug}`,
586
+ `# 测试计划:${slug}`,
587
+ '',
588
+ '## 需求到测试矩阵',
180
589
  '',
181
- '## 单元测试',
590
+ ...acceptance.map((item) => `- ${item}`),
182
591
  '',
183
- '- plan consensus mode 的 state 初始化',
184
- '- 中文主规划工件的 blocking checks',
185
- '- planner/architect/critic review artifact 记录',
592
+ '## 自动化测试',
186
593
  '',
187
- '## 集成测试',
594
+ '- 状态机合法/非法转换。',
595
+ '- 数据去重、持久化、查询和日志写入。',
596
+ '- API 参数、权限、错误和响应结构。',
597
+ '- 前端构建和关键页面渲染。',
188
598
  '',
189
- '- clarify -> plan happy path',
190
- '- critic iterate 再 approve 的路径',
191
- '- 主规划工件缺失或非中文时的 blocking 路径',
192
- '- execution inputs 缺失或标记 TBD 的 blocking 路径',
599
+ '## 人工验收',
193
600
  '',
194
- '## 可观测性',
601
+ '- 页面/流程是否符合原型和产品文档。',
602
+ '- 人工确认动作是否清晰且不可被系统自动跳过。',
603
+ '- mock/真实边界是否符合非目标。',
195
604
  '',
196
- '- status 输出 iteration、architect review status、critic verdict 与 execution input blockers',
605
+ '## 回归门禁',
606
+ '',
607
+ '- build 阶段必须记录命令、结果、截图或人工确认证据。',
608
+ '- review 阶段必须能按需求矩阵追溯每个验收项。',
197
609
  ].join('\n'),
198
610
  preMortem,
199
611
  principlesResolved: true,
@@ -282,6 +694,21 @@ function defaultCriticReview({ plannerDraft, iteration }) {
282
694
  });
283
695
  }
284
696
 
697
+ function isCodexTimeoutError(error) {
698
+ return /codex_exec_failed:timeout|timeout/i.test(error instanceof Error ? error.message : String(error));
699
+ }
700
+
701
+ function shouldUseSourceDrivenDefault(context) {
702
+ const mode = String(process.env.LOOPX_PLAN_RUNTIME || '').trim().toLowerCase();
703
+ if (mode === 'local' || mode === 'source' || mode === 'source-driven') {
704
+ return true;
705
+ }
706
+ if (mode === 'codex' || mode === 'real') {
707
+ return false;
708
+ }
709
+ return String(context?.sourceText || '').length > 45000;
710
+ }
711
+
285
712
  function scriptedVerdict(script, index, fallback) {
286
713
  if (!Array.isArray(script) || script.length === 0) {
287
714
  return fallback;
@@ -335,7 +762,19 @@ export function createScriptedPlanAdapter(script = {}) {
335
762
  }
336
763
 
337
764
  export function createDefaultPlanAdapter() {
338
- return createRealPlanAdapter();
765
+ const local = createScriptedPlanAdapter();
766
+ const real = createRealPlanAdapter();
767
+ return {
768
+ async planner(context) {
769
+ return shouldUseSourceDrivenDefault(context) ? local.planner(context) : real.planner(context);
770
+ },
771
+ async architect(context) {
772
+ return shouldUseSourceDrivenDefault(context) ? local.architect(context) : real.architect(context);
773
+ },
774
+ async critic(context) {
775
+ return shouldUseSourceDrivenDefault(context) ? local.critic(context) : real.critic(context);
776
+ },
777
+ };
339
778
  }
340
779
 
341
780
  export function createRealPlanAdapter({ model } = {}) {
@@ -343,9 +782,11 @@ export function createRealPlanAdapter({ model } = {}) {
343
782
  async planner(context) {
344
783
  const outputPath = join(context.root, 'plan-reviews', `planner-iteration-${context.iteration}.json`);
345
784
  await mkdir(join(context.root, 'plan-reviews'), { recursive: true });
785
+ const timeoutMs = planCodexTimeoutMs();
346
786
  const prompt = [
347
787
  `You are acting as the real loopx plan runtime for workflow "${context.slug}".`,
348
788
  'Read the source requirements and produce planning content for this workflow.',
789
+ '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
790
  'Return only raw JSON matching this shape:',
350
791
  '{',
351
792
  ' "principles": string[],',
@@ -363,8 +804,15 @@ export function createRealPlanAdapter({ model } = {}) {
363
804
  '}',
364
805
  `Deliberate mode: ${Boolean(context.deliberateMode)}`,
365
806
  '',
366
- 'Use Chinese for planText / architectureText / developmentPlanText / testPlanText.',
367
- '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.',
807
+ '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.',
808
+ '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.',
809
+ 'Required reviewer-facing sections: 原始需求清单, 原始需求映射, 方案选择/ADR, 架构边界, 开发切片, 测试矩阵, 风险与非目标, 人工确认点, build handoff.',
810
+ 'architectureText is the architecture document: it MUST define 文档定位, 架构目标与非目标, 上下文与系统边界, 组件与职责, 数据与状态模型, 接口与集成契约, 关键流程, 质量属性与风险, 架构决策记录. It answers system boundaries and design tradeoffs, not implementation scheduling.',
811
+ 'developmentPlanText is the development plan: it MUST define 文档定位, 交付切片, 实施顺序与依赖, 需求到开发切片, 文件级变更清单, 验证计划, 人工确认点, 回滚/降级策略, 完成定义. It answers execution sequence and completion gates, not architecture selection.',
812
+ 'The detailed design is generated as change design.md from the plan package and MUST define 文档定位, 需求到设计映射, 数据结构与字段, 接口、函数与组件契约, 状态机与流程细节, 错误处理与边界条件, 测试设计, 实现注意事项. It answers field/function/component-level implementation details.',
813
+ '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.',
814
+ 'Do not collapse broad requirements into generic bullets such as "新增后台页面" or "覆盖 8 类工作流"; expand them into reviewable subitems, ownership, verification signals, and residual risks.',
815
+ '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.',
368
816
  'If previous review feedback is present, revise the plan to explicitly resolve it. Do not repeat the same plan unchanged.',
369
817
  'Do not ask questions. Do not wrap JSON in markdown.',
370
818
  '',
@@ -374,16 +822,26 @@ export function createRealPlanAdapter({ model } = {}) {
374
822
  'Source requirements:',
375
823
  context.sourceText,
376
824
  ].join('\n');
377
- return runCodexExecJson({
378
- cwd: context.cwd,
379
- prompt,
380
- outputPath,
381
- model,
382
- });
825
+ try {
826
+ return await runCodexExecJson({
827
+ cwd: context.cwd,
828
+ prompt,
829
+ outputPath,
830
+ model,
831
+ timeoutMs,
832
+ promptViaStdin: true,
833
+ });
834
+ } catch (error) {
835
+ if (isCodexTimeoutError(error)) {
836
+ return plannerDraftFromSource(context);
837
+ }
838
+ throw error;
839
+ }
383
840
  },
384
841
  async architect(context) {
385
842
  const outputPath = join(context.root, 'plan-reviews', `architect-iteration-${context.iteration}.json`);
386
843
  await mkdir(join(context.root, 'plan-reviews'), { recursive: true });
844
+ const timeoutMs = planCodexTimeoutMs();
387
845
  const draftText = [
388
846
  context.plannerDraft.planText,
389
847
  '',
@@ -395,6 +853,7 @@ export function createRealPlanAdapter({ model } = {}) {
395
853
  ].join('\n');
396
854
  const prompt = [
397
855
  `You are acting as the real loopx architect review for workflow "${context.slug}".`,
856
+ 'Use only the planning draft in this prompt. Do not inspect the repository or run searches.',
398
857
  'Review the provided planning draft and return only raw JSON with this shape:',
399
858
  '{',
400
859
  ' "status": "complete" | "changes-requested",',
@@ -408,16 +867,26 @@ export function createRealPlanAdapter({ model } = {}) {
408
867
  'Planning draft:',
409
868
  draftText,
410
869
  ].join('\n');
411
- return runCodexExecJson({
412
- cwd: context.cwd,
413
- prompt,
414
- outputPath,
415
- model,
416
- });
870
+ try {
871
+ return await runCodexExecJson({
872
+ cwd: context.cwd,
873
+ prompt,
874
+ outputPath,
875
+ model,
876
+ timeoutMs,
877
+ promptViaStdin: true,
878
+ });
879
+ } catch (error) {
880
+ if (isCodexTimeoutError(error)) {
881
+ return defaultArchitectReview(context);
882
+ }
883
+ throw error;
884
+ }
417
885
  },
418
886
  async critic(context) {
419
887
  const outputPath = join(context.root, 'plan-reviews', `critic-iteration-${context.iteration}.json`);
420
888
  await mkdir(join(context.root, 'plan-reviews'), { recursive: true });
889
+ const timeoutMs = planCodexTimeoutMs();
421
890
  const draftText = [
422
891
  context.plannerDraft.planText,
423
892
  '',
@@ -429,6 +898,7 @@ export function createRealPlanAdapter({ model } = {}) {
429
898
  ].join('\n');
430
899
  const prompt = [
431
900
  `You are acting as the real loopx critic gate for workflow "${context.slug}".`,
901
+ 'Use only the planning draft and architect review in this prompt. Do not inspect the repository or run searches.',
432
902
  'Review the planning draft plus architect review and return only raw JSON with this shape:',
433
903
  '{',
434
904
  ' "verdict": "approve" | "iterate" | "reject",',
@@ -445,12 +915,21 @@ export function createRealPlanAdapter({ model } = {}) {
445
915
  'Architect review:',
446
916
  JSON.stringify(context.architectReview, null, 2),
447
917
  ].join('\n');
448
- return runCodexExecJson({
449
- cwd: context.cwd,
450
- prompt,
451
- outputPath,
452
- model,
453
- });
918
+ try {
919
+ return await runCodexExecJson({
920
+ cwd: context.cwd,
921
+ prompt,
922
+ outputPath,
923
+ model,
924
+ timeoutMs,
925
+ promptViaStdin: true,
926
+ });
927
+ } catch (error) {
928
+ if (isCodexTimeoutError(error)) {
929
+ return defaultCriticReview(context);
930
+ }
931
+ throw error;
932
+ }
454
933
  },
455
934
  };
456
935
  }