@chenmk/superflow 0.5.9 → 0.5.11

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 (33) hide show
  1. package/INSTALL.en.md +7 -1
  2. package/INSTALL.md +12 -2
  3. package/README.md +12 -2
  4. package/README.zh-CN.md +12 -3
  5. package/assets/manifest.json +1 -0
  6. package/assets/scripts/superflow-coding-ready.mjs +88 -19
  7. package/assets/scripts/superflow-dependency-update-hook.sh +4 -17
  8. package/assets/scripts/superflow-document-audit.mjs +29 -5
  9. package/assets/scripts/superflow-hook-guard.sh +3 -14
  10. package/assets/scripts/superflow-review-coverage.mjs +258 -0
  11. package/assets/skills/superflow-docs/SKILL.md +5 -0
  12. package/assets/skills/superflow-pipeline/references/document-readiness.md +4 -0
  13. package/assets/skills/superflow-pipeline/references/document-review-coverage.md +82 -0
  14. package/assets/skills/superflow-requirement-review/SKILL.md +4 -0
  15. package/assets/skills/superflow-verify/SKILL.md +4 -0
  16. package/assets/skills-en/superflow-docs/SKILL.md +6 -0
  17. package/assets/skills-en/superflow-pipeline/references/document-readiness.md +4 -0
  18. package/assets/skills-en/superflow-pipeline/references/document-review-coverage.md +89 -0
  19. package/assets/skills-en/superflow-requirement-review/SKILL.md +5 -0
  20. package/assets/skills-en/superflow-verify/SKILL.md +5 -0
  21. package/dist/app/cli.js +1 -0
  22. package/dist/app/cli.js.map +1 -1
  23. package/dist/app/commands/doctor.js +9 -3
  24. package/dist/app/commands/doctor.js.map +1 -1
  25. package/dist/app/commands/update.js +22 -5
  26. package/dist/app/commands/update.js.map +1 -1
  27. package/dist/domains/config/cli-help.js +4 -2
  28. package/dist/domains/config/cli-help.js.map +1 -1
  29. package/dist/domains/deps.js +62 -30
  30. package/dist/domains/deps.js.map +1 -1
  31. package/dist/domains/sdd-readiness.js +9 -0
  32. package/dist/domains/sdd-readiness.js.map +1 -1
  33. package/package.json +1 -1
package/INSTALL.en.md CHANGED
@@ -155,6 +155,12 @@ superflow update --with-package
155
155
 
156
156
  Automatic checks are enabled by default but do not install updates:
157
157
 
158
+ Default checks query npm and remind you to refresh Superpowers. Manual update or
159
+ `apply` updates the plugins. Use `superflow update --agent codex --scope global --with-dependencies`
160
+ to refresh OpenSpec, Superpowers and host assets without reinstalling the CLI.
161
+ Codex plugin identity/version/enablement comes from native inventory (currently
162
+ `superpowers@openai-curated-remote`), not legacy config.toml entries or residual caches.
163
+
158
164
  ```bash
159
165
  export SUPERFLOW_AUTO_UPDATE=check
160
166
  export SUPERFLOW_AUTO_UPDATE=0
@@ -164,7 +170,7 @@ export SUPERFLOW_UPDATE_MIN_INTERVAL_SECONDS=21600
164
170
 
165
171
  Team environments should keep `check`. Personal machines may choose `apply`.
166
172
  In `apply` mode, package upgrades are followed by the newly installed CLI, which
167
- redeploys Skills, Hooks, rules, scripts, and managed MCP registrations. Partial
173
+ updates OpenSpec/Superpowers and redeploys Skills, Hooks, rules, scripts, and managed MCP registrations. Partial
168
174
  failure removes the throttle stamp and remains eligible for the next-session retry.
169
175
  Restart the Agent after success to load the new MCP process.
170
176
 
package/INSTALL.md CHANGED
@@ -517,7 +517,7 @@ Claude Code 里也可以直接用当前阶段命令继续,例如:
517
517
 
518
518
  - `@chenmk/superflow`
519
519
  - `@fission-ai/openspec`
520
- - Claude Code / Codex 的 Superpowers 插件
520
+ - Superpowers 插件在手动更新或 `apply` 时刷新;默认检查仅提供刷新提示
521
521
 
522
522
  同一会话只检查一次;默认只提示,不自动安装。至少间隔 6 小时才真正访问
523
523
  npm/plugin 源,避免每次 prompt 都联网。可用:
@@ -540,12 +540,22 @@ export SUPERFLOW_UPDATE_MIN_INTERVAL_SECONDS=21600
540
540
  `superflow update --with-package` 更新;个人机器如果接受自动升级风险,再设置
541
541
  `SUPERFLOW_AUTO_UPDATE=apply`。
542
542
 
543
- 显式更新和 Hook `apply` 使用同一闭环:先更新依赖包,再由新版本 CLI 重新部署全部 Agent
543
+ 显式更新和 Hook `apply` 使用同一闭环:先升级 CLI 包,再由新版本 CLI 更新 OpenSpec、
544
+ 从 Codex 原生目录发现官方 Superpowers(当前为 `openai-curated-remote`),并重新部署全部 Agent
544
545
  资产并刷新 MCP 注册。任一步失败会记录日志、删除本轮节流戳并允许下次会话重试;成功后需
545
546
  重启 Agent 才会加载新的 MCP 进程。
546
547
 
547
548
  ### 8.2 手动更新
548
549
 
550
+ 仅更新 OpenSpec、Superpowers 和宿主资产(不重装 CLI):
551
+
552
+ ```bash
553
+ superflow update --agent codex --scope global --with-dependencies
554
+ ```
555
+
556
+ 最新版远端插件启用状态以 `codex plugin list --json` 为准,不一定写入 config.toml;
557
+ doctor 检查当前启用版本的技能文件。旧缓存不能代替当前版本缺失的技能。
558
+
549
559
  只刷新已安装技能、脚本、规则和 hook:
550
560
 
551
561
  ```bash
package/README.md CHANGED
@@ -48,6 +48,11 @@ state, and dependency guards for Claude Code and Codex.
48
48
 
49
49
  ## Highlights
50
50
 
51
+ 0.5.10 strengthens document delivery: requirement × real-entry coverage (including
52
+ exclusions), real-entry test contracts, and content-bound Coding Ready receipts.
53
+ Existing document receipts need [coverage migration and re-review](assets/skills-en/superflow-pipeline/references/document-review-coverage.md)
54
+ before new dispatch or source edits. Semantic correctness remains an Agent review responsibility.
55
+
51
56
  - **Dual-Agent managed delivery.** The current host Agent reviews directly while
52
57
  the peer Agent develops, starts, and verifies in one CLI session without a
53
58
  tool-turn limit, then actively returns one structured result. A later session
@@ -301,7 +306,7 @@ Design and field evidence:
301
306
 
302
307
  - OpenSpec CLI, then runs `openspec init <project> --tools ...`
303
308
  - Superpowers for the selected agent tools. Codex uses the official
304
- `superpowers@openai-api-curated` plugin, including
309
+ current `superpowers@openai-curated-remote` plugin, discovering versions from native inventory and checking
305
310
  `verification-before-completion`, `requesting-code-review`, and
306
311
  `finishing-a-development-branch`.
307
312
  - SuperBridge Flow/OpenSpec skills
@@ -369,6 +374,10 @@ default.
369
374
  Default behavior is **check only**. It reports available updates but does not
370
375
  install them. Run this to update explicitly:
371
376
 
377
+ The default check queries npm versions; Superpowers receives a refresh reminder.
378
+ Manual update or `apply` refreshes plugins through native commands. To update only
379
+ OpenSpec, Superpowers and host assets, use `superflow update --agent codex --scope global --with-dependencies`.
380
+
372
381
  ```bash
373
382
  superflow update --with-package
374
383
  ```
@@ -389,7 +398,8 @@ export SUPERFLOW_AUTO_UPDATE=apply
389
398
  export SUPERFLOW_UPDATE_MIN_INTERVAL_SECONDS=21600
390
399
  ```
391
400
 
392
- `apply` re-enters the newly installed CLI after package upgrade and refreshes all
401
+ `apply` resolves the newly installed CLI from the npm root, avoiding stale PATH shims,
402
+ then lets that runtime update OpenSpec/Superpowers and refresh all
393
403
  detected Agent assets plus managed MCP registrations. A partial failure keeps its
394
404
  log and removes the throttle stamp so the next session can retry. Restart the
395
405
  Agent after success because an existing MCP process cannot hot-load the upgrade.
package/README.zh-CN.md CHANGED
@@ -41,6 +41,11 @@ CLI 会把 SuperBridge Flow 技能、配套 hook/command 脚本整合为单一 n
41
41
 
42
42
  ## 亮点
43
43
 
44
+ 0.5.10 加强文档交付:逐需求×真实入口核对(含排除项)、真实入口验收合同、
45
+ 绑定内容指纹的 Coding Ready。旧文档凭证需先完成
46
+ [覆盖补齐与复审](assets/skills/superflow-pipeline/references/document-review-coverage.md),
47
+ 才能新启动任务或编辑源码;业务语义正确性仍由智能体独立评审负责。
48
+
44
49
  - **双 Agent 托管到真实交付。** 当前 Host Agent 直接监督,另一个 Agent 在一次不限制工具
45
50
  轮数的 CLI 会话中持续开发、启动和验证,完成后主动返回结构化结果;只有评审整改、进程异常
46
51
  或人工恢复才创建后续会话。工作区与落盘 finding 负责交接,默认最多 5 轮评审、7 次执行、
@@ -291,7 +296,8 @@ Agent 进程树;macOS/Linux 继续使用独立进程组。三端均不通过
291
296
  - 第三方(`superflow init` 自动装):
292
297
  - openspec CLI(硬依赖,npm 全局)并在当前项目执行 `openspec init --tools ...`
293
298
  - superpowers(Claude Code / Codex 为硬依赖;Codex 自动安装官方
294
- `superpowers@openai-api-curated`,确保包含验证、代码评审和分支收尾三个强制技能)
299
+ 当前渠道 `superpowers@openai-curated-remote`;从原生插件清单发现版本并核验验证、
300
+ 代码评审和分支收尾三个强制技能,不以旧 config.toml 条目或缓存存在代替可用性)
295
301
  - understand-anything(尽力安装,失败只警告)
296
302
  - api-doc-changelog(辅助 skill,复制到目标 agent skills 目录)
297
303
 
@@ -301,10 +307,12 @@ Agent 进程树;macOS/Linux 继续使用独立进程组。三端均不通过
301
307
 
302
308
  - `@chenmk/superflow`
303
309
  - `@fission-ai/openspec`
304
- - Claude Code / Codex 的 Superpowers 插件
310
+ - Superpowers 在手动更新或 `apply` 时通过原生插件命令更新;默认 `check` 仅给出刷新提示
305
311
 
306
312
  推荐策略是“自动检查,手动更新”:默认只提示,不自动安装;执行
307
313
  `superflow update --with-package` 才会统一更新。
314
+ 只更新 OpenSpec、Superpowers 和宿主资产、不重装 CLI 时,可执行
315
+ `superflow update --agent codex --scope global --with-dependencies`。
308
316
  同一会话只检查一次,并且默认至少间隔 6 小时才真正访问 npm/plugin 源。
309
317
 
310
318
  ```bash
@@ -321,7 +329,8 @@ export SUPERFLOW_AUTO_UPDATE=apply
321
329
  export SUPERFLOW_UPDATE_MIN_INTERVAL_SECONDS=21600
322
330
  ```
323
331
 
324
- `apply` 会在包升级后重新进入新版本 CLI,统一刷新所有已检测到的 Agent 资产和 MCP 注册。
332
+ `apply` 会在包升级后按 npm 根目录定位新版本 CLI(不受旧 PATH 启动器遮蔽),由新版本
333
+ 统一更新 OpenSpec/Superpowers、所有已检测到的 Agent 资产和 MCP 注册。
325
334
  任一步失败都会保留日志和下次会话重试资格,不会把半升级状态标记为成功。升级完成后需要
326
335
  重启 Agent,旧会话不会热加载新的 MCP 运行时。
327
336
 
@@ -32,6 +32,7 @@
32
32
  "superflow-test-report-lint.py",
33
33
  "superflow-managed-work-check.mjs",
34
34
  "superflow-document-audit.mjs",
35
+ "superflow-review-coverage.mjs",
35
36
  "superflow-environment-preflight.mjs",
36
37
  "superflow-coding-ready.mjs",
37
38
  "superflow-managed-executor-preflight.mjs",
@@ -5,6 +5,7 @@ import fs from "node:fs";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
+ import { fileHash, reviewPath } from "./superflow-review-coverage.mjs";
8
9
 
9
10
  const args = process.argv.slice(2);
10
11
  const changeArg = args.find((arg) => !arg.startsWith("--"));
@@ -18,12 +19,40 @@ const changeDir = fs.realpathSync(path.resolve(changeArg));
18
19
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
19
20
  const pipelineScripts = resolvePipelineScripts(scriptDir);
20
21
  const checks = [
21
- ["YAML 状态", "bash", [path.join(pipelineScripts, "superflow-yaml-validate.sh"), changeDir]],
22
- ["docs 门禁", "bash", [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "docs"]],
23
- ["design 门禁", "bash", [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "design"]],
24
- ["implement 门禁", "bash", [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "implement"]],
25
- ["文档交付审计", process.execPath, [path.join(scriptDir, "superflow-document-audit.mjs"), changeDir, "--json"]],
26
- ["环境预检", process.execPath, [path.join(scriptDir, "superflow-environment-preflight.mjs"), changeDir, "--json"]],
22
+ [
23
+ "YAML 状态",
24
+ "bash",
25
+ [path.join(pipelineScripts, "superflow-yaml-validate.sh"), changeDir],
26
+ ],
27
+ [
28
+ "docs 门禁",
29
+ "bash",
30
+ [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "docs"],
31
+ ],
32
+ [
33
+ "design 门禁",
34
+ "bash",
35
+ [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "design"],
36
+ ],
37
+ [
38
+ "implement 门禁",
39
+ "bash",
40
+ [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "implement"],
41
+ ],
42
+ [
43
+ "文档交付审计",
44
+ process.execPath,
45
+ [path.join(scriptDir, "superflow-document-audit.mjs"), changeDir, "--json"],
46
+ ],
47
+ [
48
+ "环境预检",
49
+ process.execPath,
50
+ [
51
+ path.join(scriptDir, "superflow-environment-preflight.mjs"),
52
+ changeDir,
53
+ "--json",
54
+ ],
55
+ ],
27
56
  ];
28
57
  const issues = [];
29
58
  const evidence = [];
@@ -40,17 +69,29 @@ for (const [label, command, commandArgs] of checks) {
40
69
  }
41
70
  }
42
71
 
43
- const openspec = spawnSync("openspec", ["validate", path.basename(changeDir), "--strict"], {
44
- cwd: projectRoot(changeDir),
45
- encoding: "utf-8",
46
- });
72
+ const openspec = spawnSync(
73
+ "openspec",
74
+ ["validate", path.basename(changeDir), "--strict"],
75
+ {
76
+ cwd: projectRoot(changeDir),
77
+ encoding: "utf-8",
78
+ },
79
+ );
47
80
  evidence.push({ label: "OpenSpec strict", exitCode: openspec.status ?? 1 });
48
81
  if (openspec.status !== 0) {
49
- issues.push(`OpenSpec strict 失败:${tail(openspec.stderr || openspec.stdout)}`);
82
+ issues.push(
83
+ `OpenSpec strict 失败:${tail(openspec.stderr || openspec.stdout)}`,
84
+ );
50
85
  }
51
86
 
52
87
  const handoffHash = readHandoffHash(changeDir, issues);
53
88
  const reviewRounds = readReviewRounds(changeDir);
89
+ let reviewHash = "";
90
+ try {
91
+ reviewHash = fileHash(path.join(changeDir, reviewPath));
92
+ } catch {
93
+ issues.push("无法冻结文档评审凭证指纹");
94
+ }
54
95
  const receipt = {
55
96
  schemaVersion: "superflow.coding-ready.v1",
56
97
  codingReady: issues.length === 0,
@@ -58,21 +99,28 @@ const receipt = {
58
99
  environmentReadiness: issues.length === 0 ? "READY" : "BLOCKED",
59
100
  reviewRounds,
60
101
  handoffHash,
102
+ reviewHash,
61
103
  checkedAt: new Date().toISOString(),
62
104
  evidence,
63
105
  ownerDecisions: 0,
64
106
  issues,
65
107
  };
66
108
 
67
- if (issues.length > 0) fail(issues, receipt);
68
-
69
109
  const receiptDir = path.join(changeDir, ".sdd", "readiness");
70
110
  fs.mkdirSync(receiptDir, { recursive: true });
71
111
  const target = path.join(receiptDir, "coding-ready.json");
72
- const temporary = path.join(receiptDir, `.coding-ready-${process.pid}-${Date.now()}.tmp`);
73
- fs.writeFileSync(temporary, `${JSON.stringify(receipt, null, 2)}\n`, { mode: 0o600 });
112
+ const temporary = path.join(
113
+ receiptDir,
114
+ `.coding-ready-${process.pid}-${Date.now()}.tmp`,
115
+ );
116
+ fs.writeFileSync(temporary, `${JSON.stringify(receipt, null, 2)}\n`, {
117
+ mode: 0o600,
118
+ });
74
119
  fs.renameSync(temporary, target);
75
120
 
121
+ // Persist BLOCKED too: a failed recheck must revoke an earlier READY receipt.
122
+ if (issues.length > 0) fail(issues, receipt);
123
+
76
124
  if (jsonOutput) {
77
125
  process.stdout.write(`${JSON.stringify(receipt, null, 2)}\n`);
78
126
  } else {
@@ -81,9 +129,27 @@ if (jsonOutput) {
81
129
 
82
130
  function resolvePipelineScripts(currentScriptDir) {
83
131
  const candidates = [
84
- path.resolve(currentScriptDir, "..", "skills", "superflow-pipeline", "scripts"),
85
- path.join(os.homedir(), ".codex", "skills", "superflow-pipeline", "scripts"),
86
- path.join(os.homedir(), ".claude", "skills", "superflow-pipeline", "scripts"),
132
+ path.resolve(
133
+ currentScriptDir,
134
+ "..",
135
+ "skills",
136
+ "superflow-pipeline",
137
+ "scripts",
138
+ ),
139
+ path.join(
140
+ os.homedir(),
141
+ ".codex",
142
+ "skills",
143
+ "superflow-pipeline",
144
+ "scripts",
145
+ ),
146
+ path.join(
147
+ os.homedir(),
148
+ ".claude",
149
+ "skills",
150
+ "superflow-pipeline",
151
+ "scripts",
152
+ ),
87
153
  ];
88
154
  const found = candidates.find((candidate) =>
89
155
  fs.existsSync(path.join(candidate, "superflow-guard.sh")),
@@ -123,7 +189,10 @@ function tail(value) {
123
189
  return String(value).trim().split("\n").slice(-4).join(" | ").slice(0, 800);
124
190
  }
125
191
 
126
- function fail(targetIssues, receipt = { codingReady: false, issues: targetIssues }) {
192
+ function fail(
193
+ targetIssues,
194
+ receipt = { codingReady: false, issues: targetIssues },
195
+ ) {
127
196
  if (jsonOutput) {
128
197
  process.stderr.write(`${JSON.stringify(receipt, null, 2)}\n`);
129
198
  } else {
@@ -104,7 +104,7 @@ run_logged() {
104
104
  }
105
105
 
106
106
  latest_npm_version() {
107
- npm view "$1" version 2>/dev/null | tail -n 1
107
+ npm view "$1" version --registry https://registry.npmjs.org 2>/dev/null | tail -n 1
108
108
  }
109
109
 
110
110
  installed_npm_version() {
@@ -143,7 +143,7 @@ update_npm_if_needed() {
143
143
  fi
144
144
  current="$(installed_npm_version "$package_name")"
145
145
  if [ "$current" != "$latest" ]; then
146
- run_logged npm install -g "$package_name@latest"
146
+ run_logged npm install -g "$package_name@latest" --registry https://registry.npmjs.org
147
147
  fi
148
148
  }
149
149
 
@@ -159,15 +159,12 @@ detected_agents() {
159
159
  refresh_superflow_installation() {
160
160
  local agents="$1"
161
161
  [ -n "$agents" ] || return 1
162
- if command -v superflow >/dev/null 2>&1; then
163
- run_logged superflow update --agent "$agents" --scope global
164
- return $?
165
- fi
162
+ # Always run the package just installed, not an older PATH shadow.
166
163
  local npm_root cli
167
164
  npm_root="$(npm root -g 2>/dev/null)" || return 1
168
165
  cli="$npm_root/@chenmk/superflow/dist/app/cli.js"
169
166
  [ -f "$cli" ] || return 1
170
- run_logged node "$cli" update --agent "$agents" --scope global
167
+ run_logged node "$cli" update --agent "$agents" --scope global --with-dependencies
171
168
  }
172
169
 
173
170
  check_npm_package() {
@@ -187,16 +184,6 @@ check_npm_package() {
187
184
  if [ "$MODE" = "apply" ]; then
188
185
  APPLY_FAILED=0
189
186
  update_npm_if_needed "@chenmk/superflow" || APPLY_FAILED=1
190
- update_npm_if_needed "@fission-ai/openspec" || APPLY_FAILED=1
191
-
192
- if command -v claude >/dev/null 2>&1; then
193
- run_logged claude plugin install superpowers@superpowers-marketplace || APPLY_FAILED=1
194
- fi
195
-
196
- if command -v codex >/dev/null 2>&1; then
197
- run_logged codex plugin add superpowers@openai-api-curated || APPLY_FAILED=1
198
- fi
199
-
200
187
  AGENT_VALUE="$(detected_agents)"
201
188
  refresh_superflow_installation "$AGENT_VALUE" || APPLY_FAILED=1
202
189
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
+ import {
6
+ currentHandoffHash,
7
+ validateCoverage,
8
+ } from "./superflow-review-coverage.mjs";
5
9
 
6
10
  const args = process.argv.slice(2);
7
11
  const changeArg = args.find((arg) => !arg.startsWith("--"));
@@ -34,7 +38,9 @@ for (const document of aggregateDocuments) {
34
38
  }
35
39
  const links = markdownTargets(fs.readFileSync(documentPath, "utf-8"));
36
40
  for (const prompt of prompts) {
37
- const relativePrompt = path.relative(changeDir, prompt).replaceAll("\\", "/");
41
+ const relativePrompt = path
42
+ .relative(changeDir, prompt)
43
+ .replaceAll("\\", "/");
38
44
  if (!links.has(relativePrompt)) {
39
45
  issues.push(`${document} 未链接 ${relativePrompt}`);
40
46
  }
@@ -47,6 +53,14 @@ const review = readJson(
47
53
  issues,
48
54
  );
49
55
  validateReview(review, handoffHash, issues);
56
+ validateCoverage(changeDir, review, issues);
57
+ try {
58
+ if (currentHandoffHash(changeDir) !== handoffHash) {
59
+ issues.push("冻结文档内容已变化,请刷新 handoff 并重新评审");
60
+ }
61
+ } catch (error) {
62
+ issues.push(error.message);
63
+ }
50
64
  validateMermaid(changeDir, issues);
51
65
 
52
66
  const result = {
@@ -67,7 +81,9 @@ print(result);
67
81
  function validateReview(review, currentHash, targetIssues) {
68
82
  if (!review) return;
69
83
  if (review.schemaVersion !== "superflow.document-review.v1") {
70
- targetIssues.push("文档评审凭证 schemaVersion 必须为 superflow.document-review.v1");
84
+ targetIssues.push(
85
+ "文档评审凭证 schemaVersion 必须为 superflow.document-review.v1",
86
+ );
71
87
  }
72
88
  if (review.handoffHash !== currentHash) {
73
89
  targetIssues.push("文档评审凭证未绑定当前 handoff hash");
@@ -88,12 +104,18 @@ function validateReview(review, currentHash, targetIssues) {
88
104
  "e2e-environment",
89
105
  ]);
90
106
  for (const round of review.rounds) {
107
+ if (!round || !Array.isArray(round.findings)) {
108
+ targetIssues.push("评审轮必须显式提供 findings 数组");
109
+ continue;
110
+ }
91
111
  requiredLenses.delete(round.lens);
92
112
  if (round.inputHash !== currentHash) {
93
- targetIssues.push(`第 ${round.round ?? "?"} 轮评审不是基于当前 handoff hash`);
113
+ targetIssues.push(
114
+ `第 ${round.round ?? "?"} 轮评审不是基于当前 handoff hash`,
115
+ );
94
116
  }
95
117
  const openFindings = (round.findings ?? []).filter(
96
- (finding) => finding.status !== "closed",
118
+ (finding) => finding?.status !== "closed",
97
119
  );
98
120
  if (openFindings.length > 0) {
99
121
  targetIssues.push(`第 ${round.round ?? "?"} 轮仍有未关闭评审发现`);
@@ -149,7 +171,9 @@ function validateMermaidBlock(block, targetIssues) {
149
171
  if (arrows.length === 0) {
150
172
  targetIssues.push("Mermaid sequenceDiagram 缺少有效消息箭头");
151
173
  }
152
- const openings = lines.filter((line) => /^(alt|opt|loop|par|critical)\b/.test(line)).length;
174
+ const openings = lines.filter((line) =>
175
+ /^(alt|opt|loop|par|critical)\b/.test(line),
176
+ ).length;
153
177
  const endings = lines.filter((line) => line === "end").length;
154
178
  if (openings !== endings) {
155
179
  targetIssues.push("Mermaid sequenceDiagram 分支块未正确闭合");
@@ -107,20 +107,9 @@ superflow check <change> --level coding-ready
107
107
  BLOCK_MSG
108
108
  return 2
109
109
  fi
110
- python3 - "$receipt" "$expected_hash" <<'PY'
111
- import json, sys
112
- try:
113
- with open(sys.argv[1], encoding="utf-8") as handle:
114
- receipt = json.load(handle)
115
- except Exception:
116
- raise SystemExit(2)
117
- if receipt.get("codingReady") is not True:
118
- raise SystemExit(2)
119
- if receipt.get("schemaVersion") != "superflow.coding-ready.v1":
120
- raise SystemExit(2)
121
- if receipt.get("handoffHash") != sys.argv[2]:
122
- raise SystemExit(2)
123
- PY
110
+ # Same validator as CLI/managed start. Source code is expected to change during
111
+ # implementation; only dispatch rechecks code snapshots, not every write.
112
+ node "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/superflow-review-coverage.mjs" "$change_dir"
124
113
  if [ $? -ne 0 ]; then
125
114
  cat <<'BLOCK_MSG' >&2
126
115
  [SDD Coding Ready] 凭证已过期或与当前 handoff hash 不一致。