@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.
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 +103 -258
  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 +189 -227
  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
@@ -0,0 +1,21 @@
1
+ # apply 多 change 并行检测(前置 Step 0/5 用)
2
+
3
+ apply 单 change 串行(subagent 内部并行 OK)——同期多个 change 同时 apply 会导致 git 操作竞争(branch 切换、worktree 创建、commit 写入相互干扰)。
4
+
5
+ ## 检测 bash
6
+
7
+ ```bash
8
+ PARALLEL=$(find openspec/changes -maxdepth 2 -name .alloy.yaml \
9
+ -exec grep -l "phase: apply\|phase: applied" {} \; 2>/dev/null \
10
+ | grep -v "/<name>/" | wc -l)
11
+ if [ "$PARALLEL" -gt 0 ]; then
12
+ echo "⚠️ [WARN] 检测到 $PARALLEL 个其他 change 处于 apply/applied 状态:"
13
+ find openspec/changes -maxdepth 2 -name .alloy.yaml \
14
+ -exec grep -l "phase: apply\|phase: applied" {} \; 2>/dev/null | grep -v "/<name>/"
15
+ echo ""
16
+ echo " apply 串行更安全——多 change 并行 apply = worktree/branch/commit 竞争。"
17
+ echo " 继续当前 apply 前请确认其他 change 已暂停。"
18
+ fi
19
+ ```
20
+
21
+ 不阻断——仅提示。
@@ -0,0 +1,18 @@
1
+ # apply Red Flags 完整表(第三层防御——任一借口出现即 STOP)
2
+
3
+ 主文件保留 5 条核心,完整 12 条在此。
4
+
5
+ | 借口 | 现实 |
6
+ |------|------|
7
+ | "用户说了跳过 worktree" | 隔离是软闸门——`/alloy:apply` 允许 worktree=skipped;但模糊回复("嗯"/"好")不算同意,必须 USER_GATE 明确选择。 |
8
+ | "先写代码再补测试" | TDD 次序不可颠倒。提速靠并行子任务,不靠砍测试(Iron Law 第一层)。 |
9
+ | "用户要改需求,直接改" | 需求变更必须走 tasks.md checkbox 闸门。已编码→开新 change,未编码→回溯,禁直接改 plans.md。 |
10
+ | "技能缺失没关系" | 技能是闸门不是加速器。缺失 = ⛔ PRECONDITION_FAIL。引导 `alloy init`,不存在降级。 |
11
+ | "用户很急,跳过 review" | 跳过 review = 跳过质量闸门。急不是绕过流程的理由(Iron Law 第二层)。 |
12
+ | "先建 worktree 再问用户" | consent 必须在创建前。加载 using-git-worktrees Step 0 后停手,等用户明确回复。 |
13
+ | "verify.md 措辞不太顺,直接编辑改一下" | 制品禁直接编辑——任何变更必须重新生成 + 重新 hash-lock。违反字面 = 违反精神。 |
14
+ | "verify FAIL 是小问题,retro 写'已知 FAIL'继续" | FAIL 必须修复回到 Step 2。带 FAIL 进 archive 阶段 = spec 与代码偏差永久封存。 |
15
+ | "single-commit 修复不需要 retrospective,自动跳过" | retrospective 跳过判定必须 USER_GATE,agent 不得自动选"跳过"(task #17)。 |
16
+ | "worktree 内分支看起来对,应该没问题吧" | worktree-<name> 是硬约束。子 agent 在错误分支编辑 = 用户主分支被污染(task #18)。 |
17
+ | "git stash list 有内容,但这是之前的不影响 commit" | stash 残留 = 未完成工作。commit 前必须 ⚠️ WARN 让用户确认(task #19)。 |
18
+ | "另一个 change 也在 apply,并行做完更快" | apply 单 change 串行(subagent 内部并行 OK)。多 change 同时 apply = git 操作竞争。 |
@@ -0,0 +1,49 @@
1
+ # apply 子 agent commit 三规则(SDD/EP 共享)
2
+
3
+ apply Step 2/5 内每个子 agent 任务的 commit 必须满足以下三条硬规则——任一违反即拒绝合入。
4
+
5
+ ## 1. 分支再校验(⛔ PRECONDITION_FAIL,task #18)
6
+
7
+ 子 agent 任务开始时再次校验当前分支 = `worktree-<name>`,防 subagent 中途被 `git checkout` 切换:
8
+
9
+ ```bash
10
+ EXPECTED_BRANCH="worktree-<name>"
11
+ ACTUAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
12
+ if [ "$ACTUAL_BRANCH" != "$EXPECTED_BRANCH" ] && [ "$(alloy _state read openspec/changes/<name> worktree)" != "skipped" ]; then
13
+ echo "⛔ [PRECONDITION_FAIL] 子 agent 当前分支 ($ACTUAL_BRANCH) ≠ 预期 ($EXPECTED_BRANCH)"
14
+ echo " 禁止:agent 自动 git checkout 切回 worktree-<name>。"
15
+ echo " 必须:退出子 agent 让用户检查,可能上一个子 agent 切换了分支或 worktree 失效。"
16
+ exit 1
17
+ fi
18
+ ```
19
+
20
+ ## 2. git add 规则(⛔ HARD_STOP,§5.2.1)
21
+
22
+ 只用精确路径,不用 `-A`/`-a`/`.`。违反字面 = 违反精神:哪怕"反正只有这一个文件",也禁止 `-A`——agent 看不到的副作用文件可能被一并提交。commit 前检查 untracked 文件:
23
+
24
+ - 构建产物(`.vite/`、`dist/`、`node_modules/` 等)追加 `.gitignore`
25
+ - 项目源码按精准路径 add(如 `git add src/foo.ts tests/foo.test.ts`)
26
+ - 判断不准时 🔴 USER_GATE 询问用户
27
+
28
+ ```bash
29
+ # 反例(禁用):git add -A / git add . / git add -a
30
+ # 正例:git add <精确路径列表>
31
+ git add src/<具体文件>.ts tests/<具体测试>.test.ts
32
+ ```
33
+
34
+ ## 3. stash 残留检查(⚠️ WARN,task #19)
35
+
36
+ commit 前必须运行:
37
+
38
+ ```bash
39
+ if [ -n "$(git stash list)" ]; then
40
+ echo "⚠️ [WARN] 检测到 stash 残留:"
41
+ git stash list
42
+ echo ""
43
+ echo " stash 残留可能是用户之前未完成的工作。继续 commit 不会丢失 stash,"
44
+ echo " 但用户可能需要先 git stash pop 或 drop。"
45
+ echo " 禁止:agent 自动 git stash drop / git stash clear(§3.5.1)。"
46
+ fi
47
+ ```
48
+
49
+ WARN 不阻断 commit,但提醒 agent 在 commit 完成后向用户播报 stash 列表,让用户决定后续处理。
@@ -0,0 +1,130 @@
1
+ # apply-worktree.md
2
+
3
+ apply Step 1 worktree 路径占用检查、创建后状态记录、分支锁定校验。
4
+
5
+ ## 路径占用检查(⛔ PRECONDITION_FAIL,task #10)
6
+
7
+ `git worktree add` 在目标路径已存在时会失败;agent 不得用 `git worktree remove --force` 或 `rm -rf` 自动清理——目标路径可能是用户之前未归档的工作(被 alloy 早期版本遗留 / 用户手动创建 / 同名 change 重启)。
8
+
9
+ ```bash
10
+ REPO_ROOT="$(git rev-parse --show-toplevel)"
11
+ TARGET_PATH="$REPO_ROOT/.claude/worktrees/<name>"
12
+ TARGET_BRANCH="worktree-<name>"
13
+
14
+ if [ -e "$TARGET_PATH" ] || git worktree list --porcelain | grep -qF "worktree $TARGET_PATH"; then
15
+ echo "⛔ [PRECONDITION_FAIL] worktree 目标路径已被占用:"
16
+ echo " 路径: $TARGET_PATH"
17
+ echo " 目录存在: $([ -e "$TARGET_PATH" ] && echo 是 || echo 否)"
18
+ echo " 已注册为 git worktree: $(git worktree list --porcelain | grep -qF "worktree $TARGET_PATH" && echo 是 || echo 否)"
19
+ echo ""
20
+ echo " 禁止:agent 自动运行 git worktree remove --force / rm -rf $TARGET_PATH /"
21
+ echo " git worktree prune 强行清理。这些路径可能是用户之前未归档的工作。"
22
+ echo " 违反字面 = 违反精神:哪怕看似\"覆盖一下让 apply 继续\",也算违反禁令——"
23
+ echo " 必须 USER_GATE 让用户决策。"
24
+ fi
25
+ ```
26
+
27
+ 路径已占用 → 🔴 USER_GATE:
28
+
29
+ > 目标路径 `.claude/worktrees/<name>` 已被占用。
30
+ > 选项:
31
+ > (a) 复用现有 worktree——直接 `EnterWorktree(path=...)` 进入,跳过创建(要求该路径已是有效 git worktree 且分支为 `worktree-<name>`,否则降级到 (b))
32
+ > (b) 重命名当前 change——退出 skill,让用户用 `/alloy:start <new-name>` 重新发起,或手动重命名 change 目录
33
+ > (c) 中止 apply——`alloy _state write openspec/changes/<name> worktree blocked` 后退出,待用户清理后重新运行
34
+
35
+ - 选 (a):检测分支匹配后 `EnterWorktree(path=".claude/worktrees/<name>")`,跳到"创建后状态记录"
36
+ - 选 (b):退出 skill 并提示用户重命名后重跑
37
+ - 选 (c):写入 worktree=blocked 后退出 skill
38
+
39
+ 路径未占用 → 执行创建:
40
+
41
+ ```bash
42
+ git worktree add .claude/worktrees/<name> -b worktree-<name> <feature_branch>
43
+ ```
44
+
45
+ 再用 `EnterWorktree(path=".claude/worktrees/<name>")` 进入。路径偏好 `.claude/worktrees/<name>`(`.claude/` 是 alloy 固定目录),分支命名 `worktree-<name>`(与 EnterWorktree 内置一致,archive 清理时无需猜测)。
46
+
47
+ ## 创建后状态记录
48
+
49
+ worktree 创建完成后,检测实际位置并写入状态文件:
50
+
51
+ ```bash
52
+ echo " 正在检测 worktree 实际状态..."
53
+
54
+ # 判断是否已在 worktree 中:GIT_DIR != GIT_COMMON 表示在 linked worktree 中
55
+ GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
56
+ GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
57
+
58
+ if [ "$GIT_DIR" != "$GIT_COMMON" ]; then
59
+ # 已在 worktree 中,获取实际路径和分支名
60
+ WORKTREE_PATH=$(cd "$(git rev-parse --show-toplevel)" 2>/dev/null && pwd -P)
61
+ WORKTREE_BRANCH=$(cd "$WORKTREE_PATH" && git branch --show-current)
62
+ alloy _state write openspec/changes/<name> worktree "$WORKTREE_PATH"
63
+ alloy _state write openspec/changes/<name> worktree_branch "$WORKTREE_BRANCH"
64
+ alloy _state write openspec/changes/<name> worktree_created_at "$(date '+%Y-%m-%d %H:%M:%S')"
65
+ echo " ✓ worktree 已记录: 分支=$WORKTREE_BRANCH 路径=$WORKTREE_PATH"
66
+ # commit 确保断点恢复时 state 不丢失
67
+ git add openspec/changes/<name>/.alloy.yaml
68
+ git diff --cached --quiet || git commit -m "chore(<name>): record worktree state"
69
+ else
70
+ # EnterWorktree 失败后的 git worktree fallback
71
+ # 优先检测 .claude/worktrees/(EnterWorktree 原生路径),回退 .worktrees/
72
+ WT_PATH=""
73
+ for CANDIDATE in ".claude/worktrees/<name>" ".worktrees/<name>"; do
74
+ if [ -d "$CANDIDATE" ]; then
75
+ WT_PATH=$(cd "$CANDIDATE" 2>/dev/null && pwd -P)
76
+ break
77
+ fi
78
+ done
79
+ if [ -n "$WT_PATH" ]; then
80
+ WORKTREE_BRANCH=$(cd "$WT_PATH" && git branch --show-current 2>/dev/null)
81
+ alloy _state write openspec/changes/<name> worktree "$WT_PATH"
82
+ alloy _state write openspec/changes/<name> worktree_branch "$WORKTREE_BRANCH"
83
+ alloy _state write openspec/changes/<name> worktree_created_at "$(date '+%Y-%m-%d %H:%M:%S')"
84
+ echo " ✓ worktree fallback 已记录: 分支=$WORKTREE_BRANCH 路径=$WT_PATH"
85
+ # 提交 source repo 的 state
86
+ git add openspec/changes/<name>/.alloy.yaml
87
+ git diff --cached --quiet || git commit -m "chore(<name>): record worktree state"
88
+ # worktree 内也写入并提交——bash cd 不跨工具调用持久化
89
+ cd "$WT_PATH" && \
90
+ alloy _state write openspec/changes/<name> worktree "$WT_PATH" && \
91
+ alloy _state write openspec/changes/<name> worktree_branch "$WORKTREE_BRANCH" && \
92
+ alloy _state write openspec/changes/<name> worktree_created_at "$(date '+%Y-%m-%d %H:%M:%S')" && \
93
+ git add openspec/changes/<name>/.alloy.yaml && \
94
+ git diff --cached --quiet || git commit -m "chore(<name>): record worktree state (worktree)"
95
+ else
96
+ echo " ℹ 未检测到 worktree,按用户选择记录"
97
+ alloy _state write openspec/changes/<name> worktree skipped
98
+ fi
99
+ fi
100
+ ```
101
+
102
+ ## 状态记录字段
103
+
104
+ | 字段 | 值 | 写入时机 |
105
+ |------|-----|---------|
106
+ | `worktree` | 有效路径 / `skipped` / `null` | 创建后或用户跳过时 |
107
+ | `worktree_branch` | 分支名(如 `worktree-<name>`) | 创建后 |
108
+ | `worktree_created_at` | `%Y-%m-%d %H:%M:%S` | 创建后 |
109
+
110
+ 这些数据是断点恢复的关键——Agent 重入时通过 `alloy _guard worktree-status` 读取。
111
+
112
+ ## Worktree 内分支锁定(⛔ PRECONDITION_FAIL,task #18)
113
+
114
+ 进入 worktree 后必须验证当前分支与状态记录一致——子 agent 后续在错误分支编辑 = 用户主分支被污染。
115
+
116
+ ```bash
117
+ WORKTREE_PATH=$(alloy _state read openspec/changes/<name> worktree)
118
+ EXPECTED_BRANCH="worktree-<name>"
119
+
120
+ if [ "$WORKTREE_PATH" != "skipped" ] && [ "$WORKTREE_PATH" != "blocked" ] && [ -d "$WORKTREE_PATH" ]; then
121
+ ACTUAL_BRANCH=$(git -C "$WORKTREE_PATH" rev-parse --abbrev-ref HEAD 2>/dev/null)
122
+ if [ "$ACTUAL_BRANCH" != "$EXPECTED_BRANCH" ]; then
123
+ echo "⛔ [PRECONDITION_FAIL] worktree 内分支 ($ACTUAL_BRANCH) 与预期 ($EXPECTED_BRANCH) 不一致"
124
+ echo " 可能原因:用户在 worktree 内手动切换了分支 / 旧 worktree 残留 / 复用 (a) 进入了错误 worktree"
125
+ echo " 禁止:agent 自动 git checkout 切换分支——可能丢弃用户未提交的工作(§3.5.1)。"
126
+ echo " 必须:USER_GATE 让用户决策修复方式(手动切回 / 退出 skill 重建 worktree / 复用前确认分支)。"
127
+ exit 1
128
+ fi
129
+ fi
130
+ ```
@@ -0,0 +1,16 @@
1
+ # archive Red Flags 完整表(第三层防御——任一借口出现即 STOP)
2
+
3
+ 主文件保留 5 条核心,完整 11 条在此。
4
+
5
+ | 借口 | 现实 |
6
+ |------|------|
7
+ | "verify.md FAIL 是小问题,先归档再说" | FAIL = 阻塞问题。归档不可逆——带着 FAIL 归档意味着 spec 与代码偏差被永久封存。 |
8
+ | "跳过 archive 直接 merge,spec 后面补" | Delta Spec 不同步 = 主 spec 落后。"后面补"的 spec 永远不会补。 |
9
+ | "openspec archive 报错了,但代码是对的" | 归档报错 = Delta Spec 合并失败。忽略 = 主 spec 停留在旧版本。 |
10
+ | "spec 合并看起来没问题,直接继续" | 没看过的 spec 变更 = 代码与规格可能已分叉。审查只需 1 分钟,修复分叉需要 1 小时。 |
11
+ | "worktree 合并没问题,直接清理吧" | merge 结果必须审查——未审查的合并可能引入意外变更或冲突残留。确认只需 30 秒,修复遗漏需要 1 小时。 |
12
+ | "memory 条目都挺合理的,直接写入" | memory 影响所有后续会话。写入不当"经验"污染全局行为。确认只需 1 分钟。 |
13
+ | "worktree 合并冲突了,跳过清理吧" | 冲突不解决 = 代码丢失。worktree 变更没合入 feature 就删除 = 白做。 |
14
+ | "merge 冲突了,git merge --abort 一下让流程继续" | 冲突 = 代码状态未达预期,自动 abort = 隐藏真问题。退出 skill 让用户处理是唯一合法路径(§3.5.1)。 |
15
+ | "memory 候选都对,全部写入吧" | 单次确认承担不了全局污染风险。每条独立 USER_GATE,无例外(§5.2.2)。 |
16
+ | "另一个 change 也在 archive,等一下吧" | 多 change 并行 archive = Delta Spec 合并顺序敏感。先归档晚开始的 = 主 spec 状态错乱。必须串行。 |
@@ -0,0 +1,94 @@
1
+ # archive worktree 清理
2
+
3
+ archive Step 2 末段:把 worktree 分支合入 feature 分支后清理 worktree 目录。包括 silent fallback 检测、遗留 change 兼容、merge 冲突处理三段。
4
+
5
+ > **§3.5.1 git 自救禁令(HARD_STOP):** 整段流程任何 git 失败都禁止 agent 自动 `git merge --abort` / `git reset --hard` / `git checkout .` / `git restore .` / `git stash` / `git clean -fd` / `git push --force` 任何一个。冲突现场必须报告并 USER_GATE 让用户决策。违反字面 = 违反精神。
6
+
7
+ ## 完整 bash 流程
8
+
9
+ ```bash
10
+ WORKTREE_PATH=$(alloy _state read "$ARCHIVE_DIR" worktree 2>/dev/null)
11
+ FEATURE_BRANCH=$(alloy _state read "$ARCHIVE_DIR" feature_branch 2>/dev/null)
12
+ WORKTREE_BRANCH=$(alloy _state read "$ARCHIVE_DIR" worktree_branch 2>/dev/null)
13
+
14
+ # task #21: silent fallback 检测——区分"遗留 change"(state 字段未写但 worktree 存在)
15
+ # vs "state 缺失"(apply 阶段 state 写入失败)。后者必须 PRECONDITION_FAIL。
16
+ if [ -z "$WORKTREE_PATH" ] || [ "$WORKTREE_PATH" = "null" ]; then
17
+ # state 中无 worktree 字段——检查 git 实际状态
18
+ ACTUAL_WT=$(git worktree list --porcelain | awk -v p=".claude/worktrees/<name>" '
19
+ /^worktree / { wt = substr($0, 10) }
20
+ wt ~ p { print wt; exit }
21
+ ')
22
+ if [ -n "$ACTUAL_WT" ]; then
23
+ echo "⛔ [PRECONDITION_FAIL] 检测到 git worktree 但 .alloy.yaml 未记录:"
24
+ echo " 实际 worktree: $ACTUAL_WT"
25
+ echo " 状态字段: worktree=$WORKTREE_PATH"
26
+ echo ""
27
+ echo " 可能原因:apply 阶段 state 写入失败,archive 不能 silent fallback。"
28
+ echo " 请用户检查 openspec/changes/<name>/.alloy.yaml 后手动修复 worktree 字段,"
29
+ echo " 或直接 git worktree remove $ACTUAL_WT(确认无未提交工作时)。"
30
+ exit 1
31
+ fi
32
+ # 真无 worktree → 跳过本段
33
+ elif [ "$WORKTREE_PATH" != "skipped" ]; then
34
+ echo " ℹ 检测到 worktree($WORKTREE_PATH),正在合并回 feature 分支..."
35
+
36
+ # 遗留 change 兼容:FEATURE_BRANCH 缺失 → 退回到 feature/<name>
37
+ if [ -z "$FEATURE_BRANCH" ] || [ "$FEATURE_BRANCH" = "null" ]; then
38
+ FEATURE_BRANCH="feature/<name>"
39
+ fi
40
+
41
+ # 遗留 change 兼容:WORKTREE_BRANCH 缺失 → 从 worktree 实际状态检测
42
+ if [ -z "$WORKTREE_BRANCH" ] || [ "$WORKTREE_BRANCH" = "null" ]; then
43
+ WORKTREE_BRANCH=$(git worktree list --porcelain | awk -v path="$WORKTREE_PATH" '
44
+ /^worktree / { wt = substr($0, 10) }
45
+ /^branch / && wt == path { gsub(/^refs\/heads\//, "", $2); print $2; exit }
46
+ ')
47
+ if [ -z "$WORKTREE_BRANCH" ]; then
48
+ echo "⛔ [PRECONDITION_FAIL] 无法检测 worktree 分支名(state 缺失且 git 也无法定位)"
49
+ echo " 请用户手动指定 worktree_branch 后重试。"
50
+ exit 1
51
+ fi
52
+ fi
53
+
54
+ # 从 worktree 分支合并代码到 feature 分支
55
+ MAIN_ROOT=$(cd "$WORKTREE_PATH" && git rev-parse --show-toplevel 2>/dev/null)
56
+ cd "$MAIN_ROOT"
57
+ git merge "$WORKTREE_BRANCH" --no-edit
58
+
59
+ if [ $? -eq 0 ]; then
60
+ # worktree 合并审查(USER_GATE)
61
+ : <<'USER_GATE_TEMPLATE'
62
+ 🔴 USER_GATE(必须 AskUserQuestion):
63
+ > worktree 合并完成:
64
+ > [展示 merge 的 commit 列表:git log --oneline <FEATURE_BRANCH>..HEAD]
65
+ > 选项:
66
+ > (a) 确认并清理 worktree
67
+ > (b) 需要检查——退出 skill 让用户审查
68
+ USER_GATE_TEMPLATE
69
+
70
+ # 用户选 (a) 后执行清理:
71
+ git worktree remove "$WORKTREE_PATH"
72
+ git branch -d "$WORKTREE_BRANCH"
73
+ # worktree_merged_at 由 archive.md 主流程内联记录,此处仅做清理
74
+ echo " ✓ worktree 已合并至 $FEATURE_BRANCH 分支并清理"
75
+ else
76
+ # [HARD_STOP] merge 冲突时禁止运行 git merge --abort 或任何 git 自救命令。
77
+ # 必须报告冲突现场后调用 USER_GATE 让用户决定。
78
+ echo "⛔ merge 冲突——worktree 工作未合入 feature 分支"
79
+ echo ""
80
+ echo " 冲突现场:"
81
+ git status --short
82
+ echo ""
83
+ echo " 合法路径:"
84
+ echo " 1) 用户手动解决冲突后 git add + git commit,再重新运行 /alloy:archive"
85
+ echo " 2) 用户决定放弃 worktree 工作(注意:放弃前确认无未保存改动)"
86
+ echo ""
87
+ echo " 禁止:agent 自动运行 git merge --abort / git reset --hard /"
88
+ echo " git checkout . / git restore . / git stash 任何一个。"
89
+ exit 1
90
+ fi
91
+ fi
92
+ ```
93
+
94
+ 未使用 worktree 时跳过本段。
@@ -0,0 +1,49 @@
1
+ # artifact-hash-commit.md
2
+
3
+ 制品审批后的 hash 锁定 + commit 标准序列。
4
+
5
+ ## 标准流程
6
+
7
+ 用户在审查窗口选 (a) 确认后执行:
8
+
9
+ ```bash
10
+ HASH=$(alloy _record compute openspec/changes/<name> <artifact>)
11
+ APPROVED_AT=$(date "+%Y-%m-%d %H:%M:%S")
12
+ APPROVER=$(alloy _record approver openspec/changes/<name>)
13
+ alloy _record write openspec/changes/<name> <artifact> "$HASH" "$APPROVED_AT" "$APPROVER"
14
+ git add openspec/changes/<name>/
15
+ git commit -m "docs(<name>): <artifact> 已确认"
16
+ ```
17
+
18
+ ## 阶段最后一个制品
19
+
20
+ 阶段最后一个制品审批时,phase_timings.completed_at + hash 锁定合并为一个 commit——禁止拆成两次提交:
21
+
22
+ ```bash
23
+ COMPLETED_AT=$(date "+%Y-%m-%d %H:%M:%S")
24
+ alloy _state merge openspec/changes/<name> phase_timings "{\"<phase>\":{\"completed_at\":\"${COMPLETED_AT:-$(date '+%Y-%m-%d %H:%M:%S')}\"}}"
25
+ HASH=$(alloy _record compute openspec/changes/<name> <artifact>)
26
+ APPROVED_AT=$(date "+%Y-%m-%d %H:%M:%S")
27
+ APPROVER=$(alloy _record approver openspec/changes/<name>)
28
+ alloy _record write openspec/changes/<name> <artifact> "$HASH" "$APPROVED_AT" "$APPROVER"
29
+ git add openspec/changes/<name>/
30
+ git commit -m "docs(<name>): <artifact> 已确认"
31
+ ```
32
+
33
+ phase_timings 作为元数据附着在制品提交上,不单独 commit。
34
+
35
+ ## commit message 格式
36
+
37
+ `docs(<change-name>): <artifact> 已确认`(Conventional Commits `docs` type)。
38
+
39
+ `<artifact>` 为 draft / proposal / design / specs / tasks / plans / verify / retrospective。
40
+
41
+ ## 生成下一制品前
42
+
43
+ 校验上游依赖制品的 hash 未被篡改:
44
+
45
+ ```bash
46
+ alloy _record check openspec/changes/<name> <upstream-artifact>
47
+ ```
48
+
49
+ check 返回非零 → HARD STOP,hash 不匹配意味着有未审批的篡改。
@@ -0,0 +1,65 @@
1
+ # branch-naming.md
2
+
3
+ Alloy 分支命名白名单与 PRECONDITION_FAIL 校验。`start` 创建 feature 分支、`fix` 创建热修分支、用户自定义分支名时统一引用本文件。
4
+
5
+ ## 白名单(与 `CLAUDE.md` 同源)
6
+
7
+ 允许的 prefix(kebab-case 后缀):
8
+
9
+ | prefix | 用途 | 示例 |
10
+ |--------|------|------|
11
+ | `feature/` | 新功能 change(start 默认) | `feature/user-auth` |
12
+ | `fix/` | Bug 修复 / 热修 | `fix/login-redirect` |
13
+ | `docs/` | 文档变更 | `docs/api-reference` |
14
+ | `refactor/` | 重构(无行为变更) | `refactor/extract-validator` |
15
+ | `test/` | 测试补全 / 验证分支 | `test/phase-5-validation` |
16
+ | `chore/` | 构建 / 配置 / 杂项 | `chore/bump-deps` |
17
+
18
+ **禁用 prefix**:`hotfix/`(用 `fix/` 替代)、`bugfix/`(用 `fix/`)、`master/` `main/` `dev/`(与主分支同名)、无 prefix 裸分支名(`wip` `tmp` 等)。
19
+
20
+ ## PRECONDITION_FAIL 校验 bash
21
+
22
+ 调用方在分支创建/确认前嵌入:
23
+
24
+ ```bash
25
+ # 输入:$BRANCH_NAME(待创建或用户自定义的分支名)
26
+ # 输出:通过则 echo "✓ 分支命名合法";不合法则 exit 1
27
+
28
+ ALLOY_BRANCH_PREFIXES="feature|fix|docs|refactor|test|chore"
29
+
30
+ if ! echo "$BRANCH_NAME" | grep -Eq "^(${ALLOY_BRANCH_PREFIXES})/[a-z0-9][a-z0-9._-]*$"; then
31
+ echo "⛔ [PRECONDITION_FAIL] 分支名 \"$BRANCH_NAME\" 不在白名单:"
32
+ echo " 允许的 prefix: feature/ fix/ docs/ refactor/ test/ chore/"
33
+ echo " 分支后缀须 kebab-case(小写字母数字 . _ -,不以 . _ - 开头)"
34
+ echo ""
35
+ echo " 禁止:agent 自动改写分支名后继续——分支命名是 PR / merge / 审计链入口,"
36
+ echo " 必须 USER_GATE 让用户选择合法名称。"
37
+ echo ""
38
+ echo " 违反字面 = 违反精神:哪怕 \"hotfix/ 历史上能用\" 或 \"分支名只是临时\","
39
+ echo " 也算违反白名单——历史 hotfix/ 名应在本次重新走 fix/ 路径。"
40
+ exit 1
41
+ fi
42
+
43
+ echo "✓ 分支命名合法: $BRANCH_NAME"
44
+ ```
45
+
46
+ ## 与主分支同名校验(叠加约束)
47
+
48
+ 白名单通过后仍需校验不与 `main_branch` 重合——`feature/main` 这种名称合法但语义混乱,建议另外校验。该校验由调用方(start.md 步骤 3)在白名单后追加:
49
+
50
+ ```bash
51
+ if [ "$BRANCH_NAME" = "$MAIN_BRANCH" ]; then
52
+ echo "⛔ [PRECONDITION_FAIL] 分支名与主分支同名: $BRANCH_NAME"
53
+ exit 1
54
+ fi
55
+ ```
56
+
57
+ ## 调用方
58
+
59
+ - `start.md` 步骤 3 ②:默认 `feature/<change-name>`,用户自定义时强制走白名单校验
60
+ - `fix.md` 场景 3:固定 `fix/<desc>`(不再用 `hotfix/`)。用户自定义时同样校验
61
+ - `apply.md` worktree 路径占用 (b) 重命名分支:用户输入新分支名后校验
62
+
63
+ ## Why
64
+
65
+ CLAUDE.md 规定的 6 个 prefix 是 squash merge 时 Conventional Commits 的语义来源,分支名 prefix 直接影响 PR 标题模板和归档审计链。`hotfix/` 不在白名单 = `fix/` 与 `hotfix/` 双轨并存导致 changelog 工具无法识别。**白名单是闸门型约束,不是建议。**
@@ -0,0 +1,36 @@
1
+ # branch-validation.md
2
+
3
+ `alloy _guard branch-position` 返回异常时的修复选项。
4
+
5
+ **交互规则:** 所有选项确认点均为 🔴 STOP——必须用 AskUserQuestion 等用户选择,不可自行决定。
6
+
7
+ ## 异常结果与修复路径
8
+
9
+ ### feature-missing
10
+
11
+ `.alloy.yaml` 未记录 feature_branch。这通常是 start 阶段数据写入失败——null 是状态异常,不是设计的容错。
12
+
13
+ 🔴 STOP: 选择修复方式——(a) 手动指定分支名 (b) 使用当前分支作为 feature_branch (c) 放弃,回退 start 阶段修复
14
+
15
+ 用户选择后:`alloy _state write openspec/changes/<name> feature_branch <用户确认的分支>`
16
+
17
+ **不允许静默回退**——回退值可能与实际分支不一致。
18
+
19
+ ### on-other:\<current\>
20
+
21
+ 当前分支与 feature_branch 记录不一致:
22
+
23
+ 🔴 STOP: 选择处理方式——(a) 切换到 feature_branch(推荐) (b) 使用当前分支(更新 feature_branch 记录)
24
+
25
+ 选 (a):`git checkout <feature_branch>` → 重新运行 `alloy _guard branch-position`
26
+ 选 (b):`alloy _state write openspec/changes/<name> feature_branch <current_branch>` → 继续
27
+
28
+ ### feature-lost:\<feature\>
29
+
30
+ feature_branch 记录在 .alloy.yaml 但本地不存在:
31
+
32
+ 🔴 STOP: 选择修复方式——(a) 从远程拉取 (b) 手动指定其他分支 (c) 放弃,回退 start 阶段修复
33
+
34
+ ## 跨文件复用
35
+
36
+ 此文件供 apply.md 和 start.md 的分支验证步骤引用。start.md 的分支选择逻辑更复杂(包含新建分支选项),此文件只处理 apply 阶段的修复场景。
@@ -0,0 +1,58 @@
1
+ # fix-precommit-check.md
2
+
3
+ fix 场景 1/2/3 在 commit / merge 前的 TDD + verification 校验门。**目的:阻止"跳诊后跳验证"——commit 前必须证明已加载 test-driven-development + verification-before-completion 两个 skill。**
4
+
5
+ ## 场景 1/2(有归属 change):skill_usage[] 校验
6
+
7
+ 调用方在 commit 前嵌入。校验对象:`.alloy.yaml` 的 `skill_usage[]`,匹配 `stage=fix` 且 `skill ∈ {test-driven-development, verification-before-completion}`,`action=log`(不是 skip)。
8
+
9
+ ```bash
10
+ # 输入:$NAME(change name),$CHANGE_DIR=openspec/changes/$NAME
11
+
12
+ REQUIRED_SKILLS="test-driven-development verification-before-completion"
13
+ MISSING=""
14
+
15
+ for SKILL in $REQUIRED_SKILLS; do
16
+ COUNT=$(alloy _state read "$CHANGE_DIR" skill_usage 2>/dev/null \
17
+ | python3 -c "
18
+ import sys, json
19
+ data = json.load(sys.stdin) if sys.stdin.readable() else []
20
+ hits = [e for e in (data or []) if e.get('stage')=='fix' and e.get('skill')=='$SKILL' and e.get('action')=='log']
21
+ print(len(hits))
22
+ " 2>/dev/null || echo 0)
23
+ if [ "${COUNT:-0}" -eq 0 ]; then
24
+ MISSING="$MISSING $SKILL"
25
+ fi
26
+ done
27
+
28
+ if [ -n "$MISSING" ]; then
29
+ echo "⛔ [HARD_STOP] commit 前缺失必需 skill 调用记录:"
30
+ echo " 缺失:$MISSING"
31
+ echo " 原因:fix 场景 1/2 commit 前必须先加载 test-driven-development + verification-before-completion"
32
+ echo " 并通过 alloy _skill log <change-dir> fix <skill> 记录调用。"
33
+ echo ""
34
+ echo " 禁止:agent 自动补 _skill log 后继续——记录必须反映真实加载。"
35
+ echo " 违反字面 = 违反精神:哪怕\"测试已经写过了\"或\"diff 看起来明显是修复\","
36
+ echo " 也算违反——跳过验证 = 跳过修复闸门。"
37
+ exit 1
38
+ fi
39
+
40
+ echo "✓ pre-commit 校验通过:$REQUIRED_SKILLS 均已加载"
41
+ ```
42
+
43
+ ## 场景 3(无归属 change):USER_GATE 物理确认
44
+
45
+ 热修无 change 上下文,无法读 skill_usage。改用 🔴 USER_GATE 在 merge 精确字符串确认前追加:
46
+
47
+ > 已加载 superpowers:test-driven-development(写失败测试 → 修代码)?
48
+ > 已加载 superpowers:verification-before-completion(独立验证修复结果)?
49
+ >
50
+ > 选项:
51
+ > (a) 已加载两个 skill,继续 merge 确认
52
+ > (b) 未加载——返回 Step 3 重做
53
+
54
+ **[HARD_STOP]** agent 不得基于"diff 包含测试代码"或"自己已经测过"自动选 (a)——必须用户物理选择。否则违反 interaction-style.md "沉默 ≠ 授权"通用禁令。
55
+
56
+ ## Why
57
+
58
+ systematic-debugging(已在 Step 2 强制)只解决"修对位置"——TDD 解决"修法正确",verification 解决"修完没回归"。三者缺一就把 bug 搬家而不是修复。L4 漏洞场景:用户压力下 agent 直接 `git diff` + `git commit` 跳过 Step 3 子步骤 1-2,闸门必须在 commit 前物理拦截。
@@ -51,10 +51,43 @@ AskUserQuestion: {
51
51
  > 请输入 a 或 b:
52
52
  ```
53
53
 
54
- **硬规则:凡是技能文件中出现 `AskUserQuestion` JSON 块的,同一位置必须附带降级文本格式。** Agent 执行时先检测当前平台是否支持 `AskUserQuestion` 工具——支持则用原生交互组件,不支持则自动降级为文本选项。两个格式给出相同选项、相同数量,确保不同平台上用户看到的选项一致。
54
+ **硬规则:技能文件中用 `🔴 STOP` 标记确认点,不写 JSON 块。** Agent 遇到 🔴 STOP 时自动用 AskUserQuestion(支持的平台)或降级为结构化文本选项(不支持的平台)。两个格式给出相同选项、相同数量。
55
55
 
56
56
  **反例:** 审查窗口只用文本 "(a) 确认 (b) 调整" 而不给明确的输入提示——用户不知道是要打字、复制粘贴还是直接说"确认"。
57
57
 
58
+ ## 🔴 STOP 标记规则
59
+
60
+ 技能文件中的 `🔴 STOP` 表示硬交互确认点——**必须使用 AskUserQuestion(或平台等价工具),不可跳过、不可用文本替代、不可自行决定。**
61
+
62
+ 遇到 `🔴 STOP: <确认事项>` 时:
63
+ 1. 用 AskUserQuestion 展示选项,**等用户选择后才继续**
64
+ 2. 选项从确认事项的上下文推导(通常是"确认/调整"或"选项A/选项B")
65
+ 3. 跳过任何 🔴 STOP = 违反技能 Iron Law
66
+
67
+ **Why:** 纯文本确认("Y/n"、"(a)/(b)")是软交互——agent 可自行判断"用户大概同意"后跳过。AskUserQuestion 是硬交互——agent 必须等用户物理选择才能继续。
68
+
69
+ ## 沉默 ≠ 授权(USER_GATE 通用禁令,跨 skill 适用)
70
+
71
+ **[HARD_STOP]** 所有 🔴 USER_GATE / 🔴 STOP **必须**单独调用 AskUserQuestion 等用户物理选择,下列行为全部禁止:
72
+
73
+ | 反模式 | 现实 |
74
+ |--------|------|
75
+ | **批量打包**:N 条候选 → 1 个"全部确认"问题 | 单次确认承担不了 N 项独立的污染风险(典型:archive memory 候选必须逐条问,§5.2.2) |
76
+ | **基于内容跳过**:diff 短 / 无 conflict / "看起来明显合理" → 自动跳过 USER_GATE | 内容质量不是授权来源,用户的 (a) 选择才是。哪怕 1 行 diff 也必须问 |
77
+ | **沉默推断**:用户长时间不回复 → 选默认项继续 | USER_GATE 没有默认项;超时不算授权,必须等到物理选择 |
78
+ | **改写选项**:把"(a) 确认 / (b) 调整"改成"(a) 确认(推荐)"后默认选 (a) | 推荐文案不等于已选,必须用户主动确认 |
79
+ | **降级为文本**:在支持 AskUserQuestion 的平台用纯文本提示自循环判断 | 平台支持时强制 AskUserQuestion,降级仅限不支持的平台(见上方对照表) |
80
+
81
+ **违反字面 = 违反精神:** 哪怕"用户上次同意过"、"内容看起来无争议"、"流程已经很顺了",也算违反 Iron Law。**USER_GATE 的物理实现是 AskUserQuestion 工具调用本身——没调用就是没问。**
82
+
83
+ ### 精确字符串确认特例
84
+
85
+ 部分破坏性操作要求用户输入精确字符串(discard、热修 merge),见上方"不能使用 AskUserQuestion 的场景"章节。此时禁令仍适用:
86
+
87
+ - **agent 不得回填精确字符串**:用户必须自己输入 `discard <name>` / `merge <branch> into <branch>`,禁 agent 在工具调用中预填、模拟、或基于"用户回复了'好'"自行判定
88
+ - **模糊回复不算确认**:"好"、"可以"、"y"、"go ahead" 全部不算精确确认,必须等到字面匹配的字符串
89
+ - **跨 skill 适用**:fix 场景 3 热修合并、discard 命令均按此规则
90
+
58
91
  ## 不能使用 AskUserQuestion 的场景
59
92
 
60
93
  以下场景**保持精确文本确认**,因为它们是安全机制而非便利功能:
@@ -2,6 +2,8 @@
2
2
 
3
3
  start 和 fix 命令共享的主分支自动检测逻辑。
4
4
 
5
+ **交互规则:** 确认点为 🔴 STOP——必须用 AskUserQuestion 等用户选择,不可自行决定。
6
+
5
7
  ## 检测优先级(3 级)
6
8
 
7
9
  ```bash
@@ -19,12 +21,7 @@ DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|
19
21
 
20
22
  ## 确认步骤
21
23
 
22
- 检测到主分支后,必须让用户确认(Y/n):
23
- ```
24
- 主分支: $DEFAULT_BRANCH。使用此分支作为基础分支?[Y/n]
25
- ```
26
-
27
- 选 Y 或直接回车 → 使用检测结果。选 n → 让用户输入自定义名称。
24
+ 检测到主分支后,🔴 STOP: 确认主分支(检测值 / 自定义名称)。
28
25
 
29
26
  确认后写入项目级配置:
30
27
  ```bash
@@ -0,0 +1,27 @@
1
+ # §5.2.3 phase 推进降级路径
2
+
3
+ 各 skill 在推进 phase 后,如果后续阶段失败,agent 不得自动 `git reset --hard` / `git checkout .` 清场。降级路径如下:
4
+
5
+ ## 通用降级 3 步(apply / archive 适用)
6
+
7
+ 若推进后续阶段失败,用户须手动按以下 3 步回退:
8
+
9
+ ```bash
10
+ alloy _state set <CHANGE_DIR> phase <previous-phase>
11
+ git checkout HEAD~1 -- <CHANGE_DIR>/.alloy.yaml # 撤销 phase commit 中的状态变更
12
+ git reset HEAD~1 # 退回 phase commit
13
+ ```
14
+
15
+ `<previous-phase>` 对应:
16
+ - apply 阶段降级 → `planned`
17
+ - archive 阶段降级 → `applied`
18
+ - finish 阶段降级 → `archived`
19
+
20
+ ## 禁令
21
+
22
+ - 禁止 agent 自动 `git reset --hard` / `git checkout .` 清场(详见 §3.5.1 git 自救禁令)
23
+ - 违反字面 = 违反精神:哪怕"清理一下让流程重启",也算违反禁令——退出 skill 让用户决策是唯一合法路径
24
+
25
+ ## 边界
26
+
27
+ - start 是 phase 推进起点(无前序 phase),phase=started 写入失败时降级路径只有"重跑 /alloy:start"——不存在 phase 回退场景。本阶段无 §5.2.3 适用空间。