@chenmk/superflow 0.2.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +24 -2
  2. package/README.zh-CN.md +17 -1
  3. package/assets/manifest.json +7 -4
  4. package/assets/prompts/superflow-clarify.md +6 -0
  5. package/assets/prompts/superflow-design.md +3 -0
  6. package/assets/prompts/superflow-docs.md +4 -1
  7. package/assets/prompts/superflow-pipeline.md +8 -0
  8. package/assets/scripts/superflow-archive-command-hook.sh +80 -0
  9. package/assets/scripts/superflow-delivery-check.sh +23 -4
  10. package/assets/scripts/superflow-dependency-update-hook.sh +1 -1
  11. package/assets/scripts/superflow-managed-work-check.mjs +194 -0
  12. package/assets/scripts/superflow-managed-work-guard.sh +103 -0
  13. package/assets/skills/openspec-propose/SKILL.md +11 -0
  14. package/assets/skills/superflow-archive/SKILL.md +5 -0
  15. package/assets/skills/superflow-clarify/SKILL.md +26 -7
  16. package/assets/skills/superflow-design/SKILL.md +38 -7
  17. package/assets/skills/superflow-docs/SKILL.md +27 -0
  18. package/assets/skills/superflow-pipeline/SKILL.md +75 -0
  19. package/assets/skills/superflow-pipeline/references/api-design-template.md +11 -0
  20. package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +27 -0
  21. package/assets/skills/superflow-pipeline/references/document-templates.md +8 -0
  22. package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +29 -1
  23. package/assets/skills/superflow-pipeline/references/managed-work.md +58 -0
  24. package/assets/skills/superflow-pipeline/references/quality-gate.md +3 -0
  25. package/assets/skills/superflow-pipeline/references/superpower-technical-design-template.md +51 -0
  26. package/assets/skills/superflow-pipeline/references/test-execution-template.md +10 -0
  27. package/assets/skills/superflow-pipeline/scripts/superflow-archive.sh +1 -1
  28. package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +215 -2
  29. package/assets/skills/superflow-verify/SKILL.md +7 -0
  30. package/assets/skills-en/openspec-propose/SKILL.md +14 -0
  31. package/assets/skills-en/superflow-archive/SKILL.md +6 -0
  32. package/assets/skills-en/superflow-clarify/SKILL.md +26 -7
  33. package/assets/skills-en/superflow-design/SKILL.md +38 -6
  34. package/assets/skills-en/superflow-docs/SKILL.md +27 -0
  35. package/assets/skills-en/superflow-pipeline/SKILL.md +76 -0
  36. package/assets/skills-en/superflow-pipeline/references/api-design-template.md +13 -0
  37. package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +16 -0
  38. package/assets/skills-en/superflow-pipeline/references/document-templates.md +5 -0
  39. package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +17 -0
  40. package/assets/skills-en/superflow-pipeline/references/managed-work.md +34 -0
  41. package/assets/skills-en/superflow-pipeline/references/quality-gate.md +25 -0
  42. package/assets/skills-en/superflow-pipeline/references/superpower-technical-design-template.md +57 -0
  43. package/assets/skills-en/superflow-pipeline/references/test-execution-template.md +12 -0
  44. package/assets/skills-en/superflow-pipeline/scripts/superflow-archive.sh +1 -1
  45. package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +214 -2
  46. package/assets/skills-en/superflow-verify/SKILL.md +8 -0
  47. package/dist/app/cli.js +120 -102
  48. package/dist/app/cli.js.map +1 -1
  49. package/dist/app/commands/archive.js +7 -3
  50. package/dist/app/commands/archive.js.map +1 -1
  51. package/dist/app/commands/change-guard.js +27 -0
  52. package/dist/app/commands/change-guard.js.map +1 -0
  53. package/dist/app/commands/design.js +2 -20
  54. package/dist/app/commands/design.js.map +1 -1
  55. package/dist/app/commands/init.js +7 -7
  56. package/dist/app/commands/init.js.map +1 -1
  57. package/dist/app/commands/managed-service.js +5 -0
  58. package/dist/app/commands/managed-service.js.map +1 -0
  59. package/dist/app/commands/pipeline.js +123 -3
  60. package/dist/app/commands/pipeline.js.map +1 -1
  61. package/dist/app/commands/status.js +199 -77
  62. package/dist/app/commands/status.js.map +1 -1
  63. package/dist/app/commands/uninstall.js +5 -2
  64. package/dist/app/commands/uninstall.js.map +1 -1
  65. package/dist/app/commands/update.js +33 -11
  66. package/dist/app/commands/update.js.map +1 -1
  67. package/dist/app/commands/verify.js +7 -3
  68. package/dist/app/commands/verify.js.map +1 -1
  69. package/dist/domains/config/cli-help.js +103 -73
  70. package/dist/domains/config/cli-help.js.map +1 -1
  71. package/dist/domains/config/context.js +1 -5
  72. package/dist/domains/config/context.js.map +1 -1
  73. package/dist/domains/deps.js +3 -6
  74. package/dist/domains/deps.js.map +1 -1
  75. package/dist/domains/hook.js +71 -37
  76. package/dist/domains/hook.js.map +1 -1
  77. package/dist/domains/managed-work/contract.js +218 -0
  78. package/dist/domains/managed-work/contract.js.map +1 -0
  79. package/dist/domains/managed-work/i18n.js +12 -0
  80. package/dist/domains/managed-work/i18n.js.map +1 -0
  81. package/dist/domains/managed-work/input.js +111 -0
  82. package/dist/domains/managed-work/input.js.map +1 -0
  83. package/dist/domains/managed-work/journal.js +122 -0
  84. package/dist/domains/managed-work/journal.js.map +1 -0
  85. package/dist/domains/managed-work/lock.js +40 -0
  86. package/dist/domains/managed-work/lock.js.map +1 -0
  87. package/dist/domains/managed-work/notifications.js +53 -0
  88. package/dist/domains/managed-work/notifications.js.map +1 -0
  89. package/dist/domains/managed-work/paths.js +18 -0
  90. package/dist/domains/managed-work/paths.js.map +1 -0
  91. package/dist/domains/managed-work/prompts.js +218 -0
  92. package/dist/domains/managed-work/prompts.js.map +1 -0
  93. package/dist/domains/managed-work/runner.js +626 -0
  94. package/dist/domains/managed-work/runner.js.map +1 -0
  95. package/dist/domains/managed-work/schemas.js +85 -0
  96. package/dist/domains/managed-work/schemas.js.map +1 -0
  97. package/dist/domains/managed-work/service.js +121 -0
  98. package/dist/domains/managed-work/service.js.map +1 -0
  99. package/dist/domains/managed-work/state.js +90 -0
  100. package/dist/domains/managed-work/state.js.map +1 -0
  101. package/dist/domains/managed-work/storage.js +181 -0
  102. package/dist/domains/managed-work/storage.js.map +1 -0
  103. package/dist/domains/managed-work/types.js +16 -0
  104. package/dist/domains/managed-work/types.js.map +1 -0
  105. package/dist/domains/managed-work/wait.js +40 -0
  106. package/dist/domains/managed-work/wait.js.map +1 -0
  107. package/dist/platform/agent-process.js +308 -0
  108. package/dist/platform/agent-process.js.map +1 -0
  109. package/dist/platform/assets.js +9 -0
  110. package/dist/platform/assets.js.map +1 -0
  111. package/dist/platform/process-liveness.js +10 -0
  112. package/dist/platform/process-liveness.js.map +1 -0
  113. package/dist/platform/version-check.js +30 -9
  114. package/dist/platform/version-check.js.map +1 -1
  115. package/package.json +3 -2
  116. package/scripts/release-notes-check.js +102 -0
package/README.md CHANGED
@@ -48,6 +48,11 @@ state, and dependency guards for Claude Code, Codex, and OpenCode.
48
48
 
49
49
  ## Highlights
50
50
 
51
+ - **Dual-Agent managed delivery.** The current Agent supervises while the peer
52
+ Agent develops, starts, and verifies through a non-interactive CLI. Repair
53
+ rounds resume exact sessions and are capped at 5 reviews, 7 executor calls,
54
+ and 12 total calls. Durable journals allow recovery after terminal or network
55
+ interruption, and passing work stops for explicit Git approval.
51
56
  - **9 catalogued failure modes, blocked at the gate.** Nine real ways AI breaks
52
57
  in production — swallowing the whole PRD then forgetting details, editing
53
58
  only the setter while missing consumers, SQL drift across dev/test/DB
@@ -141,12 +146,25 @@ superflow --language en --help
141
146
  SUPERFLOW_LANG=en superflow init
142
147
  ```
143
148
 
149
+ Manage an implementation prompt, OpenSpec change, or direct development task:
150
+
151
+ ```bash
152
+ superflow pipeline "<prompt path, change directory, or task>" --managed \
153
+ --project "<project root>"
154
+ ```
155
+
156
+ The selected language is frozen into the task contract and reused by Agent
157
+ prompts, reviews, journals, reports, notifications, and resumed rounds.
158
+
144
159
  ## What Init Installs
145
160
 
146
161
  `superflow init` installs and configures:
147
162
 
148
163
  - OpenSpec CLI, then runs `openspec init <project> --tools ...`
149
- - Superpowers for the selected agent tools
164
+ - Superpowers for the selected agent tools. Codex uses the official
165
+ `superpowers@openai-api-curated` plugin, including
166
+ `verification-before-completion`, `requesting-code-review`, and
167
+ `finishing-a-development-branch`.
150
168
  - SuperBridge Flow/OpenSpec skills
151
169
  - hooks, scripts, and anti-drift rules
152
170
  - Codex prompt aliases
@@ -170,6 +188,7 @@ best-effort and does not block initialization.
170
188
  | `superflow --language en --help` | Show English CLI help |
171
189
  | `superflow scan --language en` | Regenerate project context templates in English |
172
190
  | `superflow pipeline` | Check pipeline skill deployment |
191
+ | `superflow pipeline "<task>" --managed --project <path>` | Run Dual-Agent managed development to a terminal delivery state |
173
192
  | `superflow docs` | Check docs-phase skill deployment |
174
193
  | `superflow design` | Check design-phase skill deployment |
175
194
  | `superflow implement` | Check implement-phase skill deployment |
@@ -206,7 +225,10 @@ hook registration is not enabled for OpenCode yet; use `/superflow-*` commands
206
225
  to run workflow gates explicitly.
207
226
 
208
227
  For large requirements, ask the agent to read one section or one feature at a
209
- time. SuperBridge Flow will route through clarification, OpenSpec docs,
228
+ time. The total router automatically uses embedded deep clarification when a
229
+ feature needs an owner decision; it investigates available facts first and asks
230
+ one decision at a time with a recommendation. Clear, bounded work continues
231
+ without an interview. SuperBridge Flow then routes through OpenSpec docs,
210
232
  Superpowers technical design, implementation prompts, verification, and archive.
211
233
 
212
234
  ## Automatic Update Checks
package/README.zh-CN.md CHANGED
@@ -43,6 +43,9 @@ CLI 会把 SuperBridge Flow 技能、配套 hook/command 脚本整合为单一 n
43
43
 
44
44
  ## 亮点
45
45
 
46
+ - **双 Agent 托管到真实交付。** 当前 Agent 负责监督,另一个 Agent 通过无交互 CLI
47
+ 持续开发、启动和验证;评审整改复用原会话,最多 5 轮评审、7 次执行、12 次总调用。
48
+ 状态和证据全部落盘,终端或网络中断后可恢复,最终停在等待 Git 批准。
46
49
  - **9 类翻车场景,沉淀成门禁。** 我们见过的 AI 翻车——需求一次吞、字段漏查消费者、SQL 漂移、mock-only 报告、跨服务只看到"能调"……9 种典型形态都被固化成 guard,AI 想跳过都不行。
47
50
  - **跨服务代码必须先答架构 6 问。** 涉及跨仓、跨服务、SDK、MQ、设备、回调、网关的改动,技术详设里必须先回答:owner 是谁、调用方向、新入口是否允许、禁止路径、证据锚点——答不全不让进入实现。
48
51
  - **改字段之前先填"字段与状态反向影响"矩阵。** 任何 schema、状态、枚举的改动,必须枚举写入点、读取点、过滤点、派生 / 同步点、跨模块消费者、测试覆盖,还要列反向恢复场景(下线后重上线、旧值不可用但上游没传字段、历史脏数据被新过滤条件消费)。
@@ -115,12 +118,22 @@ export SUPERFLOW_LANG=en
115
118
  superflow init
116
119
  ```
117
120
 
121
+ 托管一个 implementation prompt、OpenSpec change 或简单开发任务:
122
+
123
+ ```bash
124
+ superflow pipeline "<prompt 路径、change 目录或任务>" --managed \
125
+ --project "<项目根目录>"
126
+ ```
127
+
128
+ 语言会冻结进任务合同;执行 Prompt、评审、账本、报告、通知和恢复轮次保持一致。
129
+
118
130
  ## SDD 分工
119
131
 
120
132
  - OpenSpec/SDD 负责 WHAT 和合同:需求、API、DB、SQL、字段语义、tests、真实入口验收和质量门禁。
121
133
  - Superpowers 负责 HOW:源码级技术详设、TDD/RED 顺序、团队分工、worktree/端口并行、review/tester 编排和验证闭环。
122
134
  - 完整流程是 `docs -> design -> implement -> verify -> archive`。Superpowers 技术详设落到 `docs/superpowers/specs/*-technical-design.md`,并记录到 `.sdd/state.yaml` 的 `technical_design`,避免长会话压缩后漂移。
123
135
  - Codex 侧通常用自然语言或 `$superflow-pipeline` 触发;Claude Code 侧可以直接用 `/superflow-pipeline`、`/superflow-docs`、`/superflow-design` 等 slash command。
136
+ - 只说一句话触发总路由时,遇到需要业务负责人选择的关键分歧,SuperFlow 会在 clarify 阶段自动进入深度澄清:先查代码和已有资料能确认的事实,再一次只问一个决策问题,并给出推荐方案与影响。明确、范围小的改动不会被强行拉进问答流程。
124
137
  - 飞书、语雀等在线文档读取工具不内置在 SuperBridge Flow CLI 中;可自行用 `lark-cli` 等外部工具读取,再通过 `/superflow-pipeline` 或 `$superflow-pipeline` 分段分析指定小节。
125
138
 
126
139
  ## 命令
@@ -144,6 +157,7 @@ superflow init
144
157
  | `superflow design [change]` | 校验 SuperBridge Flow design 阶段技能部署 |
145
158
  | `superflow implement [task]` | 校验 SuperBridge Flow implement 阶段技能部署 |
146
159
  | `superflow pipeline` | 校验 SuperBridge Flow pipeline 阶段技能部署 |
160
+ | `superflow pipeline "<任务>" --managed --project <目录>` | 双 Agent 托管开发并等待交付终态 |
147
161
  | `superflow check <change>` | 对照 13 项必备文件清单检查文档完整性 |
148
162
  | `superflow config <change> --review-mode <mode>` | 设置代码审查强度(off/standard/thorough) |
149
163
  | `superflow config <change> --auto-transition <bool>` | 控制阶段自动流转(true/false) |
@@ -162,7 +176,9 @@ superflow init
162
176
  - Claude Code、Codex 或 OpenCode(按 `--agent` 选择)
163
177
  - 第三方(`superflow init` 自动装):
164
178
  - openspec CLI(硬依赖,npm 全局)并在当前项目执行 `openspec init --tools ...`
165
- - superpowers(Claude Code / Codex 为硬依赖;OpenCode 侧通过已部署 skills/commands 使用流程)
179
+ - superpowers(Claude Code / Codex 为硬依赖;Codex 自动安装官方
180
+ `superpowers@openai-api-curated`,确保包含验证、代码评审和分支收尾三个强制技能;
181
+ OpenCode 侧通过已部署 skills/commands 使用流程)
166
182
  - understand-anything(尽力安装,失败只警告)
167
183
  - api-doc-changelog(辅助 skill,复制到目标 agent skills 目录)
168
184
 
@@ -17,9 +17,7 @@
17
17
  "superflow-tweak",
18
18
  "superflow-verify"
19
19
  ],
20
- "rules": [
21
- "superflow-phase-guard.md"
22
- ],
20
+ "rules": ["superflow-phase-guard.md"],
23
21
  "scripts": [
24
22
  "superflow-verify-integration.sh",
25
23
  "superflow-delivery-check.sh",
@@ -29,7 +27,10 @@
29
27
  "superflow-hook-guard.sh",
30
28
  "superflow-dependency-update-hook.sh",
31
29
  "superflow-integration-evidence-hook.sh",
30
+ "superflow-archive-command-hook.sh",
32
31
  "superflow-test-report-lint.py",
32
+ "superflow-managed-work-check.mjs",
33
+ "superflow-managed-work-guard.sh",
33
34
  "install-sql-pre-commit.sh",
34
35
  "sync-settings-json.py"
35
36
  ],
@@ -45,7 +46,9 @@
45
46
  "superflow-dependency-update-hook.sh",
46
47
  "superflow-sql-sync-hook.py",
47
48
  "superflow-delivery-check.sh",
48
- "superflow-integration-evidence-hook.sh"
49
+ "superflow-integration-evidence-hook.sh",
50
+ "superflow-archive-command-hook.sh",
51
+ "superflow-managed-work-guard.sh"
49
52
  ],
50
53
  "agentHooks": {
51
54
  "claude": ["claude-auto-backup-hook.sh"],
@@ -8,3 +8,9 @@ Use the `superflow-clarify` skill for this request.
8
8
  Read the requested source in bounded sections, freeze one feature or section at
9
9
  a time, and stop for missing product/API/DB facts before OpenSpec artifacts are
10
10
  created.
11
+
12
+ If the total router selected embedded deep clarification, establish facts from
13
+ the repository and supplied sources before asking. Ask one unresolved owner
14
+ decision at a time, include a recommended answer and consequence, wait for the
15
+ response, and record the result in the existing brainstorm handoff before moving
16
+ on. Do not turn clear, bounded work into an interview.
@@ -8,3 +8,6 @@ Use the `superflow-design` skill for this request.
8
8
  Let Superpowers handle source-level HOW: inspect real code, reverse impact,
9
9
  field references, TDD strategy, worker split, worktree and port planning. Do not
10
10
  rewrite OpenSpec contracts unless a conflict is found and returned to docs.
11
+ Complete the source-level Minimal Design Review before implementation planning:
12
+ prove reuse and necessity, choose the simplest implementation, and explicitly
13
+ remove or reject unsupported complexity.
@@ -7,4 +7,7 @@ Use the `superflow-docs` skill for this request.
7
7
 
8
8
  Keep OpenSpec as the source of WHAT and contracts. Ensure API, DB, tests,
9
9
  traceability, quality gates, and handoff files are complete before technical
10
- design starts.
10
+ design starts. For full workflows, require a Minimal Design Review in
11
+ `design.md` and a PASS/BLOCKED verdict in `sdd-quality-gate.md`; prefer reuse
12
+ and remove unsupported tables, fields, APIs, services, caches, async flows,
13
+ jobs, and compatibility layers.
@@ -9,5 +9,13 @@ Route the work through the correct SuperBridge Flow phase. OpenSpec owns WHAT
9
9
  and contracts; Superpowers owns source-level HOW. Preserve handoff state and run
10
10
  the required SuperBridge Flow guards before phase transitions.
11
11
 
12
+ When the request has unresolved decisions that need owner intent, competing
13
+ approaches, unclear source of truth, unclear acceptance behavior, or cross-system
14
+ responsibility, enter the embedded deep-clarification mode of
15
+ `superflow-clarify`. Investigate repository-verifiable facts first, then ask one
16
+ decision question at a time with a recommended answer and consequence. Keep the
17
+ decision record in the existing brainstorm handoff. Do not require the user to
18
+ invoke another command, and do not use this mode for a clear bounded change.
19
+
12
20
  Input: treat the argument after `/superflow-pipeline` as the requirement,
13
21
  document path, or change description.
@@ -0,0 +1,80 @@
1
+ #!/bin/bash
2
+ # Blocks direct OpenSpec/Superflow archive commands that bypass lifecycle gates.
3
+
4
+ set -u
5
+
6
+ INPUT=$(cat)
7
+
8
+ read_json_field() {
9
+ local field="$1"
10
+ printf '%s' "$INPUT" | python3 -c "
11
+ import json, sys
12
+ try:
13
+ data = json.load(sys.stdin)
14
+ except Exception:
15
+ print('')
16
+ raise SystemExit
17
+ tool_input = data.get('tool_input', {})
18
+ print(tool_input.get('$field') or tool_input.get('cmd') or '')
19
+ " 2>/dev/null
20
+ }
21
+
22
+ COMMAND=$(read_json_field command)
23
+ [ -n "$COMMAND" ] || exit 0
24
+
25
+ CHANGE=$(printf '%s' "$COMMAND" | python3 -c '
26
+ import shlex, sys
27
+
28
+ try:
29
+ tokens = shlex.split(sys.stdin.read())
30
+ except ValueError:
31
+ raise SystemExit
32
+
33
+ for index in range(len(tokens) - 1):
34
+ binary = tokens[index].rsplit("/", 1)[-1]
35
+ if binary not in {"openspec", "superflow"} or tokens[index + 1] != "archive":
36
+ continue
37
+ skip_value = False
38
+ for token in tokens[index + 2:]:
39
+ if skip_value:
40
+ skip_value = False
41
+ continue
42
+ if token in {"--store", "--agent"}:
43
+ skip_value = True
44
+ continue
45
+ if token.startswith("-"):
46
+ continue
47
+ print(token)
48
+ raise SystemExit
49
+ ' 2>/dev/null)
50
+
51
+ [ -n "$CHANGE" ] || exit 0
52
+
53
+ CWD=$(read_json_field cwd)
54
+ [ -n "$CWD" ] || CWD=$(pwd)
55
+ REPO_ROOT=$(git -C "$CWD" rev-parse --show-toplevel 2>/dev/null) || exit 0
56
+
57
+ if [ -d "$CWD/$CHANGE" ]; then
58
+ CHANGE_DIR="$CWD/$CHANGE"
59
+ else
60
+ CHANGE_DIR="$REPO_ROOT/openspec/changes/$CHANGE"
61
+ fi
62
+
63
+ [ -d "$CHANGE_DIR" ] || exit 0
64
+
65
+ ARCHIVE_SCRIPT=${SUPERFLOW_ARCHIVE_SCRIPT:-$HOME/.codex/skills/superflow-pipeline/scripts/superflow-archive.sh}
66
+ if [ ! -x "$ARCHIVE_SCRIPT" ]; then
67
+ echo "[Superflow 归档门禁] 归档脚本不存在或不可执行:$ARCHIVE_SCRIPT" >&2
68
+ exit 2
69
+ fi
70
+
71
+ if ! "$ARCHIVE_SCRIPT" "$CHANGE_DIR" --dry-run; then
72
+ cat <<'BLOCK_MSG' >&2
73
+ [Superflow 归档门禁] 归档门禁未通过,已阻止直接归档。
74
+ 请先完成 tasks、test-report、测试环境证据、verify 状态和归档确认;
75
+ 不要用 openspec archive 绕过 Superflow 生命周期。
76
+ BLOCK_MSG
77
+ exit 2
78
+ fi
79
+
80
+ exit 0
@@ -52,27 +52,46 @@ if [ -z "$CHANGED" ]; then
52
52
  exit 0
53
53
  fi
54
54
 
55
+ ACTIVE_SDD_CHANGED=$(printf '%s\n' "$CHANGED" | grep -Ev \
56
+ '(^|/)(doc/)?openspec/changes/archive/' || true)
57
+
55
58
  SDD_ACTIVE=0
56
59
  if [ -f "$REPO_ROOT/.sdd-enforced" ]; then
57
60
  SDD_ACTIVE=1
58
61
  fi
59
62
 
63
+ if [ "$SDD_ACTIVE" -eq 0 ]; then
64
+ ACTIVE_STATE=$(find "$REPO_ROOT/openspec/changes" \
65
+ -path '*/archive/*' -prune -o \
66
+ -path '*/.sdd/state.yaml' -type f -print 2>/dev/null \
67
+ | while IFS= read -r state; do
68
+ phase=$(awk -F':' '$1=="phase"{gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2}' "$state")
69
+ archived=$(awk -F':' '$1=="archived"{gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2}' "$state")
70
+ if [ "$archived" != "true" ] && { [ "$phase" = "implement" ] || [ "$phase" = "verify" ]; }; then
71
+ printf '%s\n' "$state"
72
+ fi
73
+ done | head -n 1)
74
+ if [ -n "$ACTIVE_STATE" ]; then
75
+ SDD_ACTIVE=1
76
+ fi
77
+ fi
78
+
60
79
  RUNTIME_CHANGED=$(printf '%s\n' "$CHANGED" | grep -E \
61
80
  '(^|/)src/main/.*\.(java|xml|yml|yaml|properties)$|(^|/)mapper/.*\.xml$|(^|/)sql/.*\.sql$' || true)
62
81
 
63
82
  CODE_RUNTIME_CHANGED=$(printf '%s\n' "$CHANGED" | grep -E \
64
83
  '(^|/)src/main/.*\.(java|xml|yml|yaml|properties)$|(^|/)mapper/.*\.xml$' || true)
65
84
 
66
- P_DIRS=$(printf '%s\n' "$CHANGED" | sed -nE \
85
+ P_DIRS=$(printf '%s\n' "$ACTIVE_SDD_CHANGED" | sed -nE \
67
86
  's#^(.*embedded-changes/(p[0-9]+[^/]*))/.*$#\1#p' | sort -u)
68
87
 
69
- REPORTS=$(printf '%s\n' "$CHANGED" | grep -E \
88
+ REPORTS=$(printf '%s\n' "$ACTIVE_SDD_CHANGED" | grep -E \
70
89
  '(^|/)embedded-changes/p[0-9][^/]*/test-report\.md$|(^|/)test-report\.md$' || true)
71
90
 
72
- AGGREGATE_DOCS=$(printf '%s\n' "$CHANGED" | grep -E \
91
+ AGGREGATE_DOCS=$(printf '%s\n' "$ACTIVE_SDD_CHANGED" | grep -E \
73
92
  '(^|/)openspec/changes/[^/]+/(tasks|test-report|traceability-matrix|sdd-quality-gate|tests)\.md$|(^|/)doc/openspec/changes/[^/]+/(tasks|test-report|traceability-matrix|sdd-quality-gate|tests)\.md$' || true)
74
93
 
75
- SDD_DOCS_CHANGED=$(printf '%s\n' "$CHANGED" | grep -E \
94
+ SDD_DOCS_CHANGED=$(printf '%s\n' "$ACTIVE_SDD_CHANGED" | grep -E \
76
95
  '(^|/)(doc/)?openspec/changes/|(^|/)embedded-changes/p[0-9]' || true)
77
96
 
78
97
  AGGREGATE_ALLOWED=0
@@ -143,7 +143,7 @@ if [ "$MODE" = "apply" ]; then
143
143
  fi
144
144
 
145
145
  if command -v codex >/dev/null 2>&1; then
146
- run_logged codex plugin add superpowers@openai-curated || true
146
+ run_logged codex plugin add superpowers@openai-api-curated || true
147
147
  fi
148
148
  else
149
149
  STATUS_FILE="$STATE_DIR/dependency-update-status.txt"
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import crypto from 'crypto';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+
6
+ const [projectArg = '.', taskId, mode] = process.argv.slice(2);
7
+ const beforeReadyEvent = mode === '--before-ready-event';
8
+ if (!taskId) {
9
+ console.error('用法: superflow-managed-work-check.mjs <project-root> <task-id>');
10
+ process.exit(2);
11
+ }
12
+
13
+ const projectRoot = path.resolve(projectArg);
14
+ const taskDir = path.join(projectRoot, '.superflow', 'tasks', taskId);
15
+ const taskFile = path.join(taskDir, 'task.json');
16
+ if (!fs.existsSync(taskFile)) fail(`缺少任务状态: ${taskFile}`);
17
+ const task = JSON.parse(fs.readFileSync(taskFile, 'utf8'));
18
+ verifyContract(task);
19
+ const runsDir = path.join(taskDir, 'runs');
20
+ const runs = fs.existsSync(runsDir)
21
+ ? fs.readdirSync(runsDir).filter((entry) => fs.statSync(path.join(runsDir, entry)).isDirectory())
22
+ : [];
23
+ if (runs.length !== 1) fail(`首版要求一个活动 run,实际 ${runs.length}`);
24
+
25
+ const runDir = path.join(runsDir, runs[0]);
26
+ const stateFile = path.join(runDir, 'run-state.json');
27
+ const journalFile = path.join(runDir, 'progress.jsonl');
28
+ const reportFile = path.join(runDir, 'task-report.md');
29
+ for (const file of [stateFile, journalFile, reportFile]) {
30
+ if (!fs.existsSync(file)) fail(`缺少必备运行产物: ${file}`);
31
+ }
32
+ const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
33
+
34
+ assertMax('reviewRound', state.reviewRound, 5);
35
+ assertMax('executorInvocations', state.executorInvocations, 7);
36
+ assertMax('totalAgentInvocations', state.totalAgentInvocations, 12);
37
+ assertMax('reviewInvocations', state.reviewInvocations, 5);
38
+ if (state.contractHash !== task.contractHash) fail('任务合同哈希与运行状态不一致');
39
+ if (state.status !== task.status) fail('任务合同状态与运行状态不一致');
40
+ if (state.executorInvocations > 0 && !state.executorSession?.sessionId) {
41
+ fail('已有执行调用但缺少执行会话编号');
42
+ }
43
+ if (state.reviewInvocations > 0 && !state.supervisorSession?.sessionId) {
44
+ fail('已有正式检查但缺少监督会话编号');
45
+ }
46
+ const events = verifyJournal(journalFile);
47
+
48
+ if (state.status === 'awaiting_git_approval') {
49
+ if (!state.lastExecutorResult || !fs.existsSync(state.lastExecutorResult)) {
50
+ fail('等待 Git 批准状态缺少最终执行结果');
51
+ }
52
+ if (!state.lastReviewResult || !fs.existsSync(state.lastReviewResult)) {
53
+ fail('等待 Git 批准状态缺少最终检查结果');
54
+ }
55
+ assertInsideRun(state.lastExecutorResult, runDir, '最终执行结果');
56
+ assertInsideRun(state.lastReviewResult, runDir, '最终检查结果');
57
+ const executor = JSON.parse(fs.readFileSync(state.lastExecutorResult, 'utf8'));
58
+ verifyExecutorEvidence(task, executor);
59
+ const review = JSON.parse(fs.readFileSync(state.lastReviewResult, 'utf8'));
60
+ if (review.result !== 'pass' || review.findings?.some((item) => item.blocking)) {
61
+ fail('等待 Git 批准状态与最终检查结论不一致');
62
+ }
63
+ const readyEvents = events.filter((event) => event.eventType === 'run.delivery_ready');
64
+ const expectedReadyEvents = beforeReadyEvent ? 0 : 1;
65
+ if (readyEvents.length !== expectedReadyEvents) {
66
+ fail(`交付就绪事件数量应为 ${expectedReadyEvents},实际 ${readyEvents.length}`);
67
+ }
68
+ }
69
+
70
+ console.log(`OK 托管任务状态完整: ${taskId} (${state.status})`);
71
+
72
+ function assertMax(name, value, max) {
73
+ if (!Number.isInteger(value) || value < 0 || value > max) {
74
+ fail(`${name}=${value} 超出范围 0..${max}`);
75
+ }
76
+ }
77
+
78
+ function verifyContract(contract) {
79
+ 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);
83
+ assertPositiveMax('maxActiveRunHours', budgets.maxActiveRunHours, 24, false);
84
+ assertPositiveMax('maxSingleInvocationHours', budgets.maxSingleInvocationHours, 3, false);
85
+ if (contract.supervisorAgent === contract.executorAgent) {
86
+ fail('监督 Agent 和执行 Agent 不能相同');
87
+ }
88
+ const permissions = contract.permissions ?? {};
89
+ for (const key of ['gitCommit', 'gitPush', 'productionWrites', 'bypassSandbox']) {
90
+ if (permissions[key] !== false) fail(`不可覆盖的权限边界被放宽: ${key}`);
91
+ }
92
+ if (contract.taskPrompt) {
93
+ const expectedSnapshot = path.join(
94
+ path.resolve(contract.projectRoot),
95
+ '.superflow',
96
+ 'tasks',
97
+ contract.taskId,
98
+ 'source-prompt.md',
99
+ );
100
+ if (path.resolve(contract.taskPrompt.snapshotPath) !== expectedSnapshot) {
101
+ fail('冻结 Prompt 快照路径不属于当前托管任务');
102
+ }
103
+ if (!fs.existsSync(expectedSnapshot)) fail('冻结 Prompt 快照不存在');
104
+ const promptHash = crypto.createHash('sha256')
105
+ .update(fs.readFileSync(expectedSnapshot, 'utf8'))
106
+ .digest('hex');
107
+ if (promptHash !== contract.taskPrompt.sha256) fail('冻结 Prompt 快照哈希不一致');
108
+ }
109
+ const immutable = {
110
+ schemaVersion: contract.schemaVersion,
111
+ taskId: contract.taskId,
112
+ request: contract.request,
113
+ source: contract.source,
114
+ projectRoot: path.resolve(contract.projectRoot),
115
+ relatedProjectRoots: contract.relatedProjectRoots.map((root) => path.resolve(root)),
116
+ profile: contract.profile,
117
+ ...(contract.language ? { language: contract.language } : {}),
118
+ objective: contract.objective,
119
+ doneCriteria: contract.doneCriteria,
120
+ taskPrompt: contract.taskPrompt,
121
+ supervisorAgent: contract.supervisorAgent,
122
+ executorAgent: contract.executorAgent,
123
+ permissions: contract.permissions,
124
+ budgets: contract.budgets,
125
+ };
126
+ const actual = crypto.createHash('sha256').update(JSON.stringify(immutable)).digest('hex');
127
+ if (actual !== contract.contractHash) fail('任务合同内容与冻结哈希不一致');
128
+ }
129
+
130
+ function assertPositiveMax(name, value, max, integer) {
131
+ if (!Number.isFinite(value) || value <= 0 || value > max || (integer && !Number.isInteger(value))) {
132
+ fail(`${name}=${value} 超出硬上限或格式非法`);
133
+ }
134
+ }
135
+
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('最终执行结果缺少成功命令证据或仍含失败命令');
140
+ }
141
+ if (Array.isArray(result.blockers) && result.blockers.length > 0) {
142
+ fail('最终执行结果仍含阻塞项');
143
+ }
144
+ 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('工程任务缺少至少两类成功验证证据');
147
+ }
148
+ }
149
+
150
+ function verificationCategories(command = '') {
151
+ const value = command.toLowerCase();
152
+ const categories = [];
153
+ if (/(^|\s)(test|vitest|jest|pytest|phpunit|go\s+test|cargo\s+test)(\s|$)/.test(value)
154
+ || /(?:npm|pnpm|yarn)\s+(?:run\s+)?test/.test(value)
155
+ || /mvn(?:\s+[^\s]+)*\s+test/.test(value)
156
+ || /gradle\w*\s+test/.test(value)) categories.push('test');
157
+ if (/(?:npm|pnpm|yarn)\s+(?:run\s+)?build/.test(value)
158
+ || /(^|\s)(tsc|make|cmake|compile)(\s|$)/.test(value)
159
+ || /mvn(?:\s+[^\s]+)*\s+(?:compile|package|verify)/.test(value)
160
+ || /gradle\w*\s+(?:build|assemble)/.test(value)
161
+ || /cargo\s+build|go\s+build/.test(value)) categories.push('build');
162
+ if (/(?:npm|pnpm|yarn)\s+(?:run\s+)?(?:start|dev|serve)/.test(value)
163
+ || /spring-boot:run|docker\s+compose\s+up|(^|\s)(curl|wget)(\s|$)/.test(value)
164
+ || /health(?:check)?|integration|e2e/.test(value)) categories.push('runtime');
165
+ return categories;
166
+ }
167
+
168
+ function assertInsideRun(file, runDir, label) {
169
+ const relative = path.relative(path.resolve(runDir), path.resolve(file));
170
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
171
+ fail(`${label}不在当前运行目录内`);
172
+ }
173
+ }
174
+
175
+ function verifyJournal(file) {
176
+ const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/).filter(Boolean);
177
+ let previous = null;
178
+ const events = lines.map((line, index) => {
179
+ const event = JSON.parse(line);
180
+ const { eventHash, ...base } = event;
181
+ const expected = crypto.createHash('sha256').update(JSON.stringify(base)).digest('hex');
182
+ if (event.sequence !== index + 1) fail(`事件序号断裂: ${event.sequence}`);
183
+ if (event.previousEventHash !== previous) fail(`事件前序哈希不一致: ${event.sequence}`);
184
+ if (eventHash !== expected) fail(`事件哈希不一致: ${event.sequence}`);
185
+ previous = eventHash;
186
+ return event;
187
+ });
188
+ return events;
189
+ }
190
+
191
+ function fail(message) {
192
+ console.error(`FAIL ${message}`);
193
+ process.exit(1);
194
+ }
@@ -0,0 +1,103 @@
1
+ #!/bin/bash
2
+ # Managed-work role guard. Active only inside a Superflow managed Agent process.
3
+
4
+ set -u
5
+
6
+ ROLE="${SUPERFLOW_MANAGED_ROLE:-}"
7
+ [ -n "$ROLE" ] || exit 0
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
+ }
48
+
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
101
+ fi
102
+
103
+ exit 0
@@ -22,6 +22,17 @@ When ready to implement, run /opsx:apply
22
22
 
23
23
  **Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
24
24
 
25
+ ### OpenSpec canonical change 目录(阻塞级)
26
+
27
+ - change 必须只有一个实体目录:`openspec/changes/<name>/`。
28
+ - 版本归属必须编码进 `<name>`,例如版本 `v1.1.1` 的账号权限需求使用
29
+ `v1-1-1-account-permission-optimization`。
30
+ - 禁止创建 `openspec/changes/v1.1.1/<feature>/` 这类嵌套版本目录。
31
+ - 禁止为了兼容 OpenSpec CLI 再创建扁平软链接、目录副本或第二套 canonical 文档。
32
+ - 创建、查询、校验和后续 apply 必须始终使用同一个 `<name>`:
33
+ `openspec new change`、`openspec status --change`、
34
+ `openspec validate <name> --strict` 的名称必须一致。
35
+
25
36
  **Steps**
26
37
 
27
38
  1. **If no clear input provided, ask what they want to build**
@@ -14,6 +14,9 @@ do not mark archived until the user confirms final closeout.
14
14
  - `phase: archive`.
15
15
  - `verify_result: pass`.
16
16
  - `verification_report` points to an existing report.
17
+ - Every checkbox in `tasks.md` is checked.
18
+ - The verification report contains `验证结果: PASS` and `归档就绪: PASS`.
19
+ - `branch_status: handled` and `verified_at` are recorded.
17
20
  - `superflow-state.sh recover <change-dir>` gives a coherent archive recovery action.
18
21
 
19
22
  ## Procedure
@@ -42,6 +45,8 @@ do not mark archived until the user confirms final closeout.
42
45
  the user explicitly accepts the boundary.
43
46
  - Do not use archive to hide missing API, DB, hook, SQL, test-report, or real
44
47
  integration evidence.
48
+ - Never call `openspec archive` directly to bypass the lifecycle gate. The
49
+ installed archive-command hook blocks that path when native hooks are active.
45
50
  - In a standard `openspec/changes/<name>` layout, archive must use OpenSpec
46
51
  archive semantics so delta specs are merged and the change is moved to
47
52
  `openspec/changes/archive/`. State-only archive is only a fallback for