@aipper/aiws-spec 0.0.24 → 0.0.25

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 (191) hide show
  1. package/README.md +22 -0
  2. package/docs/aiws-bootstrap-routing-design.md +138 -0
  3. package/docs/aiws-governance-positioning.md +69 -0
  4. package/docs/cli-interface.md +87 -5
  5. package/docs/collaboration-artifacts.md +62 -0
  6. package/docs/opencode-omo-adapter.md +135 -0
  7. package/docs/opencode-omo-validation-checklist.md +90 -0
  8. package/docs/spec-contract.md +26 -11
  9. package/docs/superpowers-collaboration-adoption.md +92 -0
  10. package/docs/workflow-delegation-contracts.json +274 -0
  11. package/docs/workflow-delegation-contracts.md +248 -0
  12. package/docs/workflow-delegation-contracts.schema.json +176 -0
  13. package/docs/workflow-governance-rules.json +294 -0
  14. package/docs/workflow-governance-rules.md +63 -0
  15. package/docs/workflow-governance-rules.schema.json +182 -0
  16. package/docs/workflow-review-gates.json +72 -0
  17. package/docs/workflow-review-gates.md +36 -0
  18. package/docs/workflow-review-gates.schema.json +67 -0
  19. package/docs/workflow-router-rules.json +196 -0
  20. package/docs/workflow-router-rules.md +83 -0
  21. package/docs/workflow-router-rules.schema.json +119 -0
  22. package/docs/workflow-stage-contracts.json +148 -0
  23. package/docs/workflow-stage-contracts.md +70 -0
  24. package/docs/workflow-stage-contracts.schema.json +98 -0
  25. package/package.json +1 -1
  26. package/templates/workspace/.agents/skills/using-aiws/SKILL.md +83 -0
  27. package/templates/workspace/.agents/skills/ws-commit/SKILL.md +23 -0
  28. package/templates/workspace/.agents/skills/ws-delegate/SKILL.md +75 -0
  29. package/templates/workspace/.agents/skills/ws-deliver/SKILL.md +22 -0
  30. package/templates/workspace/.agents/skills/ws-dev/SKILL.md +35 -6
  31. package/templates/workspace/.agents/skills/ws-finish/SKILL.md +24 -0
  32. package/templates/workspace/.agents/skills/ws-frontend-design/SKILL.md +126 -0
  33. package/templates/workspace/.agents/skills/ws-handoff/SKILL.md +22 -1
  34. package/templates/workspace/.agents/skills/ws-plan/SKILL.md +25 -1
  35. package/templates/workspace/.agents/skills/ws-plan-verify/SKILL.md +20 -0
  36. package/templates/workspace/.agents/skills/ws-preflight/SKILL.md +23 -0
  37. package/templates/workspace/.agents/skills/ws-quality-review/SKILL.md +56 -0
  38. package/templates/workspace/.agents/skills/ws-review/SKILL.md +24 -0
  39. package/templates/workspace/.agents/skills/ws-spec-review/SKILL.md +57 -0
  40. package/templates/workspace/.agents/skills/ws-verify-before-complete/SKILL.md +59 -0
  41. package/templates/workspace/.claude/commands/using-aiws.md +24 -0
  42. package/templates/workspace/.claude/commands/ws-quality-review.md +24 -0
  43. package/templates/workspace/.claude/commands/ws-spec-review.md +24 -0
  44. package/templates/workspace/.claude/commands/ws-verify-before-complete.md +24 -0
  45. package/templates/workspace/.claude/skills/p-aiws-change-archive/SKILL.md +24 -0
  46. package/templates/workspace/.claude/skills/p-aiws-change-finish/SKILL.md +24 -0
  47. package/templates/workspace/.claude/skills/p-aiws-change-list/SKILL.md +18 -0
  48. package/templates/workspace/.claude/skills/p-aiws-change-new/SKILL.md +26 -0
  49. package/templates/workspace/.claude/skills/p-aiws-change-next/SKILL.md +19 -0
  50. package/templates/workspace/.claude/skills/p-aiws-change-start/SKILL.md +33 -0
  51. package/templates/workspace/.claude/skills/p-aiws-change-status/SKILL.md +19 -0
  52. package/templates/workspace/.claude/skills/p-aiws-change-sync/SKILL.md +19 -0
  53. package/templates/workspace/.claude/skills/p-aiws-change-templates-init/SKILL.md +18 -0
  54. package/templates/workspace/.claude/skills/p-aiws-change-templates-which/SKILL.md +18 -0
  55. package/templates/workspace/.claude/skills/p-aiws-change-validate/SKILL.md +23 -0
  56. package/templates/workspace/.claude/skills/p-aiws-hooks-install/SKILL.md +30 -0
  57. package/templates/workspace/.claude/skills/p-aiws-hooks-status/SKILL.md +18 -0
  58. package/templates/workspace/.claude/skills/p-aiws-init/SKILL.md +27 -0
  59. package/templates/workspace/.claude/skills/p-aiws-rollback/SKILL.md +18 -0
  60. package/templates/workspace/.claude/skills/p-aiws-update/SKILL.md +26 -0
  61. package/templates/workspace/.claude/skills/p-aiws-validate/SKILL.md +22 -0
  62. package/templates/workspace/.claude/skills/p-tasks-plan/SKILL.md +37 -0
  63. package/templates/workspace/.claude/skills/using-aiws/SKILL.md +83 -0
  64. package/templates/workspace/.claude/skills/ws-analyze/SKILL.md +26 -0
  65. package/templates/workspace/.claude/skills/ws-bugfix/SKILL.md +111 -0
  66. package/templates/workspace/.claude/skills/ws-commit/SKILL.md +127 -0
  67. package/templates/workspace/.claude/skills/ws-deliver/SKILL.md +231 -0
  68. package/templates/workspace/.claude/skills/ws-dev/SKILL.md +168 -0
  69. package/templates/workspace/.claude/skills/ws-finish/SKILL.md +220 -0
  70. package/templates/workspace/.claude/skills/ws-frontend-design/SKILL.md +126 -0
  71. package/templates/workspace/.claude/skills/ws-handoff/SKILL.md +52 -0
  72. package/templates/workspace/.claude/skills/ws-migrate/SKILL.md +54 -0
  73. package/templates/workspace/.claude/skills/ws-plan/SKILL.md +138 -0
  74. package/templates/workspace/.claude/skills/ws-plan-verify/SKILL.md +61 -0
  75. package/templates/workspace/.claude/skills/ws-preflight/SKILL.md +55 -0
  76. package/templates/workspace/.claude/skills/ws-pull/SKILL.md +119 -0
  77. package/templates/workspace/.claude/skills/ws-push/SKILL.md +110 -0
  78. package/templates/workspace/.claude/skills/ws-quality-review/SKILL.md +56 -0
  79. package/templates/workspace/.claude/skills/ws-req-change/SKILL.md +41 -0
  80. package/templates/workspace/.claude/skills/ws-req-contract-sync/SKILL.md +17 -0
  81. package/templates/workspace/.claude/skills/ws-req-contract-validate/SKILL.md +12 -0
  82. package/templates/workspace/.claude/skills/ws-req-flow-sync/SKILL.md +28 -0
  83. package/templates/workspace/.claude/skills/ws-req-review/SKILL.md +32 -0
  84. package/templates/workspace/.claude/skills/ws-review/SKILL.md +50 -0
  85. package/templates/workspace/.claude/skills/ws-rule/SKILL.md +23 -0
  86. package/templates/workspace/.claude/skills/ws-spec-review/SKILL.md +57 -0
  87. package/templates/workspace/.claude/skills/ws-submodule-setup/SKILL.md +65 -0
  88. package/templates/workspace/.claude/skills/ws-verify-before-complete/SKILL.md +59 -0
  89. package/templates/workspace/.opencode/command/using-aiws.md +29 -0
  90. package/templates/workspace/.opencode/command/ws-delegate.md +33 -0
  91. package/templates/workspace/.opencode/command/ws-plan.md +3 -0
  92. package/templates/workspace/.opencode/command/ws-preflight.md +4 -0
  93. package/templates/workspace/.opencode/command/ws-quality-review.md +30 -0
  94. package/templates/workspace/.opencode/command/ws-review.md +3 -0
  95. package/templates/workspace/.opencode/command/ws-spec-review.md +30 -0
  96. package/templates/workspace/.opencode/command/ws-verify-before-complete.md +27 -0
  97. package/templates/workspace/.opencode/commands/p-aiws-change-archive.md +27 -0
  98. package/templates/workspace/.opencode/commands/p-aiws-change-finish.md +27 -0
  99. package/templates/workspace/.opencode/commands/p-aiws-change-list.md +26 -0
  100. package/templates/workspace/.opencode/commands/p-aiws-change-new.md +27 -0
  101. package/templates/workspace/.opencode/commands/p-aiws-change-next.md +27 -0
  102. package/templates/workspace/.opencode/commands/p-aiws-change-start.md +27 -0
  103. package/templates/workspace/.opencode/commands/p-aiws-change-status.md +27 -0
  104. package/templates/workspace/.opencode/commands/p-aiws-change-sync.md +27 -0
  105. package/templates/workspace/.opencode/commands/p-aiws-change-templates-init.md +26 -0
  106. package/templates/workspace/.opencode/commands/p-aiws-change-templates-which.md +26 -0
  107. package/templates/workspace/.opencode/commands/p-aiws-change-validate.md +27 -0
  108. package/templates/workspace/.opencode/commands/p-aiws-hooks-install.md +26 -0
  109. package/templates/workspace/.opencode/commands/p-aiws-hooks-status.md +26 -0
  110. package/templates/workspace/.opencode/commands/p-aiws-init.md +23 -0
  111. package/templates/workspace/.opencode/commands/p-aiws-rollback.md +16 -0
  112. package/templates/workspace/.opencode/commands/p-aiws-update.md +22 -0
  113. package/templates/workspace/.opencode/commands/p-aiws-validate.md +17 -0
  114. package/templates/workspace/.opencode/commands/using-aiws.md +29 -0
  115. package/templates/workspace/.opencode/commands/ws-analyze.md +30 -0
  116. package/templates/workspace/.opencode/commands/ws-bugfix.md +35 -0
  117. package/templates/workspace/.opencode/commands/ws-commit.md +70 -0
  118. package/templates/workspace/.opencode/commands/ws-delegate.md +33 -0
  119. package/templates/workspace/.opencode/commands/ws-deliver.md +58 -0
  120. package/templates/workspace/.opencode/commands/ws-dev.md +36 -0
  121. package/templates/workspace/.opencode/commands/ws-finish.md +71 -0
  122. package/templates/workspace/.opencode/commands/ws-handoff.md +25 -0
  123. package/templates/workspace/.opencode/commands/ws-migrate.md +25 -0
  124. package/templates/workspace/.opencode/commands/ws-plan-verify.md +28 -0
  125. package/templates/workspace/.opencode/commands/ws-plan.md +27 -0
  126. package/templates/workspace/.opencode/commands/ws-preflight.md +34 -0
  127. package/templates/workspace/.opencode/commands/ws-pull.md +87 -0
  128. package/templates/workspace/.opencode/commands/ws-push.md +93 -0
  129. package/templates/workspace/.opencode/commands/ws-quality-review.md +30 -0
  130. package/templates/workspace/.opencode/commands/ws-req-change.md +45 -0
  131. package/templates/workspace/.opencode/commands/ws-req-contract-sync.md +21 -0
  132. package/templates/workspace/.opencode/commands/ws-req-contract-validate.md +16 -0
  133. package/templates/workspace/.opencode/commands/ws-req-flow-sync.md +23 -0
  134. package/templates/workspace/.opencode/commands/ws-req-review.md +36 -0
  135. package/templates/workspace/.opencode/commands/ws-review.md +33 -0
  136. package/templates/workspace/.opencode/commands/ws-rule.md +27 -0
  137. package/templates/workspace/.opencode/commands/ws-spec-review.md +30 -0
  138. package/templates/workspace/.opencode/commands/ws-submodule-setup.md +57 -0
  139. package/templates/workspace/.opencode/commands/ws-verify-before-complete.md +27 -0
  140. package/templates/workspace/.opencode/oh-my-opencode.json.example +17 -0
  141. package/templates/workspace/.opencode/skills/p-aiws-change-archive/SKILL.md +24 -0
  142. package/templates/workspace/.opencode/skills/p-aiws-change-finish/SKILL.md +24 -0
  143. package/templates/workspace/.opencode/skills/p-aiws-change-list/SKILL.md +18 -0
  144. package/templates/workspace/.opencode/skills/p-aiws-change-new/SKILL.md +26 -0
  145. package/templates/workspace/.opencode/skills/p-aiws-change-next/SKILL.md +19 -0
  146. package/templates/workspace/.opencode/skills/p-aiws-change-start/SKILL.md +33 -0
  147. package/templates/workspace/.opencode/skills/p-aiws-change-status/SKILL.md +19 -0
  148. package/templates/workspace/.opencode/skills/p-aiws-change-sync/SKILL.md +19 -0
  149. package/templates/workspace/.opencode/skills/p-aiws-change-templates-init/SKILL.md +18 -0
  150. package/templates/workspace/.opencode/skills/p-aiws-change-templates-which/SKILL.md +18 -0
  151. package/templates/workspace/.opencode/skills/p-aiws-change-validate/SKILL.md +23 -0
  152. package/templates/workspace/.opencode/skills/p-aiws-hooks-install/SKILL.md +30 -0
  153. package/templates/workspace/.opencode/skills/p-aiws-hooks-status/SKILL.md +18 -0
  154. package/templates/workspace/.opencode/skills/p-aiws-init/SKILL.md +27 -0
  155. package/templates/workspace/.opencode/skills/p-aiws-rollback/SKILL.md +18 -0
  156. package/templates/workspace/.opencode/skills/p-aiws-update/SKILL.md +26 -0
  157. package/templates/workspace/.opencode/skills/p-aiws-validate/SKILL.md +22 -0
  158. package/templates/workspace/.opencode/skills/p-tasks-plan/SKILL.md +37 -0
  159. package/templates/workspace/.opencode/skills/using-aiws/SKILL.md +87 -0
  160. package/templates/workspace/.opencode/skills/ws-analyze/SKILL.md +26 -0
  161. package/templates/workspace/.opencode/skills/ws-bugfix/SKILL.md +111 -0
  162. package/templates/workspace/.opencode/skills/ws-commit/SKILL.md +127 -0
  163. package/templates/workspace/.opencode/skills/ws-delegate/SKILL.md +63 -0
  164. package/templates/workspace/.opencode/skills/ws-deliver/SKILL.md +231 -0
  165. package/templates/workspace/.opencode/skills/ws-dev/SKILL.md +168 -0
  166. package/templates/workspace/.opencode/skills/ws-finish/SKILL.md +220 -0
  167. package/templates/workspace/.opencode/skills/ws-frontend-design/SKILL.md +126 -0
  168. package/templates/workspace/.opencode/skills/ws-handoff/SKILL.md +52 -0
  169. package/templates/workspace/.opencode/skills/ws-migrate/SKILL.md +54 -0
  170. package/templates/workspace/.opencode/skills/ws-plan/SKILL.md +148 -0
  171. package/templates/workspace/.opencode/skills/ws-plan-verify/SKILL.md +61 -0
  172. package/templates/workspace/.opencode/skills/ws-preflight/SKILL.md +60 -0
  173. package/templates/workspace/.opencode/skills/ws-pull/SKILL.md +119 -0
  174. package/templates/workspace/.opencode/skills/ws-push/SKILL.md +110 -0
  175. package/templates/workspace/.opencode/skills/ws-quality-review/SKILL.md +63 -0
  176. package/templates/workspace/.opencode/skills/ws-req-change/SKILL.md +41 -0
  177. package/templates/workspace/.opencode/skills/ws-req-contract-sync/SKILL.md +17 -0
  178. package/templates/workspace/.opencode/skills/ws-req-contract-validate/SKILL.md +12 -0
  179. package/templates/workspace/.opencode/skills/ws-req-flow-sync/SKILL.md +28 -0
  180. package/templates/workspace/.opencode/skills/ws-req-review/SKILL.md +32 -0
  181. package/templates/workspace/.opencode/skills/ws-review/SKILL.md +57 -0
  182. package/templates/workspace/.opencode/skills/ws-rule/SKILL.md +23 -0
  183. package/templates/workspace/.opencode/skills/ws-spec-review/SKILL.md +64 -0
  184. package/templates/workspace/.opencode/skills/ws-submodule-setup/SKILL.md +65 -0
  185. package/templates/workspace/.opencode/skills/ws-verify-before-complete/SKILL.md +59 -0
  186. package/templates/workspace/AGENTS.md +72 -30
  187. package/templates/workspace/changes/README.md +18 -0
  188. package/templates/workspace/changes/templates/proposal.md +15 -0
  189. package/templates/workspace/changes/templates/tasks.md +9 -2
  190. package/templates/workspace/manifest.json +277 -2
  191. package/templates/workspace/tools/ws_change_check.py +26 -4
@@ -0,0 +1,231 @@
1
+ ---
2
+ name: ws-deliver
3
+ description: 交付(submodules + superproject 分步提交,并安全合并回目标分支)
4
+ ---
5
+
6
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
7
+
8
+ 目标:
9
+ - 适配 superproject + submodule(数量不固定)的交付收尾:
10
+ 1) 先逐个提交 submodule(每个 repo 单独确认 commit message)
11
+ 2) 再提交 superproject(包含 submodule gitlink 指针更新 + 变更工件/代码)
12
+ 3) 最后 fast-forward 合并回目标分支(复用 `aiws change finish`,减少手动 merge 出错)
13
+
14
+ 非目标(强制):
15
+ - 不自动 `git add -A`(避免误提交)
16
+ - 不自动 push
17
+ - 不自动删除分支
18
+
19
+ 前置(强制):
20
+ 1) 先运行 `$ws-preflight`。
21
+ 2) 确认当前处于 change 分支(推荐):`change/<change-id>`(也支持 `changes/`、`ws/`、`ws-change/`)。
22
+ - 若不在 change 分支:要求用户先切换到 `change/<change-id>`(或在命令里显式提供 `<change-id>`)。
23
+ 3) 任何自动提交都必须在提交前输出:
24
+ - 该 repo 的 `git status --porcelain`
25
+ - 该 repo 的 `git diff --staged`
26
+ 并让用户确认 commit message(每个 repo 单独确认)。
27
+
28
+ 阶段定位:
29
+ - delivery 阶段;负责在多 repo / submodule 场景下完成顺序提交、证据收敛和最终合并前准备。
30
+
31
+ 必需输入:
32
+ - 当前 `change/<change-id>` 上下文
33
+ - 若存在 submodule:`.gitmodules` + `changes/<change-id>/submodules.targets`
34
+ - 各 repo 当前状态与 staged diff
35
+
36
+ 必需输出:
37
+ - `Submodules:` 每个 submodule 的提交摘要
38
+ - `Superproject:` 主仓库提交摘要
39
+ - `Evidence:` validate stamp 与持久证据路径
40
+ - `Next:` 进入 `$ws-finish` 或 `aiws change finish`
41
+
42
+ 阻断条件:
43
+ - 不在正确的 change 上下文
44
+ - submodule branch / targets 真值不完整
45
+ - 任一 repo 未确认 commit message 或存在未处理冲突
46
+
47
+ 完成判定:
48
+ - submodule 与 superproject 提交都已完成,证据已收敛,且可以安全进入 finish 阶段。
49
+
50
+ 建议流程(按顺序):
51
+
52
+ ## 0) submodule branch 真值检查(减少 detached 与人为差异)
53
+ 如果存在 `.gitmodules` 但缺少 `submodule.<name>.branch`,先运行 `$ws-submodule-setup` 并提交 `.gitmodules`,否则后续 `aiws validate .` 会失败,且 `ws-pull/ws-finish` 无法确定性工作。
54
+ > 说明:若同一 superproject 分支内存在多渠道 submodule 目标分支的交付需求,可在 `changes/<change-id>/submodules.targets` 额外声明本次 change 的目标分支;交付时会优先使用该文件(不改变 `.gitmodules` 的团队默认真值)。
55
+ > 生成该文件时,可以按当前状态做默认预填,但必须显式说明来源并让用户确认:detached HEAD 默认建议取 `.gitmodules` 声明分支;已附着在某个本地分支时默认建议取当前分支;finish/push 最终只认 `submodules.targets`。
56
+ ```bash
57
+ if [[ -f .gitmodules ]]; then
58
+ missing=0
59
+ while read -r key sub_path; do
60
+ name="${key#submodule.}"; name="${name%.path}"
61
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
62
+ if [[ -z "${b:-}" ]]; then
63
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
64
+ missing=1
65
+ fi
66
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
67
+ if [[ "$missing" -ne 0 ]]; then
68
+ echo "hint: run $ws-submodule-setup (and commit .gitmodules), then retry"
69
+ exit 2
70
+ fi
71
+
72
+ # 强约束:当 .gitmodules 声明 submodules 时,要求本次 change 存在 submodules.targets 且覆盖所有 submodule path
73
+ if git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' >/dev/null 2>&1; then
74
+ change_id="$(git branch --show-current | sed -n 's|^change/||p')"
75
+ targets="changes/${change_id}/submodules.targets"
76
+ if [[ ! -f "${targets}" ]]; then
77
+ echo "error: missing ${targets} (required when .gitmodules declares submodules)"
78
+ exit 2
79
+ fi
80
+ t_missing=0
81
+ while read -r _ sub_path; do
82
+ [[ -z "${sub_path:-}" ]] && continue
83
+ if ! awk -v p="${sub_path}" '$1==p && $0 !~ /^[[:space:]]*#/ && $2!="" { found=1 } END { exit(found?0:1) }' "${targets}" 2>/dev/null; then
84
+ echo "error: ${targets} missing entry for submodule path=${sub_path}"
85
+ t_missing=1
86
+ fi
87
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
88
+ if [[ "${t_missing}" -ne 0 ]]; then
89
+ echo "hint: fill ${targets} as: <path> <target_branch> [remote]"
90
+ exit 2
91
+ fi
92
+ fi
93
+ fi
94
+ ```
95
+
96
+ ## A) 发现 submodules 清单(数量不固定)
97
+ 在 superproject 根目录执行:
98
+ ```bash
99
+ git submodule status --recursive
100
+ ```
101
+ 如果没有 submodule:跳到 C)。
102
+
103
+ ## B) 逐个提交 submodules(先 submodule,后 superproject)
104
+ 对每个 submodule path(可递归)重复以下步骤(建议按 `git submodule status --recursive` 顺序):
105
+ 1) 定位并检查状态:
106
+ ```bash
107
+ sub_path="<path>"
108
+ git -C "$sub_path" branch --show-current
109
+ git -C "$sub_path" status --porcelain
110
+ ```
111
+ 2) 先确定该 submodule 的目标分支来源,并显式说明给用户:
112
+ - `branch --show-current` 非空:默认建议用当前分支
113
+ - `branch --show-current` 为空(detached HEAD):默认建议用 `.gitmodules` 的 `submodule.<name>.branch`
114
+ - 若建议值与 `changes/<change-id>/submodules.targets` 已落盘值不一致:以 `submodules.targets` 为准,并先提示差异
115
+ 3) 若 submodule 处于 detached HEAD(`branch --show-current` 为空):
116
+ - 说明:这通常是因为 superproject 的 gitlink checkout(例如 `git submodule update`)导致 detached。
117
+ - 不要直接切 `change/<change-id>` / `main` / `master` 来解 detached。
118
+ - 若你要在该 submodule 里提交:先按目标分支挂到 pin 分支 `aiws/pin/<target-branch>`,再在其上提交:
119
+ - 目标分支真值优先级:`changes/<change-id>/submodules.targets`(若存在)> `.gitmodules submodule.<name>.branch`
120
+ ```bash
121
+ change_id="<change-id>"
122
+ targets="changes/${change_id}/submodules.targets"
123
+ sub_name="$(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null | awk -v p="${sub_path}" '$2==p { name=$1; sub(/^submodule\\./,"",name); sub(/\\.path$/,"",name); print name; exit }')"
124
+ base_branch="$(python3 - <<'PY'
125
+ import json, pathlib
126
+ change_id = "<change-id>"
127
+ meta = pathlib.Path("changes") / change_id / ".ws-change.json"
128
+ data = json.loads(meta.read_text(encoding="utf-8"))
129
+ print((data.get("base_branch") or "").strip())
130
+ PY
131
+ )"
132
+ test -n "${sub_name}"
133
+ test -n "${base_branch}"
134
+
135
+ source tools/ws_resolve_sub_target.sh
136
+ ws_resolve_sub_target "${sub_path}" "${sub_name}" "${targets}" "${base_branch}" || exit 2
137
+ target_branch="${_resolved_branch}"
138
+ remote="${_resolved_remote}"
139
+
140
+ git -C "$sub_path" fetch "${remote}" --prune
141
+ if ! git -C "$sub_path" show-ref --verify --quiet "refs/remotes/${remote}/${target_branch}"; then
142
+ echo "error: missing ${remote}/${target_branch} for submodule path=${sub_path}"
143
+ exit 2
144
+ fi
145
+ git -C "$sub_path" checkout -B "aiws/pin/${target_branch}" HEAD
146
+ git -C "$sub_path" branch --set-upstream-to "${remote}/${target_branch}" "aiws/pin/${target_branch}" >/dev/null 2>&1 || true
147
+ ```
148
+ - 若 `origin/<target-branch>` 不存在,或用户明确不想使用 pin 分支:停止,解释风险(提交可能不可追溯/难以推送)。
149
+ 4) 选择性 staging(默认用 `-p` 更安全):
150
+ ```bash
151
+ git -C "$sub_path" add -p
152
+ git -C "$sub_path" diff --staged --stat
153
+ git -C "$sub_path" diff --staged
154
+ ```
155
+ 5) AI 生成该 submodule 的 commit message(标题+可选 body),并让用户确认(每个 repo 单独确认)。
156
+ 6) 执行提交(不带 `--no-verify`):
157
+ ```bash
158
+ git -C "$sub_path" commit -m "<message>"
159
+ ```
160
+ 7) 若该 submodule 没有 staged changes:跳过(不要硬提交空 commit)。
161
+
162
+ ## C) 提交 superproject(更新 gitlinks + 自身改动 + changes 工件)
163
+ 1) 先检查 submodule 指针差异(gitlinks):
164
+ ```bash
165
+ git diff --submodule
166
+ ```
167
+ 2) 选择性 staging:
168
+ - 先 stage 发生指针变化的 submodule 路径(明确列出):
169
+ ```bash
170
+ git add <submodule-path-1> <submodule-path-2>
171
+ ```
172
+ - 再 stage superproject 自身改动(默认 `-p`):
173
+ ```bash
174
+ git add -p
175
+ git diff --staged --stat
176
+ git diff --staged
177
+ ```
178
+ 3) AI 生成 superproject 的 commit message(应包含 `bump submodule <name> -> <sha>` 等关键信息),并让用户确认。
179
+ 4) 提交:
180
+ ```bash
181
+ git commit -m "<message>"
182
+ ```
183
+
184
+ ## D) 门禁与证据(推荐)
185
+ ```bash
186
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
187
+ ./node_modules/.bin/aiws validate . --stamp
188
+ elif command -v aiws >/dev/null 2>&1; then
189
+ aiws validate . --stamp
190
+ else
191
+ npx @aipper/aiws validate . --stamp
192
+ fi
193
+ ```
194
+
195
+ ## D2) 生成持久证据并回填 Evidence_Path(强烈建议)
196
+ > 说明:`.agentdocs/tmp/...` 默认 gitignored;交付前建议把关键结果落到 `changes/<change-id>/evidence/...` 并回填 `proposal.md`/`plan` 的 `Evidence_Path`,避免后续评审/二次会话读不到证据。
197
+ ```bash
198
+ change_id="<change-id>"
199
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
200
+ ./node_modules/.bin/aiws change evidence "${change_id}"
201
+ elif command -v aiws >/dev/null 2>&1; then
202
+ aiws change evidence "${change_id}"
203
+ else
204
+ npx @aipper/aiws change evidence "${change_id}"
205
+ fi
206
+ ```
207
+
208
+ ## D3) 生成状态快照(可选,建议)
209
+ ```bash
210
+ aiws change state "${change_id}" --write
211
+ ```
212
+
213
+ ## E) 安全合并回目标分支(fast-forward)
214
+ 优先使用 `$ws-finish`(底层调用 `aiws change finish`,并在 push 成功后清理对应 change worktree)。
215
+
216
+ 若需要显式指定目标分支:
217
+ ```bash
218
+ aiws change finish <change-id> --into <base-branch>
219
+ ```
220
+
221
+ ## F) (可选)归档变更工件
222
+ ```bash
223
+ aiws change archive <change-id>
224
+ ```
225
+
226
+ 输出要求:
227
+ - `Submodules:` 每个 submodule 的分支/提交摘要(repo → commit sha → message)
228
+ - `Superproject:` 提交摘要
229
+ - `Merge:` `aiws change finish` 的输出(into/from)
230
+ - `Worktree cleanup:` 若存在独立 change worktree,输出清理结果(removed/skipped + reason)
231
+ - `Evidence:` `.agentdocs/tmp/aiws-validate/*.json`(若使用 --stamp)
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: ws-dev
3
+ description: 开发(按需求实现并验证;适用于任何需要修改代码/配置的任务)
4
+ ---
5
+
6
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
7
+
8
+ 目标:在 AIWS 约束下完成一个可回放、可验证的小步交付。
9
+
10
+ 阶段定位:
11
+ - implementation 阶段;负责在既定计划/需求绑定下完成最小实现与验证。
12
+
13
+ 必需输入:
14
+ - 真值文件:`AI_PROJECT.md` / `REQUIREMENTS.md` / `AI_WORKSPACE.md`
15
+ - 当前任务的归因目标(`Req_ID` 或 `Problem_ID`)
16
+ - 若为 medium/complex:已通过 `$ws-plan` / `$ws-plan-verify` 的计划
17
+ - 当前 `change/<change-id>` 上下文或等价变更归因
18
+
19
+ 必需输出:
20
+ - `变更文件(Changed):` 实际改动清单
21
+ - `验证(Verify):` 实际运行的命令与结果说明
22
+ - `证据(Evidence):` `plan/...`、`changes/<change-id>/...`、`.agentdocs/tmp/...` 等证据路径
23
+ - `Next:` 若准备提交,建议 `$ws-review` 或 `$ws-commit`
24
+
25
+ 阻断条件:
26
+ - 无法把改动归因到 `REQUIREMENTS.md` 或问题单
27
+ - 没有可复现验证入口
28
+ - 需要创建 change 上下文但当前工作区状态不允许安全切换
29
+
30
+ 完成判定:
31
+ - 改动已落盘、验证已执行或明确未执行原因、证据路径可回放,并可进入 review/commit 阶段。
32
+
33
+ 建议流程:
34
+ 1) 先做 preflight:定位项目根目录,读取 `AI_PROJECT.md` / `REQUIREMENTS.md` / `AI_WORKSPACE.md`,输出约束摘要。
35
+ - 若是中大型任务:建议先用 `$ws-plan` 生成 `plan/` 工件,再进入实现(便于可回放与对齐验证入口)。
36
+ - 若已有 `plan/` 工件:先执行 `$ws-plan-verify`;通过后再进入实现(防止计划过长/跑偏)。
37
+ - 若 `$ws-plan` 刚创建了 `change/<change-id>` worktree:后续实现必须在该 worktree 中继续;不要回到原工作区重复 `aiws change start ...`
38
+ 2) 建立变更归因(推荐):
39
+ - ⚠️ 若你准备执行 `aiws change start ... --switch/--worktree` 或手工 `git switch ...` 改变 checkout 上下文,先确认工作区状态:`git status --porcelain`。否则切分支/创建 worktree 后,未提交改动可能看起来丢了(worktree 只从 `HEAD` checkout,未提交内容会留在原目录)。
40
+ - 若当前目录已经是 `change/<change-id>` worktree(例如由 `$ws-plan` 创建):直接在这里继续,不要再创建第二个 worktree,也不要回原工作区写代码。
41
+ - 若 `git status --porcelain` 非空仅因为上一轮 `$ws-plan` / `aiws change new|sync` 生成了 `plan/...`、`changes/<change-id>/proposal.md`、`tasks.md`、`design.md`、`submodules.targets`,这是预期行为,不等于流程出错。处理方式:
42
+ - 已经在 `change/<change-id>` 分支:直接继续实现,并把这些文件作为计划/证据工件保留。
43
+ - 还没进入 `change/<change-id>`:先运行 `aiws change start <change-id> --hooks --no-switch` 建立 change 上下文;若你仍要 `--switch/--worktree`,先提交这些规划工件,再切换上下文。
44
+ - 强制(当你准备执行 `aiws change start ... --worktree/--switch` 创建新 change 时):先同步线上代码(含 submodules),避免基线过旧导致的 rebase/冲突与别人已更新但本地没拉的协作摩擦。
45
+ - 若你已经在 `change/<change-id>` 上继续开发:不要在此处强制 pull(避免把远端变动拉进变更分支);改为按需 `git fetch`/rebase,并保持门禁与验证可复现。
46
+ - 在 AI 工具中运行:`$ws-pull`(推荐;会在工作区不干净时阻断)
47
+ - 或等价手工(必须工作区干净;失败则停止并人工处理):
48
+ ```bash
49
+ cur="$(git branch --show-current)"
50
+ if [[ "${cur}" =~ ^(change|changes|ws|ws-change)/ ]]; then
51
+ echo "info: already on change branch (${cur}); skip pull here"
52
+ else
53
+ git status --porcelain
54
+ git pull --ff-only
55
+ if [[ -f .gitmodules ]]; then
56
+ git submodule sync --recursive
57
+ git submodule update --init --recursive
58
+ fi
59
+ fi
60
+ ```
61
+ - 推荐更安全(默认):`aiws change start <change-id> --hooks --no-switch`(只创建分支/工件 + 启用 hooks;不切分支)
62
+ - 准备进入实现时:若当前已在 `change/<change-id>` 直接继续;若需切换到该分支,先确认除规划工件外无额外未提交改动,再执行:`git switch change/<change-id>`
63
+ - 若你明确要一键切分支(不推荐,且 dirty 会被拦截):`aiws change start <change-id> --hooks --switch`
64
+ - superproject + submodule(推荐):`aiws change start <change-id> --hooks --worktree --submodules`(创建独立 worktree;当前目录分支保持不变;会在新 worktree 内初始化 submodules;若忘了 `--submodules` 也会强制初始化)
65
+ - 若后续需要在 detached submodule 内提交:先挂到 `aiws/pin/<target-branch>`;不要直接切 `change/<change-id>` / `main` / `master`
66
+ - 若仓库存在 submodule(`.gitmodules` 声明了 submodule 条目):进入编码前必须准备好 `changes/<change-id>/submodules.targets`,并把每个 submodule 挂到对应的 `aiws/pin/<target_branch>`(必要时切到 `<remote>/<target_branch>`;这可能会改变 superproject 的 gitlink 指针,属于预期的选渠道行为;缺失该文件会被门禁阻断)
67
+ - 允许用当前 submodule 状态来做默认预填,但必须显式说明来源,并最终落盘到 `submodules.targets`:
68
+ - detached HEAD:默认建议取 `.gitmodules` 中声明的 `submodule.<name>.branch`
69
+ - 已附着在某个本地分支:默认建议取该 submodule 的当前分支名
70
+ - 上述两条都只是建议值,不是运行时真值;真正的 finish/push 只认 `changes/<change-id>/submodules.targets`
71
+ ```bash
72
+ change_id="<change-id>"
73
+ targets="changes/${change_id}/submodules.targets"
74
+ has_submodules=0
75
+ if [[ -f .gitmodules ]]; then
76
+ if git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' >/dev/null 2>&1; then
77
+ has_submodules=1
78
+ fi
79
+ fi
80
+ if [[ "${has_submodules}" -eq 1 && ! -f "${targets}" ]]; then
81
+ echo "error: missing ${targets} (required when .gitmodules declares submodules)"
82
+ echo "hint: create it first; defaults may be inferred, but the final truth must be written explicitly"
83
+ exit 2
84
+ fi
85
+
86
+ if [[ -f "${targets}" ]]; then
87
+ echo "info: applying submodule targets: ${targets}"
88
+ while read -r sub_path target_branch remote; do
89
+ [[ -z "${sub_path:-}" ]] && continue
90
+ [[ "${sub_path}" == \#* ]] && continue
91
+ [[ -z "${target_branch:-}" ]] && { echo "error: missing target_branch for path=${sub_path}"; exit 2; }
92
+ remote="${remote:-origin}"
93
+
94
+ echo "== submodule: ${sub_path} target=${remote}/${target_branch} =="
95
+ if [[ -n "$(git -C "${sub_path}" status --porcelain 2>/dev/null)" ]]; then
96
+ echo "error: submodule dirty: ${sub_path} (commit/stash first)"
97
+ exit 2
98
+ fi
99
+ git -C "${sub_path}" fetch "${remote}" --prune
100
+ # 选渠道:默认切到远端目标分支,并用 pin 分支承载本地提交(避免 detached)
101
+ git -C "${sub_path}" checkout -B "aiws/pin/${target_branch}" "${remote}/${target_branch}"
102
+ git -C "${sub_path}" branch --set-upstream-to "${remote}/${target_branch}" "aiws/pin/${target_branch}" >/dev/null 2>&1 || true
103
+ git -C "${sub_path}" status -sb
104
+ done < "${targets}"
105
+
106
+ # 检查 superproject 的 gitlink 是否发生变化(预期:若切了不同渠道,会看到差异)
107
+ git diff --submodule
108
+ fi
109
+ ```
110
+ - 若你还没写 `submodules.targets`,可按下面方式先生成建议值,再人工确认后落盘:
111
+ ```bash
112
+ change_id="<change-id>"
113
+ targets="changes/${change_id}/submodules.targets"
114
+ : > "${targets}"
115
+
116
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null | while read -r key sub_path; do
117
+ name="${key#submodule.}"; name="${name%.path}"
118
+ current_branch="$(git -C "${sub_path}" branch --show-current 2>/dev/null || true)"
119
+ declared_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
120
+
121
+ if [[ -n "${current_branch}" ]]; then
122
+ inferred_branch="${current_branch}"
123
+ inferred_from="current branch"
124
+ else
125
+ inferred_branch="${declared_branch}"
126
+ inferred_from=".gitmodules"
127
+ fi
128
+
129
+ if [[ -z "${inferred_branch}" ]]; then
130
+ echo "error: cannot infer target branch for ${sub_path}; set it manually"
131
+ exit 2
132
+ fi
133
+
134
+ printf "%s %s\n" "${sub_path}" "${inferred_branch}" >> "${targets}"
135
+ echo "info: ${sub_path} -> ${inferred_branch} (inferred from ${inferred_from}; remote defaults to origin unless you edit ${targets})"
136
+ done
137
+
138
+ echo "review required: ${targets}"
139
+ cat "${targets}"
140
+ ```
141
+ - 若你明确要在 superproject 直接切分支:`aiws change start <change-id> --hooks --switch`(仅在存在 `.gitmodules` 时有意义;会尝试让 submodules 工作区跟随 superproject 指针)
142
+ - 或手工:`git switch -c change/<change-id>`,并创建 `changes/<change-id>/proposal.md` 与 `changes/<change-id>/tasks.md`(参考 `changes/README.md`)
143
+ 3) 若需要外部 / 子 agent 协作:
144
+ - 分析结论落盘到 `changes/<change-id>/analysis/`
145
+ - patch 草案落盘到 `changes/<change-id>/patches/`
146
+ - 不要把 `patches/` 当成“已应用代码”;主 agent 必须先审查再决定是否采用
147
+ - 若采用了委托结果,后续在 `ws-review` 或 `changes/<change-id>/review/` 中收敛最终结论
148
+ 4) 如涉及需求调整:先做需求评审(可用 `$ws-req-review`)→ 用户确认后再做需求落盘(可用 `$ws-req-change`)(避免需求漂移)。
149
+ 5) 实施最小改动:任何改动都要能归因到 `REQUIREMENTS.md`(验收)或 `issues/problem-issues.csv`(问题)。
150
+ 6) 运行 `AI_WORKSPACE.md` 里声明的验证命令;未运行不声称已运行。
151
+ 7) 多步任务(≥2 步):使用 `update_plan` 工具跟踪 `pending → in_progress → completed`,每完成一步立即更新(不要事后批量更新)。
152
+ 8) 提交前强制门禁(commit/push hooks 也会阻断):
153
+ ```bash
154
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
155
+ ./node_modules/.bin/aiws validate .
156
+ elif command -v aiws >/dev/null 2>&1; then
157
+ aiws validate .
158
+ else
159
+ npx @aipper/aiws validate .
160
+ fi
161
+ ```
162
+ 9) 若使用了协同工件,交付前建议执行 `aiws change evidence <change-id>`,把 review / validate / collaboration summary 收敛到 `changes/<change-id>/evidence/`。
163
+ 10) 交付收尾(推荐,减少手动 merge 出错):运行 `$ws-finish`(底层调用 `aiws change finish`,默认 fast-forward 安全合并回目标分支)。
164
+
165
+ 输出要求:
166
+ - `变更文件(Changed):` 文件清单
167
+ - `验证(Verify):` 实际运行的命令 + 期望结果
168
+ - `证据(Evidence):` 证据路径(例如 `changes/<change-id>/review/...`、`plan/...`、`.agentdocs/tmp/aiws-validate/...` 或 `changes/<change-id>/...`)
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: ws-finish
3
+ description: 收尾(门禁 + 安全合并 + submodule→主仓库顺序 push)
4
+ ---
5
+
6
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
7
+
8
+ 目标:
9
+ - 在结束一次变更交付时,用 fast-forward 安全合并 `change/<change-id>` 回目标分支,减少手输分支名导致的错误
10
+ - 合并成功后,按 `submodule -> superproject` 顺序 push,避免遗漏导致其它仓库拉取异常
11
+ - 不自动删分支;AI 入口执行前应先向用户说明将要 push 的 remote/branch,显式传入 `--push` 即视为确认
12
+ - 若团队希望减少 submodule detached 的人为差异:建议在 `.gitmodules` 配置 `submodule.<name>.branch`,并在日常拉取时使用 `$ws-pull`
13
+
14
+ 前置(必须):
15
+ - 工作区是干净的:`git status --porcelain` 无输出(若有未提交改动:先 commit 或 stash)
16
+ - change 分支已存在:`change/<change-id>`(也支持 `changes/`、`ws/`、`ws-change/`)
17
+ - 若使用 worktree:在目标分支所在 worktree 执行(`aiws change finish` 会提示正确的 worktree)
18
+ - 若存在 `.gitmodules`:必须为每个 submodule 配置 `submodule.<name>.branch`(否则无法确定性减少 detached;先运行 `$ws-submodule-setup` 并提交 `.gitmodules`)
19
+
20
+ 阶段定位:
21
+ - finish 阶段;负责把已提交的 change 安全合并回目标分支,并按约束完成 push / worktree cleanup。
22
+
23
+ 必需输入:
24
+ - 干净的当前工作区
25
+ - 已提交完成的 `change/<change-id>`
26
+ - 若存在 submodule:`.gitmodules` 与 `changes/<change-id>/submodules.targets`
27
+ - 当前 push 目标说明(remote / branch)
28
+
29
+ 必需输出:
30
+ - `Merge:` `aiws change finish` 的结果(from / into)
31
+ - `Push:` 实际 remote / branch 或未 push 原因
32
+ - `Worktree cleanup:` 清理结果
33
+ - `Evidence:` validate stamp / evidence / state 路径
34
+
35
+ 阻断条件:
36
+ - 工作区不干净
37
+ - 缺失 submodule branch / targets 真值
38
+ - fast-forward 失败
39
+ - 子模块目标分支无法安全 push
40
+
41
+ 完成判定:
42
+ - change 已安全合并回目标分支,必要的 push 与 worktree cleanup 已完成或明确说明未完成原因。
43
+
44
+ 建议步骤:
45
+ 0) 若存在 `.gitmodules`,先检查 submodule branch 配置是否齐全(缺失则停止并提示 setup):
46
+ ```bash
47
+ if [[ -f .gitmodules ]]; then
48
+ missing=0
49
+ while read -r key sub_path; do
50
+ name="${key#submodule.}"; name="${name%.path}"
51
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
52
+ if [[ -z "${b:-}" ]]; then
53
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
54
+ missing=1
55
+ fi
56
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
57
+ if [[ "$missing" -ne 0 ]]; then
58
+ echo "hint: run $ws-submodule-setup (and commit .gitmodules), then retry"
59
+ exit 2
60
+ fi
61
+ fi
62
+ ```
63
+ 1) (推荐)先跑一次门禁并落盘证据:
64
+ ```bash
65
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
66
+ ./node_modules/.bin/aiws validate . --stamp
67
+ elif command -v aiws >/dev/null 2>&1; then
68
+ aiws validate . --stamp
69
+ else
70
+ npx @aipper/aiws validate . --stamp
71
+ fi
72
+ ```
73
+ 1.1) (强烈建议)收敛持久证据并回填 `Evidence_Path`:
74
+ ```bash
75
+ change_id="<change-id>"
76
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
77
+ ./node_modules/.bin/aiws change evidence "${change_id}"
78
+ elif command -v aiws >/dev/null 2>&1; then
79
+ aiws change evidence "${change_id}"
80
+ else
81
+ npx @aipper/aiws change evidence "${change_id}"
82
+ fi
83
+ ```
84
+ 1.2) (可选)生成状态快照(建议):
85
+ ```bash
86
+ aiws change state "${change_id}" --write
87
+ ```
88
+ 2) 若不存在 `.gitmodules`,或 submodules 已按顺序处理完成,优先直接使用最小收尾闭环:
89
+ ```bash
90
+ # 若当前就在 change/<change-id> 分支上,可省略 <change-id>
91
+ change_id="<change-id>"
92
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
93
+ ./node_modules/.bin/aiws change finish "${change_id}" --push
94
+ elif command -v aiws >/dev/null 2>&1; then
95
+ aiws change finish "${change_id}" --push
96
+ else
97
+ npx @aipper/aiws change finish "${change_id}" --push
98
+ fi
99
+ ```
100
+ 说明:
101
+ - 该命令会在 fast-forward 合并成功后 push 目标分支。
102
+ - 默认会优先使用当前分支的 upstream 配置(`branch.<name>.remote` + `branch.<name>.merge`);只有在你明确知道要推向别的 remote 时,才追加 `--remote <name>`。
103
+ - 若 `change/<change-id>` 位于独立 worktree,且该 worktree 干净,则会在 push 成功后自动执行 `git worktree remove` + `git worktree prune`。
104
+ - AI 工具内执行前,应先向用户说明将要 push 的 remote/branch;CLI 本身只有在显式传入 `--push` 时才会真正执行 push。
105
+
106
+ 3) 若你需要先处理 submodules,则不要依赖当前分支名推断目标分支,先显式解析 `base_branch`,再执行 submodule 步骤,最后回到主仓库执行 `aiws change finish --push`:
107
+ ```bash
108
+ change_id="<change-id>"
109
+ base_branch="$(python3 - <<'PY'
110
+ import json, pathlib
111
+ change_id = "<change-id>"
112
+ meta = pathlib.Path("changes") / change_id / ".ws-change.json"
113
+ data = json.loads(meta.read_text(encoding="utf-8"))
114
+ print((data.get("base_branch") or "").strip())
115
+ PY
116
+ )"
117
+ test -n "$base_branch"
118
+ ```
119
+ 4) 若 fast-forward 失败(提示需要 rebase):先在 change 分支(或对应 worktree)里 `git rebase <target-branch>`,再重试 `aiws change finish --push`。
120
+ 5) 若存在 `.gitmodules`,先把每个 submodule 的 gitlink commit 合并回其目标分支(解决 detached HEAD),并按顺序 push:
121
+ ```bash
122
+ # 不要用当前分支名代替目标分支;这里显式使用 .ws-change.json 的 base_branch
123
+ change_id="<change-id>"
124
+ targets="changes/${change_id}/submodules.targets"
125
+
126
+ # 子模块清单(没有则跳过)
127
+ git config --file .gitmodules --get-regexp '^submodule\..*\.path$' 2>/dev/null || true
128
+
129
+ # 对每个 submodule.<name>.path <sub_path>:
130
+ # 说明:`git submodule update` 会把 submodule checkout 到固定 gitlink commit,导致 detached HEAD。
131
+ # 为减少游离状态的协作摩擦,本步骤采用 pin 分支策略:
132
+ # - `changes/<change-id>/submodules.targets` 是本次 finish/push 的真值;每个 submodule path 都必须在该文件中声明目标分支(可选 remote)
133
+ # - `.gitmodules` 的 `submodule.<name>.branch` 仍建议保留,用于团队默认配置与校验,但不再作为 finish/push 的 fallback
134
+ # - 生成/检查 `submodules.targets` 时可显式说明默认来源:
135
+ # - detached HEAD:默认建议取 `.gitmodules` 的 `submodule.<name>.branch`
136
+ # - 已附着在某个本地分支:默认建议取当前分支
137
+ # - 以上仅为预填建议;真正执行 finish/push 时仍只认 `submodules.targets`
138
+ # - 不要直接切 `change/<change-id>` / `main` / `master` 等业务分支来解 detached
139
+ # - 不改动 submodule 现有分支指针(例如不强行移动 main/master)
140
+ # - 创建/更新本地 pin 分支:`aiws/pin/<target_branch>` 指向 gitlink commit,并将其 upstream 设为 `origin/<target_branch>`
141
+ if git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' >/dev/null 2>&1; then
142
+ if [[ ! -f "${targets}" ]]; then
143
+ echo "error: missing ${targets} (required when .gitmodules declares submodules)"
144
+ exit 2
145
+ fi
146
+
147
+ source tools/ws_resolve_sub_target.sh
148
+
149
+ while read -r key sub_path; do
150
+ name="${key#submodule.}"; name="${name%.path}"
151
+ [[ -z "${sub_path:-}" ]] && continue
152
+ echo "== submodule: ${sub_path} (${name}) =="
153
+
154
+ current_branch="$(git -C "${sub_path}" branch --show-current 2>/dev/null || true)"
155
+ declared_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
156
+ if [[ -n "${current_branch}" ]]; then
157
+ echo "info: ${sub_path} default suggestion would be current branch: ${current_branch}"
158
+ elif [[ -n "${declared_branch}" ]]; then
159
+ echo "info: ${sub_path} default suggestion would be .gitmodules branch: ${declared_branch}"
160
+ else
161
+ echo "warn: ${sub_path} has no current branch and no .gitmodules branch; submodules.targets must be filled manually"
162
+ fi
163
+
164
+ sub_sha="$(git rev-parse "HEAD:${sub_path}")"
165
+
166
+ ws_resolve_sub_target "${sub_path}" "${name}" "${targets}" "${base_branch}" || exit 2
167
+ target_branch="${_resolved_branch}"
168
+ remote="${_resolved_remote}"
169
+ pin_branch="aiws/pin/${target_branch}"
170
+
171
+ git -C "${sub_path}" fetch "${remote}" --prune
172
+ if ! git -C "${sub_path}" show-ref --verify --quiet "refs/remotes/${remote}/${target_branch}"; then
173
+ echo "error: ${sub_path}: missing ${remote}/${target_branch}; refusing to push superproject (would break gitlink fetchability)"
174
+ exit 2
175
+ fi
176
+
177
+ # 仅当 gitlink commit 属于 <remote>/<target_branch> 的历史时才挂回分支
178
+ if ! git -C "${sub_path}" merge-base --is-ancestor "${sub_sha}" "${remote}/${target_branch}"; then
179
+ echo "[warn] ${sub_path}: ${sub_sha} is not in ${remote}/${target_branch}; keep detached and stop (need manual reconcile)"
180
+ exit 1
181
+ fi
182
+
183
+ git -C "${sub_path}" checkout -B "${pin_branch}" "${sub_sha}"
184
+ git -C "${sub_path}" branch --set-upstream-to "${remote}/${target_branch}" "${pin_branch}" >/dev/null 2>&1 || true
185
+ git -C "${sub_path}" status -sb
186
+
187
+ # push:只允许 fast-forward(若远端分叉会被拒绝;此时必须人工处理)
188
+ git -C "${sub_path}" push "${remote}" "${sub_sha}:refs/heads/${target_branch}"
189
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
190
+ fi
191
+ ```
192
+ 规则:
193
+ - 每个 submodule 必须先执行 pin 分支挂回 + fast-forward push,再 push 主仓库。
194
+ - 若任一 submodule 的 gitlink commit 不在 `<remote>/<target_branch>` 历史中:立即停止,先人工处理分叉,再继续。
195
+
196
+ 6) 仅当 submodules 全部成功后,再在 superproject 当前分支执行最小收尾闭环:
197
+ ```bash
198
+ change_id="<change-id>"
199
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
200
+ ./node_modules/.bin/aiws change finish "${change_id}" --push
201
+ elif command -v aiws >/dev/null 2>&1; then
202
+ aiws change finish "${change_id}" --push
203
+ else
204
+ npx @aipper/aiws change finish "${change_id}" --push
205
+ fi
206
+ ```
207
+ 说明:
208
+ - 该命令内部已经包含主仓库 push 成功后安全清理独立 change worktree 的逻辑。
209
+
210
+ 7) (可选)归档变更工件(完成交付后推荐):
211
+ ```bash
212
+ change_id="<change-id>"
213
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
214
+ ./node_modules/.bin/aiws change archive "${change_id}"
215
+ elif command -v aiws >/dev/null 2>&1; then
216
+ aiws change archive "${change_id}"
217
+ else
218
+ npx @aipper/aiws change archive "${change_id}"
219
+ fi
220
+ ```