Commit 9e8db6d8d15bb2b1df65f96c92c62c21107ef2b5

Authored by yanghl
1 parent bbc8907e

feat(hooks): Stop 钩子自动续跑编码流程 + 去除 A 类人工停点

为实现「coding 阶段后端+前端连跑、无需手敲 continue」:

- 新增 Stop 钩子 auto-continue.sh:回合中途结束自动续跑;命中
  [ERP-HALT] 终止标记或连续空转上限/transcript 无增长时才放行停下,
  自包含防循环(PostToolUse reset-stall-counter.sh 辅助清零)。
- 给所有真终止/硬护栏停点打 [ERP-HALT]:coding-start / test-gate /
  feature-tdd / fe-feature-tdd / module-start / module-report /
  frontend-start / interrupt-check / db-init / downstream-gen。
  审阅停(scope-lock D、db-design-gen E)不打标记 → 钩子自动跨过。
- 删 A 类确认弹窗:scope-lock 技术栈检查、skeleton-gen .env 核对 →
  自动放行。
- feature/fe-feature 的 spec/plan 覆盖确认 → 直接覆盖(CC 内部产物)。

注:钩子在会话启动时加载,已运行的会话需重启才生效。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hooks/hooks.json
@@ -6,6 +6,19 @@ @@ -6,6 +6,19 @@
6 "hooks": [ 6 "hooks": [
7 { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/scripts/log-cross-module.sh" } 7 { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/scripts/log-cross-module.sh" }
8 ] 8 ]
  9 + },
  10 + {
  11 + "matcher": "",
  12 + "hooks": [
  13 + { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/scripts/reset-stall-counter.sh" }
  14 + ]
  15 + }
  16 + ],
  17 + "Stop": [
  18 + {
  19 + "hooks": [
  20 + { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/scripts/auto-continue.sh" }
  21 + ]
9 } 22 }
10 ] 23 ]
11 } 24 }
hooks/scripts/auto-continue.sh 0 → 100755
  1 +#!/usr/bin/env bash
  2 +# Stop 钩子:让 ERP workflow 在回合中途结束时自动续跑,除非命中「终止标记」或「防循环上限」。
  3 +#
  4 +# 设计:
  5 +# 1. 终止标记 [ERP-HALT] —— 终止/硬护栏 skill(coding-start 全部完成、test-gate 红、
  6 +# feature-tdd 路径护栏、interrupt-check 中断)会打印它。出现即放行停下。
  7 +# 2. 防循环 —— 连续「无工具调用」的空转停止累加计数;达到上限放行停下。
  8 +# 正常推进时由 PostToolUse 的 reset-stall-counter.sh 清零,故真流程不受限。
  9 +# 3. 否则 —— block 并喂一句「继续推进」指令,用户无需手敲 continue。
  10 +set -u
  11 +
  12 +INPUT="$(cat)"
  13 +
  14 +SENTINEL='[ERP-HALT]'
  15 +MAX_STALL=8
  16 +
  17 +# —— 从 stdin JSON 取字段(不依赖 jq)——
  18 +get_str() {
  19 + printf '%s' "$INPUT" \
  20 + | sed -n "s/.*\"$1\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" \
  21 + | head -n1
  22 +}
  23 +TRANSCRIPT="$(get_str transcript_path)"
  24 +# 反转义 Windows 路径里的 \\ 与 \/
  25 +TRANSCRIPT="${TRANSCRIPT//\\\\//}"
  26 +TRANSCRIPT="${TRANSCRIPT//\\//}"
  27 +
  28 +# 计数器文件(按会话 transcript 路径派生 key,避免多会话互相干扰)
  29 +KEY="$(printf '%s' "$TRANSCRIPT" | tr -cd 'A-Za-z0-9' | tail -c 40)"
  30 +[ -z "$KEY" ] && KEY="default"
  31 +CNT_FILE="${TMPDIR:-/tmp}/erp-autocont-$KEY"
  32 +SIZE_FILE="${TMPDIR:-/tmp}/erp-autosize-$KEY"
  33 +
  34 +allow_stop() { rm -f "$CNT_FILE" "$SIZE_FILE" 2>/dev/null; exit 0; }
  35 +
  36 +# 1) 终止标记出现在最近的 transcript 尾部 → 放行停下
  37 +if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then
  38 + if tail -c 16000 "$TRANSCRIPT" 2>/dev/null | grep -qF "$SENTINEL"; then
  39 + allow_stop
  40 + fi
  41 +fi
  42 +
  43 +# 2) 进展自复位(自包含,不依赖 PostToolUse):transcript 自上次停止以来显著增长
  44 +# → 说明这一轮做了实质工作(工具调用 + 结果),清零空转计数。
  45 +CUR_SIZE=0
  46 +[ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ] && CUR_SIZE="$(wc -c < "$TRANSCRIPT" 2>/dev/null | tr -d ' ')"
  47 +case "$CUR_SIZE" in (''|*[!0-9]*) CUR_SIZE=0 ;; esac
  48 +LAST_SIZE=0
  49 +[ -f "$SIZE_FILE" ] && LAST_SIZE="$(cat "$SIZE_FILE" 2>/dev/null || echo 0)"
  50 +case "$LAST_SIZE" in (''|*[!0-9]*) LAST_SIZE=0 ;; esac
  51 +echo "$CUR_SIZE" > "$SIZE_FILE"
  52 +if [ $((CUR_SIZE - LAST_SIZE)) -gt 4000 ]; then
  53 + rm -f "$CNT_FILE" 2>/dev/null
  54 +fi
  55 +
  56 +# 3) 防循环:连续空转停止达到上限 → 放行停下
  57 +CNT=0
  58 +[ -f "$CNT_FILE" ] && CNT="$(cat "$CNT_FILE" 2>/dev/null || echo 0)"
  59 +case "$CNT" in (''|*[!0-9]*) CNT=0 ;; esac
  60 +if [ "$CNT" -ge "$MAX_STALL" ]; then
  61 + allow_stop
  62 +fi
  63 +echo $((CNT + 1)) > "$CNT_FILE"
  64 +
  65 +# 3) 拦下停止 → 续跑
  66 +printf '%s' '{"decision":"block","reason":"继续推进 ERP workflow:直接执行流程的下一步——若当前 skill 已结束,立即用 Skill 工具调用对应派发器(A 阶段 plan-start / B 阶段 coding-start)或链中的下一个 skill;不要输出收尾叙述、不要停下等用户输入。只有打印了 [ERP-HALT] 的终止/硬护栏点才应停下。"}'
  67 +exit 0
hooks/scripts/reset-stall-counter.sh 0 → 100755
  1 +#!/usr/bin/env bash
  2 +# PostToolUse 钩子:任何工具调用都视为「有进展」,清零 auto-continue.sh 的空转计数。
  3 +# 这样真正在推进的流程可无限续跑,只有「连续多次纯文字、零工具」的空转才会触发防循环上限。
  4 +set -u
  5 +
  6 +INPUT="$(cat)"
  7 +TRANSCRIPT="$(printf '%s' "$INPUT" \
  8 + | sed -n 's/.*"transcript_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
  9 + | head -n1)"
  10 +TRANSCRIPT="${TRANSCRIPT//\\\\//}"
  11 +TRANSCRIPT="${TRANSCRIPT//\\//}"
  12 +
  13 +KEY="$(printf '%s' "$TRANSCRIPT" | tr -cd 'A-Za-z0-9' | tail -c 40)"
  14 +[ -z "$KEY" ] && KEY="default"
  15 +rm -f "${TMPDIR:-/tmp}/erp-autocont-$KEY" 2>/dev/null
  16 +exit 0
skills/coding/fe-feature-brainstorm/SKILL.md
@@ -55,7 +55,7 @@ allowed-tools: Read Write Skill AskUserQuestion Glob Grep @@ -55,7 +55,7 @@ allowed-tools: Read Write Skill AskUserQuestion Glob Grep
55 - 消费的后端端点(与 docs/05 对齐,按本 FE 的 `associated_reqs[]` 过滤) 55 - 消费的后端端点(与 docs/05 对齐,按本 FE 的 `associated_reqs[]` 过滤)
56 - 业务规则前端复刻清单(逐条,每条标注:规则描述 / 触发时机 / 报错文案 / 来源 REQ) 56 - 业务规则前端复刻清单(逐条,每条标注:规则描述 / 触发时机 / 报错文案 / 来源 REQ)
57 - Design Tokens 引用清单(本 FE 用到的 `var(--color-*)` 名称) 57 - Design Tokens 引用清单(本 FE 用到的 `var(--color-*)` 名称)
58 - - 文件已存在 → 征求用户确认后覆盖 58 + - 文件已存在 → 直接覆盖(spec 是 CC 内部产物,无需确认)
59 59
60 4. **Spec 自审**(inline 修,无须用户等待): 60 4. **Spec 自审**(inline 修,无须用户等待):
61 - 所有顶级节非空 61 - 所有顶级节非空
skills/coding/fe-feature-plan/SKILL.md
@@ -56,7 +56,7 @@ allowed-tools: Read Write Grep Skill AskUserQuestion @@ -56,7 +56,7 @@ allowed-tools: Read Write Grep Skill AskUserQuestion
56 56
57 5. **写 plan 到 `docs/superpowers/plans/<YYYY-MM-DD>-<fe_id>.md`**: 57 5. **写 plan 到 `docs/superpowers/plans/<YYYY-MM-DD>-<fe_id>.md`**:
58 - 按 `${CLAUDE_SKILL_DIR}/templates/fe-feature-plan-template.md` 渲染 58 - 按 `${CLAUDE_SKILL_DIR}/templates/fe-feature-plan-template.md` 渲染
59 - - 文件已存在 → 征求用户确认后覆盖 59 + - 文件已存在 → 直接覆盖(plan 是 CC 内部产物,无需确认)
60 60
61 6. **Plan 自审**(inline 修,无须用户等待): 61 6. **Plan 自审**(inline 修,无须用户等待):
62 - 每个任务必须含 `test_file::test_name`、`impl_file`、完成标准 62 - 每个任务必须含 `test_file::test_name`、`impl_file`、完成标准
skills/coding/fe-feature-tdd/SKILL.md
@@ -13,8 +13,8 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *) @@ -13,8 +13,8 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *)
13 13
14 ## 路径护栏(前端阶段) 14 ## 路径护栏(前端阶段)
15 15
16 -- 任务的 `impl_file` 路径若**不以** `frontend/`(或项目实际前端根目录,从 `docs/09-项目目录结构.md § 前端目录结构` 取)开头 → 硬停并打印:`fe-feature-tdd 不允许写非前端文件:<impl_file>。前端阶段任务的 impl_file 必须落在前端目录下。`  
17 -- 命中 `backend/` / `sql/` / `scripts/` → 同上硬停 16 +- 任务的 `impl_file` 路径若**不以** `frontend/`(或项目实际前端根目录,从 `docs/09-项目目录结构.md § 前端目录结构` 取)开头 → 硬停并打印:`fe-feature-tdd 不允许写非前端文件:<impl_file>。前端阶段任务的 impl_file 必须落在前端目录下。 [ERP-HALT]`
  17 +- 命中 `backend/` / `sql/` / `scripts/` → 同上硬停 `[ERP-HALT]`
18 - 不允许在主会话直接 `pnpm playwright` / `pnpm test` / `pnpm e2e`,必须派发子会话 18 - 不允许在主会话直接 `pnpm playwright` / `pnpm test` / `pnpm e2e`,必须派发子会话
19 19
20 ## 执行步骤 20 ## 执行步骤
skills/coding/feature-brainstorm/SKILL.md
@@ -43,7 +43,7 @@ allowed-tools: Read Write Skill AskUserQuestion Bash(mysql *) @@ -43,7 +43,7 @@ allowed-tools: Read Write Skill AskUserQuestion Bash(mysql *)
43 3. **写 spec 到 `docs/superpowers/specs/<YYYY-MM-DD>-<REQ-id>.md`**: 43 3. **写 spec 到 `docs/superpowers/specs/<YYYY-MM-DD>-<REQ-id>.md`**:
44 - 按 `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md` 渲染 44 - 按 `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md` 渲染
45 - 覆盖:goal / 输入输出 / 业务规则 / 约束 / schema / API 引用 / acceptance criteria 45 - 覆盖:goal / 输入输出 / 业务规则 / 约束 / schema / API 引用 / acceptance criteria
46 - - 文件已存在 → 征求用户确认后覆盖 46 + - 文件已存在 → 直接覆盖(spec 是 CC 内部产物,无需确认)
47 47
48 4. **Spec 自审**(inline 修,无须用户等待): 48 4. **Spec 自审**(inline 修,无须用户等待):
49 - **占位符扫描**:`TBD` / `TODO` / `【人工填写:】` / 含糊段 → 修(按"占位符规则"流程解决) 49 - **占位符扫描**:`TBD` / `TODO` / `【人工填写:】` / 含糊段 → 修(按"占位符规则"流程解决)
skills/coding/feature-plan/SKILL.md
@@ -56,7 +56,7 @@ allowed-tools: Read Write Grep Skill AskUserQuestion @@ -56,7 +56,7 @@ allowed-tools: Read Write Grep Skill AskUserQuestion
56 56
57 5. **写 plan 到 `docs/superpowers/plans/<YYYY-MM-DD>-<REQ-id>.md`**: 57 5. **写 plan 到 `docs/superpowers/plans/<YYYY-MM-DD>-<REQ-id>.md`**:
58 - 按 `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md` 渲染 58 - 按 `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md` 渲染
59 - - 文件已存在 → 征求用户确认后覆盖 59 + - 文件已存在 → 直接覆盖(plan 是 CC 内部产物,无需确认)
60 60
61 6. **Plan 自审**(inline 修,无须用户等待): 61 6. **Plan 自审**(inline 修,无须用户等待):
62 - **占位符扫描**:按"占位符规则"清单逐项 grep;命中即修 62 - **占位符扫描**:按"占位符规则"清单逐项 grep;命中即修
skills/coding/feature-tdd/SKILL.md
@@ -35,7 +35,7 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *) @@ -35,7 +35,7 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *)
35 - **绝不**在主会话直接跑 `./gradlew test` / `pnpm test` / `scripts/test.sh`,必须通过子会话 35 - **绝不**在主会话直接跑 `./gradlew test` / `pnpm test` / `scripts/test.sh`,必须通过子会话
36 - **绝不**在主会话直接 `mysql -e "ALTER ..."`;业务 schema 改动一律走 `sql/migrations/V*.sql` 文件(只读查询 / 临时调试除外) 36 - **绝不**在主会话直接 `mysql -e "ALTER ..."`;业务 schema 改动一律走 `sql/migrations/V*.sql` 文件(只读查询 / 临时调试除外)
37 - 每次 commit 必须含 `REQ-XXX-NNN` 标签;不混合无关改动到同一 commit 37 - 每次 commit 必须含 `REQ-XXX-NNN` 标签;不混合无关改动到同一 commit
38 -- **后端阶段路径硬护栏**:任务表里的 `impl_file` 路径以 `frontend/` 开头 → 硬停并打印 `feature-tdd 后端阶段不允许写前端代码:<impl_file>。请检查 plan 任务定义;UI 推迟到前端阶段(fe-feature-*)`,不再继续 TDD 循环 38 +- **后端阶段路径硬护栏**:任务表里的 `impl_file` 路径以 `frontend/` 开头 → 硬停并打印 `feature-tdd 后端阶段不允许写前端代码:<impl_file>。请检查 plan 任务定义;UI 推迟到前端阶段(fe-feature-*) [ERP-HALT]`,不再继续 TDD 循环
39 39
40 ## 参考 40 ## 参考
41 41
skills/coding/frontend-start/SKILL.md
@@ -19,7 +19,7 @@ allowed-tools: Read Write Edit Skill Glob Grep AskUserQuestion Bash(git branch * @@ -19,7 +19,7 @@ allowed-tools: Read Write Edit Skill Glob Grep AskUserQuestion Bash(git branch *
19 19
20 读 docs/08 § 三 "功能:" 项: 20 读 docs/08 § 三 "功能:" 项:
21 21
22 -- 已有 `- [ ] FE-NN ...` 或 `- [x] FE-NN ...` 行 → **加载**:逐行解析得 `fe_list[]`,每项 `{ fe_id, name, status, associated_reqs[], associated_prototypes[] }`。行格式不符 → 硬停打印问题行 22 +- 已有 `- [ ] FE-NN ...` 或 `- [x] FE-NN ...` 行 → **加载**:逐行解析得 `fe_list[]`,每项 `{ fe_id, name, status, associated_reqs[], associated_prototypes[] }`。行格式不符 → 硬停打印问题行 `[ERP-HALT]`
23 - 仅有 HTML 注释占位(无 FE bullet)→ **推导**: 23 - 仅有 HTML 注释占位(无 FE bullet)→ **推导**:
24 1. 读 `prototype/**/*.html` + `docs/01-需求清单/**/*.md` + `docs/05-API接口契约.md` 24 1. 读 `prototype/**/*.html` + `docs/01-需求清单/**/*.md` + `docs/05-API接口契约.md`
25 2. 以**业务功能**为单位拆分(同流程多屏可合 1 FE;一 HTML 多功能可拆多 FE;无 UI 的 REQ 不产生 FE)。每个 FE:`{ fe_id: FE-NN, name, associated_reqs[], associated_prototypes[] }`,prototype 路径可带 anchor 区分文件内多区域 25 2. 以**业务功能**为单位拆分(同流程多屏可合 1 FE;一 HTML 多功能可拆多 FE;无 UI 的 REQ 不产生 FE)。每个 FE:`{ fe_id: FE-NN, name, associated_reqs[], associated_prototypes[] }`,prototype 路径可带 anchor 区分文件内多区域
@@ -35,12 +35,12 @@ allowed-tools: Read Write Edit Skill Glob Grep AskUserQuestion Bash(git branch * @@ -35,12 +35,12 @@ allowed-tools: Read Write Edit Skill Glob Grep AskUserQuestion Bash(git branch *
35 35
36 读 § 三 `整体里程碑:` 字段并 `git tag -l 'milestone/frontend-phase'` 校验: 36 读 § 三 `整体里程碑:` 字段并 `git tag -l 'milestone/frontend-phase'` 校验:
37 37
38 -- 字段为 `milestone/frontend-phase` 且 tag 存在 → 打印"前端阶段已完成"并停(冗余保护,正常由 coding-start 拦掉) 38 +- 字段为 `milestone/frontend-phase` 且 tag 存在 → 打印"前端阶段已完成 `[ERP-HALT]`"并停(冗余保护,正常由 coding-start 拦掉)
39 - 否则(`—` 或 tag 不存在)→ 进入步骤 4 39 - 否则(`—` 或 tag 不存在)→ 进入步骤 4
40 40
41 ### 步骤 4:切到 frontend-phase 分支 41 ### 步骤 4:切到 frontend-phase 分支
42 42
43 -目标分支 `frontend-phase`,分支管理逻辑同 module-start 步骤 3:已在 → 继续;存在 → checkout;不存在 → 切本地默认分支后 `git checkout -b frontend-phase`。任何错误硬停 + 诊断,不自动 stash / 覆盖。 43 +目标分支 `frontend-phase`,分支管理逻辑同 module-start 步骤 3:已在 → 继续;存在 → checkout;不存在 → 切本地默认分支后 `git checkout -b frontend-phase`。任何错误硬停 + 诊断,不自动 stash / 覆盖。`[ERP-HALT]`
44 44
45 ### 步骤 5:识别已完成的 FE 45 ### 步骤 5:识别已完成的 FE
46 46
skills/coding/module-report/SKILL.md
@@ -15,7 +15,7 @@ allowed-tools: Read Write Glob Grep Skill Bash(git diff *) Bash(git log *) Bash( @@ -15,7 +15,7 @@ allowed-tools: Read Write Glob Grep Skill Bash(git diff *) Bash(git log *) Bash(
15 15
16 - `module-<id>` → `phase=backend`,`phase_id=<id>` 16 - `module-<id>` → `phase=backend`,`phase_id=<id>`
17 - `frontend-phase` → `phase=frontend`,`phase_id=frontend-phase` 17 - `frontend-phase` → `phase=frontend`,`phase_id=frontend-phase`
18 -- 其它分支 → 硬停 18 +- 其它分支 → 硬停 `[ERP-HALT]`
19 19
20 ## 执行步骤 20 ## 执行步骤
21 21
skills/coding/module-start/SKILL.md
@@ -26,7 +26,7 @@ allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout * @@ -26,7 +26,7 @@ allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout *
26 约束: 26 约束:
27 27
28 - 模块完成 = `docs/08 § 二` 该模块 `里程碑:` 字段为 `milestone/<module_id>` 且 `git tag -l` 能查到该 tag;二者任一缺失即视为未完成 28 - 模块完成 = `docs/08 § 二` 该模块 `里程碑:` 字段为 `milestone/<module_id>` 且 `git tag -l` 能查到该 tag;二者任一缺失即视为未完成
29 -- 任何文件读取或解析失败 → 打印错误并停止 29 +- 任何文件读取或解析失败 → 打印错误并停止 `[ERP-HALT]`
30 30
31 ### 步骤 2:找不到未完成后端模块的处理 31 ### 步骤 2:找不到未完成后端模块的处理
32 32
@@ -43,7 +43,7 @@ allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout * @@ -43,7 +43,7 @@ allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout *
43 - 该分支已存在但当前不在 → checkout 过去 43 - 该分支已存在但当前不在 → checkout 过去
44 - 该分支不存在 → 先把工作树切到本地默认分支(main 或 master,已由 milestone-tag 的本地 merge 累积所有已完成模块),作为新分支的干净 base,再 `git checkout -b` 创建模块分支 44 - 该分支不存在 → 先把工作树切到本地默认分支(main 或 master,已由 milestone-tag 的本地 merge 累积所有已完成模块),作为新分支的干净 base,再 `git checkout -b` 创建模块分支
45 45
46 -任何错误(定位不到默认分支 / 切换前工作树脏 / checkout 失败)一律停下并打印诊断信息,不自动 stash、不强制覆盖。 46 +任何错误(定位不到默认分支 / 切换前工作树脏 / checkout 失败)一律停下并打印诊断信息,不自动 stash、不强制覆盖。`[ERP-HALT]`
47 47
48 ### 步骤 4:计算已完成 REQ 集合 `done_reqs[]` 48 ### 步骤 4:计算已完成 REQ 集合 `done_reqs[]`
49 49
skills/coding/test-gate/SKILL.md
@@ -24,7 +24,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) Bash(gi @@ -24,7 +24,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) Bash(gi
24 24
25 - `module-<id>` → `phase=backend`,`phase_id=<id>`,`command=./scripts/test.sh` 25 - `module-<id>` → `phase=backend`,`phase_id=<id>`,`command=./scripts/test.sh`
26 - `frontend-phase` → `phase=frontend`,`phase_id=frontend-phase`;命令从 `docs/04-技术规范.md § 零 frontend.test_command` / `frontend.e2e_command` 拼接(缺失则 `pnpm test:ci && pnpm e2e:ci`) 26 - `frontend-phase` → `phase=frontend`,`phase_id=frontend-phase`;命令从 `docs/04-技术规范.md § 零 frontend.test_command` / `frontend.e2e_command` 拼接(缺失则 `pnpm test:ci && pnpm e2e:ci`)
27 -- 其它分支 → 硬停打印 `test-gate 仅在 module-* 或 frontend-phase 分支可调用,当前 <branch>` 27 +- 其它分支 → 硬停打印 `test-gate 仅在 module-* 或 frontend-phase 分支可调用,当前 <branch> [ERP-HALT]`
28 28
29 1. 派发 Agent 子会话(general-purpose)运行上述 `command`,子会话只返回结构化 JSON(不输出描述文字): 29 1. 派发 Agent 子会话(general-purpose)运行上述 `command`,子会话只返回结构化 JSON(不输出描述文字):
30 ```json 30 ```json
@@ -47,7 +47,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) Bash(gi @@ -47,7 +47,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) Bash(gi
47 47
48 ``` 48 ```
49 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 49 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
50 - [test-gate] ⚠️ 未通过 (phase=<phase>) 50 + [test-gate] ⚠️ 未通过 (phase=<phase>) [ERP-HALT]
51 失败清单: <子会话 JSON 的 failed 摘要> 51 失败清单: <子会话 JSON 的 failed 摘要>
52 详细证据: docs/superpowers/module-reports/<phase_id>-test-gate.md 52 详细证据: docs/superpowers/module-reports/<phase_id>-test-gate.md
53 53
skills/crosscut/coding-start/SKILL.md
@@ -22,13 +22,13 @@ cat &quot;${CLAUDE_PLUGIN_ROOT}/skills/crosscut/coding-start/banners/flow-overview.tx @@ -22,13 +22,13 @@ cat &quot;${CLAUDE_PLUGIN_ROOT}/skills/crosscut/coding-start/banners/flow-overview.tx
22 ### 步骤 1:确认 docs/08 存在 22 ### 步骤 1:确认 docs/08 存在
23 23
24 检查 `docs/08-模块任务管理.md` 存在。 24 检查 `docs/08-模块任务管理.md` 存在。
25 -- 不存在 → 打印"⚠️ 项目尚未初始化,请先运行 `/erp-workflow:plan-start`"并停下。 25 +- 不存在 → 打印"⚠️ 项目尚未初始化,请先运行 `/erp-workflow:plan-start` `[ERP-HALT]`"并停下。
26 26
27 ### 步骤 2:Plan 完成性检查 27 ### 步骤 2:Plan 完成性检查
28 28
29 读取 `docs/08-模块任务管理.md § 一`,判断 A0~A5 是否全部勾选(含子项)。 29 读取 `docs/08-模块任务管理.md § 一`,判断 A0~A5 是否全部勾选(含子项)。
30 30
31 -- 任一未勾选 → 提示用户先运行 `/erp-workflow:plan-start` 完成 A 阶段,并停下 31 +- 任一未勾选 → 提示用户先运行 `/erp-workflow:plan-start` 完成 A 阶段并打印 `[ERP-HALT]`,停下
32 - 全部勾选 → 进入步骤 3 32 - 全部勾选 → 进入步骤 3
33 33
34 ### 步骤 3:后端完成性检查 + 派发 34 ### 步骤 3:后端完成性检查 + 派发
@@ -43,7 +43,7 @@ cat &quot;${CLAUDE_PLUGIN_ROOT}/skills/crosscut/coding-start/banners/flow-overview.tx @@ -43,7 +43,7 @@ cat &quot;${CLAUDE_PLUGIN_ROOT}/skills/crosscut/coding-start/banners/flow-overview.tx
43 43
44 读 `docs/08 § 三 整体里程碑:` 字段(并用 `git tag -l 'milestone/frontend-phase'` 校验): 44 读 `docs/08 § 三 整体里程碑:` 字段(并用 `git tag -l 'milestone/frontend-phase'` 校验):
45 45
46 -- **`整体里程碑: milestone/frontend-phase` 且 tag 存在** → 前端已完成,打印 `所有阶段已完成(后端模块 + 前端阶段里程碑均已标记)`,结束本 skill。 46 +- **`整体里程碑: milestone/frontend-phase` 且 tag 存在** → 前端已完成,打印 `所有阶段已完成(后端模块 + 前端阶段里程碑均已标记) [ERP-HALT]`,结束本 skill。
47 47
48 - **`整体里程碑: —` 或 tag 不存在** → 前端未完成,打印 `[coding-start] 后端已完成、前端未完成 → 派发 frontend-start(写前端)`,立即用 Skill 工具调用 frontend-start(直接调用,**不要**先输出"已完成 / 接下来 / 请检查 / 等你确认"之类桥接叙述——会被解读为 turn 结束信号、害用户手敲 continue),本 skill 结束。 48 - **`整体里程碑: —` 或 tag 不存在** → 前端未完成,打印 `[coding-start] 后端已完成、前端未完成 → 派发 frontend-start(写前端)`,立即用 Skill 工具调用 frontend-start(直接调用,**不要**先输出"已完成 / 接下来 / 请检查 / 等你确认"之类桥接叙述——会被解读为 turn 结束信号、害用户手敲 continue),本 skill 结束。
49 49
skills/crosscut/interrupt-check/SKILL.md
@@ -30,7 +30,7 @@ allowed-tools: Read Write Bash(mysql *) @@ -30,7 +30,7 @@ allowed-tools: Read Write Bash(mysql *)
30 30
31 1. 逐项核对 3 个中断条件。**全部未触发** → 输出 `interrupt-check: 通过`,结束。 31 1. 逐项核对 3 个中断条件。**全部未触发** → 输出 `interrupt-check: 通过`,结束。
32 2. **触发任一** → 按 `${CLAUDE_SKILL_DIR}/templates/interrupt-block-template.md` 渲染 Blocker 块,追加到当前 plan 文件(典型路径 `docs/superpowers/plans/<YYYY-MM-DD>-<REQ-id>.md`;test-gate 场景下追加到本模块任一已有 plan 文件)。 32 2. **触发任一** → 按 `${CLAUDE_SKILL_DIR}/templates/interrupt-block-template.md` 渲染 Blocker 块,追加到当前 plan 文件(典型路径 `docs/superpowers/plans/<YYYY-MM-DD>-<REQ-id>.md`;test-gate 场景下追加到本模块任一已有 plan 文件)。
33 -3. 向会话打印一句话摘要 + 指向 plan 文件路径,**停下**。 33 +3. 向会话打印一句话摘要 + 指向 plan 文件路径 + `[ERP-HALT]`,**停下**。
34 34
35 ## 参考 35 ## 参考
36 36
skills/plan/db-init/SKILL.md
@@ -66,8 +66,8 @@ bash &quot;${CLAUDE_SKILL_DIR}/scripts/validate.sh&quot; \ @@ -66,8 +66,8 @@ bash &quot;${CLAUDE_SKILL_DIR}/scripts/validate.sh&quot; \
66 - `1` → **自主修正循环**(最多 3 轮,docs/03 是 SSoT 不动): 66 - `1` → **自主修正循环**(最多 3 轮,docs/03 是 SSoT 不动):
67 1. 解析 stderr 差异清单,修正 V1.sql 67 1. 解析 stderr 差异清单,修正 V1.sql
68 2. 重跑 validate.sh 68 2. 重跑 validate.sh
69 - 3. 退出 0 → 进入 B;退出 1 且本轮 < 3 → 回步骤 1;本轮 ≥ 3 仍失败 → 停下,打印最终残留差异 + 已尝试的 3 轮修正摘要,让用户介入  
70 -- `2` → 用法错(V1 / docs 路径找不到),打印路径并停下 69 + 3. 退出 0 → 进入 B;退出 1 且本轮 < 3 → 回步骤 1;本轮 ≥ 3 仍失败 → 停下,打印最终残留差异 + 已尝试的 3 轮修正摘要,让用户介入 `[ERP-HALT]`
  70 +- `2` → 用法错(V1 / docs 路径找不到),打印路径并停下 `[ERP-HALT]`
71 71
72 完成后(V1 写入并通过 validate.sh 校验),用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: 72 完成后(V1 写入并通过 validate.sh 校验),用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选:
73 - ` - [ ] sql/migrations/V1__initial_schema.sql 已生成` 73 - ` - [ ] sql/migrations/V1__initial_schema.sql 已生成`
@@ -77,7 +77,7 @@ bash &quot;${CLAUDE_SKILL_DIR}/scripts/validate.sh&quot; \ @@ -77,7 +77,7 @@ bash &quot;${CLAUDE_SKILL_DIR}/scripts/validate.sh&quot; \
77 77
78 #### B.1 检查 .env.local 凭据 78 #### B.1 检查 .env.local 凭据
79 79
80 -用 `Glob` 检查 `.env.local` 是否存在;不存在 → 提示用户重新运行 A2 `skeleton-gen` 重建并停下。 80 +用 `Glob` 检查 `.env.local` 是否存在;不存在 → 提示用户重新运行 A2 `skeleton-gen` 重建并停下。`[ERP-HALT]`
81 81
82 用 `Bash` 加载并校验 5 个必填字段非空: 82 用 `Bash` 加载并校验 5 个必填字段非空:
83 83
@@ -89,7 +89,7 @@ for v in DB_HOST DB_PORT DB_USER DB_PASSWORD DB_SCHEMA; do @@ -89,7 +89,7 @@ for v in DB_HOST DB_PORT DB_USER DB_PASSWORD DB_SCHEMA; do
89 done 89 done
90 ``` 90 ```
91 91
92 -任一缺失 → 打印缺失字段名并停下,提示用户编辑 `.env.local` 后重跑。 92 +任一缺失 → 打印缺失字段名并停下,提示用户编辑 `.env.local` 后重跑。`[ERP-HALT]`
93 93
94 #### B.2 验证 MySQL 连接 94 #### B.2 验证 MySQL 连接
95 95
@@ -99,7 +99,7 @@ mysql -h&quot;$DB_HOST&quot; -P&quot;$DB_PORT&quot; -u&quot;$DB_USER&quot; -p&quot;$DB_PASSWORD&quot; -e &quot;SELECT 1;&quot; @@ -99,7 +99,7 @@ mysql -h&quot;$DB_HOST&quot; -P&quot;$DB_PORT&quot; -u&quot;$DB_USER&quot; -p&quot;$DB_PASSWORD&quot; -e &quot;SELECT 1;&quot;
99 ``` 99 ```
100 100
101 - **成功** → 进入步骤 C 101 - **成功** → 进入步骤 C
102 -- **失败** → 打印具体错误(认证 / 主机不可达 / 端口拒接等),提示检查 `.env.local`,**停下**。 102 +- **失败** → 打印具体错误(认证 / 主机不可达 / 端口拒接等),提示检查 `.env.local`,**停下**。`[ERP-HALT]`
103 103
104 完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: 104 完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选:
105 - ` - [ ] .env.local 凭据已验证(mysql -e "SELECT 1" OK)` 105 - ` - [ ] .env.local 凭据已验证(mysql -e "SELECT 1" OK)`
@@ -120,7 +120,7 @@ mysql -h&quot;$DB_HOST&quot; -P&quot;$DB_PORT&quot; -u&quot;$DB_USER&quot; -p&quot;$DB_PASSWORD&quot; &quot;$DB_SCHEMA&quot; \ @@ -120,7 +120,7 @@ mysql -h&quot;$DB_HOST&quot; -P&quot;$DB_PORT&quot; -u&quot;$DB_USER&quot; -p&quot;$DB_PASSWORD&quot; &quot;$DB_SCHEMA&quot; \
120 < sql/migrations/V1__initial_schema.sql 120 < sql/migrations/V1__initial_schema.sql
121 ``` 121 ```
122 122
123 -非零退出 → 报错停下,打印 mysql stderr。 123 +非零退出 → 报错停下,打印 mysql stderr。`[ERP-HALT]`
124 124
125 完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: 125 完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选:
126 - ` - [ ] setup-test-db.sh 防护通过 + DROP+CREATE + apply V1 已执行` 126 - ` - [ ] setup-test-db.sh 防护通过 + DROP+CREATE + apply V1 已执行`
@@ -135,7 +135,7 @@ EXPECTED=$(grep -c &#39;^## `&#39; docs/03-数据库设计文档.md) @@ -135,7 +135,7 @@ EXPECTED=$(grep -c &#39;^## `&#39; docs/03-数据库设计文档.md)
135 [ "$ACTUAL" = "$EXPECTED" ] || { echo "MISMATCH: actual=$ACTUAL expected=$EXPECTED"; exit 1; } 135 [ "$ACTUAL" = "$EXPECTED" ] || { echo "MISMATCH: actual=$ACTUAL expected=$EXPECTED"; exit 1; }
136 ``` 136 ```
137 137
138 -行数不一致 → 报错停下;一致 → 进入步骤 D。 138 +行数不一致 → 报错停下 `[ERP-HALT]`;一致 → 进入步骤 D。
139 139
140 ### D. 勾选 docs/08 进度 + 进入 A5 140 ### D. 勾选 docs/08 进度 + 进入 A5
141 141
skills/plan/downstream-gen/SKILL.md
@@ -94,11 +94,11 @@ cp &quot;${CLAUDE_SKILL_DIR}/templates/docs-10-header-template.md&quot; docs/10-验收检æ @@ -94,11 +94,11 @@ cp &quot;${CLAUDE_SKILL_DIR}/templates/docs-10-header-template.md&quot; docs/10-验收检æ
94 - **module_id 缺 docs/08 § 二** → 按步骤 D è§„åˆ™æ¸²æŸ“è¯¥æ¨¡å— bulletï¼ˆå« REQ å­é¡¹ï¼‰ï¼ŒæŒ‰ `module_id` å­—æ¯åºæ’入正确ä½ç½® 94 - **module_id 缺 docs/08 § 二** → 按步骤 D è§„åˆ™æ¸²æŸ“è¯¥æ¨¡å— bulletï¼ˆå« REQ å­é¡¹ï¼‰ï¼ŒæŒ‰ `module_id` å­—æ¯åºæ’入正确ä½ç½®
95 - **module_id 缺 docs/02 § 二** → é‡ç®—该模å—çš„ `req_order` 段(步骤 A å­æµç¨‹ï¼‰ï¼ŒæŒ‰æ‹“æ‰‘åºæ’å…¥ docs/02 § 二 95 - **module_id 缺 docs/02 § 二** → é‡ç®—该模å—çš„ `req_order` 段(步骤 A å­æµç¨‹ï¼‰ï¼ŒæŒ‰æ‹“æ‰‘åºæ’å…¥ docs/02 § 二
96 96
97 - ä¿®å¤åŽé‡è·‘检查;通过 → 进入 2ï¼›3 è½®ä»å¤±è´¥ → åœä¸‹ï¼Œæ‰“å°æœ€ç»ˆæ®‹ç•™å·®å¼‚ + å·²å°è¯•çš„ 3 è½®ä¿®å¤æ‘˜è¦è®©ç”¨æˆ·ä»‹å…¥ã€‚ 97 + ä¿®å¤åŽé‡è·‘检查;通过 → 进入 2ï¼›3 è½®ä»å¤±è´¥ → åœä¸‹ï¼Œæ‰“å°æœ€ç»ˆæ®‹ç•™å·®å¼‚ + å·²å°è¯•çš„ 3 è½®ä¿®å¤æ‘˜è¦è®©ç”¨æˆ·ä»‹å…¥ã€‚`[ERP-HALT]`
98 98
99 2. **最终å ä½ç¬¦æ‰«æ**(覆盖 Plan 阶段全部产出): 99 2. **最终å ä½ç¬¦æ‰«æ**(覆盖 Plan 阶段全部产出):
100 100
101 - a. **`TBD` → 自动补é½**:Grep æœç´¢ `TBD(A3 自动补)` å’Œ `TBD(A5 自动补)`。有命中则就地补填(A3 残留 → 查 docs/03 å¡« `ä¾èµ–表`ï¼›A5 残留 → 查 docs/05 按 REQ-ID å¡« `ä¾èµ–接å£`ï¼‰ï¼Œå† Grep 确认 0 å‘½ä¸­ï¼›ä»æ®‹ç•™æŠ¥é”™åœä¸‹ã€‚ 101 + a. **`TBD` → 自动补é½**:Grep æœç´¢ `TBD(A3 自动补)` å’Œ `TBD(A5 自动补)`。有命中则就地补填(A3 残留 → 查 docs/03 å¡« `ä¾èµ–表`ï¼›A5 残留 → 查 docs/05 按 REQ-ID å¡« `ä¾èµ–接å£`ï¼‰ï¼Œå† Grep 确认 0 å‘½ä¸­ï¼›ä»æ®‹ç•™æŠ¥é”™åœä¸‹ã€‚`[ERP-HALT]`
102 102
103 b. **`ã€äººå·¥å¡«å†™ï¼š...】` → QA 循环等用户补**: 103 b. **`ã€äººå·¥å¡«å†™ï¼š...】` → QA 循环等用户补**:
104 104
skills/plan/scope-lock/SKILL.md
@@ -47,7 +47,7 @@ cat &quot;${CLAUDE_PLUGIN_ROOT}/skills/plan/scope-lock/banners/flow.txt&quot; @@ -47,7 +47,7 @@ cat &quot;${CLAUDE_PLUGIN_ROOT}/skills/plan/scope-lock/banners/flow.txt&quot;
47 0 命中后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: 47 0 命中后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选:
48 - ` - [ ] 项目概述已填写(CLAUDE.md § 🎯 项目概述)` 48 - ` - [ ] 项目概述已填写(CLAUDE.md § 🎯 项目概述)`
49 49
50 -### B. 提示用户检查默认技术栈并等待 50 +### B. 默认技术栈(自动,无停顿)
51 51
52 `docs/04-技术规范.md` 已由 A0 `project-init` 用模板复制(默认技术栈,见 `project-init/templates/docs-04-stack-template.md`)。本步骤让用户检查 / 调整 § 零。 52 `docs/04-技术规范.md` 已由 A0 `project-init` 用模板复制(默认技术栈,见 `project-init/templates/docs-04-stack-template.md`)。本步骤让用户检查 / 调整 § 零。
53 53
@@ -64,16 +64,11 @@ cat &quot;${CLAUDE_PLUGIN_ROOT}/skills/plan/scope-lock/banners/flow.txt&quot; @@ -64,16 +64,11 @@ cat &quot;${CLAUDE_PLUGIN_ROOT}/skills/plan/scope-lock/banners/flow.txt&quot;
64 - 不需要的行直接删除(如纯后端项目删前端行) 64 - 不需要的行直接删除(如纯后端项目删前端行)
65 - 需要替换的技术直接改 65 - 需要替换的技术直接改
66 - 需要新增的条目直接加行 66 - 需要新增的条目直接加行
67 - 改完后回来选择「继续」。 67 + (默认栈开箱即用;如需改,直接编辑该文件即可,流程不会在此等待。)
68 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 68 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
69 ``` 69 ```
70 70
71 -用 `AskUserQuestion` 询问:  
72 -- **question**: `技术栈检查完毕了吗?`  
73 - - 用户选择「继续」→ 进入步骤 C。  
74 - - 用户选择「有疑问想先沟通」→ 回答用户问题后,再次弹出同样的 QA。  
75 -  
76 -完成后,用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: 71 +**不再弹窗等待。** 默认技术栈直接采用;若用户在本轮对话中已明确要求调整某项技术,就地用 `Edit` 改 docs/04 § 零,否则保持默认。随后用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选,并直接进入步骤 C:
77 - ` - [ ] 技术栈已确认(docs/04 § 零)` 72 - ` - [ ] 技术栈已确认(docs/04 § 零)`
78 73
79 ### C. 提示用户填写需求清单并等待 74 ### C. 提示用户填写需求清单并等待
skills/plan/skeleton-gen/SKILL.md
@@ -135,23 +135,15 @@ bash &quot;${CLAUDE_SKILL_DIR}/scripts/merge-gitignore.sh&quot; &quot;${CLAUDE_SKILL_DIR}/templ @@ -135,23 +135,15 @@ bash &quot;${CLAUDE_SKILL_DIR}/scripts/merge-gitignore.sh&quot; &quot;${CLAUDE_SKILL_DIR}/templ
135 135
136 > `.env.local` 的 DB 连接核对**不在此处停**,统一放到 E.4 最终闸门——等所有产出(docs / scripts / .env.local / .gitignore)都初始化完成后再停一次,避免「一生成就停」。 136 > `.env.local` 的 DB 连接核对**不在此处停**,统一放到 E.4 最终闸门——等所有产出(docs / scripts / .env.local / .gitignore)都初始化完成后再停一次,避免「一生成就停」。
137 137
138 -#### E.4 验证 + QA 闸门(含 .env.local 连接核对 138 +#### E.4 验证闸门(自动,无停顿
139 139
140 -**这是 skeleton-gen 唯一的收尾停顿——所有产出(docs / scripts / .env.local / .gitignore)全部初始化完成后才在此停一次**,**严禁**在 `.env.local` 一生成(C.1)时就提前停。 140 +所有产出(docs / scripts / .env.local / .gitignore)初始化完成后在此自动校验放行,**不再弹窗等待**:
141 141
142 -循环直到两条件**同时**满足:  
143 -(a) `Grep` 重新扫 8 路径,0 命中  
144 -(b) 用户 `AskUserQuestion` 选「继续」  
145 -  
146 -每次弹 QA 前重扫;有残留则打印残留位置清单(文件:行号 — 说明)+ 再弹 QA。  
147 -  
148 -QA 横幅涵盖:  
149 -- 产出文件清单(docs/04 / 06 / 07 / 09 + scripts/*.sh + .env.local + .gitignore)  
150 -- 占位状态(N=0 或待填清单)  
151 -- **`.env.local` DB 连接核对**:用 `Bash`(`set -a; . .env.local; set +a`)读出后列出当前 `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_SCHEMA` 的**值**;`DB_PASSWORD` / `JWT_SECRET` **只列字段名、不回显值**(凭据不进会话)。提示「默认连接开箱即用;若与本地 MySQL 不符,**现在直接编辑 `.env.local`** 再选『继续』(`cp -n` 不会覆盖你的修改,下一步 A4 db-init 会真正连库验证)」。  
152 -- 两选项:「继续」/「有疑问先沟通」  
153 -  
154 -通过后(N=0 且用户选「继续」),用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选: 142 +1. `Grep` 重扫 8 路径的占位符:
  143 + - 0 命中 → 进入 2。
  144 + - 仍有残留 → 打印残留清单(文件:行号 — 说明)并停下 `[ERP-HALT]`(骨架产出不应残留占位符,属生成异常,修正后重跑)。
  145 +2. **`.env.local` DB 连接信息(仅供参考,无需在此确认)**:用 `Bash`(`set -a; . .env.local; set +a`)列出 `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_SCHEMA` 的**值**;`DB_PASSWORD` / `JWT_SECRET` **只列字段名、不回显**(凭据不进会话)。默认连接开箱即用;如与本地 MySQL 不符,可直接编辑 `.env.local`,下一步 A4 db-init 会真正连库验证(连不上会在 A4 报错停下)。
  146 +3. 用 `Edit` 在 `docs/08-模块任务管理.md` 中勾选,并直接进入 F:
155 - `- [ ] A2 骨架生成 — skeleton-gen` 147 - `- [ ] A2 骨架生成 — skeleton-gen`
156 148
157 ### F. 进入 A3 149 ### F. 进入 A3