@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.
- package/README.md +24 -2
- package/README.zh-CN.md +17 -1
- package/assets/manifest.json +7 -4
- package/assets/prompts/superflow-clarify.md +6 -0
- package/assets/prompts/superflow-design.md +3 -0
- package/assets/prompts/superflow-docs.md +4 -1
- package/assets/prompts/superflow-pipeline.md +8 -0
- package/assets/scripts/superflow-archive-command-hook.sh +80 -0
- package/assets/scripts/superflow-delivery-check.sh +23 -4
- package/assets/scripts/superflow-dependency-update-hook.sh +1 -1
- package/assets/scripts/superflow-managed-work-check.mjs +194 -0
- package/assets/scripts/superflow-managed-work-guard.sh +103 -0
- package/assets/skills/openspec-propose/SKILL.md +11 -0
- package/assets/skills/superflow-archive/SKILL.md +5 -0
- package/assets/skills/superflow-clarify/SKILL.md +26 -7
- package/assets/skills/superflow-design/SKILL.md +38 -7
- package/assets/skills/superflow-docs/SKILL.md +27 -0
- package/assets/skills/superflow-pipeline/SKILL.md +75 -0
- package/assets/skills/superflow-pipeline/references/api-design-template.md +11 -0
- package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +27 -0
- package/assets/skills/superflow-pipeline/references/document-templates.md +8 -0
- package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +29 -1
- package/assets/skills/superflow-pipeline/references/managed-work.md +58 -0
- package/assets/skills/superflow-pipeline/references/quality-gate.md +3 -0
- package/assets/skills/superflow-pipeline/references/superpower-technical-design-template.md +51 -0
- package/assets/skills/superflow-pipeline/references/test-execution-template.md +10 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-archive.sh +1 -1
- package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +215 -2
- package/assets/skills/superflow-verify/SKILL.md +7 -0
- package/assets/skills-en/openspec-propose/SKILL.md +14 -0
- package/assets/skills-en/superflow-archive/SKILL.md +6 -0
- package/assets/skills-en/superflow-clarify/SKILL.md +26 -7
- package/assets/skills-en/superflow-design/SKILL.md +38 -6
- package/assets/skills-en/superflow-docs/SKILL.md +27 -0
- package/assets/skills-en/superflow-pipeline/SKILL.md +76 -0
- package/assets/skills-en/superflow-pipeline/references/api-design-template.md +13 -0
- package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +16 -0
- package/assets/skills-en/superflow-pipeline/references/document-templates.md +5 -0
- package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +17 -0
- package/assets/skills-en/superflow-pipeline/references/managed-work.md +34 -0
- package/assets/skills-en/superflow-pipeline/references/quality-gate.md +25 -0
- package/assets/skills-en/superflow-pipeline/references/superpower-technical-design-template.md +57 -0
- package/assets/skills-en/superflow-pipeline/references/test-execution-template.md +12 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-archive.sh +1 -1
- package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +214 -2
- package/assets/skills-en/superflow-verify/SKILL.md +8 -0
- package/dist/app/cli.js +120 -102
- package/dist/app/cli.js.map +1 -1
- package/dist/app/commands/archive.js +7 -3
- package/dist/app/commands/archive.js.map +1 -1
- package/dist/app/commands/change-guard.js +27 -0
- package/dist/app/commands/change-guard.js.map +1 -0
- package/dist/app/commands/design.js +2 -20
- package/dist/app/commands/design.js.map +1 -1
- package/dist/app/commands/init.js +7 -7
- package/dist/app/commands/init.js.map +1 -1
- package/dist/app/commands/managed-service.js +5 -0
- package/dist/app/commands/managed-service.js.map +1 -0
- package/dist/app/commands/pipeline.js +123 -3
- package/dist/app/commands/pipeline.js.map +1 -1
- package/dist/app/commands/status.js +199 -77
- package/dist/app/commands/status.js.map +1 -1
- package/dist/app/commands/uninstall.js +5 -2
- package/dist/app/commands/uninstall.js.map +1 -1
- package/dist/app/commands/update.js +33 -11
- package/dist/app/commands/update.js.map +1 -1
- package/dist/app/commands/verify.js +7 -3
- package/dist/app/commands/verify.js.map +1 -1
- package/dist/domains/config/cli-help.js +103 -73
- package/dist/domains/config/cli-help.js.map +1 -1
- package/dist/domains/config/context.js +1 -5
- package/dist/domains/config/context.js.map +1 -1
- package/dist/domains/deps.js +3 -6
- package/dist/domains/deps.js.map +1 -1
- package/dist/domains/hook.js +71 -37
- package/dist/domains/hook.js.map +1 -1
- package/dist/domains/managed-work/contract.js +218 -0
- package/dist/domains/managed-work/contract.js.map +1 -0
- package/dist/domains/managed-work/i18n.js +12 -0
- package/dist/domains/managed-work/i18n.js.map +1 -0
- package/dist/domains/managed-work/input.js +111 -0
- package/dist/domains/managed-work/input.js.map +1 -0
- package/dist/domains/managed-work/journal.js +122 -0
- package/dist/domains/managed-work/journal.js.map +1 -0
- package/dist/domains/managed-work/lock.js +40 -0
- package/dist/domains/managed-work/lock.js.map +1 -0
- package/dist/domains/managed-work/notifications.js +53 -0
- package/dist/domains/managed-work/notifications.js.map +1 -0
- package/dist/domains/managed-work/paths.js +18 -0
- package/dist/domains/managed-work/paths.js.map +1 -0
- package/dist/domains/managed-work/prompts.js +218 -0
- package/dist/domains/managed-work/prompts.js.map +1 -0
- package/dist/domains/managed-work/runner.js +626 -0
- package/dist/domains/managed-work/runner.js.map +1 -0
- package/dist/domains/managed-work/schemas.js +85 -0
- package/dist/domains/managed-work/schemas.js.map +1 -0
- package/dist/domains/managed-work/service.js +121 -0
- package/dist/domains/managed-work/service.js.map +1 -0
- package/dist/domains/managed-work/state.js +90 -0
- package/dist/domains/managed-work/state.js.map +1 -0
- package/dist/domains/managed-work/storage.js +181 -0
- package/dist/domains/managed-work/storage.js.map +1 -0
- package/dist/domains/managed-work/types.js +16 -0
- package/dist/domains/managed-work/types.js.map +1 -0
- package/dist/domains/managed-work/wait.js +40 -0
- package/dist/domains/managed-work/wait.js.map +1 -0
- package/dist/platform/agent-process.js +308 -0
- package/dist/platform/agent-process.js.map +1 -0
- package/dist/platform/assets.js +9 -0
- package/dist/platform/assets.js.map +1 -0
- package/dist/platform/process-liveness.js +10 -0
- package/dist/platform/process-liveness.js.map +1 -0
- package/dist/platform/version-check.js +30 -9
- package/dist/platform/version-check.js.map +1 -1
- package/package.json +3 -2
- 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.
|
|
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 为硬依赖;
|
|
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
|
|
package/assets/manifest.json
CHANGED
|
@@ -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' "$
|
|
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' "$
|
|
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' "$
|
|
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' "$
|
|
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
|