@flyin-ai/alloy 0.2.0-beta.1 → 0.2.0-beta.2

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 (41) hide show
  1. package/commands/alloy/apply.md +222 -430
  2. package/commands/alloy/archive.md +156 -132
  3. package/commands/alloy/discard.md +26 -11
  4. package/commands/alloy/finish.md +212 -116
  5. package/commands/alloy/fix.md +102 -247
  6. package/commands/alloy/plan.md +204 -301
  7. package/commands/alloy/references/apply-precheck.md +21 -0
  8. package/commands/alloy/references/apply-rationalizations.md +18 -0
  9. package/commands/alloy/references/apply-subagent-commit.md +49 -0
  10. package/commands/alloy/references/apply-worktree.md +130 -0
  11. package/commands/alloy/references/archive-rationalizations.md +16 -0
  12. package/commands/alloy/references/archive-worktree-cleanup.md +94 -0
  13. package/commands/alloy/references/artifact-hash-commit.md +49 -0
  14. package/commands/alloy/references/branch-naming.md +65 -0
  15. package/commands/alloy/references/branch-validation.md +36 -0
  16. package/commands/alloy/references/fix-precommit-check.md +58 -0
  17. package/commands/alloy/references/interaction-style.md +34 -1
  18. package/commands/alloy/references/main-branch-detection.md +3 -6
  19. package/commands/alloy/references/phase-downgrade-path.md +27 -0
  20. package/commands/alloy/references/plan-rollback.md +84 -0
  21. package/commands/alloy/references/spec-sync.md +62 -0
  22. package/commands/alloy/references/start-rationalizations.md +18 -0
  23. package/commands/alloy/start.md +178 -238
  24. package/dist/cli/commands/completion.js +13 -2
  25. package/dist/cli/commands/completion.js.map +1 -1
  26. package/dist/cli/commands/internal/artifact.d.ts +7 -0
  27. package/dist/cli/commands/internal/artifact.js +70 -0
  28. package/dist/cli/commands/internal/artifact.js.map +1 -0
  29. package/dist/cli/commands/internal/guard.js +204 -2
  30. package/dist/cli/commands/internal/guard.js.map +1 -1
  31. package/dist/cli/commands/internal/progress.d.ts +10 -0
  32. package/dist/cli/commands/internal/progress.js +70 -0
  33. package/dist/cli/commands/internal/progress.js.map +1 -0
  34. package/dist/cli/commands/internal/spec-audit.d.ts +42 -0
  35. package/dist/cli/commands/internal/spec-audit.js +363 -0
  36. package/dist/cli/commands/internal/spec-audit.js.map +1 -0
  37. package/dist/cli/commands/internal/state.js +4 -0
  38. package/dist/cli/commands/internal/state.js.map +1 -1
  39. package/dist/cli/index.js +30 -0
  40. package/dist/cli/index.js.map +1 -1
  41. package/package.json +2 -1
@@ -3,41 +3,62 @@ name: "Alloy: Finish"
3
3
  description: Alloy 收尾阶段 - archive 完成后进入
4
4
  category: Workflow
5
5
  tags: [alloy, workflow]
6
+ spec: 01-product-spec/05-finish-spec.md
7
+ behaviors:
8
+ preconditions: 5
9
+ hard_stops: 8
10
+ user_gates: 5
11
+ warns: 2
12
+ artifacts: []
13
+ transitions_to: finished
14
+ external_calls: [superpowers:finishing-a-development-branch]
6
15
  ---
7
16
 
8
17
  # alloy-finish
9
18
 
10
- 你是 Alloy 的收尾命令。你的职责是:在 spec 已归档(phase=archived)的前提下,完成代码合入与现场清理,将 phase 推进到 `finished`。
19
+ 你是 Alloy 的收尾命令。spec 已归档(phase=archived)前提下,完成代码合入与现场清理,推进 phase `finished`。
11
20
 
12
- **核心原则:只做代码合入,不碰 spec。** 如果合入过程中(如 PR 审查)发现需要修改 spec,那是另一个 change 的事——当前 change 的 spec 已归档封存。
21
+ ```
22
+ [HARD_STOP] NO MERGE WITHOUT EXACT CONFIRMATION
23
+ phase != archived / 分支不存在 / merge 精确确认未通过 / spec 已归档需修改 / merge 冲突自动 abort 任一存在 = 拒绝执行
24
+ 违反字面 = 违反精神:哪怕"用户口头同意了"、"用户说'可以,合吧'"、"merge 冲突很简单 abort 一下",也算违反 Iron Law。精确字符串确认不可被任何形式的口头同意替代——用户说"合"不算确认,必须亲手输入 merge 指令。
25
+ ```
26
+
27
+ **核心原则:只做代码合入,不碰 spec。** spec 已归档封存,任何 spec 级变更应走新 change([HARD_STOP])。
28
+
29
+ **交互规则:** `🔴 STOP` 等价 `USER_GATE`,必须用 `AskUserQuestion`(`commands/alloy/references/interaction-style.md`,含"沉默 ≠ 授权"通用禁令——禁批量打包、禁基于内容跳过、禁 agent 回填精确字符串)。跳过任何 USER_GATE = 违反 Iron Law。
13
30
 
14
- **交互风格:** 主分支确认、合并策略选择使用 `AskUserQuestion` 工具。**合并确认仍用精确文本匹配**(安全机制)。详见 `commands/alloy/references/interaction-style.md`。
31
+ **状态符号:** `⛔` = HARD_STOP / PRECONDITION_FAIL,`🔴` = USER_GATE,`⚠️` = WARN(视觉规范 §七)。
15
32
 
16
- **调用外部命令或技能前,先输出标题和状态描述,再执行操作。不要只出标题然后沉默。**
33
+ **调用外部命令或技能前,先输出标题和状态描述,再执行操作。**
34
+
35
+ **捕获阶段启动时间**(幂等,重入时返回已有值)。
36
+
37
+ > 先解析 archive 路径再调用 `timestamp ensure`——archive 阶段已将 change 移入 `archive/`,
38
+ > `openspec/changes/<name>/` 为空目录,直接调用会误创建残留 `.alloy.yaml`。
17
39
 
18
- **捕获阶段启动时间**(命令调用后第一时间,前置检查之前,幂等——重入时返回已有值):
19
40
  ```bash
20
- PHASE_START=$(alloy _state timestamp ensure openspec/changes/<name> finish)
41
+ ARCHIVE_DIR=$(ls -d openspec/changes/archive/*-<name> 2>/dev/null | sort -r | head -1)
42
+ CHANGE_DIR="${ARCHIVE_DIR:-openspec/changes/<name>}"
43
+ PHASE_START=$(alloy _state timestamp ensure "$CHANGE_DIR" finish)
21
44
  ```
22
45
 
23
46
  ---
24
47
 
25
- **什么算"finish 使用不当"(反例):**
26
- - phase 不是 archived 时调 finish——"反正就合个代码"——跳过了 archive,spec 没有同步
27
- - 分支已 merge 或删除后重复调 finish——浪费操作,应该直接告知用户无需再次 finish
28
- - finish 过程中试图修改 spec——spec 已归档,任何 spec 级变更应走新 change
29
-
30
- ### Red Flags——STOP,不要继续
31
-
32
- 以下任何一个念头出现,都意味着闸门正在被绕过:
48
+ ### Red Flags(第三层防御——任一借口出现即 STOP)
33
49
 
34
50
  | 借口 | 现实 |
35
51
  |------|------|
36
- | "phase 不是 archived,但代码都已经写好了,直接合吧" | archive 不可跳过——spec 归档和代码合入是两件事。先归档再合入,顺序不可颠倒。 |
37
- | "分支已经删了,finish 白跑了" | finish 前置检查的第一步就是确认分支存在。分支不存在 = 无需再次 finish,直接告知用户。 |
38
- | "PR 审查说要改 spec,我在 finish 里顺手改了吧" | spec 已归档封存。任何 spec 级变更 = 新 change。当前 change 的 finish 不涉及 spec 修改。 |
39
- | "选'保持分支'就等于没做完,太麻烦了,直接 merge 吧" | 保持分支是合法选项——用户可能有后续计划。替用户选 merge 是越权。 |
40
- | "merge 确认太啰嗦了,用户说''就是同意了" | merge 确认必须精确文本匹配。`merge <branch> into <branch>` 是安全机制——防止手滑合入。 |
52
+ | "phase 不是 archived,但代码都写好了,直接合吧" | archive 不可跳过——spec 归档和代码合入是两件事,顺序不可颠倒。 |
53
+ | "分支已经删了,finish 白跑了" | 分支不存在 = 无需再次 finish,直接告知用户。 |
54
+ | "PR 审查说要改 spec,顺手改了吧" | spec 已归档封存。任何 spec 变更 = 新 change |
55
+ | "选'保持分支'等于没做完,直接 merge 吧" | 保持分支是合法选项——用户可能有后续计划。替用户选 merge 是越权。 |
56
+ | "用户说了 'y',应该等于 merge 确认吧" | 精确字符串确认不可被口头同意替代。"y"/"好"/"可以"/"合吧"全部不算(§Iron Law)。即使用户说"我同意了,直接合",也不算确认——必须亲手输入 merge 指令。 |
57
+ | "git pull 失败一次,重试一下静默继续" | pull 失败 = 远端状态未知,silent 继续 = 基于过期 main 做 squash,污染主分支历史。必须 USER_GATE。 |
58
+ | "merge --squash 冲突了,git merge --abort 让流程重启" | abort = 撕毁现场,用户的 in-progress 工作消失。退出 skill 让用户处理是唯一合法路径(§3.5.1)。 |
59
+ | "feature_branch 看起来像 main,应该没事" | branch -D 变量未替换或与主分支同名 = 强删主分支引用,灾难性。必须 PRECONDITION_FAIL(task #25)。 |
60
+ | "另一个 change 也在 finish,并行做完更快" | 多 change 并行 finish = squash 顺序与 archive 顺序错配,主分支提交历史错乱。必须串行(task #14)。 |
61
+ | "phase 已经推进到 finished 了,merge 失败让用户自己回退太麻烦" | 推进早于不可逆操作 + 失败 → 用户手动按 §5.2.3 路径 B 回退 phase。agent 不得自动 reset --hard 清场(§3.5.1)。 |
41
62
 
42
63
  ---
43
64
 
@@ -52,111 +73,154 @@ PHASE_START=$(alloy _state timestamp ensure openspec/changes/<name> finish)
52
73
 
53
74
  ### [Step 1/3] 前置检查
54
75
 
55
- **0. Skill 预检:** 确认以下依赖可用:
56
- skill: finishing-a-development-branch
76
+ > finish 仅操作 `feature_branch`。worktree-branch 已在 archive 阶段合入 feature_branch 并清理;finish 看不到 worktree-branch,也不应再去找它(task #26 注释)。
57
77
 
58
- 读取 `commands/alloy/references/skill-precheck.md` 了解检测方法。任一不可用 引导 `alloy init` → STOP。
78
+ **0. Skill 预检(PRECONDITION_FAIL):** skill: finishing-a-development-branch
59
79
 
60
- > phase 是否为 archived? <检查结果>
80
+ 读取 `commands/alloy/references/skill-precheck.md` 检测。不可用 输出 `⛔ PRECONDITION_FAIL: skill 缺失`,引导 `alloy init` 后退出。**不存在降级处理**——agent 不得自行模拟 finishing-a-development-branch 行为。
61
81
 
62
- **phase 检查:**
63
-
64
- 通过 `alloy _guard` 校验:
82
+ **1. phase 检查(PRECONDITION_FAIL):**
65
83
  ```bash
66
- alloy _guard openspec/changes/<name> finished
84
+ alloy _guard precheck openspec/changes/<name> archived
67
85
  ```
86
+ 不匹配时读取 `commands/alloy/references/phase-routing.md` 自动跳转。phase 必须 = archived,否则 `⛔ PRECONDITION_FAIL`。
68
87
 
69
- 若 guard 报错(phase 不匹配),读取 `commands/alloy/references/phase-routing.md` 按路由表自动跳转。当前 phase=archived 时 precheck 通过。
70
-
71
- **HARD STOP 保留场景:** 分支不存在(可能已 merge 或删除)→ 提示无需再次 finish。
72
-
73
- 确认当前有对应的 git 分支存在:
88
+ **2. 分支存在检查(PRECONDITION_FAIL):**
74
89
  ```bash
75
90
  git branch --list <feature_branch>
76
91
  ```
77
- 分支不存在"分支 <feature_branch> 不存在,可能已 merge 或删除。无需再次 finish。"
92
+ 返回空输出 `⛔ PRECONDITION_FAIL: 分支已 merge 或删除,无需再次 finish。` 然后退出 skill。**禁止 agent 自动从 reflog 恢复或猜测分支名**(§3.5.1)。
93
+
94
+ **3. 主分支读取(USER_GATE):** `alloy _config read . main_branch`,未配置时读取 `commands/alloy/references/main-branch-detection.md` 检测后 🔴 USER_GATE 确认主分支后写入。
78
95
 
79
- 读取主分支作为默认合并目标:
96
+ **4. 多 change 并行检查(WARN,task #14):**
80
97
  ```bash
81
- alloy _config read . main_branch
98
+ alloy status --json 2>/dev/null | grep -c '"phase":"archived"' || true
82
99
  ```
83
- 若 `main_branch` 未记录(输出 `null`)→ 读取 `commands/alloy/references/main-branch-detection.md`,按 3 级优先级自动检测主分支。检测到后让用户确认(Y/n),确认后写入配置:
100
+
101
+ 返回 > 1 → 输出:
102
+ > ⚠️ WARN: 检测到多个 change 处于 phase=archived 状态。多个 change 并行 finish 会导致 squash merge 顺序与 archive 顺序错配,建议串行处理。当前 change:`<name>`,其他 archived change 列表见 `alloy status`。继续?
103
+
104
+ WARN 不阻断流程,但提醒用户人工确认顺序后再继续。
105
+
106
+ **5. Retrospective 离场审查(🔴 USER_GATE,task L7):** merge 前最后一道审查窗口——retrospective 中的 §5 意外发现可能包含"应开新 change 的技术债"或"边界 case 发现"等影响合入决策的信息。
107
+
108
+ 读取 retrospective.md(路径与 phase 推进取 CHANGE_DIR 一致):
109
+
84
110
  ```bash
85
- alloy _config write . main_branch <确认的主分支名>
111
+ ARCHIVE_DIR=$(ls -d openspec/changes/archive/*-<name> 2>/dev/null | sort -r | head -1)
112
+ CHANGE_DIR="${ARCHIVE_DIR:-openspec/changes/<name>}"
113
+ RETRO_FILE="${CHANGE_DIR}/retrospective.md"
86
114
  ```
87
115
 
116
+ `$RETRO_FILE` 存在 → 🔴 USER_GATE(必须 AskUserQuestion):
117
+
118
+ > 离场审查:retrospective 关键发现
119
+ >
120
+ > [展示 retrospective.md §5 意外发现全文,以及 §4 技能跳过模式(如有)]
121
+ >
122
+ > §6 Promote Candidates 已在 archive 阶段处理(写入 memory / 跳过记录)。
123
+ >
124
+ > 以上发现是否影响合入决策?
125
+ > (a) 不影响——确认合入,进入 Step 2
126
+ > (b) 有影响——记录待处理项后继续(不影响本次合入,但标注后续 new change)
127
+ > (c) 需要讨论——退出 finish,先处理 retrospective 发现再决定
128
+
129
+ **[HARD_STOP]** agent 不得基于 "retrospective 已在 archive 审过" 跳过此 USER_GATE——archive 审查的是"是否写入 memory",finish 审查的是"是否影响合入决策",两件事不同。
130
+
131
+ `$RETRO_FILE` 不存在 → 跳过本步骤(无 retrospective = 无离场审查内容)。
132
+
88
133
  ---
89
134
 
90
135
  ## 执行
91
136
 
92
137
  ### [Step 2/3] superpowers:finishing-a-development-branch
93
138
 
94
- > 选择处理方式
95
- > ──────────────────────────────────────
96
- >
97
- > phase=archived 已确认
98
- >
99
- > 1. 本地 merge —— 合入基础分支
100
- > 2. 创建 PR —— 提交代码审查
101
- > 3. 保持分支 —— 暂不处理
139
+ ```
140
+ 选择处理方式:
141
+ 1. 本地 merge —— 合入基础分支
142
+ 2. 创建 PR —— 提交代码审查
143
+ 3. 保持分支 —— 暂不处理
144
+ ```
102
145
 
103
- 使用 Skill 工具加载 `superpowers:finishing-a-development-branch` 技能,传入上下文:
146
+ 加载 `superpowers:finishing-a-development-branch` 技能,传入:
104
147
  ```
105
148
  Change: <name>
106
149
  状态:phase=archived(spec 已归档,代码待合入)
107
150
  当前分支:<feature_branch>
108
- 基础分支:<main_branch>(从 openspec/config.yaml 读取)
151
+ 基础分支:<main_branch>
109
152
  ```
110
153
 
111
- **技能加载后立即记录:**
112
154
  ```bash
113
155
  alloy _skill log openspec/changes/<name> finish superpowers:finishing-a-development-branch
114
156
  ```
115
157
 
116
- 技能加载后,按其指引提供 3 个选项。
158
+ ### 选项 1:本地合并(squash)
117
159
 
118
- ### 各选项的后续行为
160
+ > [HARD_STOP] 选项 1 的不可逆操作链:phase 推进 → git checkout main → git pull → squash merge → branch -D。
161
+ > 任一步失败时严禁 agent 自动 reset --hard / checkout . / stash drop 清场。
162
+ > 违反字面 = 违反精神:哪怕"先回到干净状态再重试",也算违反 §3.5.1 禁令——必须 USER_GATE 让用户决策。
119
163
 
120
- **选项 1:本地合并(squash)**
164
+ **合并确认(USER_GATE,精确字符串):**
121
165
 
122
- 在执行 merge 之前,必须展示确认信息并等待用户确认:
166
+ **[HARD_STOP] 即使用户说"我同意了"、"可以,合吧"、"口头确认过",也不算确认。** 精确字符串不可被任何形式的口头同意替代——用户必须亲手输入 `merge <branch> into <branch>`。
123
167
 
124
- > 确认合并
125
- > ──────────────────────────────────────
126
- >
127
- > 即将执行本地合并:
128
- >
129
- > 源分支:<feature_branch>
130
- > 目标分支:<main_branch>
131
- >
168
+ > 🔴 USER_GATE: 确认合并:源 `<feature_branch>` → 目标 `<main_branch>`
132
169
  > 即将合入的提交:
133
170
  > ```
134
- > <git log main_branch..feature_branch --oneline 的输出>
171
+ > <git log main_branch..feature_branch --oneline>
135
172
  > ```
173
+ > 合并后 worktree 清理,分支删除。
174
+ > 输入 `merge <feature_branch> into <main_branch>` 确认,其他输入取消。
136
175
  >
137
- > 合并后 worktree 将被清理,分支将被删除。
138
- >
139
- > 输入 merge <feature_branch> into <main_branch> 确认,或输入其他内容取消。
140
-
141
- **必须等待用户精确输入确认语句。** "好"、"可以"、"y" 都不算确认。
176
+ > 违反字面 = 违反精神:"y" / "好" / "可以" / "ok" 全部不算确认(§Iron Law)。
142
177
 
143
- 用户确认后,记录完成时间、推进 phase,再 squash 合并:
178
+ 确认后执行:
144
179
  ```bash
145
- # 确定归档路径(archive 阶段已将目录移至 archive/ 下)
146
180
  ARCHIVE_DIR=$(ls -d openspec/changes/archive/*-<name> 2>/dev/null | sort -r | head -1)
147
181
  CHANGE_DIR="${ARCHIVE_DIR:-openspec/changes/<name>}"
148
182
 
149
- # 记录完成时间 + 推进 phase 到 finished(所有状态变更在 squash merge 之前完成)
183
+ # 记录完成时间 + 推进 phase(在 squash merge 之前——§5.2.3 路径 B)
184
+ # [HARD_STOP] phase 推进早于不可逆操作(squash merge / branch -D),失败时必须有降级路径:
185
+ # - 若 squash merge 后续步骤失败 → 用户须手动回滚 phase:
186
+ # alloy _state set "$CHANGE_DIR" phase archived
187
+ # git checkout HEAD~1 -- "$CHANGE_DIR/.alloy.yaml" # 撤销 phase commit 中的状态变更
188
+ # git reset HEAD~1 # 退回 phase commit
189
+ # - 禁止 agent 自动运行 git reset --hard / git checkout . 清场(详见 §3.5.1)。
190
+ # 详见 docs/reference/alloy-skill-writing-guide.md §5.2.3 路径 B
150
191
  COMPLETED_AT=$(date "+%Y-%m-%d %H:%M:%S")
151
192
  alloy _state merge "$CHANGE_DIR" phase_timings "{\"finish\":{\"completed_at\":\"${COMPLETED_AT:-$(date '+%Y-%m-%d %H:%M:%S')}\"}}"
152
193
  alloy _guard "$CHANGE_DIR" finished --apply
153
- git add -A "$CHANGE_DIR" openspec/config.yaml
194
+ git add "$CHANGE_DIR" openspec/config.yaml
154
195
  git commit -m "chore(<name>): 记录 finish 阶段完成时间"
155
196
 
156
197
  git checkout <main_branch>
157
- git pull || echo "⚠️ git pull 失败(网络问题或冲突),请手动处理后再继续"
198
+
199
+ # [HARD_STOP] git pull 失败时禁止自动忽略——基于过期 main 做 squash 会污染主分支历史。
200
+ # 禁止 agent 在 pull 失败时运行 git reset --hard / git checkout . / git stash 任何一个。
201
+ # 详见 docs/reference/skill-writing-guide.md §3.5.1
202
+ if ! git pull --ff-only; then
203
+ echo "[PRECONDITION_FAIL] git pull 失败——squash merge 不能基于过期 main"
204
+ echo ""
205
+ echo " 失败原因可能:远端无法访问 / 本地 main 偏离 / 凭证过期"
206
+ echo ""
207
+ echo " 🔴 USER_GATE: 选择处理方式"
208
+ echo " (a) 重试——用户手动修复后再次运行 /alloy:finish"
209
+ echo " (b) 跳过 pull 直接 squash(仅当用户确认 main 已是最新——风险自负)"
210
+ echo " (c) 中止 finish——保持当前分支,回退 phase:"
211
+ echo " alloy _state set \"$CHANGE_DIR\" phase archived"
212
+ echo ""
213
+ echo " 禁止:agent 自动运行 git reset --hard origin/<main_branch> 强制对齐。"
214
+ exit 1
215
+ fi
216
+
217
+ # [HARD_STOP] git merge --squash 冲突时禁止 agent 自动运行:
218
+ # - git merge --abort(撕毁现场,用户 in-progress 修改消失)
219
+ # - git reset --hard <main_branch>(撕毁本地未推送 commit)
220
+ # - git checkout . / git restore .(撕毁工作目录改动)
221
+ # 详见 docs/reference/skill-writing-guide.md §3.5.1
222
+ # 冲突时必须:列出冲突文件 → 退出 skill → 让用户解决 → 用户重新运行 /alloy:finish
158
223
  git merge --squash <feature_branch>
159
- # 抓取被合入分支的完整 commit 列表,生成类似 GitHub squash merge 的 commit message
160
224
  COMMIT_LOG=$(git log <main_branch>..<feature_branch> --format="* %s")
161
225
  git commit -m "$(cat <<EOF
162
226
  chore(<name>): 合入 main(squash merge)
@@ -164,63 +228,95 @@ chore(<name>): 合入 main(squash merge)
164
228
  ${COMMIT_LOG}
165
229
  EOF
166
230
  )"
167
- # squash merge 不产生 merge commit,git 无法识别分支已合入,使用 -D 强删
231
+
232
+ # [task #13 备注] squash merge 产生新 commit hash,但 retrospective.md / verify.md / plans.md 等
233
+ # 制品 hash 已在 archive 阶段被 alloy _record 锁定到 records——records 记录的是制品文件 SHA-256,
234
+ # 而非 git commit hash。git 历史变化(squash / rebase)不影响已归档制品的不可篡改性。
235
+ # 因此 squash 后无需重录任何 hash;finish 阶段不再调 alloy _record write。
236
+
237
+ # [PRECONDITION_FAIL] git branch -D 前必须校验变量——
238
+ # <feature_branch> 是模板占位符,agent 在执行前必须替换为实际分支名。
239
+ # 如果替换缺失或意外指向 main_branch,强删会丢失主分支引用。
240
+ if [ -z "<feature_branch>" ] || [ "<feature_branch>" = "<main_branch>" ] || [ "<feature_branch>" = "main" ] || [ "<feature_branch>" = "master" ]; then
241
+ echo "[PRECONDITION_FAIL] feature_branch 变量未替换或与主分支同名,拒绝执行 git branch -D"
242
+ echo " feature_branch=<feature_branch>"
243
+ echo " main_branch=<main_branch>"
244
+ echo " 禁止:agent 自动猜测分支名继续执行。退出 skill 让用户检查 .alloy.yaml。"
245
+ exit 1
246
+ fi
168
247
  git branch -D <feature_branch>
169
248
  ```
170
249
 
171
- `git pull` 失败(网络不可达、认证失败),输出警告并暂停,让用户决定是否跳过 pull 直接 merge。若 `git merge --squash` 冲突,输出冲突文件列表,让用户手动解决后继续。
172
-
173
- 提示:"代码已合入 <main_branch>。Alloy 工作流完成。"
174
-
175
- **选项 2:创建 PR**
176
- - 先记录完成时间并推进 phase,作为分支上最后一个提交(PR squash merge 后主分支仅 1 个 commit):
177
- ```bash
178
- ARCHIVE_DIR=$(ls -d openspec/changes/archive/*-<name> 2>/dev/null | sort -r | head -1)
179
- CHANGE_DIR="${ARCHIVE_DIR:-openspec/changes/<name>}"
180
- COMPLETED_AT=$(date "+%Y-%m-%d %H:%M:%S")
181
- alloy _state merge "$CHANGE_DIR" phase_timings "{\"finish\":{\"completed_at\":\"${COMPLETED_AT:-$(date '+%Y-%m-%d %H:%M:%S')}\"}}"
182
- alloy _guard "$CHANGE_DIR" finished --apply
183
- git add -A "$CHANGE_DIR" openspec/config.yaml
184
- git commit -m "chore(<name>): 记录 finish 阶段完成时间"
185
- ```
186
- - 提示:"PR 已创建。审查通过后 squash merge 即可完成。"
187
- - 当用户收到 PR 审查反馈并在对话中讨论时,遵循以下行为规范(来自 superpowers:receiving-code-review):
188
- - **验证优先** —— 不要盲从审查意见。先验证 reviewer 指出的问题是否真实存在,再决定是否修改
189
- - **技术推理** —— 如果你的实现有技术理由,解释原因而不是被动接受。reviewer 可能缺少上下文
190
- - **不要表演性认同** —— 不理解的评论不要假装同意。追问清楚再动手
191
- - **每条反馈独立回应** —— 不要批量处理,逐一确认、验证、修改
192
- - **spec 级变更 = 新 change** —— 如果审查反馈要求修改 spec,当前 change 的 spec 已归档,应走新 change
193
-
194
- **选项 3:保持分支**
195
- - 提示:"分支已保留。后续需要时再次运行 `/alloy:finish <name>` 进行处理。"
196
- - phase 保持 archived,不推进到 finished
250
+ `git pull` 失败按上述 USER_GATE 三选项(重试 / 跳过 pull / 中止)处理;agent 不得自动绕过。`git merge --squash` 冲突时列出冲突文件让用户手动解决,禁止 `git merge --abort`(详见 §3.5.1)。
251
+
252
+ ### 选项 2:创建 PR
253
+
254
+ 先记录完成时间并推进 phase:
255
+ ```bash
256
+ ARCHIVE_DIR=$(ls -d openspec/changes/archive/*-<name> 2>/dev/null | sort -r | head -1)
257
+ CHANGE_DIR="${ARCHIVE_DIR:-openspec/changes/<name>}"
258
+ COMPLETED_AT=$(date "+%Y-%m-%d %H:%M:%S")
259
+ # [§5.2.3 路径 B] phase 推进发生在 PR 创建之前。PR 后续被 close / 不合入时,
260
+ # 用户须手动按以下 3 步回退(与选项 1 同款手动回退路径):
261
+ # alloy _state set "$CHANGE_DIR" phase archived
262
+ # git checkout HEAD~1 -- "$CHANGE_DIR/.alloy.yaml" # 撤销 phase commit 中的状态变更
263
+ # git reset HEAD~1 # 退回 phase commit
264
+ # 禁止 agent 自动 git reset --hard / git checkout . 清场(§3.5.1)。
265
+ alloy _state merge "$CHANGE_DIR" phase_timings "{\"finish\":{\"completed_at\":\"${COMPLETED_AT:-$(date '+%Y-%m-%d %H:%M:%S')}\"}}"
266
+ alloy _guard "$CHANGE_DIR" finished --apply
267
+ # [§5.2.1] git add 路径化——禁用 -A / -a / .,避免误提交 agent 看不到的副作用文件
268
+ git add "$CHANGE_DIR" openspec/config.yaml
269
+ git commit -m "chore(<name>): 记录 finish 阶段完成时间"
270
+ ```
271
+
272
+ PR 审查反馈的处理规范:
273
+ - **验证优先** —— 不盲从审查意见,先验证问题是否真实
274
+ - **技术推理** —— 有技术理由时解释而非被动接受
275
+ - **不表演性认同** —— 不理解的追问清楚
276
+ - **每条独立回应** —— 不批量处理
277
+ - **spec 变更 = 新 change(HARD_STOP)** —— 当前 spec 已归档封存。当代码修改可能影响 spec 行为时,必须 🔴 USER_GATE:
278
+
279
+ > AskUserQuestion: PR 审查反馈是否需要 spec 级修改?
280
+ > (a) 不需要——仅代码调整不影响行为
281
+ > (b) 需要——退出 finish,运行 /alloy:start <new-name> 开新 change
282
+ > (c) 暂不决定——保持 PR 不合入,等待澄清
283
+ >
284
+ > 选 (b):[HARD_STOP] 禁止 agent 直接修改已归档 spec。退出 skill。
285
+
286
+ ### 选项 3:保持分支
287
+
288
+ 记录延期时间戳供后续 `alloy status` 统计:
289
+ ```bash
290
+ ARCHIVE_DIR=$(ls -d openspec/changes/archive/*-<name> 2>/dev/null | sort -r | head -1)
291
+ CHANGE_DIR="${ARCHIVE_DIR:-openspec/changes/<name>}"
292
+ DEFERRED_AT=$(date "+%Y-%m-%d %H:%M:%S")
293
+ alloy _state merge "$CHANGE_DIR" phase_timings "{\"finish\":{\"deferred_at\":\"${DEFERRED_AT}\"}}"
294
+ git add "$CHANGE_DIR/.alloy.yaml"
295
+ git commit -m "chore(<name>): finish 延期,分支已保留"
296
+ ```
297
+
298
+ > ⚠️ WARN(task #27):finish 延期已记录 deferred_at=${DEFERRED_AT}。
299
+ > 后续 `alloy status` 会基于此提示分支堆积;建议在合适时机重新运行 /alloy:finish 完成合入。
300
+
301
+ 提示:"分支已保留。后续需要时再次运行 `/alloy:finish <name>`。" phase 保持 archived,不推进——此选项不破坏不变式:`deferred_at` 仅是观测信号,phase 字段保持 archived 由 `alloy _guard` 校验(task #27)。
197
302
 
198
303
  ---
199
304
 
200
305
  ### [Step 3/3] 完成
201
306
 
202
- 完成时间已在 Step 2 的记录 commit 中写入。finish 阶段不产生额外 commit——合入 commit(选项 1)或 PR(选项 2)本身就是终端动作。
203
-
204
307
  ```
205
308
  ┌──────────────────────────────────────┐
206
309
  │ Alloy [5/5] · Phase: Finish — DONE │
207
- │ 启动时间: phase_timings.finish.started_at 读取
208
- │ 完成时间: phase_timings.finish.completed_at 读取
209
- │ 耗时: completed_at - started_at
310
+ │ 启动时间: phase_timings.finish.started_at
311
+ │ 完成时间: phase_timings.finish.completed_at
312
+ │ 耗时: completed_at - started_at
210
313
  └──────────────────────────────────────┘
211
314
 
212
- → Change: <name>
213
- Phase: finished
214
- → 处理方式: <本地 merge / PR / 保留分支>
215
- → 分支: <merged / 已删除 / 保留>
315
+ → Change: <name> Phase: finished
316
+ 处理方式: <本地 merge / PR / 保留分支> 分支: <merged / 已删除 / 保留>
317
+ ```
216
318
 
217
- ---
319
+ finish 不产生额外 commit——合入 commit 或 PR 本身就是终端动作。
218
320
 
219
- ## 闸门规则
321
+ **git add 路径化(§5.2.1):** 仅用精确路径(`"$CHANGE_DIR" openspec/config.yaml`),禁用 `-A` / `-a` / `.`。违反字面 = 违反精神:哪怕"反正只有一个文件改动",也禁止 `-A`——agent 看不到的副作用文件可能被一并提交。
220
322
 
221
- - **git add 只用精确路径** — 永远不用 `-a`、`.`。
222
- finish 阶段用 `git add -A "$CHANGE_DIR" openspec/config.yaml`(`$CHANGE_DIR` 为当前 change 的归档路径,`-A` 限路径,只追踪 change 目录和 openspec 配置的新增/修改/删除),代码合入由 git merge 处理
223
- - **phase 必须为 archived** —— spec 已归档的 change 才能 finish
224
- - **分支必须存在** —— 分支已 merge 或删除时无需再次 finish
225
- - **不涉及 spec 变更** —— spec 已归档封存,任何 spec 级修改应走新 change
226
- - **选项 3 不推进 phase** —— 保持分支意味着未真正收尾,phase 停留在 archived