@flyin-ai/alloy 0.2.0-beta.0 → 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.
- package/commands/alloy/apply.md +222 -430
- package/commands/alloy/archive.md +156 -132
- package/commands/alloy/discard.md +26 -11
- package/commands/alloy/finish.md +212 -116
- package/commands/alloy/fix.md +103 -258
- package/commands/alloy/plan.md +204 -301
- package/commands/alloy/references/apply-precheck.md +21 -0
- package/commands/alloy/references/apply-rationalizations.md +18 -0
- package/commands/alloy/references/apply-subagent-commit.md +49 -0
- package/commands/alloy/references/apply-worktree.md +130 -0
- package/commands/alloy/references/archive-rationalizations.md +16 -0
- package/commands/alloy/references/archive-worktree-cleanup.md +94 -0
- package/commands/alloy/references/artifact-hash-commit.md +49 -0
- package/commands/alloy/references/branch-naming.md +65 -0
- package/commands/alloy/references/branch-validation.md +36 -0
- package/commands/alloy/references/fix-precommit-check.md +58 -0
- package/commands/alloy/references/interaction-style.md +34 -1
- package/commands/alloy/references/main-branch-detection.md +3 -6
- package/commands/alloy/references/phase-downgrade-path.md +27 -0
- package/commands/alloy/references/plan-rollback.md +84 -0
- package/commands/alloy/references/spec-sync.md +62 -0
- package/commands/alloy/references/start-rationalizations.md +18 -0
- package/commands/alloy/start.md +189 -227
- package/dist/cli/commands/completion.js +13 -2
- package/dist/cli/commands/completion.js.map +1 -1
- package/dist/cli/commands/internal/artifact.d.ts +7 -0
- package/dist/cli/commands/internal/artifact.js +70 -0
- package/dist/cli/commands/internal/artifact.js.map +1 -0
- package/dist/cli/commands/internal/guard.js +204 -2
- package/dist/cli/commands/internal/guard.js.map +1 -1
- package/dist/cli/commands/internal/progress.d.ts +10 -0
- package/dist/cli/commands/internal/progress.js +70 -0
- package/dist/cli/commands/internal/progress.js.map +1 -0
- package/dist/cli/commands/internal/spec-audit.d.ts +42 -0
- package/dist/cli/commands/internal/spec-audit.js +363 -0
- package/dist/cli/commands/internal/spec-audit.js.map +1 -0
- package/dist/cli/commands/internal/state.js +4 -0
- package/dist/cli/commands/internal/state.js.map +1 -1
- package/dist/cli/index.js +30 -0
- package/dist/cli/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -3,45 +3,53 @@ name: "Alloy: Archive"
|
|
|
3
3
|
description: Alloy 归档阶段 - apply 完成后进入
|
|
4
4
|
category: Workflow
|
|
5
5
|
tags: [alloy, workflow]
|
|
6
|
+
spec: 01-product-spec/04-archive-spec.md
|
|
7
|
+
behaviors:
|
|
8
|
+
preconditions: 5
|
|
9
|
+
hard_stops: 7
|
|
10
|
+
user_gates: 3
|
|
11
|
+
warns: 1
|
|
12
|
+
artifacts: [delta-spec, archive]
|
|
13
|
+
transitions_to: archived
|
|
14
|
+
external_calls: [opsx:archive]
|
|
6
15
|
---
|
|
7
16
|
|
|
8
17
|
# alloy-archive
|
|
9
18
|
|
|
10
|
-
你是 Alloy
|
|
19
|
+
你是 Alloy 的归档阶段编排器。验证 change 已完成执行,执行 Delta Spec 合并和归档,推进 phase 到 `archived`。
|
|
11
20
|
|
|
12
|
-
|
|
21
|
+
```
|
|
22
|
+
[HARD_STOP] NO ARCHIVE WITH FAIL
|
|
23
|
+
verify.md FAIL / merge 冲突 / memory 批量 / git status dirty 任一存在 = 拒绝归档
|
|
24
|
+
违反字面 = 违反精神:哪怕看似"小问题"、"先归档再补"、或用户主动说"一次过吧"要求批量打包 memory,也算违反 Iron Law。逐条 = 逐条——用户要求合并不算授权。
|
|
25
|
+
```
|
|
13
26
|
|
|
14
|
-
|
|
27
|
+
**核心原则:先锁定文档证据链,再合入代码。** archive 只负责 spec 归档,代码合入由 `/alloy:finish` 完成。
|
|
15
28
|
|
|
16
|
-
|
|
29
|
+
**调用外部命令或技能前,先输出标题和状态描述,再执行操作。**
|
|
30
|
+
|
|
31
|
+
**捕获阶段启动时间**(幂等,重入时返回已有值):
|
|
17
32
|
```bash
|
|
18
33
|
PHASE_START=$(alloy _state timestamp ensure openspec/changes/<name> archive)
|
|
19
34
|
```
|
|
20
35
|
|
|
21
36
|
---
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
- verify.md 的 Overall Decision 是 FAIL 但仍然继续归档——阻塞问题被无视
|
|
25
|
-
- 跳过 archive 直接手动 merge——Delta Spec 没有被同步,主 spec 落后于代码
|
|
26
|
-
- openspec archive 返回错误但忽视警告继续——"反正代码对的,spec 后面再说"
|
|
27
|
-
|
|
28
|
-
### Red Flags——STOP,不要继续
|
|
38
|
+
### Red Flags(第三层防御——任一借口出现即 STOP)
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
主文件保留 5 条核心借口,完整 11 条见 `commands/alloy/references/archive-rationalizations.md`。
|
|
31
41
|
|
|
32
42
|
| 借口 | 现实 |
|
|
33
43
|
|------|------|
|
|
34
|
-
| "verify.md FAIL 是小问题,先归档再说" | FAIL = 阻塞问题。归档不可逆——带着 FAIL 归档意味着 spec
|
|
35
|
-
| "
|
|
36
|
-
| "
|
|
37
|
-
| "
|
|
38
|
-
| "
|
|
44
|
+
| "verify.md FAIL 是小问题,先归档再说" | FAIL = 阻塞问题。归档不可逆——带着 FAIL 归档意味着 spec 与代码偏差被永久封存。 |
|
|
45
|
+
| "spec 合并看起来没问题,直接继续" | 没看过的 spec 变更 = 代码与规格可能已分叉。审查只需 1 分钟,修复分叉需要 1 小时。 |
|
|
46
|
+
| "merge 冲突了,git merge --abort 一下让流程继续" | 冲突 = 代码状态未达预期,自动 abort = 隐藏真问题。退出 skill 让用户处理是唯一合法路径(§3.5.1)。 |
|
|
47
|
+
| "memory 候选都对,全部写入吧" | 单次确认承担不了全局污染风险。每条独立 USER_GATE,无例外(§5.2.2)。即使用户说"一次过吧",逐条规则不可合并——用户要求打包不算授权。 |
|
|
48
|
+
| "另一个 change 也在 archive,等一下吧" | 多 change 并行 archive = Delta Spec 合并顺序敏感。先归档晚开始的 = 主 spec 状态错乱。必须串行。 |
|
|
39
49
|
|
|
40
50
|
---
|
|
41
51
|
|
|
42
|
-
##
|
|
43
|
-
|
|
44
|
-
### [Step 1/3] 前置检查
|
|
52
|
+
## 前置检查
|
|
45
53
|
|
|
46
54
|
```
|
|
47
55
|
┌──────────────────────────────────────┐
|
|
@@ -50,177 +58,193 @@ PHASE_START=$(alloy _state timestamp ensure openspec/changes/<name> archive)
|
|
|
50
58
|
└──────────────────────────────────────┘
|
|
51
59
|
```
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
cmd: opsx/archive
|
|
61
|
+
### [Step 1/3] 前置检查
|
|
55
62
|
|
|
56
|
-
|
|
63
|
+
**0. Skill 预检:** cmd: opsx/archive
|
|
57
64
|
|
|
58
|
-
|
|
65
|
+
读取 `commands/alloy/references/skill-precheck.md` 检测。不可用 → 引导 `alloy init` → STOP。
|
|
66
|
+
|
|
67
|
+
**1. Worktree 清洁度(PRECONDITION_FAIL):** archive 会 commit 归档变更并合并 worktree——未 commit 的非 spec/changes 路径变更会污染结果。
|
|
59
68
|
|
|
60
|
-
先通过 `alloy _guard` 做硬校验:
|
|
61
69
|
```bash
|
|
62
|
-
|
|
70
|
+
DIRTY=$(git status --porcelain -uno)
|
|
71
|
+
if [ -n "$DIRTY" ]; then
|
|
72
|
+
echo "⛔ [PRECONDITION_FAIL] worktree 有未提交变更,archive 拒绝执行:"
|
|
73
|
+
git status --short
|
|
74
|
+
echo ""
|
|
75
|
+
echo " 请先 commit 或 stash 保留变更。"
|
|
76
|
+
echo " 禁止:git stash drop / git reset --hard / git checkout . / git restore . 直接丢弃工作(§3.5.1)。"
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
63
79
|
```
|
|
64
80
|
|
|
65
|
-
|
|
81
|
+
跳过 untracked(`-uno`)——untracked 不会被 commit/merge 影响 archive。
|
|
66
82
|
|
|
67
|
-
**
|
|
83
|
+
**2. phase 检查(PRECONDITION_FAIL):**
|
|
68
84
|
|
|
69
|
-
**2. verify.md 存在且 Overall Decision 不是 FAIL:**
|
|
70
85
|
```bash
|
|
71
|
-
|
|
86
|
+
alloy _guard precheck openspec/changes/<name> applied
|
|
72
87
|
```
|
|
73
|
-
|
|
88
|
+
|
|
89
|
+
不匹配时读取 `commands/alloy/references/phase-routing.md` 自动跳转。change 目录不存在 → 引导 `/alloy:start`。
|
|
90
|
+
|
|
91
|
+
**3. verify.md 检查(PRECONDITION_FAIL):**
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
alloy _guard verify-passed openspec/changes/<name>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
FAIL → "verify.md 有阻塞问题。请先修复。" PASS/WARNING → 继续。
|
|
98
|
+
|
|
99
|
+
**4. 多 change 并行 archive 检测(WARN,task #14):** Delta Spec 合并顺序敏感——同期多个 change 在 archive 状态时,先归档晚开始的可能导致主 spec 状态错乱。
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
PARALLEL=$(find openspec/changes -maxdepth 2 -name .alloy.yaml \
|
|
103
|
+
-exec grep -l "phase: applied\|phase: archive" {} \; 2>/dev/null \
|
|
104
|
+
| grep -v "/<name>/" | wc -l)
|
|
105
|
+
if [ "$PARALLEL" -gt 0 ]; then
|
|
106
|
+
echo "⚠️ [WARN] 检测到 $PARALLEL 个其他 change 处于 applied/archive 状态:"
|
|
107
|
+
find openspec/changes -maxdepth 2 -name .alloy.yaml \
|
|
108
|
+
-exec grep -l "phase: applied\|phase: archive" {} \; 2>/dev/null | grep -v "/<name>/"
|
|
109
|
+
echo ""
|
|
110
|
+
echo " Delta Spec 合并顺序敏感,建议按 archive 启动时间串行处理。"
|
|
111
|
+
echo " 继续当前 archive 前请确认其他 change 不会同时归档。"
|
|
112
|
+
fi
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
不阻断——仅提示。
|
|
74
116
|
|
|
75
117
|
---
|
|
76
|
-
### [Step 2/3] /opsx:archive
|
|
77
118
|
|
|
78
|
-
|
|
79
|
-
> 正在归档——Delta Spec 合并到主 spec → 移入 archive/...
|
|
119
|
+
### [Step 2/3] /opsx:archive
|
|
80
120
|
|
|
81
|
-
|
|
121
|
+
```
|
|
122
|
+
[Step 2/3] /opsx:archive
|
|
123
|
+
正在归档——Delta Spec 合并到主 spec → 移入 archive/...
|
|
124
|
+
```
|
|
82
125
|
|
|
83
|
-
|
|
84
|
-
- Delta Spec 合并到主 spec(`openspec/specs/`)
|
|
85
|
-
- Change 目录移至 `openspec/changes/archive/YYYY-MM-DD-<name>/`
|
|
86
|
-
- 自有幂等检查——已归档则 Skip
|
|
126
|
+
调用 `/opsx:archive`,传入 change name。该命令自动完成 Delta Spec 合并 + 目录移动。自有幂等检查——已归档则 Skip。
|
|
87
127
|
|
|
88
|
-
|
|
89
|
-
- `/opsx:archive` 返回错误(权限、冲突等)→ [HARD STOP],不推进 phase
|
|
90
|
-
- `/opsx:archive` 不可用(OpenSpec 未安装)→ 引导用户运行 `alloy init` 安装 OpenSpec
|
|
128
|
+
**错误处理(HARD_STOP):** 返回错误 → ⛔ `[HARD_STOP] /opsx:archive 失败,归档中止`。不可用 → 引导 `alloy init`。**禁止:忽略错误继续后续步骤——Delta Spec 未合并时主 spec 与代码已分叉,强行推进 phase 会永久封存分叉。**
|
|
91
129
|
|
|
92
|
-
**命令执行后立即记录:**
|
|
93
130
|
```bash
|
|
94
131
|
alloy _skill log openspec/changes/<name> archive opsx:archive
|
|
95
132
|
```
|
|
96
133
|
|
|
97
|
-
|
|
134
|
+
**Delta Spec 合并审查(USER_GATE,task #22 强制 diff 注入):**
|
|
98
135
|
|
|
99
|
-
|
|
136
|
+
合并完成后,**必须先采集 diff 写入 AskUserQuestion 上下文**,沉默不算授权——agent 不可基于"看起来没问题"自动通过。
|
|
100
137
|
|
|
101
138
|
```bash
|
|
102
|
-
git
|
|
103
|
-
git diff
|
|
139
|
+
SPEC_DIFF=$(git diff --stat openspec/specs/)
|
|
140
|
+
SPEC_DIFF_FULL=$(git diff openspec/specs/ | head -200) # 截 200 行防爆量
|
|
104
141
|
```
|
|
105
142
|
|
|
106
|
-
|
|
143
|
+
🔴 USER_GATE(必须 AskUserQuestion,问题模板):
|
|
144
|
+
|
|
145
|
+
> Delta Spec 合并结果:
|
|
146
|
+
> ```
|
|
147
|
+
> [SPEC_DIFF stat 摘要]
|
|
148
|
+
> ```
|
|
149
|
+
> 前 200 行 diff:
|
|
150
|
+
> ```
|
|
151
|
+
> [SPEC_DIFF_FULL]
|
|
152
|
+
> ```
|
|
153
|
+
> 选项:
|
|
154
|
+
> (a) 确认并继续提交归档变更
|
|
155
|
+
> (b) 调整 spec 合并内容——退出 skill,回到 `/opsx:archive` 参数调整或手动修正 spec 后重新运行
|
|
156
|
+
|
|
157
|
+
**违反字面 = 违反精神:** 哪怕 diff 看似"明显合理",没经过用户明确选择 (a) = 不算授权。禁止 agent 基于"diff 短"或"无 conflict"自动跳过此 USER_GATE。
|
|
107
158
|
|
|
108
|
-
|
|
159
|
+
**归档变更提交(HARD_STOP §5.2.1 git add 限路径):** 必须在 worktree 清理之前 commit,否则清理时 merge 会丢失归档操作。**禁止 `git add -A` 无路径——只 add `openspec/specs/ openspec/changes/` 两个明确路径,避免把无关 working tree 变更卷入归档 commit(§5.2.1)。**
|
|
109
160
|
|
|
110
161
|
```bash
|
|
111
|
-
|
|
112
|
-
|
|
162
|
+
git add openspec/specs/ openspec/changes/
|
|
163
|
+
git diff --cached --quiet || git commit -m "chore(<name>): 归档目录移动"
|
|
113
164
|
```
|
|
114
165
|
|
|
115
|
-
|
|
166
|
+
`git commit` 失败 → ⛔ `[HARD_STOP] 归档 commit 失败,archive 中止。检查 git 状态后重试。`
|
|
167
|
+
|
|
168
|
+
归档路径:`ARCHIVE_DIR="openspec/changes/archive/$(date +%Y-%m-%d)-<name>"`
|
|
169
|
+
|
|
170
|
+
**读取 retrospective.md §6 Promote Candidates:** 标记 `→ Promote to: memory` 的条目,将 Why/How to apply 写入 `~/.claude/memory/` 对应文件。这是 retrospective 从"死文档"变"活反馈"的关键。
|
|
171
|
+
|
|
172
|
+
**memory 写入逐条确认(USER_GATE + HARD_STOP §5.2.2):**
|
|
173
|
+
|
|
174
|
+
详细禁令见 `commands/alloy/references/interaction-style.md` "沉默 ≠ 授权"章节——批量打包是首条反模式。本阶段适用:retrospective Promote Candidates 必须**每条独立** AskUserQuestion,无论候选数量、相似度或"看起来都对"。**[HARD_STOP] 即使用户主动说"一次过吧"或"都挺合理的",也不可合并——用户要求打包不算授权,agent 必须拒绝并逐条展示。**
|
|
175
|
+
|
|
176
|
+
逐条流程:
|
|
177
|
+
|
|
178
|
+
1. 解析 retrospective.md §6,提取每条 `→ Promote to: memory` 候选
|
|
179
|
+
2. 对每条候选**单独** AskUserQuestion:
|
|
180
|
+
|
|
181
|
+
> 候选 [N/M]:写入 ~/.claude/memory/?
|
|
182
|
+
> 内容:[Why + How to apply 摘要]
|
|
183
|
+
> 选项:(a) 写入 (b) 跳过 (c) 修改后写入
|
|
184
|
+
|
|
185
|
+
3. (a) → 立即写入对应 memory 文件
|
|
186
|
+
4. (b) → 跳过,记录到 retrospective.md 末尾"Skipped from memory promotion"章节
|
|
187
|
+
5. (c) → 用户提供调整后的 Why/How 文本,写入修改版
|
|
188
|
+
6. 全部条目处理后输出汇总:N 条写入、M 条跳过、K 条修改后写入
|
|
116
189
|
|
|
117
|
-
|
|
190
|
+
无 Promote Candidates → 跳过本步骤。
|
|
118
191
|
|
|
119
|
-
**Worktree 清理(如果 apply 期间使用了 worktree):**
|
|
192
|
+
**Worktree 清理(如果 apply 期间使用了 worktree):** 读取 `commands/alloy/references/archive-worktree-cleanup.md` 执行完整流程。要点:
|
|
193
|
+
|
|
194
|
+
- task #21 silent fallback 检测:state 字段未写但 worktree 存在 → ⛔ PRECONDITION_FAIL,禁 silent fallback
|
|
195
|
+
- 遗留 change 兼容:FEATURE_BRANCH / WORKTREE_BRANCH 缺失时回退/检测,仍缺失 → ⛔ PRECONDITION_FAIL
|
|
196
|
+
- merge 成功 → 🔴 USER_GATE(确认并清理 / 退出审查),(a) 选清理 → `git worktree remove` + `git branch -d`
|
|
197
|
+
- merge 冲突 → 报告冲突现场后退出 skill。**[HARD_STOP §3.5.1] 禁 agent 自动 `git merge --abort` / `git reset --hard` / `git checkout .` / `git restore .` / `git stash` / `git clean -fd` / `git push --force` 任何一个。** 违反字面 = 违反精神。
|
|
198
|
+
|
|
199
|
+
未使用 worktree(`worktree=skipped` 或字段缺失且 git 无残留)时跳过本段。
|
|
200
|
+
|
|
201
|
+
**Worktree 合并时间记录:** worktree merge + remove + branch -d 全部成功后,写入 `worktree_merged_at`:
|
|
120
202
|
|
|
121
203
|
```bash
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
WORKTREE_BRANCH=$(alloy _state read "$ARCHIVE_DIR" worktree_branch 2>/dev/null)
|
|
125
|
-
|
|
126
|
-
if [ "$WORKTREE_PATH" != "null" ] && [ -n "$WORKTREE_PATH" ] && [ "$WORKTREE_PATH" != "skipped" ]; then
|
|
127
|
-
echo " ℹ 检测到 worktree($WORKTREE_PATH),正在合并回 feature 分支..."
|
|
128
|
-
|
|
129
|
-
# 向下兼容:遗留 change 无 feature_branch → 退回到 feature/<name>
|
|
130
|
-
if [ -z "$FEATURE_BRANCH" ] || [ "$FEATURE_BRANCH" = "null" ]; then
|
|
131
|
-
FEATURE_BRANCH="feature/<name>"
|
|
132
|
-
fi
|
|
133
|
-
|
|
134
|
-
# 向下兼容:遗留 change 无 worktree_branch → 从 worktree 实际状态检测
|
|
135
|
-
if [ -z "$WORKTREE_BRANCH" ] || [ "$WORKTREE_BRANCH" = "null" ]; then
|
|
136
|
-
WORKTREE_BRANCH=$(git worktree list --porcelain | awk -v path="$WORKTREE_PATH" '
|
|
137
|
-
/^worktree / { wt = substr($0, 10) }
|
|
138
|
-
/^branch / && wt == path { gsub(/^refs\/heads\//, "", $2); print $2; exit }
|
|
139
|
-
')
|
|
140
|
-
if [ -z "$WORKTREE_BRANCH" ]; then
|
|
141
|
-
echo " ⚠ 无法检测 worktree 分支名(worktree_branch 为空且 git worktree list 未匹配)"
|
|
142
|
-
echo " 请手动指定: git merge <worktree-branch> && git worktree remove $WORKTREE_PATH"
|
|
143
|
-
exit 1
|
|
144
|
-
fi
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
# 切回主仓库目录
|
|
148
|
-
MAIN_ROOT=$(cd "$WORKTREE_PATH" && git rev-parse --show-toplevel 2>/dev/null)
|
|
149
|
-
|
|
150
|
-
# 从 worktree 分支合并代码到 feature 分支
|
|
151
|
-
cd "$MAIN_ROOT"
|
|
152
|
-
git merge "$WORKTREE_BRANCH" --no-edit
|
|
153
|
-
|
|
154
|
-
if [ $? -eq 0 ]; then
|
|
155
|
-
# 删除 worktree 目录和分支
|
|
156
|
-
git worktree remove "$WORKTREE_PATH"
|
|
157
|
-
git branch -d "$WORKTREE_BRANCH"
|
|
158
|
-
# 标记已清理(保留 worktree/worktree_branch 原值不删除)
|
|
159
|
-
WORKTREE_MERGED_AT=$(date '+%Y-%m-%d %H:%M:%S')
|
|
160
|
-
alloy _state write "$ARCHIVE_DIR" worktree_merged_at "$WORKTREE_MERGED_AT"
|
|
161
|
-
echo " ✓ worktree 已合并至 $FEATURE_BRANCH 分支并清理"
|
|
162
|
-
else
|
|
163
|
-
echo " ⚠ merge 冲突,请手动解决后再继续"
|
|
164
|
-
echo " 先: git checkout $FEATURE_BRANCH && git merge $WORKTREE_BRANCH"
|
|
165
|
-
exit 1
|
|
166
|
-
fi
|
|
167
|
-
fi
|
|
204
|
+
WORKTREE_MERGED_AT=$(date '+%Y-%m-%d %H:%M:%S')
|
|
205
|
+
alloy _state write "$ARCHIVE_DIR" worktree_merged_at "$WORKTREE_MERGED_AT"
|
|
168
206
|
```
|
|
169
207
|
|
|
170
|
-
|
|
208
|
+
未使用 worktree 时跳过本步。
|
|
171
209
|
|
|
172
|
-
|
|
210
|
+
**记录完成时间并提交(HARD_STOP §5.2.1 git add 限路径):**
|
|
173
211
|
|
|
174
212
|
```bash
|
|
175
|
-
# 写入完成时间(:="${...:-...}" 兜底:防 Agent 漏 capture)
|
|
176
213
|
COMPLETED_AT="${COMPLETED_AT:-$(date '+%Y-%m-%d %H:%M:%S')}"
|
|
177
214
|
COMPLETED_AT_JSON=$(python3 -c "import json; print(json.dumps({'archive':{'completed_at': '$COMPLETED_AT'}}))")
|
|
178
215
|
alloy _state merge "$ARCHIVE_DIR" phase_timings "$COMPLETED_AT_JSON"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
# 归档目录移动已在 worktree 清理前提交(或无 worktree 时在上方提交)
|
|
182
|
-
git add -A openspec/specs/ openspec/changes/
|
|
216
|
+
# §5.2.1: git add 限路径,禁 -A 无路径
|
|
217
|
+
git add openspec/specs/ openspec/changes/
|
|
183
218
|
git commit -m "chore(<name>): 归档阶段完成"
|
|
184
219
|
```
|
|
185
|
-
commit 失败必须阻断——.alloy.yaml 变更未提交时,后续 finish 阶段状态不一致。
|
|
186
220
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
221
|
+
`git commit` 失败 → ⛔ `[HARD_STOP] 归档 commit 失败,archive 中止。.alloy.yaml 变更未提交时 finish 状态不一致。检查 git 状态后重试,禁止在 commit 失败时继续执行后续步骤。`
|
|
222
|
+
|
|
223
|
+
### [Step 3/3] 推进 phase
|
|
190
224
|
|
|
191
|
-
|
|
225
|
+
**通过 `alloy _guard` 校验并推进 phase(HARD_STOP §5.2.3 路径 B 降级):**
|
|
226
|
+
|
|
227
|
+
降级路径详见 `commands/alloy/references/phase-downgrade-path.md`(archive 阶段降级 → `applied`)。**禁止 agent 自动 `git reset --hard` / `git checkout .` 清场(§3.5.1)。**
|
|
192
228
|
|
|
193
|
-
**通过 `alloy _guard` 校验并推进 phase:**
|
|
194
229
|
```bash
|
|
195
230
|
alloy _guard "$ARCHIVE_DIR" archived --apply
|
|
196
|
-
git add
|
|
231
|
+
git add openspec/specs/ openspec/changes/
|
|
197
232
|
git commit -m "chore(<name>): phase → archived"
|
|
198
233
|
```
|
|
199
234
|
|
|
200
235
|
```
|
|
201
236
|
┌──────────────────────────────────────┐
|
|
202
237
|
│ Alloy [4/5] · Phase: Archive — DONE │
|
|
203
|
-
│ 启动时间:
|
|
204
|
-
│ 完成时间:
|
|
205
|
-
│ 耗时: completed_at - started_at
|
|
238
|
+
│ 启动时间: phase_timings.archive.started_at
|
|
239
|
+
│ 完成时间: phase_timings.archive.completed_at
|
|
240
|
+
│ 耗时: completed_at - started_at
|
|
206
241
|
└──────────────────────────────────────┘
|
|
207
242
|
|
|
208
|
-
→ Change: <name>
|
|
209
|
-
→ Phase: archived
|
|
243
|
+
→ Change: <name> Phase: archived
|
|
210
244
|
→ 归档位置: archive/YYYY-MM-DD-<name>/
|
|
245
|
+
→ ✓ Delta Spec 已合并 ✓ Change 已归档
|
|
246
|
+
→ 代码合入由 /alloy:finish 处理
|
|
247
|
+
```
|
|
211
248
|
|
|
212
|
-
|
|
213
|
-
✓ Change 已归档
|
|
214
|
-
|
|
215
|
-
> → 代码合入由 `/alloy:finish` 处理
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
## 闸门规则
|
|
249
|
+
archive 不做代码合并——代码合入由 `/alloy:finish` 处理。准备好后运行 `/alloy:finish` 进入收尾阶段。
|
|
220
250
|
|
|
221
|
-
- **git add 只用精确路径** — 永远不用 `-a`、`.`。
|
|
222
|
-
archive 用 `git add -A openspec/specs/ openspec/changes/`(`-A` 限路径,只追踪这两个目录的新增/修改/删除),反例:`git add -A`(无路径限定,扫全仓)
|
|
223
|
-
- **phase 必须为 applied** —— 只有 apply 完成的 change 才能归档
|
|
224
|
-
- **verify.md 必须存在且非 FAIL** —— 阻塞问题必须先修复
|
|
225
|
-
- **先归档后合入** —— spec 文档先锁定,代码后通过 `/alloy:finish` 合入,避免"代码合入了 spec 还没跟上"
|
|
226
|
-
- **archive 不做代码合并** —— 代码合入是 `/alloy:finish` 的职责
|
|
@@ -3,13 +3,21 @@ name: "Alloy: Discard"
|
|
|
3
3
|
description: Alloy 放弃 change - 需要放弃时调用
|
|
4
4
|
category: Workflow
|
|
5
5
|
tags: [alloy, workflow]
|
|
6
|
+
spec: 01-product-spec/07-discard-spec.md
|
|
7
|
+
behaviors:
|
|
8
|
+
stops: 0
|
|
9
|
+
hard_stops: 1
|
|
10
|
+
artifacts: [discard-archive]
|
|
11
|
+
artifacts: []
|
|
12
|
+
transitions_to: ""
|
|
13
|
+
external_calls: []
|
|
6
14
|
---
|
|
7
15
|
|
|
8
16
|
# alloy-discard
|
|
9
17
|
|
|
10
18
|
你是 Alloy 的放弃清理器。你的职责是:根据 change 的当前 phase 执行分级清理,确保用户明确确认后再删除。
|
|
11
19
|
|
|
12
|
-
|
|
20
|
+
**核心原则:软删除(移到 archive/)而非物理删除,保留完整审计链,允许误删后恢复。**
|
|
13
21
|
|
|
14
22
|
每个 change 必须有独立的 feature 分支(start step 6 保证),discard 时可安全删除整个分支。
|
|
15
23
|
|
|
@@ -36,8 +44,8 @@ alloy _config read . main_branch
|
|
|
36
44
|
|
|
37
45
|
| phase | 清理动作 |
|
|
38
46
|
|-------|---------|
|
|
39
|
-
| started / planned | `git checkout <main_branch>` + `git branch -D <feature_branch>` +
|
|
40
|
-
| applied / archived | `git worktree remove` + `git checkout <main_branch>` + `git branch -D <feature_branch>` +
|
|
47
|
+
| started / planned | `git checkout <main_branch>` + `git branch -D <feature_branch>` + 软删除 → `archive/` |
|
|
48
|
+
| applied / archived | `git worktree remove` + `git checkout <main_branch>` + `git branch -D <feature_branch>` + 软删除 → `archive/` |
|
|
41
49
|
| finished | **[HARD STOP] 已完成的 change 不可 discard。** finished 是终态 |
|
|
42
50
|
|
|
43
51
|
---
|
|
@@ -55,13 +63,13 @@ alloy _config read . main_branch
|
|
|
55
63
|
清理前必须展示将要删除的内容并等待用户精确确认:
|
|
56
64
|
|
|
57
65
|
```
|
|
58
|
-
|
|
66
|
+
将软删除以下内容(移到 archive/ 保留审计链,可手动恢复):
|
|
59
67
|
|
|
60
68
|
Change: <name>
|
|
61
69
|
Phase: <phase>
|
|
62
70
|
Feature 分支: <feature_branch>(如有)
|
|
63
71
|
Worktree: <path>(如有)
|
|
64
|
-
目录: openspec/changes/<name>/
|
|
72
|
+
目录: openspec/changes/<name>/ → archive/YYYY-MM-DD-discard-<name>/
|
|
65
73
|
切回分支: <main_branch>(如有)
|
|
66
74
|
|
|
67
75
|
输入 'discard <name>' 确认,或输入其他任意内容取消。
|
|
@@ -80,10 +88,10 @@ alloy _config read . main_branch
|
|
|
80
88
|
|
|
81
89
|
| 借口 | 现实 |
|
|
82
90
|
|------|------|
|
|
83
|
-
| "就删个目录而已,y 就行了" |
|
|
91
|
+
| "就删个目录而已,y 就行了" | 即使软删除可恢复,确认步骤不可跳过。`discard <name>` 的精确匹配是防手滑的最后一道防线。"y"、"好"、"删了吧"都不算。 |
|
|
84
92
|
| "这个 change 已完成,直接删了吧" | finished 是终态——不可 discard。已完成的 change 有完整审计链,删除会破坏追溯性。 |
|
|
85
93
|
| "不用列出清单了,我知道有什么" | 必须先展示六行删除清单(Change/Phase/分支/Worktree/目录/切回分支),让用户确认每个待删除项的完整性和正确性。 |
|
|
86
|
-
| "不用按顺序清理,直接 rm -rf 就行了" | 清理必须按序:worktree remove → checkout main → branch -D → rm
|
|
94
|
+
| "不用按顺序清理,直接 rm -rf 就行了" | 清理必须按序:worktree remove → checkout main → branch -D → mv 到 archive。rm -rf 不可恢复,软删除保留审计链。 |
|
|
87
95
|
|
|
88
96
|
---
|
|
89
97
|
|
|
@@ -101,8 +109,13 @@ git checkout <main_branch>
|
|
|
101
109
|
# 3. git branch -D <feature_branch>
|
|
102
110
|
git branch -D <feature_branch>
|
|
103
111
|
|
|
104
|
-
# 4.
|
|
105
|
-
|
|
112
|
+
# 4. 软删除——移动到 archive/ 保留审计链
|
|
113
|
+
DISCARD_DIR="openspec/changes/archive/$(date +%Y-%m-%d)-discard-<name>"
|
|
114
|
+
mkdir -p openspec/changes/archive/
|
|
115
|
+
mv openspec/changes/<name>/ "$DISCARD_DIR"
|
|
116
|
+
|
|
117
|
+
# 5. 记录 discarded_at 时间戳
|
|
118
|
+
alloy _state merge "$DISCARD_DIR" phase_timings "{\"discarded_at\":\"$(date '+%Y-%m-%d %H:%M:%S')\"}"
|
|
106
119
|
```
|
|
107
120
|
|
|
108
121
|
若 `main_branch` 未记录,跳过步骤 2,提示用户手动切回主分支。
|
|
@@ -116,7 +129,9 @@ rm -rf openspec/changes/<name>/
|
|
|
116
129
|
Alloy · 放弃 Change — DONE
|
|
117
130
|
──────────────────────────────────────
|
|
118
131
|
|
|
119
|
-
✓ <name>
|
|
120
|
-
|
|
132
|
+
✓ <name> 已软删除
|
|
133
|
+
分支/worktree:已清理
|
|
134
|
+
归档位置:archive/YYYY-MM-DD-discard-<name>/
|
|
135
|
+
恢复方式:mv 回 openspec/changes/<name>/ 即可恢复
|
|
121
136
|
当前分支:<main_branch>(或提示用户手动切换)
|
|
122
137
|
```
|