@peterxiaoyang/superspec 0.1.51 → 0.1.53

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 CHANGED
@@ -4,194 +4,49 @@
4
4
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20.19.0-brightgreen?style=flat-square)](https://nodejs.org)
5
5
  [![OpenSpec](https://img.shields.io/badge/OpenSpec-compatible-6f42c1?style=flat-square)](https://github.com/Fission-AI/OpenSpec)
6
6
 
7
- > SuperSpec 是一套“先想清楚,再动手”的工作流。
8
-
9
- 它解决的是一个很常见的问题:AI 编程工具写代码很快,但有时候还没搞清楚需求、现有代码和测试边界,就已经开始改文件了。
10
-
11
- SuperSpec 会把一次需求变更拆成 4 个阶段:
7
+ SuperSpec 是一套面向 AI 编程代理的需求变更工作流。它在 OpenSpec 材料之上增加阶段控制、审查和验证记录,帮助 AI 从需求理解推进到可回放的交付结果。
12
8
 
13
9
  ```text
14
- 探索需求 -> 写方案 -> 做实现 -> 代码审查与最终验证 -> accepted
10
+ Explore Propose Apply Review Accepted
15
11
  ```
16
12
 
17
- 这样做的目的很简单:
18
-
19
- - 改代码前先弄清楚现状
20
- - 写实现前先有方案和任务
21
- - 任务完成前先有测试或验证记录
22
- - 宣布完成前先经过审查
23
- - 进入 accepted 时保留关键过程记录
24
-
25
- ## 适合谁
26
-
27
- 适合已经在用 AI 编程工具或命令行代理做项目开发,并希望流程更稳一点的团队或个人。
28
-
29
- 如果你遇到过这些情况,SuperSpec 会有帮助:
13
+ SuperSpec 适合跨模块、涉及接口或需要方案审查的变更。简单的单文件修改不必强行使用完整工作流。普通请求也不会自动进入 SuperSpec,只有明确调用对应入口或继续已有 change 时才会启动。
30
14
 
31
- - 需求还没说清楚,AI 就开始写代码
32
- - 方案写得很粗,后面实现时靠猜
33
- - 测试只跑了命令,但没人说明它证明了什么
34
- - 代码写完后缺少真正的审查
35
- - 过几天想回看当时为什么这么改,却找不到过程记录
36
-
37
- 如果你只是想让 AI 快速改一个很小的文件,且不需要完整方案、审查和记录,那 SuperSpec 可能会显得偏重。
38
-
39
- ## 和 OpenSpec 有什么区别
40
-
41
- 一句话区别:
42
-
43
- ```text
44
- OpenSpec 管“这次要改什么”。
45
- SuperSpec 管“AI 应该怎样把这次改动做稳”。
46
- ```
15
+ ## 安装
47
16
 
48
- 更具体一点:
49
-
50
- | 问题 | OpenSpec 主要负责 | SuperSpec 额外补上 |
51
- |---|---|---|
52
- | 这次变更是什么 | 方案、规格、设计、任务和归档 | 要求 AI 在写方案前先调查现状 |
53
- | 方案怎么写 | 提供标准的变更文档结构 | 要求方案前后有范围、风险、业务约束和测试思路 |
54
- | 代码怎么做 | 记录任务清单和完成状态 | 要求按任务实现,并留下测试或验证记录 |
55
- | 做完怎么算稳 | 可以校验规格和归档 | 增加代码审查、架构审查、反方审查和最终验证 |
56
- | 以后怎么追溯 | 保留 OpenSpec 的变更文档 | 额外保留探索、测试和审查记录 |
57
-
58
- 举个例子:
59
-
60
- OpenSpec 会帮你记录“要增加登录功能、需要哪些规格、设计和任务”。
61
- SuperSpec 会进一步要求 AI 先看看现有登录/权限代码在哪里、哪些业务规则不能破坏、哪些场景必须测试、实现后要经过哪些审查,最终进入 accepted 完成本轮流程。
62
-
63
- 所以 SuperSpec 不是 OpenSpec 的替代品。它更像是 OpenSpec 外面的一层执行纪律,专门约束 AI 编程工具不要跳过关键步骤。
64
-
65
- ## 快速开始
66
-
67
- ### 1. 安装
17
+ 要求 Node.js `>=20.19.0`。
68
18
 
69
19
  ```bash
70
20
  npm install -g @peterxiaoyang/superspec@latest
71
- ```
72
-
73
- 需要 Node.js `>= 20.19.0`。
74
-
75
- ### 2. 初始化当前项目
76
-
77
- 进入你的项目根目录,然后运行:
78
-
79
- ```bash
21
+ cd <your-project>
80
22
  superspec install
81
23
  ```
82
24
 
83
- 这条命令的意思是:把 SuperSpec 当前可用的工作流入口安装到项目里。
84
- 安装前会检查全局 `openspec` CLI;缺失或版本不一致时,会自动执行 `npm install -g @fission-ai/openspec@1.4.1`,确保后续工作流能调用 OpenSpec。
85
- 当前 beta 会安装 `.superspec/` 引擎目录、`.codex/skills/superspec-*` 阶段入口、`.codex/prompts/*.md` 角色 prompt、`.codex/agents/*.toml` 子智能体配置,补齐 `.codex/config.toml` 的多 agent 开关,并在项目根 `AGENTS.md` 中维护 SuperSpec 轻量门禁片段。`superspec init --scope project` 仍作为兼容别名可用。
86
-
87
- Windows PowerShell 如果拦截 npm 的 `.ps1` 脚本,请改用:
88
-
89
- ```powershell
90
- superspec.cmd install
91
- ```
92
-
93
- ### 3. 按步骤使用
94
-
95
- 在你使用的 AI 编程工具或 CLI 里,按下面的阶段入口推进。不同工具的触发方式可以不同,但入口名和顺序保持一致。
96
-
97
- 开始时先探索需求:
98
-
99
- ```text
100
- 使用 superspec-explore,帮我梳理这个需求:……
101
- ```
102
-
103
- 探索完成后,写正式方案:
104
-
105
- ```text
106
- 使用 superspec-propose,把刚才的探索结果整理成方案。
107
- ```
108
-
109
- 方案确认后,开始实现:
110
-
111
- ```text
112
- 使用 superspec-apply,按任务实现。
113
- ```
114
-
115
- 实现完成后,代码审查与最终验证:
116
-
117
- ```text
118
- 使用 superspec-review,完成代码审查、问题处理和最终验证。
119
- ```
120
-
121
- 审查通过并进入 `accepted` 后,本轮工作流完成。如果之后需要补充或修改需求、方案或验收内容,直接用自然语言告诉 Agent;Agent 会按引擎返回的内部 continuation 自动回到计划阶段并重新完成后续审查。
25
+ `superspec install` 会把工作流入口、角色配置和运行时目录同步到当前项目,并准备 OpenSpec 依赖。`superspec init --scope project` 是兼容别名。
122
26
 
123
- ## 四个入口分别做什么
124
-
125
- | 入口 | 什么时候用 | 它会要求做什么 |
126
- |---|---|---|
127
- | `superspec-explore` | 需求刚开始时 | 读代码、查现状、整理范围和风险;这一步不改业务代码 |
128
- | `superspec-propose` | 需求已经清楚后 | 写正式方案、规格、设计和任务,并提前规划测试 |
129
- | `superspec-apply` | 方案通过后 | 按任务实现代码,记录测试或验证结果 |
130
- | `superspec-review` | 实现完成后 | 检查代码实现是否符合方案,处理审查问题,并完成最终验证 |
131
-
132
- 你日常主要记住这四个入口就够了。
133
-
134
- 工作流档位由项目配置统一控制,不通过命令行临时指定。探索阶段会按配置创建相应审查工作项;计划阶段按同一档位创建 `critic`、`architect`、`test-engineer` 等必要审查后再进入实现准备。
135
-
136
- ## 它会多保存哪些记录
137
-
138
- SuperSpec 会在每次变更下面保存一些辅助记录,方便后续追溯。
139
-
140
- 主要包括:
141
-
142
- - 探索记录:这次需求是什么、当前代码是什么情况、有哪些风险
143
- - 业务约束:哪些业务规则不能被改坏
144
- - 测试约定:哪些场景必须验证
145
- - 实现记录:每个任务怎么验证通过
146
- - 审查记录:谁检查了什么、发现了什么、最后为什么通过或退回
147
-
148
- 这些记录默认放在:
149
-
150
- ```text
151
- openspec/changes/<变更ID>/.superspec/
152
- ```
153
-
154
- 这里的 `<变更ID>` 就是一次需求变更的名字。
155
-
156
- ## 重要边界
157
-
158
- SuperSpec 能让流程更规范,但它不是安全锁。
159
-
160
- 它能帮助你:
161
-
162
- - 减少 AI 还没想清楚就改代码的情况
163
- - 让测试、审查和用户确认留下记录
164
- - 在进入下一步前提醒缺少什么
165
- - 让一次变更之后更容易回看原因
166
-
167
- 它不能保证:
168
-
169
- - 阻止人手动绕过流程直接改文件
170
- - 阻止人删除过程记录
171
- - 阻止恶意伪造记录
172
- - 替代正式的安全审计、合规审计或法律证明
173
-
174
- 也就是说,SuperSpec 目前是“流程纪律 + 审计辅助工具”,不是“强制安全系统”。默认 hook 只增强子智能体活动的可见性;显式/manual `PreToolUse` 才会进入保守写入策略,但仍然不能替代正式的安全控制。
175
-
176
- ## 常用命令
177
-
178
- 查看当前 SuperSpec CLI 版本:
27
+ 检查安装:
179
28
 
180
29
  ```bash
181
- superspec --version
30
+ superspec version
31
+ superspec status
182
32
  ```
183
33
 
184
- 安装到当前项目:
34
+ ## 工作流入口
185
35
 
186
- ```bash
187
- superspec install
188
- ```
36
+ 在 Codex 中显式调用对应 Skill。新需求通常从 `$superspec-explore` 开始;已有 change 则从当前阶段继续。
37
+
38
+ | Skill | 作用 |
39
+ | --- | --- |
40
+ | `$superspec-explore` | 调查现状,确认事实和待决策事项 |
41
+ | `$superspec-propose` | 生成规格、设计和可执行任务 |
42
+ | `$superspec-apply` | 按已批准任务修改代码并验证 |
43
+ | `$superspec-review` | 审查实现并完成最终验证 |
189
44
 
190
- `superspec init --scope project` 是兼容别名,也会执行同一套安装逻辑。
45
+ 工作流会根据问题性质留在当前阶段修复,或回到计划阶段重新确认需求、验收和技术取舍。
191
46
 
192
- `superspec install` 会创建缺失的 `openspec/config.yaml`,或在没有顶层 `context` 时追加这段官方中文 context。如果文件已经有顶层 `context`,SuperSpec 不会覆盖它。
47
+ ## 配置
193
48
 
194
- 它还会创建项目级 `.superspec/config.json`,用于统一设置整个工作流的默认档位:
49
+ 项目级配置位于 `.superspec/config.json`。未创建配置或未声明模式时,默认使用 `normal`:
195
50
 
196
51
  ```json
197
52
  {
@@ -201,75 +56,21 @@ superspec install
201
56
  }
202
57
  ```
203
58
 
204
- 可选值为 `minimal`、`normal`、`strict`。`next`、Explore、Propose、进入 Apply Review 都读取此配置;`--risk` 不再是用户可用的工作流入口。已启动的 task attempt 和已经建立的审查策略仍按其事件快照执行,不会被中途改配置追溯改写。
205
-
206
- 可以用下面的命令检查生成的 instructions 是否包含语言上下文:
207
-
208
- ```bash
209
- openspec instructions proposal --change <change>
210
- ```
211
-
212
- 检查当前项目的 OpenSpec 探测结果:
213
-
214
- ```bash
215
- superspec status
216
- ```
217
-
218
- 同步当前项目的 SuperSpec 工作流入口:
219
-
220
- ```bash
221
- superspec update
222
- ```
223
-
224
- 这条命令会先检查 npm 上的 latest 版本;如果有新版,会自动执行全局升级并用新版 CLI 重新同步项目入口。随后会把全局 OpenSpec CLI 拉齐到 `@fission-ai/openspec@1.4.1`。同步内容包括补齐 `.superspec/changes` 运行时目录,把当前 CLI 内置的 `.codex/skills/superspec-*`、`.codex/prompts/*.md`、`.codex/agents/*.toml` 同步到项目里,并更新 `AGENTS.md` 中 marker 包裹的 SuperSpec 轻量门禁片段。
59
+ 可选模式为 `minimal`、`normal` 和 `strict`。模式主要影响计划阶段的审查强度;最终代码审查和验证仍由 Review 阶段负责。
225
60
 
226
- ## 进阶信息
61
+ ## 与 OpenSpec 的关系
227
62
 
228
- 以当前内置的 Codex 适配器为例,初始化后项目里会出现这些入口文件:
63
+ OpenSpec 负责变更材料和规格结构;SuperSpec 负责组织 AI 的探索、计划、实现、审查与证据记录。两者互补:
229
64
 
230
65
  ```text
231
- .codex/
232
- skills/superspec-explore/
233
- skills/superspec-propose/
234
- skills/superspec-apply/
235
- skills/superspec-review/
236
- prompts/architect.md
237
- prompts/code-reviewer.md
238
- prompts/critic.md
239
- prompts/executor.md
240
- prompts/explore.md
241
- prompts/test-engineer.md
242
- prompts/test-runner.md
243
- prompts/verifier.md
244
- agents/architect.toml
245
- agents/code-reviewer.toml
246
- agents/critic.toml
247
- agents/executor.toml
248
- agents/explore.toml
249
- agents/test-engineer.toml
250
- agents/test-runner.toml
251
- agents/verifier.toml
252
- config.toml
253
- ```
254
-
255
- 当前 beta 的阶段入口由 `superspec transition next --change <变更ID>` 驱动,不再提供旧版 `superspec check ...` surface。
256
-
257
- 如果你要开发 SuperSpec 本身:
258
-
259
- ```bash
260
- npm run build
261
- npm run typecheck
262
- npm test
263
- npm pack --dry-run
66
+ OpenSpec:这次要改变什么
67
+ SuperSpec:如何在边界内把它交付
264
68
  ```
265
69
 
266
- 更多细节见:
70
+ ## 设计边界
267
71
 
268
- - `docs/plans/SUPERSPEC_TRANSITION_ENGINE_SPEC_LITE.md`:transition engine 设计
269
- - `templates/workflow/skills/superspec-*/SKILL.md`:当前 Codex 适配器使用的阶段入口说明
72
+ SuperSpec 是流程和审计辅助工具,不是安全隔离或发布审批系统。它不能替代代码审计、权限控制、合规检查和人工判断。
270
73
 
271
- ## 致谢与灵感来源
74
+ ## 致谢
272
75
 
273
76
  - [OpenSpec](https://github.com/Fission-AI/OpenSpec)
274
- - [oh-my-codex](https://github.com/Yeachan-Heo/oh-my-codex)
275
- - [Superpowers](https://github.com/obra/superpowers)
package/dist/format.js CHANGED
@@ -799,14 +799,18 @@ export function pendingTasksInContent(content) {
799
799
  }
800
800
  /** 在 tasks.md 中按 taskId 精确查找任务(词边界,不误判子串) */
801
801
  export function findTaskInLines(lines, taskId) {
802
+ const escaped = taskId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
803
+ // 回退匹配也必须停留在顶格 checkbox 任务行内;否则上一任务的
804
+ // “依赖/边界/验收”文本提到该 ID 时,会被误当成目标任务行。
805
+ const taskIdAtLineStart = new RegExp(`^${escaped}(?=\\s|$|[.,:;!?)\\]])`);
802
806
  for (let i = 0; i < lines.length; i++) {
803
807
  // 精确匹配行内的 taskId token
804
808
  const m = lines[i].match(TASK_LINE_RE);
805
809
  if (m && m[3] === taskId)
806
810
  return i;
807
- // 回退:用转义正则匹配(兼容 taskId 后跟标点的情况)
808
- const escaped = taskId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
809
- if (new RegExp("(?:^|\\s)" + escaped + "(?:\\s|$|[.,:;!?)\\]])").test(lines[i]))
811
+ // 回退:兼容 taskId 后跟标点的任务行,但不扫描普通说明文本。
812
+ const taskLine = lines[i].match(/^-\s+\[[ xX]\]\s+(.+)$/);
813
+ if (taskLine && taskIdAtLineStart.test(taskLine[1]))
810
814
  return i;
811
815
  }
812
816
  return -1;
package/dist/install.js CHANGED
@@ -253,7 +253,6 @@ function ensureWorkflowConfig(projectRoot) {
253
253
  mkdirSync(dirname(configPath), { recursive: true });
254
254
  if (!existsSync(configPath)) {
255
255
  // install/update 是显式迁移动作:为以后各轮写入 normal 默认值。
256
- // 未执行安装的旧项目仍由 workflowRiskForProject 保守回放 strict。
257
256
  writeFileSync(configPath, JSON.stringify({ workflow: { mode: DEFAULT_WORKFLOW_RISK } }, null, 2) + "\n");
258
257
  }
259
258
  return WORKFLOW_CONFIG_PATH;
@@ -245,20 +245,21 @@ function buildActions(change, boundary, scope, question, specs, risk) {
245
245
  export function isPhaseConfirmationScope(value) {
246
246
  return typeof value === "string" && value.startsWith(PHASE_CONFIRMATION_SCOPE_PREFIX);
247
247
  }
248
- export function phaseConfirmationForBoundary(projectRoot, events, snapshot, boundary, risk = "strict") {
248
+ export function phaseConfirmationForBoundary(projectRoot, events, snapshot, boundary, risk) {
249
249
  const spec = SPECS[boundary];
250
250
  if (snapshot.state !== spec.state)
251
251
  return null;
252
+ const resolvedRisk = risk ?? workflowRiskForPhaseConfirmation(projectRoot, events, snapshot);
252
253
  const epoch = spec.epoch(events);
253
254
  const epochEventId = epoch?.event_id ?? `legacy-${spec.state}`;
254
- const digest = materialDigest(projectRoot, events, snapshot, boundary, risk);
255
+ const digest = materialDigest(projectRoot, events, snapshot, boundary, resolvedRisk);
255
256
  const scope = `${spec.scopePrefix}:${epochEventId}:${digest}`;
256
257
  const summary = boundary === "propose_to_apply"
257
258
  ? proposeTaskDeliverySummary(openspecChangeRoot(projectRoot, snapshot.change_id))
258
259
  : null;
259
260
  const boundaryQuestion = `${spec.question}\n\n${CURRENT_USER_DECISION_NOTICE}`;
260
261
  const question = summary ? `${summary}\n\n${boundaryQuestion}` : boundaryQuestion;
261
- const actions = buildActions(snapshot.change_id, boundary, scope, question, spec.actions, risk);
262
+ const actions = buildActions(snapshot.change_id, boundary, scope, question, spec.actions, resolvedRisk);
262
263
  return {
263
264
  boundary,
264
265
  epoch_event_id: epochEventId,
@@ -273,11 +274,12 @@ export function phaseConfirmationForBoundary(projectRoot, events, snapshot, boun
273
274
  },
274
275
  };
275
276
  }
276
- export function phaseConfirmationForCurrentState(projectRoot, events, snapshot, risk = "strict") {
277
+ export function phaseConfirmationForCurrentState(projectRoot, events, snapshot, risk) {
277
278
  const boundary = boundaryForState(snapshot.state);
278
- return boundary
279
- ? phaseConfirmationForBoundary(projectRoot, events, snapshot, boundary, risk)
280
- : null;
279
+ if (!boundary)
280
+ return null;
281
+ const resolvedRisk = risk ?? workflowRiskForPhaseConfirmation(projectRoot, events, snapshot);
282
+ return phaseConfirmationForBoundary(projectRoot, events, snapshot, boundary, resolvedRisk);
281
283
  }
282
284
  export function phaseActionForAnswer(confirmation, answer) {
283
285
  return typeof answer === "string"
package/dist/record.js CHANGED
@@ -72,9 +72,9 @@ function previousRejectionInstruction(job) {
72
72
  return `${reason}上一轮没有可复核的历史 finding;请按当前 gate 的完整范围独立审查,不要把拒绝原因当作需求或验收标准,`;
73
73
  }
74
74
  const identityRule = job.role === "code-reviewer"
75
- ? "逐项核对修复 task 的代码变化、scope_note 和验证证据;实现者用可核实证据说明被质疑实现确有必要时,独立验证后关闭原问题。证据不能支撑必要性且问题仍存在时复用原 finding ID;legacy finding 没有 ID 时沿用原始语义并补一个稳定 ID"
75
+ ? "逐项核对修复 task 的代码变化、scope_note 和验证证据;实现者用可核实证据说明被质疑实现确有必要时,独立验证后关闭原问题。证据不能支撑必要性且问题仍存在时复用原 finding ID;legacy finding 没有 ID 时沿用原始语义并补一个稳定 ID;同一批准行为的直接消费者若因本次修正暴露出新的遗漏,可以提出新的稳定 finding,但必须给出修正变化或直接消费者链路的因果证据;"
76
76
  : "已解决或已由等价证据闭环的问题不要重复报告,不得通过更换标题或措辞重复同一问题;";
77
- return `${reason}本轮是修复复核:逐项判断本工作项附带的上一次同角色 finding 是否仍成立。Finding 中的 recommendation 只是非绑定建议,不是需求或验收标准;先独立核对 underlying problem、直接证据和本次验收,不得因原建议指定了某种架构就要求照做。修正不得通过缩小已确认范围、改写用户决定或删除验收来让 finding 字面消失;这类偏离属于本次修正直接引入的回归。${identityRule}默认只复核历史 finding;新 blocker 仅允许是本次修正直接引入的回归,并必须说明“修正动作新问题”的因果链,不得展开无关的故障模型、消费者或架构议题。`;
77
+ return `${reason}本轮是修复复核:逐项判断本工作项附带的上一次同角色 finding 是否仍成立。Finding 中的 recommendation 只是非绑定建议,不是需求或验收标准;先独立核对 underlying problem、直接证据和本次验收,不得因原建议指定了某种架构就要求照做。修正不得通过缩小已确认范围、改写用户决定或删除验收来让 finding 字面消失;这类偏离属于本次修正直接引入的回归。${identityRule}默认围绕历史 finding 及其直接影响链路复核;新 blocker 必须能说明“本次修正或同一批准行为当前问题”的因果链,不得展开无关的故障模型、消费者或架构议题。`;
78
78
  }
79
79
  function reviewScopeForJob(job) {
80
80
  if (job.review_targets !== undefined || job.read_only_refs !== undefined) {
@@ -1237,7 +1237,7 @@ export function jobsPacket(projectRoot, change, jobId) {
1237
1237
  ? `格式骨架:{"role":"code-reviewer","verdict":"pass","review_scope":{"job_id":"${job.job_id}","packet_digest":"${job.packet_digest}","checked_paths":[],"checked_docs":[],"unchecked":[]},"findings":[],"reviewer":{"kind":"codex-subagent","id":"<thread-or-agent-id>"}}。提交前按真实审查结果填写数组;不得从 boundFiles 自动复制 checked_paths。verdict 只能为 pass 或 fail;审查覆盖范围(review_scope)用来说明本次审查覆盖了哪些文件和文档,已检查路径(checked_paths)与未检查项(unchecked)必须合起来覆盖全部绑定文件(boundFiles),unchecked 条目格式为 {"path":"<path>","reason":"<reason>"};pass 不允许仍有未检查的绑定文件。`
1238
1238
  + `报告结论为 fail 时,问题列表(findings)至少包含一个可处理、可追溯的阻塞问题,字段为 {"id":"<stable-id>","blocking":true,"type":"implementation|spec|mixed","description":"<what>","evidence":"<why>","source_refs":["<path:line>"],"impact":"<impact>","suggested_action":"apply|propose"}。问题类型(type)中 implementation 表示纯代码实现问题,spec 表示方案/需求文档问题,mixed 表示需要使用者判断的混合问题。`
1239
1239
  + (packetContext?.task_execution_index
1240
- ? `本工作项带任务执行索引(task_execution_index):按 task 对照其执行依据快照(contract)审查——实现路线对照 design 引用原文、累计 diff 对照 guard 边界、测试断言对照 tests 声明的 scenario;每项的 required_evidence 是 task-start 冻结的证据口径,red_required/green_required 分别说明是否需要 RED/GREEN;fix 非空表示状态机创建的实现修复,source、parent_task_id 和 reason 说明其归属,code_review 来源还需核对 review_finding;scope_note 既可能解释必要的范围扩大,也可能说明代码审查修复为何保留原实现,均需结合 Diff、调用链和验证证据独立判断;changed_paths 是归属线索不是结论(null 表示未知);unattributed_paths 中的无主改动逐个判断合理性;coverage_exemption_refs 解释未绑定 task 的 TEST 豁免。`
1240
+ ? `本工作项带任务执行索引(task_execution_index):按 task 对照其执行依据快照(contract)审查——实现路线对照 design 引用原文、累计 diff 对照 guard 边界、测试断言对照 tests 声明的 scenario;每项的 required_evidence 是 task-start 冻结的证据口径,red_required/green_required 分别说明是否需要 RED/GREEN;fix 非空表示状态机创建的实现修复,source、parent_task_id 和 reason 说明其归属,code_review 来源还需核对 review_finding;scope_note 既可能解释必要的范围扩大,也可能说明代码审查修复为何保留原实现,均需结合 Diff、调用链和验证证据独立判断;changed_paths 是归属线索不是结论(null 表示未知);unattributed_paths 中的无主改动逐个判断合理性;coverage_exemption_refs 解释未绑定 task 的 TEST 豁免。当前 packet 的 boundFiles 是本轮冻结的审查范围;若它来自前一轮审查后的增量,只复核本轮变化及其直接影响链路,不要求重复审查未变化文件,但仍要判断批准行为是否完整闭合。`
1241
1241
  : "")
1242
1242
  : job.role === "verifier"
1243
1243
  ? `最小格式:{"role":"verifier","verdict":"pass","findings":[]${hasReviewScope ? `,"review_scope":{"checked_paths":${JSON.stringify(job.boundFiles.map(file => file.path))}}` : ""}}。verdict 只能为 pass 或 fail;核对代码审查记录(code_review_gate):passed 必须能追溯到已接受的代码审查工作项,skipped 必须能证明本次没有代码类改动。核对修复闭环:task_execution_index.fix.source=code_review 时必须核对 review_finding 对应问题是否关闭;source=self_test 时必须核对 parent_task_id、记录的自测原因、本次 attempt 验证和最新代码审查是否共同闭环。方案/混合问题必须有用户决策或后续修复证据。按 task_execution_index 的 required_evidence 核对测试证据:red_required 时需要同一 TEST 的 RED(expected_failure)后 GREEN;green_required 时每个声明 TEST 都需要允许的 GREEN 语义状态;测试运行证据应包含测试 ID(test_id)、命令(command)、工作目录(cwd)、退出码(exit_code)、语义状态(semantic_status)。修复 task 的回归测试运行可用回归覆盖任务列表(covers_task_ids)说明覆盖了哪些已完成任务;缺少任务尝试 ID(attempt_id)的旧证据只能弱引用。` +
@@ -1376,6 +1376,13 @@ export function taskComplete(projectRoot, change, changeRoot, taskId, inputConte
1376
1376
  if (applyPlanningMaterialsChanged(changeRoot, events)) {
1377
1377
  return { skip: true, message: "Apply 期间计划材料已变化,不能完成当前任务;请回到 Propose 核对并重新批准计划" };
1378
1378
  }
1379
+ // task_completed 与 tasks.md 的完成标记必须作为一个可重试的提交边界;
1380
+ // 先确认目标仍是实际 checkbox 任务,避免契约模式只凭 attempt 记录完成事件。
1381
+ const taskLines = readFileSync(join(changeRoot, "tasks.md"), "utf8").split("\n");
1382
+ const taskLine = findTaskLine(taskLines, taskId);
1383
+ if (taskLine < 0) {
1384
+ return { skip: true, message: `tasks.md 中找不到任务 ${taskId},未登记完成事件` };
1385
+ }
1379
1386
  const readiness = taskEvidenceReadiness(projectRoot, change, changeRoot, attempt);
1380
1387
  if (!readiness.ready)
1381
1388
  return { skip: true, message: `任务 ${taskId} 无法完成:${readiness.reason}` };
@@ -1393,24 +1400,23 @@ export function taskComplete(projectRoot, change, changeRoot, taskId, inputConte
1393
1400
  reason: `任务 ${taskId} 完成`,
1394
1401
  extraEvents: [{ type: "task_completed", payload: completedPayload }],
1395
1402
  postCommit: (pr, _ch, cr) => {
1396
- completedPayload.java_staging = stageProductionJavaFilesSince(pr, taskStartBoundary);
1397
1403
  const lines = readFileSync(join(cr, "tasks.md"), "utf8").split("\n");
1398
1404
  const idx = findTaskLine(lines, taskId);
1399
1405
  if (idx < 0) {
1400
- completedPayload.checkbox_update = { status: "failed", reason: `找不到 ${taskId} 的任务行` };
1401
- return;
1406
+ throw new Error(`任务 ${taskId} 完成失败:tasks.md 中找不到任务行,未写入完成事件`);
1402
1407
  }
1403
1408
  if (lines[idx].match(/- \[[xX]\]/)) {
1404
1409
  completedPayload.checkbox_update = { status: "applied" };
1405
- return;
1406
1410
  }
1407
- if (!lines[idx].match(/- \[ \]/)) {
1408
- completedPayload.checkbox_update = { status: "failed", reason: `找不到 ${taskId} 的未完成复选框` };
1409
- return;
1411
+ else {
1412
+ if (!lines[idx].match(/- \[ \]/)) {
1413
+ throw new Error(`任务 ${taskId} 完成失败:tasks.md 中找不到可勾选复选框,未写入完成事件`);
1414
+ }
1415
+ lines[idx] = lines[idx].replace(/- \[ \]/, "- [x]");
1416
+ writeFileSync(join(cr, "tasks.md"), lines.join("\n"));
1417
+ completedPayload.checkbox_update = { status: "applied" };
1410
1418
  }
1411
- lines[idx] = lines[idx].replace(/- \[ \]/, "- [x]");
1412
- writeFileSync(join(cr, "tasks.md"), lines.join("\n"));
1413
- completedPayload.checkbox_update = { status: "applied" };
1419
+ completedPayload.java_staging = stageProductionJavaFilesSince(pr, taskStartBoundary);
1414
1420
  },
1415
1421
  };
1416
1422
  },
@@ -1,7 +1,7 @@
1
1
  import type { ReviewRisk } from "./review.ts";
2
2
  import type { Event, State } from "./types.ts";
3
3
  export declare const WORKFLOW_CONFIG_PATH = ".superspec/config.json";
4
- /** 新安装项目写入配置时采用的默认档位。 */
4
+ /** 项目未声明 workflow.mode 时采用的默认档位。 */
5
5
  export declare const DEFAULT_WORKFLOW_RISK: ReviewRisk;
6
6
  export declare class WorkflowConfigError extends Error {
7
7
  constructor(message: string);
@@ -18,7 +18,7 @@ export declare function workflowRiskForApplyRound(events: Event[], fallback: Rev
18
18
  /** 供阶段确认和登记共用,避免任何调用者从 JSON/CLI 注入本轮 mode。 */
19
19
  export declare function workflowRiskForState(events: Event[], state: State, fallback: ReviewRisk): ReviewRisk;
20
20
  /**
21
- * 读取项目默认模式。新安装项目由配置提供 normal;缺少配置的旧项目保留 strict
21
+ * 读取项目默认模式。缺少配置或缺少 workflow.mode 时使用 normal。
22
22
  * 配置格式:{ "workflow": { "mode": "normal" } }
23
23
  */
24
24
  export declare function workflowRiskForProject(projectRoot: string): ReviewRisk;
@@ -3,10 +3,8 @@
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  export const WORKFLOW_CONFIG_PATH = ".superspec/config.json";
6
- /** 新安装项目写入配置时采用的默认档位。 */
6
+ /** 项目未声明 workflow.mode 时采用的默认档位。 */
7
7
  export const DEFAULT_WORKFLOW_RISK = "normal";
8
- /** 未安装项目继续沿用历史 strict 默认,避免无配置的旧项目静默放宽。 */
9
- const LEGACY_WORKFLOW_RISK = "strict";
10
8
  export class WorkflowConfigError extends Error {
11
9
  constructor(message) {
12
10
  super(message);
@@ -94,13 +92,13 @@ export function workflowRiskForState(events, state, fallback) {
94
92
  }
95
93
  }
96
94
  /**
97
- * 读取项目默认模式。新安装项目由配置提供 normal;缺少配置的旧项目保留 strict
95
+ * 读取项目默认模式。缺少配置或缺少 workflow.mode 时使用 normal。
98
96
  * 配置格式:{ "workflow": { "mode": "normal" } }
99
97
  */
100
98
  export function workflowRiskForProject(projectRoot) {
101
99
  const configPath = join(projectRoot, WORKFLOW_CONFIG_PATH);
102
100
  if (!existsSync(configPath))
103
- return LEGACY_WORKFLOW_RISK;
101
+ return DEFAULT_WORKFLOW_RISK;
104
102
  let parsed;
105
103
  try {
106
104
  parsed = JSON.parse(readFileSync(configPath, "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peterxiaoyang/superspec",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "description": "SuperSpec 流程引擎 — transition engine with lightweight fact-sync",
5
5
  "type": "module",
6
6
  "engines": {
@@ -28,6 +28,8 @@
28
28
  "eval:dynamic": "node evals/probe.mjs --scenario evals/scenarios/probe-dynamic-accepted.json",
29
29
  "eval:m2": "node evals/m2.mjs",
30
30
  "eval:m2:validate": "node evals/m2.mjs --validate-faults",
31
+ "eval:m3": "node evals/regression.mjs",
32
+ "eval:m3:validate": "node evals/regression.mjs --validate-faults",
31
33
  "eval:arena": "node evals/arena.mjs",
32
34
  "eval:delegation": "node evals/delegation-probe.mjs",
33
35
  "prepack": "npm run build",
@@ -1,7 +1,8 @@
1
1
  # SuperSpec Codex agent: architect
2
2
  name = "architect"
3
3
  description = "System design, boundaries, interfaces, long-horizon tradeoffs"
4
- model_reasoning_effort = "medium"
4
+ model = "gpt-5.6-sol"
5
+ model_reasoning_effort = "high"
5
6
  developer_instructions = """
6
7
  Role: Architect. Review system boundaries, interface contracts, data flow, maintenance risk, rollback risk, and design tradeoffs.
7
8
 
@@ -1,7 +1,8 @@
1
1
  # SuperSpec Codex agent: code-reviewer
2
2
  name = "code-reviewer"
3
3
  description = "Code-level review for spec fit, bugs, safety, and test gaps"
4
- model_reasoning_effort = "medium"
4
+ model = "gpt-5.6-sol"
5
+ model_reasoning_effort = "high"
5
6
  developer_instructions = """
6
7
  Role: Code Reviewer. Check spec fit, correctness, security, test adequacy, code quality, performance, and maintainability without making the workflow heavy.
7
8
 
@@ -1,6 +1,7 @@
1
1
  # SuperSpec Codex agent: critic
2
2
  name = "critic"
3
3
  description = "Plan/design critical challenge and review"
4
+ model = "gpt-5.6-sol"
4
5
  model_reasoning_effort = "medium"
5
6
  developer_instructions = """
6
7
  Role: Critic. Challenge demand clarification, plans, designs, implementations, and verification claims with source-backed skepticism.
@@ -1,6 +1,7 @@
1
1
  # SuperSpec Codex agent: executor
2
2
  name = "executor"
3
3
  description = "Bounded SuperSpec apply implementation worker"
4
+ model = "gpt-5.6-terra"
4
5
  model_reasoning_effort = "medium"
5
6
  developer_instructions = """
6
7
  Role: Executor. Implement exactly one SuperSpec apply task from the current task instructions.
@@ -1,6 +1,7 @@
1
1
  # SuperSpec Codex agent: explore
2
2
  name = "explore"
3
3
  description = "Repo-local read-only factual scan for SuperSpec discovery"
4
+ model = "gpt-5.6-terra"
4
5
  model_reasoning_effort = "medium"
5
6
  developer_instructions = """
6
7
  Role: Explore. Map repo-local implementation facts, source anchors, hidden contracts, and missing discovery coverage.
@@ -1,6 +1,7 @@
1
1
  # SuperSpec Codex agent: test-engineer
2
2
  name = "test-engineer"
3
3
  description = "Test strategy, coverage, flaky-test hardening"
4
+ model = "gpt-5.6-sol"
4
5
  model_reasoning_effort = "medium"
5
6
  developer_instructions = """
6
7
  Role: Test Engineer. Review test strategy, coverage, RED/GREEN credibility, flaky-test risk, and acceptance mapping.
@@ -1,6 +1,7 @@
1
1
  # SuperSpec Codex agent: test-runner
2
2
  name = "test-runner"
3
3
  description = "Bounded SuperSpec apply test execution worker"
4
+ model = "gpt-5.6-terra"
4
5
  model_reasoning_effort = "medium"
5
6
  developer_instructions = """
6
7
  Role: Test Runner. Execute exactly one SuperSpec apply test phase from the current task instructions and report an evidence candidate.
@@ -1,7 +1,8 @@
1
1
  # SuperSpec Codex agent: verifier
2
2
  name = "verifier"
3
3
  description = "Completion evidence, claim validation, test adequacy"
4
- model_reasoning_effort = "medium"
4
+ model = "gpt-5.6-sol"
5
+ model_reasoning_effort = "high"
5
6
  developer_instructions = """
6
7
  Role: Verifier. Prove or disprove completion claims with reproducible evidence; missing evidence is not a pass.
7
8
 
@@ -13,7 +13,7 @@ argument-hint: "本次代码审查说明"
13
13
 
14
14
  - 先读任务说明、指定代码范围和相关计划材料;范围和停止条件以任务说明为准。不要把未打开的材料当作审查依据。
15
15
  - 只读;不实现修复、不修改计划或证据、不自行宣布完成。上下文不足时明确指出缺口。
16
- - 修复复核优先关闭原问题,并审查本次变化及其直接影响链路。此前漏报的问题只有在当前代码中存在直接证据、影响既定验收或兼容边界时才能成为新 blocker;不要重新打开与本次修复无关、未变化的模块。
16
+ - 修复复核优先关闭原问题,并审查本次变化及其直接影响链路。此前漏报的问题只有在当前代码中存在直接证据、影响既定验收或兼容边界,并且能够与本次修复或同一批准行为的直接消费者建立因果关系时,才能成为新 blocker;不要重新打开与本次修复无关、未变化的模块。
17
17
 
18
18
  ## 审查判断
19
19
 
@@ -24,6 +24,7 @@ argument-hint: "本次代码审查说明"
24
24
  - 实现是否兑现当前任务的验收和边界,且与已批准的方案/规格一致。
25
25
  - 是否引入功能、数据、一致性、安全、权限、性能或兼容问题,以及直接的边界条件遗漏。
26
26
  - 从批准范围反查实现是否覆盖已确认的消费者、兼容路径和直接影响链路;任务勾选和测试通过不能替代完整性判断。
27
+ - 对当前审查范围内的 Diff,分别判断“是否漏实现”和“是否超出必要范围”:直接消费者没有实现或没有现有实现已满足验收的证据,属于完整性问题;新增共享语义、公共契约或无关生产逻辑没有直接必要性证据,属于范围问题。两者都应锚定当前批准行为和实际 Diff,不把消费者类别或可能性清单当成覆盖义务。
27
28
  - 从实际 Diff 反查每项语义变化是否为当前验收所需。文件数量、新增方法或重载本身不是问题;若公共契约、共享行为或无关生产逻辑被扩大,而现有证据不能说明局部方案为何无法安全、完整地满足验收,应作为纯实现问题交回 Apply 收缩。
28
29
  - 测试是否实际证明相关行为和直接回归风险,而非只存在一条通过记录。
29
30
  - 需求源已更新时,代码是否仍在执行过期计划;此类问题按方案或需求缺口归因,不把旧材料当作当前依据。
@@ -31,7 +31,7 @@ metadata:
31
31
 
32
32
  - 重构、清理或技术调整只有在其本身是兑现当前 task 的必要组成部分时才进入本次交付。
33
33
  - 公共契约、共享行为、默认行为或兼容语义的变化需要当前 task 和真实调用链支持,并验证直接受影响的既有行为;局部实现已能完整满足验收时,保持公共边界稳定。
34
- - 整体差异应能解释并覆盖批准范围内的真实消费者;影响面最小不能以遗漏需求或验收路径为代价。
34
+ - 整体差异应能从已批准行为追溯到真实消费者:每个直接受影响的消费者都应有必要的实现变化,或有仓库事实证明现有实现已经满足验收。影响面最小不能以遗漏需求或验收路径为代价,也不要求为了“覆盖”而扩展到没有直接因果关系的模块。
35
35
 
36
36
  ### 保留得住的测试
37
37