@chenmk/superflow 0.4.0 → 0.5.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 (242) hide show
  1. package/INSTALL.en.md +58 -12
  2. package/INSTALL.md +62 -33
  3. package/README.md +181 -54
  4. package/README.zh-CN.md +158 -51
  5. package/assets/brand/managed-review-loop.png +0 -0
  6. package/assets/manifest.json +10 -5
  7. package/assets/scripts/superflow-coding-ready.mjs +135 -0
  8. package/assets/scripts/superflow-dependency-update-hook.sh +81 -8
  9. package/assets/scripts/superflow-document-audit.mjs +240 -0
  10. package/assets/scripts/superflow-environment-preflight.mjs +398 -0
  11. package/assets/scripts/superflow-hook-guard.sh +40 -1
  12. package/assets/scripts/superflow-managed-command-guard.py +309 -0
  13. package/assets/scripts/superflow-managed-executor-preflight.mjs +421 -0
  14. package/assets/scripts/superflow-managed-owner-verification.sh +117 -0
  15. package/assets/scripts/superflow-managed-work-check.mjs +287 -26
  16. package/assets/scripts/superflow-managed-work-guard.sh +6 -92
  17. package/assets/skills/superflow-clarify/SKILL.md +14 -0
  18. package/assets/skills/superflow-design/SKILL.md +38 -13
  19. package/assets/skills/superflow-docs/SKILL.md +93 -2
  20. package/assets/skills/superflow-pipeline/SKILL.md +167 -10
  21. package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +17 -9
  22. package/assets/skills/superflow-pipeline/references/document-readiness.md +157 -0
  23. package/assets/skills/superflow-pipeline/references/document-templates.md +9 -5
  24. package/assets/skills/superflow-pipeline/references/feature-gated-workflow.md +14 -0
  25. package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +20 -11
  26. package/assets/skills/superflow-pipeline/references/managed-work.md +123 -15
  27. package/assets/skills/superflow-pipeline/references/orchestration.md +2 -2
  28. package/assets/skills/superflow-pipeline/references/p0-baseline-template.md +3 -2
  29. package/assets/skills/superflow-pipeline/references/quality-gate.md +7 -5
  30. package/assets/skills/superflow-pipeline/references/reviewer-checklist.md +7 -0
  31. package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +205 -9
  32. package/assets/skills/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
  33. package/assets/skills/superflow-requirement-review/SKILL.md +178 -0
  34. package/assets/skills/superflow-requirement-review/agents/openai.yaml +4 -0
  35. package/assets/skills-en/superflow-clarify/SKILL.md +17 -0
  36. package/assets/skills-en/superflow-design/SKILL.md +31 -12
  37. package/assets/skills-en/superflow-docs/SKILL.md +99 -0
  38. package/assets/skills-en/superflow-pipeline/SKILL.md +163 -9
  39. package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +4 -0
  40. package/assets/skills-en/superflow-pipeline/references/document-readiness.md +125 -0
  41. package/assets/skills-en/superflow-pipeline/references/document-templates.md +10 -0
  42. package/assets/skills-en/superflow-pipeline/references/feature-gated-workflow.md +7 -0
  43. package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +7 -0
  44. package/assets/skills-en/superflow-pipeline/references/managed-work.md +160 -10
  45. package/assets/skills-en/superflow-pipeline/references/quality-gate.md +12 -0
  46. package/assets/skills-en/superflow-pipeline/references/reviewer-checklist.md +7 -0
  47. package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +205 -9
  48. package/assets/skills-en/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
  49. package/assets/skills-en/superflow-requirement-review/SKILL.md +99 -0
  50. package/assets/skills-en/superflow-requirement-review/agents/openai.yaml +4 -0
  51. package/dist/app/cli.js +64 -9
  52. package/dist/app/cli.js.map +1 -1
  53. package/dist/app/commands/change-guard.js +19 -2
  54. package/dist/app/commands/change-guard.js.map +1 -1
  55. package/dist/app/commands/check.js +107 -45
  56. package/dist/app/commands/check.js.map +1 -1
  57. package/dist/app/commands/docs.js +7 -3
  58. package/dist/app/commands/docs.js.map +1 -1
  59. package/dist/app/commands/doctor.js +174 -198
  60. package/dist/app/commands/doctor.js.map +1 -1
  61. package/dist/app/commands/eval.js +64 -0
  62. package/dist/app/commands/eval.js.map +1 -0
  63. package/dist/app/commands/implement.js +7 -3
  64. package/dist/app/commands/implement.js.map +1 -1
  65. package/dist/app/commands/init.js +12 -24
  66. package/dist/app/commands/init.js.map +1 -1
  67. package/dist/app/commands/mcp.js +167 -0
  68. package/dist/app/commands/mcp.js.map +1 -0
  69. package/dist/app/commands/pipeline.js +652 -18
  70. package/dist/app/commands/pipeline.js.map +1 -1
  71. package/dist/app/commands/requirement-review.js +6 -0
  72. package/dist/app/commands/requirement-review.js.map +1 -0
  73. package/dist/app/commands/status.js +66 -3
  74. package/dist/app/commands/status.js.map +1 -1
  75. package/dist/app/commands/uninstall.js +3 -3
  76. package/dist/app/commands/uninstall.js.map +1 -1
  77. package/dist/app/commands/update.js +83 -9
  78. package/dist/app/commands/update.js.map +1 -1
  79. package/dist/domains/agent.js +3 -11
  80. package/dist/domains/agent.js.map +1 -1
  81. package/dist/domains/config/cli-help.js +54 -12
  82. package/dist/domains/config/cli-help.js.map +1 -1
  83. package/dist/domains/config/i18n.js +6 -8
  84. package/dist/domains/config/i18n.js.map +1 -1
  85. package/dist/domains/config/manifest.js +0 -3
  86. package/dist/domains/config/manifest.js.map +1 -1
  87. package/dist/domains/deps.js +68 -7
  88. package/dist/domains/deps.js.map +1 -1
  89. package/dist/domains/managed-work/completion-policy.js +174 -0
  90. package/dist/domains/managed-work/completion-policy.js.map +1 -0
  91. package/dist/domains/managed-work/context-manifest.js +208 -0
  92. package/dist/domains/managed-work/context-manifest.js.map +1 -0
  93. package/dist/domains/managed-work/contract.js +135 -4
  94. package/dist/domains/managed-work/contract.js.map +1 -1
  95. package/dist/domains/managed-work/control-signals.js +34 -0
  96. package/dist/domains/managed-work/control-signals.js.map +1 -0
  97. package/dist/domains/managed-work/control.js +628 -0
  98. package/dist/domains/managed-work/control.js.map +1 -0
  99. package/dist/domains/managed-work/evaluation.js +497 -0
  100. package/dist/domains/managed-work/evaluation.js.map +1 -0
  101. package/dist/domains/managed-work/evidence-path.js +14 -0
  102. package/dist/domains/managed-work/evidence-path.js.map +1 -0
  103. package/dist/domains/managed-work/execution-contract.js +167 -0
  104. package/dist/domains/managed-work/execution-contract.js.map +1 -0
  105. package/dist/domains/managed-work/executor-policy.js +65 -0
  106. package/dist/domains/managed-work/executor-policy.js.map +1 -0
  107. package/dist/domains/managed-work/failure.js +65 -0
  108. package/dist/domains/managed-work/failure.js.map +1 -0
  109. package/dist/domains/managed-work/host-review.js +77 -0
  110. package/dist/domains/managed-work/host-review.js.map +1 -0
  111. package/dist/domains/managed-work/human-messages.js +31 -0
  112. package/dist/domains/managed-work/human-messages.js.map +1 -0
  113. package/dist/domains/managed-work/input.js +29 -3
  114. package/dist/domains/managed-work/input.js.map +1 -1
  115. package/dist/domains/managed-work/journal.js +77 -3
  116. package/dist/domains/managed-work/journal.js.map +1 -1
  117. package/dist/domains/managed-work/lock.js +67 -1
  118. package/dist/domains/managed-work/lock.js.map +1 -1
  119. package/dist/domains/managed-work/notifications.js +0 -15
  120. package/dist/domains/managed-work/notifications.js.map +1 -1
  121. package/dist/domains/managed-work/pair-admission.js +108 -0
  122. package/dist/domains/managed-work/pair-admission.js.map +1 -0
  123. package/dist/domains/managed-work/prompts.js +257 -22
  124. package/dist/domains/managed-work/prompts.js.map +1 -1
  125. package/dist/domains/managed-work/redaction.js +90 -0
  126. package/dist/domains/managed-work/redaction.js.map +1 -0
  127. package/dist/domains/managed-work/review-convergence.js +41 -0
  128. package/dist/domains/managed-work/review-convergence.js.map +1 -0
  129. package/dist/domains/managed-work/review-facts.js +181 -0
  130. package/dist/domains/managed-work/review-facts.js.map +1 -0
  131. package/dist/domains/managed-work/rule-selection.js +84 -0
  132. package/dist/domains/managed-work/rule-selection.js.map +1 -0
  133. package/dist/domains/managed-work/runner.js +1967 -175
  134. package/dist/domains/managed-work/runner.js.map +1 -1
  135. package/dist/domains/managed-work/schemas.js +72 -2
  136. package/dist/domains/managed-work/schemas.js.map +1 -1
  137. package/dist/domains/managed-work/service.js +82 -13
  138. package/dist/domains/managed-work/service.js.map +1 -1
  139. package/dist/domains/managed-work/state.js +195 -5
  140. package/dist/domains/managed-work/state.js.map +1 -1
  141. package/dist/domains/managed-work/storage.js +66 -24
  142. package/dist/domains/managed-work/storage.js.map +1 -1
  143. package/dist/domains/managed-work/types.js +12 -9
  144. package/dist/domains/managed-work/types.js.map +1 -1
  145. package/dist/domains/managed-work/verification-categories.js +39 -0
  146. package/dist/domains/managed-work/verification-categories.js.map +1 -0
  147. package/dist/domains/managed-work/verification-policy.js +116 -0
  148. package/dist/domains/managed-work/verification-policy.js.map +1 -0
  149. package/dist/domains/managed-work/wait.js +21 -3
  150. package/dist/domains/managed-work/wait.js.map +1 -1
  151. package/dist/domains/managed-work/workspace-binding.js +133 -0
  152. package/dist/domains/managed-work/workspace-binding.js.map +1 -0
  153. package/dist/domains/sdd-readiness.js +44 -0
  154. package/dist/domains/sdd-readiness.js.map +1 -0
  155. package/dist/domains/skill/deploy.js +0 -6
  156. package/dist/domains/skill/deploy.js.map +1 -1
  157. package/dist/domains/skill/scripts.js +0 -8
  158. package/dist/domains/skill/scripts.js.map +1 -1
  159. package/dist/domains/state.js +1 -2
  160. package/dist/domains/state.js.map +1 -1
  161. package/dist/mcp/runtime-identity.js +21 -0
  162. package/dist/mcp/runtime-identity.js.map +1 -0
  163. package/dist/mcp/server.js +369 -0
  164. package/dist/mcp/server.js.map +1 -0
  165. package/dist/platform/agent-process.js +499 -38
  166. package/dist/platform/agent-process.js.map +1 -1
  167. package/dist/platform/executable.js +28 -0
  168. package/dist/platform/executable.js.map +1 -0
  169. package/dist/platform/paths.js +1 -17
  170. package/dist/platform/paths.js.map +1 -1
  171. package/dist/platform/process-tree.js +44 -0
  172. package/dist/platform/process-tree.js.map +1 -0
  173. package/dist/platform/process.js +1 -1
  174. package/dist/platform/process.js.map +1 -1
  175. package/package.json +17 -7
  176. package/scripts/check-design-principles.mjs +107 -0
  177. package/scripts/check-managed-work-design.mjs +507 -0
  178. package/scripts/clean-dist.js +9 -0
  179. package/scripts/ensure-bin-modes.js +11 -0
  180. package/scripts/preinstall.js +57 -0
  181. package/dist/cli/index.js +0 -193
  182. package/dist/cli/index.js.map +0 -1
  183. package/dist/commands/archive.js +0 -6
  184. package/dist/commands/archive.js.map +0 -1
  185. package/dist/commands/clarify.js +0 -6
  186. package/dist/commands/clarify.js.map +0 -1
  187. package/dist/commands/design.js +0 -28
  188. package/dist/commands/design.js.map +0 -1
  189. package/dist/commands/docs.js +0 -6
  190. package/dist/commands/docs.js.map +0 -1
  191. package/dist/commands/doctor.js +0 -494
  192. package/dist/commands/doctor.js.map +0 -1
  193. package/dist/commands/implement.js +0 -6
  194. package/dist/commands/implement.js.map +0 -1
  195. package/dist/commands/init.js +0 -520
  196. package/dist/commands/init.js.map +0 -1
  197. package/dist/commands/pipeline.js +0 -6
  198. package/dist/commands/pipeline.js.map +0 -1
  199. package/dist/commands/scan.js +0 -63
  200. package/dist/commands/scan.js.map +0 -1
  201. package/dist/commands/status.js +0 -173
  202. package/dist/commands/status.js.map +0 -1
  203. package/dist/commands/uninstall.js +0 -214
  204. package/dist/commands/uninstall.js.map +0 -1
  205. package/dist/commands/update.js +0 -193
  206. package/dist/commands/update.js.map +0 -1
  207. package/dist/commands/verify.js +0 -6
  208. package/dist/commands/verify.js.map +0 -1
  209. package/dist/core/assets.js +0 -27
  210. package/dist/core/assets.js.map +0 -1
  211. package/dist/core/cli-help.js +0 -98
  212. package/dist/core/cli-help.js.map +0 -1
  213. package/dist/core/context.js +0 -123
  214. package/dist/core/context.js.map +0 -1
  215. package/dist/core/dependencies.js +0 -146
  216. package/dist/core/dependencies.js.map +0 -1
  217. package/dist/core/detect.js +0 -64
  218. package/dist/core/detect.js.map +0 -1
  219. package/dist/core/i18n.js +0 -105
  220. package/dist/core/i18n.js.map +0 -1
  221. package/dist/core/integrity.js +0 -46
  222. package/dist/core/integrity.js.map +0 -1
  223. package/dist/core/manifest.js +0 -21
  224. package/dist/core/manifest.js.map +0 -1
  225. package/dist/core/prompts.js +0 -20
  226. package/dist/core/prompts.js.map +0 -1
  227. package/dist/core/registry.js +0 -134
  228. package/dist/core/registry.js.map +0 -1
  229. package/dist/core/rules.js +0 -17
  230. package/dist/core/rules.js.map +0 -1
  231. package/dist/core/scripts.js +0 -48
  232. package/dist/core/scripts.js.map +0 -1
  233. package/dist/core/skill-check.js +0 -32
  234. package/dist/core/skill-check.js.map +0 -1
  235. package/dist/core/skills.js +0 -62
  236. package/dist/core/skills.js.map +0 -1
  237. package/dist/core/state.js +0 -54
  238. package/dist/core/state.js.map +0 -1
  239. package/dist/utils/path.js +0 -11
  240. package/dist/utils/path.js.map +0 -1
  241. package/dist/utils/shell.js +0 -29
  242. package/dist/utils/shell.js.map +0 -1
@@ -18,7 +18,14 @@ const task = JSON.parse(fs.readFileSync(taskFile, 'utf8'));
18
18
  verifyContract(task);
19
19
  const runsDir = path.join(taskDir, 'runs');
20
20
  const runs = fs.existsSync(runsDir)
21
- ? fs.readdirSync(runsDir).filter((entry) => fs.statSync(path.join(runsDir, entry)).isDirectory())
21
+ ? fs.readdirSync(runsDir).filter((entry) => {
22
+ const candidate = path.join(runsDir, entry);
23
+ return (
24
+ entry.startsWith('run-') &&
25
+ fs.statSync(candidate).isDirectory() &&
26
+ fs.existsSync(path.join(candidate, 'run-state.json'))
27
+ );
28
+ })
22
29
  : [];
23
30
  if (runs.length !== 1) fail(`首版要求一个活动 run,实际 ${runs.length}`);
24
31
 
@@ -31,39 +38,68 @@ for (const file of [stateFile, journalFile, reportFile]) {
31
38
  }
32
39
  const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
33
40
 
34
- assertMax('reviewRound', state.reviewRound, 5);
35
- assertMax('executorInvocations', state.executorInvocations, 7);
36
- assertMax('totalAgentInvocations', state.totalAgentInvocations, 12);
37
- assertMax('reviewInvocations', state.reviewInvocations, 5);
41
+ if (task.budgets?.unlimitedAgentInvocations !== true) {
42
+ assertMax('reviewRound', state.reviewRound, 10);
43
+ assertMax(
44
+ 'executorInvocations',
45
+ state.executorInvocations,
46
+ 20 + (state.executorInvocationCredits ?? 0),
47
+ );
48
+ assertMax(
49
+ 'totalAgentInvocations',
50
+ state.totalAgentInvocations,
51
+ 30 + (state.totalAgentInvocationCredits ?? 0),
52
+ );
53
+ assertMax('reviewInvocations', state.reviewInvocations, 10);
54
+ }
38
55
  if (state.contractHash !== task.contractHash) fail('任务合同哈希与运行状态不一致');
39
56
  if (state.status !== task.status) fail('任务合同状态与运行状态不一致');
40
57
  if (state.executorInvocations > 0 && !state.executorSession?.sessionId) {
41
58
  fail('已有执行调用但缺少执行会话编号');
42
59
  }
43
- if (state.reviewInvocations > 0 && !state.supervisorSession?.sessionId) {
44
- fail('已有正式检查但缺少监督会话编号');
45
- }
46
60
  const events = verifyJournal(journalFile);
47
61
 
48
- if (state.status === 'awaiting_git_approval') {
62
+ const deliveryStatuses = new Set([
63
+ 'local_delivery_ready',
64
+ 'environment_validation_blocked',
65
+ 'release_ready',
66
+ ]);
67
+ if (deliveryStatuses.has(state.status)) {
49
68
  if (!state.lastExecutorResult || !fs.existsSync(state.lastExecutorResult)) {
50
- fail('等待 Git 批准状态缺少最终执行结果');
69
+ fail('交付状态缺少最终执行结果');
51
70
  }
52
71
  if (!state.lastReviewResult || !fs.existsSync(state.lastReviewResult)) {
53
- fail('等待 Git 批准状态缺少最终检查结果');
72
+ fail('交付状态缺少最终检查结果');
54
73
  }
55
74
  assertInsideRun(state.lastExecutorResult, runDir, '最终执行结果');
56
75
  assertInsideRun(state.lastReviewResult, runDir, '最终检查结果');
57
76
  const executor = JSON.parse(fs.readFileSync(state.lastExecutorResult, 'utf8'));
58
- verifyExecutorEvidence(task, executor);
59
77
  const review = JSON.parse(fs.readFileSync(state.lastReviewResult, 'utf8'));
78
+ const completion = verifyOpenSpecTasksComplete(task, executor);
79
+ verifyDeliveryStatus(state.status, completion);
80
+ if (
81
+ task.source === 'sdd' &&
82
+ !review.verificationCommands?.some(
83
+ (item) =>
84
+ item.exitCode === 0 &&
85
+ /openspec\s+instructions\s+apply/i.test(item.command),
86
+ )
87
+ ) {
88
+ fail('SDD 最终评审缺少 openspec instructions apply 命令证据');
89
+ }
60
90
  if (review.result !== 'pass' || review.findings?.some((item) => item.blocking)) {
61
- fail('等待 Git 批准状态与最终检查结论不一致');
91
+ fail('交付状态与最终检查结论不一致');
62
92
  }
93
+ verifyExecutorEvidence(task, executor, review, runDir);
63
94
  const readyEvents = events.filter((event) => event.eventType === 'run.delivery_ready');
95
+ const reopenedEvents = events.filter(
96
+ (event) => event.eventType === 'run.delivery_reopened',
97
+ );
64
98
  const expectedReadyEvents = beforeReadyEvent ? 0 : 1;
65
- if (readyEvents.length !== expectedReadyEvents) {
66
- fail(`交付就绪事件数量应为 ${expectedReadyEvents},实际 ${readyEvents.length}`);
99
+ if (readyEvents.length !== reopenedEvents.length + expectedReadyEvents) {
100
+ fail(
101
+ `交付就绪事件应比重新打开事件多 ${expectedReadyEvents} 条,实际就绪 ${readyEvents.length}、重新打开 ${reopenedEvents.length}`,
102
+ );
67
103
  }
68
104
  }
69
105
 
@@ -77,18 +113,35 @@ function assertMax(name, value, max) {
77
113
 
78
114
  function verifyContract(contract) {
79
115
  const budgets = contract.budgets ?? {};
80
- assertPositiveMax('maxReviewRounds', budgets.maxReviewRounds, 5, true);
81
- assertPositiveMax('maxExecutorInvocations', budgets.maxExecutorInvocations, 7, true);
82
- assertPositiveMax('maxTotalAgentInvocations', budgets.maxTotalAgentInvocations, 12, true);
116
+ if (
117
+ budgets.executorPhysicalStopAt !== undefined &&
118
+ (!Number.isInteger(budgets.executorPhysicalStopAt) ||
119
+ budgets.executorPhysicalStopAt <= 0)
120
+ ) {
121
+ fail('executorPhysicalStopAt 必须是正整数');
122
+ }
123
+ assertPositiveMax('maxReviewRounds', budgets.maxReviewRounds, 10, true);
124
+ assertPositiveMax('maxExecutorInvocations', budgets.maxExecutorInvocations, 20, true);
125
+ assertPositiveMax('maxTotalAgentInvocations', budgets.maxTotalAgentInvocations, 30, true);
83
126
  assertPositiveMax('maxActiveRunHours', budgets.maxActiveRunHours, 24, false);
84
127
  assertPositiveMax('maxSingleInvocationHours', budgets.maxSingleInvocationHours, 3, false);
85
128
  if (contract.supervisorAgent === contract.executorAgent) {
86
129
  fail('监督 Agent 和执行 Agent 不能相同');
87
130
  }
131
+ if (contract.supervisorExecution !== 'external_host') {
132
+ fail('监督执行模式只允许 external_host');
133
+ }
88
134
  const permissions = contract.permissions ?? {};
89
135
  for (const key of ['gitCommit', 'gitPush', 'productionWrites', 'bypassSandbox']) {
90
136
  if (permissions[key] !== false) fail(`不可覆盖的权限边界被放宽: ${key}`);
91
137
  }
138
+ const disclosure = permissions.externalModelDataDisclosure;
139
+ if (!disclosure?.approved || !disclosure.approvedBy || !disclosure.approvedAt) {
140
+ fail('缺少任务级外部研发 Agent 源码披露授权');
141
+ }
142
+ if (!Array.isArray(disclosure.scope) || disclosure.scope.length === 0) {
143
+ fail('外部研发 Agent 源码披露授权缺少仓库作用域');
144
+ }
92
145
  if (contract.taskPrompt) {
93
146
  const expectedSnapshot = path.join(
94
147
  path.resolve(contract.projectRoot),
@@ -117,9 +170,16 @@ function verifyContract(contract) {
117
170
  ...(contract.language ? { language: contract.language } : {}),
118
171
  objective: contract.objective,
119
172
  doneCriteria: contract.doneCriteria,
173
+ ...(contract.mandatoryEngineeringRules?.length
174
+ ? { mandatoryEngineeringRules: contract.mandatoryEngineeringRules }
175
+ : {}),
120
176
  taskPrompt: contract.taskPrompt,
121
177
  supervisorAgent: contract.supervisorAgent,
122
178
  executorAgent: contract.executorAgent,
179
+ ...(contract.supervisorExecution
180
+ ? { supervisorExecution: contract.supervisorExecution }
181
+ : {}),
182
+ ...(contract.executionMode ? { executionMode: contract.executionMode } : {}),
123
183
  permissions: contract.permissions,
124
184
  budgets: contract.budgets,
125
185
  };
@@ -133,20 +193,220 @@ function assertPositiveMax(name, value, max, integer) {
133
193
  }
134
194
  }
135
195
 
136
- function verifyExecutorEvidence(task, result) {
137
- if (result.status !== 'ready_for_review') fail('最终执行结果不是可检查状态');
138
- if (!Array.isArray(result.commands) || result.commands.some((item) => item.exitCode !== 0)) {
139
- fail('最终执行结果缺少成功命令证据或仍含失败命令');
196
+ function verifyOpenSpecTasksComplete(task, executor) {
197
+ if (task.source !== 'sdd' || !task.taskPrompt?.originalPath) {
198
+ return { localPending: [], environmentPending: [], releasePending: [] };
199
+ }
200
+ let current = path.dirname(path.resolve(task.taskPrompt.originalPath));
201
+ const projectRoot = path.resolve(task.projectRoot);
202
+ while (current.startsWith(projectRoot)) {
203
+ const tasksFile = path.join(current, 'tasks.md');
204
+ if (fs.existsSync(tasksFile)) {
205
+ const tasks = fs.readFileSync(tasksFile, 'utf8')
206
+ .split(/\r?\n/)
207
+ .filter((line) => /^\s*- \[[ xX]\]/.test(line))
208
+ .map((line, index) => parseCompletionTask(line, index));
209
+ const localPending = tasks.filter(
210
+ (item) => !item.completed && item.category === 'local_required',
211
+ );
212
+ const environmentPending = tasks.filter(
213
+ (item) => !item.completed && item.category === 'environment_required',
214
+ );
215
+ const releasePending = tasks.filter(
216
+ (item) => !item.completed && item.category === 'release_required',
217
+ );
218
+ if (localPending.length > 0) {
219
+ fail(`OpenSpec tasks.md 仍有 ${localPending.length} 个本地可执行任务未完成`);
220
+ }
221
+ const evidence = new Map(
222
+ (executor.taskEvidence ?? []).map((item) => [item.taskId, item]),
223
+ );
224
+ const successfulCommands = (executor.commands ?? []).filter(
225
+ isAcceptedEvidenceCommand,
226
+ );
227
+ const derivedEvidencePaths = [
228
+ ...(executor.evidence ?? []),
229
+ tasksFile,
230
+ path.join(current, 'test-report.md'),
231
+ ].filter((item) => taskEvidencePathExists(task, item));
232
+ for (const item of tasks.filter(
233
+ (taskItem) =>
234
+ taskItem.completed && taskItem.category === 'local_required',
235
+ )) {
236
+ const proof = evidence.get(item.taskId);
237
+ if (successfulCommands.length > 0 && derivedEvidencePaths.length > 0) {
238
+ continue;
239
+ }
240
+ if (!proof) {
241
+ fail(`已勾选本地任务 ${item.taskId} 缺少可推导的真实交付证据`);
242
+ }
243
+ if (
244
+ proof.owner !== 'executor' ||
245
+ !proof.evidencePaths?.length ||
246
+ !proof.verificationCommands?.length ||
247
+ !proof.evidencePaths.every((item) => taskEvidencePathExists(task, item)) ||
248
+ !proof.verificationCommands.every((command) =>
249
+ executor.commands?.some(
250
+ (item) =>
251
+ isAcceptedEvidenceCommand(item) &&
252
+ verificationCommandCovered(command, item.command),
253
+ ),
254
+ ) ||
255
+ !proof.changedFiles.every((file) => executor.changedFiles?.includes(file))
256
+ ) {
257
+ fail(`已勾选本地任务 ${item.taskId} 缺少结构化交付证据`);
258
+ }
259
+ }
260
+ if (
261
+ environmentPending.length + releasePending.length > 0 &&
262
+ (!Array.isArray(executor.releasePrerequisites) ||
263
+ executor.releasePrerequisites.length === 0)
264
+ ) {
265
+ fail(
266
+ `OpenSpec tasks.md 有 ${environmentPending.length + releasePending.length} 个环境/发布前置,但执行结果未记录 releasePrerequisites`,
267
+ );
268
+ }
269
+ return { localPending, environmentPending, releasePending };
270
+ }
271
+ const parent = path.dirname(current);
272
+ if (parent === current) break;
273
+ current = parent;
274
+ }
275
+ fail('SDD 托管任务未找到 OpenSpec tasks.md');
276
+ }
277
+
278
+ function isAcceptedEvidenceCommand(command) {
279
+ if (command.exitCode === 0) return true;
280
+ if (command.exitCode !== 1 && command.exitCode !== 2) return false;
281
+ return (
282
+ /^(?:\s*)(?:grep|rg|pgrep|lsof|ls|ss|netstat|git\s+(?:status|diff)|find)\b/i.test(
283
+ command.command,
284
+ ) &&
285
+ /(?:none|empty|no\s+(?:match|orphan|process|change|listener|such\s+file)|not found|does not exist|无匹配|无残留|未发现|没有|不存在|空)/i.test(
286
+ command.result,
287
+ )
288
+ );
289
+ }
290
+
291
+ function verificationCommandCovered(expected, actual) {
292
+ if (expected === actual) return true;
293
+ const expectedTests = mavenTestSelectors(expected);
294
+ const actualTests = mavenTestSelectors(actual);
295
+ return (
296
+ expectedTests.length > 0 &&
297
+ actualTests.length > 0 &&
298
+ expectedTests.every((test) => actualTests.includes(test))
299
+ );
300
+ }
301
+
302
+ function mavenTestSelectors(command) {
303
+ if (!/(?:^|\s)(?:\.\/)?mvn(?:\s|$)/.test(command)) return [];
304
+ const match = command.match(/-Dtest=([^\s]+)/);
305
+ return match?.[1].split(',').filter(Boolean) ?? [];
306
+ }
307
+
308
+ function taskEvidencePathExists(task, evidencePath) {
309
+ if (path.isAbsolute(evidencePath)) return fs.existsSync(evidencePath);
310
+ const bases = [
311
+ ...(findTaskDirectory(task) ? [findTaskDirectory(task)] : []),
312
+ ...(task.taskPrompt?.originalPath
313
+ ? [path.dirname(task.taskPrompt.originalPath)]
314
+ : []),
315
+ task.projectRoot,
316
+ ...(task.relatedProjectRoots ?? []),
317
+ ];
318
+ return bases.some((base) => fs.existsSync(path.resolve(base, evidencePath)));
319
+ }
320
+
321
+ function findTaskDirectory(task) {
322
+ if (!task.taskPrompt?.originalPath) return null;
323
+ let current = path.dirname(path.resolve(task.taskPrompt.originalPath));
324
+ const roots = [task.projectRoot, ...(task.relatedProjectRoots ?? [])].map(
325
+ (root) => path.resolve(root),
326
+ );
327
+ while (roots.some((root) => current.startsWith(root))) {
328
+ if (fs.existsSync(path.join(current, 'tasks.md'))) return current;
329
+ const parent = path.dirname(current);
330
+ if (parent === current) break;
331
+ current = parent;
332
+ }
333
+ return null;
334
+ }
335
+
336
+ function parseCompletionTask(line, index) {
337
+ const completed = /^\s*- \[[xX]\]/.test(line);
338
+ const text = line.replace(/^\s*- \[[ xX]\]\s*/, '').trim();
339
+ const match = text.match(/^([A-Za-z]*\d+(?:\.\d+)*|[A-Z][A-Z0-9_-]+)\b/);
340
+ const category = /\[environment_required\]/i.test(text)
341
+ ? 'environment_required'
342
+ : /\[release_required\]/i.test(text)
343
+ ? 'release_required'
344
+ : 'local_required';
345
+ return { taskId: match?.[1] ?? `task-${index + 1}`, completed, category };
346
+ }
347
+
348
+ function verifyDeliveryStatus(status, completion) {
349
+ if (status === 'environment_validation_blocked' && completion.environmentPending.length === 0) {
350
+ fail('environment_validation_blocked 状态没有待完成环境验收');
351
+ }
352
+ if (status === 'local_delivery_ready' && completion.environmentPending.length > 0) {
353
+ fail('local_delivery_ready 状态仍有环境验收未完成');
140
354
  }
141
- if (Array.isArray(result.blockers) && result.blockers.length > 0) {
142
- fail('最终执行结果仍含阻塞项');
355
+ if (
356
+ status === 'release_ready' &&
357
+ completion.environmentPending.length + completion.releasePending.length > 0
358
+ ) {
359
+ fail('release_ready 状态仍有环境或发布任务未完成');
360
+ }
361
+ }
362
+
363
+ function verifyExecutorEvidence(task, result, review, runDir) {
364
+ if (!['ready_for_review', 'blocked'].includes(result.status)) {
365
+ fail('最终执行结果既不可检查也不是诚实阻塞状态');
366
+ }
367
+ if (!Array.isArray(result.commands)) fail('最终执行结果缺少命令证据');
368
+ if (result.status === 'blocked' && review.result !== 'pass') {
369
+ fail('执行者阻塞结果未经监督者判定为非阻断发布前置');
370
+ }
371
+ if (result.status === 'ready_for_review' && result.blockers?.length > 0) {
372
+ fail('可检查执行结果仍含阻塞项');
143
373
  }
144
374
  if (task.profile === 'engineering' || task.profile === 'sdd') {
145
- const categories = new Set(result.commands.flatMap((item) => verificationCategories(item.command)));
146
- if (categories.size < 2) fail('工程任务缺少至少两类成功验证证据');
375
+ const successful = loadExecutorCommands(runDir)
376
+ .filter((item) => item.exitCode === 0);
377
+ const categories = new Set(
378
+ successful.flatMap((item) => verificationCategories(item.command)),
379
+ );
380
+ const required = requiredVerificationCategories(projectRoot);
381
+ if (categories.size < required) fail('工程任务缺少至少两类成功验证证据');
147
382
  }
148
383
  }
149
384
 
385
+ function requiredVerificationCategories(root) {
386
+ const packageFile = path.join(root, 'package.json');
387
+ if (!fs.existsSync(packageFile)) return 2;
388
+ try {
389
+ const scripts = JSON.parse(fs.readFileSync(packageFile, 'utf8')).scripts ?? {};
390
+ const hasTest = typeof scripts.test === 'string';
391
+ const hasRunnableTarget = ['build', 'start', 'dev', 'serve']
392
+ .some((name) => typeof scripts[name] === 'string');
393
+ return hasTest && !hasRunnableTarget ? 1 : 2;
394
+ } catch {
395
+ return 2;
396
+ }
397
+ }
398
+
399
+ function loadExecutorCommands(runDir) {
400
+ return fs.readdirSync(runDir)
401
+ .filter((entry) => /^executor-result-\d+\.json$/.test(entry))
402
+ .flatMap((entry) => {
403
+ const result = JSON.parse(
404
+ fs.readFileSync(path.join(runDir, entry), 'utf8'),
405
+ );
406
+ return Array.isArray(result.commands) ? result.commands : [];
407
+ });
408
+ }
409
+
150
410
  function verificationCategories(command = '') {
151
411
  const value = command.toLowerCase();
152
412
  const categories = [];
@@ -155,6 +415,7 @@ function verificationCategories(command = '') {
155
415
  || /mvn(?:\s+[^\s]+)*\s+test/.test(value)
156
416
  || /gradle\w*\s+test/.test(value)) categories.push('test');
157
417
  if (/(?:npm|pnpm|yarn)\s+(?:run\s+)?build/.test(value)
418
+ || /(?:^|[\s/])(?:vite|vue-cli-service)\s+build(?:\s|$)/.test(value)
158
419
  || /(^|\s)(tsc|make|cmake|compile)(\s|$)/.test(value)
159
420
  || /mvn(?:\s+[^\s]+)*\s+(?:compile|package|verify)/.test(value)
160
421
  || /gradle\w*\s+(?:build|assemble)/.test(value)
@@ -6,98 +6,12 @@ set -u
6
6
  ROLE="${SUPERFLOW_MANAGED_ROLE:-}"
7
7
  [ -n "$ROLE" ] || exit 0
8
8
 
9
- block() {
10
- echo "[Superflow 托管任务门禁] $1" >&2
11
- exit 2
12
- }
13
-
14
- case "$ROLE" in
15
- supervisor|executor) ;;
16
- *) block "检测到非法托管角色,按失败关闭。" ;;
17
- esac
18
-
19
- INPUT=""
20
- if [ ! -t 0 ]; then
21
- INPUT=$(cat 2>/dev/null || true)
22
- fi
23
- [ -n "$INPUT" ] || block "Hook 没有收到工具调用数据,按失败关闭。"
24
-
25
- VALID_JSON=$(printf '%s' "$INPUT" | python3 -c "
26
- import json, sys
27
- try:
28
- value = json.load(sys.stdin)
29
- print('yes' if isinstance(value, dict) else 'no')
30
- except Exception:
31
- print('no')
32
- " 2>/dev/null)
33
- [ "$VALID_JSON" = "yes" ] || block "Hook 输入不是有效 JSON,按失败关闭。"
34
-
35
- read_field() {
36
- local field="$1"
37
- printf '%s' "$INPUT" | python3 -c "
38
- import json, sys
39
- try:
40
- data = json.load(sys.stdin)
41
- except Exception:
42
- print('')
43
- raise SystemExit
44
- tool_input = data.get('tool_input', {})
45
- print(tool_input.get('$field') or '')
46
- " 2>/dev/null
47
- }
9
+ SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
10
+ GUARD="$SCRIPT_DIR/superflow-managed-command-guard.py"
48
11
 
49
- TOOL_NAME=$(printf '%s' "$INPUT" | python3 -c "
50
- import json, sys
51
- try:
52
- data = json.load(sys.stdin)
53
- except Exception:
54
- print('')
55
- raise SystemExit
56
- print(data.get('tool_name') or data.get('tool') or '')
57
- " 2>/dev/null)
58
- FILE_PATH=$(read_field file_path)
59
- [ -n "$FILE_PATH" ] || FILE_PATH=$(read_field path)
60
- [ -n "$FILE_PATH" ] || FILE_PATH=$(read_field file)
61
- COMMAND=$(read_field command)
62
- [ -n "$COMMAND" ] || COMMAND=$(read_field cmd)
63
-
64
- normalize() {
65
- printf '%s' "$1" | sed 's|\\|/|g'
66
- }
67
-
68
- FILE_PATH=$(normalize "$FILE_PATH")
69
- COMMAND=$(printf '%s' "$COMMAND" | tr '\n' ' ')
70
-
71
- if [ "$ROLE" = "supervisor" ]; then
72
- case "$TOOL_NAME" in
73
- Edit|Write|MultiEdit|NotebookEdit|apply_patch)
74
- block "监督角色只允许检查,禁止修改目标文件。"
75
- ;;
76
- esac
77
- if [ -n "$COMMAND" ] && printf '%s' "$COMMAND" | grep -Eiq \
78
- '(^|[[:space:]])(rm|mv|cp|mkdir|touch|chmod|chown)([[:space:]]|$)|git[[:space:]]+(add|commit|push|tag|reset|checkout|restore)|npm[[:space:]]+publish|apply_patch|sed[[:space:]]+-i|(^|[^>])>[>|[:space:]]|tee[[:space:]]'; then
79
- block "监督角色的 Bash 仅允许只读检查,检测到可能写入的命令。"
80
- fi
81
- fi
82
-
83
- if [ "$ROLE" = "executor" ]; then
84
- case "$TOOL_NAME" in
85
- Edit|Write|MultiEdit|NotebookEdit|apply_patch)
86
- [ -n "$FILE_PATH" ] || block "写入工具缺少目标路径,按失败关闭。"
87
- ;;
88
- esac
89
- if [ -n "$FILE_PATH" ] && printf '%s' "$FILE_PATH" | grep -Eq \
90
- '(^|/)\.superflow/tasks/'; then
91
- block "执行角色禁止修改托管状态、事件、Prompt 和过程报告。"
92
- fi
93
- if [ -n "$COMMAND" ] && printf '%s' "$COMMAND" | grep -Eq \
94
- '\.superflow/tasks/'; then
95
- block "执行角色的 Bash 禁止直接访问托管运行目录,请使用提示词中提供的只读证据。"
96
- fi
97
- if [ -n "$COMMAND" ] && printf '%s' "$COMMAND" | grep -Eiq \
98
- 'git[[:space:]]+(add|commit|push|tag)|npm[[:space:]]+publish|dangerously-(bypass|skip)|kubectl[[:space:]]+(apply|delete)|helm[[:space:]]+(install|upgrade)|DROP[[:space:]]+(DATABASE|TABLE)|TRUNCATE[[:space:]]+TABLE'; then
99
- block "执行角色禁止自动 Git 提交/推送、发布、部署、绕过沙箱或高破坏数据库操作。"
100
- fi
12
+ if [ ! -f "$GUARD" ]; then
13
+ echo "[Superflow 托管任务门禁] 缺少命令门禁脚本,按失败关闭:$GUARD" >&2
14
+ exit 2
101
15
  fi
102
16
 
103
- exit 0
17
+ exec python3 "$GUARD"
@@ -23,6 +23,19 @@ any full SDD document or implementation prompt is generated.
23
23
  - Do not freeze a feature from product text or understand-anything output alone
24
24
  when code impact is relevant. Current source, Mapper/XML, database samples,
25
25
  API contracts, or real consumer entry points must confirm the design.
26
+ - 对依赖存量系统行为、表关系或调用链的功能,冻结前必须生成
27
+ `source-code-audit.md` 的“源码事实冻结卡”:
28
+ `业务结论 | understand定位 | 数据模型 | 所有写入方 | 真实用户入口 |
29
+ 当前调用方 | 遗留冲突 | DB是否必查 | 结论等级 | owner决策`。
30
+ understand-anything 只允许作为定位器;每项结论必须回到源码、Mapper/SQL、
31
+ 当前调用方或只读 DB 证据。
32
+ - 证据必须分类为 `current`、`legacy`、`unmounted`、`data-model-only`、
33
+ `owner-confirmed` 或 `blocked`。看到 `List`、`orderIds`、`batchInsert`、
34
+ 一对多关系表,而真实 UI/小程序/H5 只提交单笔时,必须写“冲突审计”,禁止
35
+ 直接推导为现行批量业务。
36
+ - 向用户提问前,必须在“提问资格门禁”记录源码、Mapper/SQL、前端/小程序/H5
37
+ 调用方、sibling repo 检索以及 DB 核查或跳过原因。能从仓库或只读数据查明的
38
+ 事实不得转嫁给用户;只有 owner 意图决策才允许提问。
26
39
  - When the feature depends on existing code relationships, field ownership,
27
40
  table joins, data permissions, status transitions, or upstream/downstream
28
41
  calls, verify the relationship before freezing with current evidence:
@@ -81,6 +94,7 @@ Create or update these files in the active OpenSpec change directory:
81
94
  - `ui-contract.md`: page fields, controls, buttons, list columns, dropdowns, API/DTO/DB mapping.
82
95
  - `gap-analysis.md`: UI/document/source gaps and over-design risks.
83
96
  - `spec-freeze-review.md`: freeze card for the current feature.
97
+ - `source-code-audit.md`: 源码事实冻结卡、证据分类、冲突审计和提问资格门禁。
84
98
  - Platform impact evidence in one of the above documents: discovery method,
85
99
  understand-anything index status when available, analyzed platform scope,
86
100
  impacted modules/interfaces/tables, regression risks, source/DB/API
@@ -21,6 +21,12 @@ Superpowers own source-level HOW without weakening OpenSpec/SDD contracts.
21
21
 
22
22
  ## Preconditions
23
23
 
24
+ - When the change modifies a database schema, index, constraint, seed data, or
25
+ historical data, `release-sql.md` must already contain the complete executable
26
+ release SQL, an exact target `.sql` path, `copy_policy: verbatim`, and a valid
27
+ `sql_sha256`. Source-level HOW design must not defer SQL design to an
28
+ implementation prompt.
29
+
24
30
  - `proposal.md`, `api.md`, `design.md`, `tasks.md`, `tests.md`,
25
31
  `traceability-matrix.md`, `sdd-quality-gate.md`, and `test-report.md` exist.
26
32
  - `.sdd/state.yaml` exists and is in `phase: design`. If still `docs`, run:
@@ -38,15 +44,27 @@ Superpowers own source-level HOW without weakening OpenSpec/SDD contracts.
38
44
 
39
45
  1. Read `.sdd/handoff/sdd-context.md` plus original `api.md`, `design.md`,
40
46
  `tasks.md`, and `tests.md`.
41
- 2. If design discussion is still evolving, maintain
47
+ 2. If the change has any schema, index, constraint, seed-data, or historical
48
+ migration impact, verify `release-sql.md` before source-level HOW design:
49
+ - front matter sets `database_change: true`, an exact `target_sql_path`,
50
+ `copy_policy: verbatim`, and the normalized SQL payload `sql_sha256`;
51
+ - exactly one `sql` block contains the complete executable release script,
52
+ including prechecks, DDL/DML, and post-verification required by the
53
+ contract;
54
+ - `design.md`, `tasks.md`, and `sdd-quality-gate.md` reference the artifact
55
+ and prohibit implementation agents from editing its SQL;
56
+ - unresolved SQL types, defaults, indexes, migration rules, or target paths
57
+ return to `$superflow-docs`. They must not be deferred to
58
+ `$superflow-implement`.
59
+ 3. If design discussion is still evolving, maintain
42
60
  `.sdd/handoff/brainstorm-summary.md` with confirmed, candidate, pending, and
43
61
  rejected items. Do not create final technical design from memory only.
44
- 3. Use Superpowers `brainstorming` or equivalent deep design reasoning to
62
+ 4. Use Superpowers `brainstorming` or equivalent deep design reasoning to
45
63
  decide source-level HOW.
46
- 4. Write:
64
+ 5. Write:
47
65
  `docs/superpowers/specs/YYYY-MM-DD-<change-id>-technical-design.md`
48
66
  using `../superflow-pipeline/references/superpower-technical-design-template.md`.
49
- 5. Complete `复杂度减法评审` before expanding source-level HOW. For every new
67
+ 6. Complete `复杂度减法评审` before expanding source-level HOW. For every new
50
68
  table, field, API, Service/component, abstraction, cache, async/MQ/event
51
69
  flow, scheduled job, or compatibility layer, prove existing reuse options,
52
70
  necessity, and why the simplest direct implementation is insufficient.
@@ -55,7 +73,7 @@ Superpowers own source-level HOW without weakening OpenSpec/SDD contracts.
55
73
  duplicate DTO/API layers, derivable persistence, premature caches, and
56
74
  async compensation without measured need. Record the retained and removed
57
75
  counts; unresolved simpler alternatives keep the design blocked.
58
- 6. For cross-repository, service-to-service, SDK, MQ, scheduler, device,
76
+ 7. For cross-repository, service-to-service, SDK, MQ, scheduler, device,
59
77
  callback, third-party, mini-program, gateway, or adapter changes, include
60
78
  `Architecture Boundary And Call Direction`. Prove owner module, call
61
79
  direction, existing entry/exit points, proposed entry/exit points, evidence
@@ -63,7 +81,7 @@ Superpowers own source-level HOW without weakening OpenSpec/SDD contracts.
63
81
  adapter/protocol translator/device gateway into a business-entry
64
82
  orchestrator, stop and return to `$superflow-docs` unless the OpenSpec/SDD
65
83
  contract explicitly grants that ownership with approval evidence.
66
- 7. For third-party platforms/tools, SDKs, MQ/Kafka, callbacks, payment
84
+ 8. For third-party platforms/tools, SDKs, MQ/Kafka, callbacks, payment
67
85
  gateways, cloud services, or other external integrations, include
68
86
  `External Integration Configuration And Deployment Contract`. Inventory
69
87
  every endpoint, app/tenant/project ID, Topic, Tag, Consumer Group,
@@ -75,7 +93,7 @@ Superpowers own source-level HOW without weakening OpenSpec/SDD contracts.
75
93
  auto-creation or pre-existing resources must not be treated as proof that
76
94
  production will provision them. Missing production provisioning evidence
77
95
  blocks design completion.
78
- 8. For concurrent requests, batch issue/activation/renewal, duplicate callbacks,
96
+ 9. For concurrent requests, batch issue/activation/renewal, duplicate callbacks,
79
97
  duplicate consumption, or repeated external delivery, include
80
98
  `Concurrency And Idempotency Ownership`. Use a stable business idempotency
81
99
  key and an application-layer atomic claim in a short transaction; persist
@@ -85,17 +103,24 @@ Superpowers own source-level HOW without weakening OpenSpec/SDD contracts.
85
103
  optional fallback with proven natural uniqueness, historical cleanup,
86
104
  NULL/soft-delete semantics, and a conflict contract. Check-then-insert,
87
105
  process-local locks, and random IDs alone are blocked.
88
- 9. For field/status/enum/sync changes, include `Field And Status Reverse
106
+ When the selected design is version-based optimistic locking, record the
107
+ exact database atomic compare-and-set statement and affected-row decision:
108
+ the update predicate contains both the stable row identity and expected
109
+ version; a zero-row result distinguishes missing data from version conflict.
110
+ The TDD plan references the `tests.md` concurrent stale-version case proving
111
+ one winner, one conflict, and one increment. Do not defer this decision to
112
+ the managed Executor prompt.
113
+ 10. For field/status/enum/sync changes, include `Field And Status Reverse
89
114
  Impact` and prove writers, readers, filters, derived sync paths, consumers,
90
115
  and tests. Direct setter-only design is blocked.
91
- 10. For third-party, SDK, BEM/parking, payment/refund, financial display,
116
+ 11. For third-party, SDK, BEM/parking, payment/refund, financial display,
92
117
  source/origin, status sync, or external dictionary fields, include
93
118
  `External Enum Binding`. Prove local source fields/enums, external fields,
94
119
  external enum values, display/business/financial meaning, source evidence,
95
120
  owner confirmation, unresolved handling, and test evidence. A request
96
121
  succeeding or a field being non-null is not enough; unconfirmed values are
97
122
  blockers and must be returned to `$superflow-docs` or the user.
98
- 11. For amount, fee, discount, deduction, refund, sharing, payment, invoice,
123
+ 12. For amount, fee, discount, deduction, refund, sharing, payment, invoice,
99
124
  balance, electricity fee, service fee, package settlement, proration,
100
125
  allocation, reconciliation, or financial display changes,
101
126
  include `Money Precision Boundary`. Prove calculation-state fields,
@@ -114,12 +139,12 @@ Impact` and prove writers, readers, filters, derived sync paths, consumers,
114
139
  level and policy source, deterministic residual strategy with stable
115
140
  tie-breaker, and positive/zero/negative evidence. For FX changes, freeze the
116
141
  directional rate metadata and one canonical conversion path.
117
- 12. Record state:
142
+ 13. Record state:
118
143
  ```bash
119
144
  ../superflow-pipeline/scripts/superflow-state.sh set <change-dir> technical_design docs/superpowers/specs/YYYY-MM-DD-<change-id>-technical-design.md
120
145
  ../superflow-pipeline/scripts/superflow-state.sh set <change-dir> design_doc design.md
121
146
  ```
122
- 13. Update `design.md` with `Superpowers Technical Design Handoff`, update
147
+ 14. Update `design.md` with `Superpowers Technical Design Handoff`, update
123
148
  `sdd-quality-gate.md` with the technical design path and hash, then run
124
149
  `../superflow-pipeline/scripts/superflow-handoff.sh <change-dir> --refresh`.
125
150
  If the guard reports a stale or missing hash, record the printed hash in
@@ -127,7 +152,7 @@ Impact` and prove writers, readers, filters, derived sync paths, consumers,
127
152
  `--refresh` again. Do not set `design_doc` to the Superpowers document;
128
153
  `design_doc` is the OpenSpec/SDD contract design, while `technical_design`
129
154
  is the Superpowers HOW document.
130
- 14. Run:
155
+ 15. Run:
131
156
 
132
157
  ```bash
133
158
  ../superflow-pipeline/scripts/superflow-yaml-validate.sh <change-dir>