SKILL.md 15.9 KB

name: downstream-gen description: A5 下游文档生成——基于 docs/01 和 docs/03 推导,一次性生成 docs/02 + docs/05 + docs/06 § 五 + docs/10,回填 REQ 卡片依赖接口,把模块清单追加到 docs/08 § 二。 user-invocable: false

allowed-tools: Read Write Edit Glob Grep Skill AskUserQuestion Bash(cat *) Bash(git remote *)

所有输出必须使用中文。

downstream-gen

前置条件

  • docs/01-需求清单/<module>/_module.md + docs/01-需求清单/<module>/REQ-*.md 完整 REQ 卡片已就绪(A1 生成 + 人工审阅过 + A3 已回填依赖表)。
  • docs/03-数据库设计文档.md 已就绪(A3 生成 + 人工审阅过)。
  • sql/migrations/V1__initial_schema.sql 已生成并 apply(A4 完成)。
  • docs/05 / docs/06 § 五 / docs/10 等下游文件尚不存在(本 skill 创建)。

执行步骤

步骤 0:打印当前位置流程图

向用户展示当前在 A 阶段流程中的位置(A-only, 标在 A5):

┌──────────────────────────────────────────────────────┐
│ 📋 阶段 A:规划(一次性)                               │
│                                                       │
│   A0 初始化项目 → A1 锁范围(REQ 卡片)                 │
│                          ↓                            │
│     ⏸ 等你审阅 REQ,重新运行 /plan-start 继续      │
│                          ↓                            │
│   A2 生成骨架 → A3 生成 DB 设计 → A4 初始化 DB → ▶ A5 生成下游文档│
│                                  ↓                    │
│                    规划阶段到此结束                     │
└──────────────────────────────────────────────────────┘

A. docs/02 — 开发计划(含 REQ 级开发顺序清单,CC 分发权威)

清单粒度:一行一个 REQ,同一模块的 REQ 必须连续排列

  1. 构建模块依赖 DAG
    • docs/03 中的外键(表 A → 表 B ⇒ A 的模块依赖 B 的模块)
    • docs/01 index 中的显式 depends_on 提示
  2. 对模块 DAG 做拓扑排序得 module_topo_order[]
  3. 每个模块内部构建 REQ 间依赖(从 REQ 卡片 goal / rules / 依赖表 推断 REQ-A 是 REQ-B 的前置),得到模块内 REQ 顺序。
  4. 合成 req_order[]:按 module_topo_order[] 依次铺开每个模块内的 REQ 序列(同模块 REQ 连续)。
  5. 环依赖打破
    • 模块级:若模块 DAG 存在环(module_A ↔ module_B),按启发式(字母序 / 被依赖次数多者先)破环排出 module_topo_order,并在参与环的模块里第一个 REQnote 字段填入原因(如 "A↔B 互依赖:先做 A 的骨架")。
    • REQ 级(同模块内):若模块内 REQ 互依赖,同样破环,note 填原因。
    • 非环 REQ note
  6. req_order[] 每项生成字段:
    • index:行号(从 1 开始)
    • req_id:如 REQ-SYS-001
    • module_id:该 REQ 所属模块,如 module_sys
    • rationale(一行选中理由):依赖驱动的简短描述,如 所属模块无依赖,基础模块 / 依赖 REQ-SYS-001 已在前 / 所属模块依赖 module_sys 已在前
    • note(一行备注):默认 ;仅环依赖打破场景填原因
  7. Read 读取 ${CLAUDE_SKILL_DIR}/templates/docs-02-template.md,填充 modules[](含 id / name / deps / tables)/ req_order[](每项含 index / req_id / module_id / rationale / note)/ notes
  8. 写入 docs/02-开发计划.md

B. docs/05 — API 接口契约

  1. Read 读取 ${CLAUDE_SKILL_DIR}/templates/docs-05-header-template.md,写入 docs/05-API接口契约.md 头部。
  2. 对所有模块的每个 REQ:用 Read 读取 ${CLAUDE_SKILL_DIR}/templates/docs-05-endpoint-template.md,推断 method / path / auth / permission / 请求和响应 schema(不明确的询问用户),追加到 docs/05。

B2. 回填模块头 + REQ 卡片的 TBD(A5) 字段

  1. Glob 列出 docs/01-需求清单/*/_module.md(模块头)和 docs/01-需求清单/*/REQ-*.md(REQ 卡片)。
  2. Grep 在这些文件中搜索 TBD(A5 自动补) 的行号(不读全文)。两种行命中:
    • 依赖模块: TBD(A5 自动补) → 模块级(仅在 _module.md,每文件一次)
    • 依赖接口: TBD(A5 自动补) → REQ 级(仅在 REQ-*.md,每文件一次)
  3. 对每个命中行按类型回填:
    • 模块级 依赖模块_module.md):用 Read 取该文件 module_code + module_name,从步骤 A 的 module_topo_order[] + DAG 查该模块的上游依赖(多模块用 , 分隔);Edit 替换为 依赖模块: <module_x>, <module_y>(无依赖填
    • REQ 级 依赖接口REQ-*.md):从文件名直接得 req_idREQ-USR-001.mdREQ-USR-001);在步骤 B 刚生成的 docs/05-API接口契约.md 里 Grep 属于该 REQ 的 endpoint;Edit 替换为 依赖接口: POST /api/xxx, GET /api/yyy(多个用 , 分隔)
  4. 打印回填统计:A5 回填 <M> 处模块依赖 + <N> 处 REQ api_refs

C. docs/06 — 页面清单

  1. 对每个有前端页面的模块:用 Read 读取 ${CLAUDE_SKILL_DIR}/templates/docs-06-module-pagelist-template.md,填充 pages[](page_name、route、page_type、req_ids、menu_path、interactions)。
  2. 将每个渲染块追加到 docs/06-UI交互规范.md § 五。

D. docs/08 § 二 — 追加模块清单(含 REQ 子项)

docs/08 已由 A0 project-init 创建(含 Plan 进度骨架)。本步骤只往 § 二 追加模块行,不重写整个文件

  1. Read 读取 ${CLAUDE_SKILL_DIR}/templates/docs-08-module-row-template.md(单模块 bullet 行模板)。
  2. module_id 字母序对每个模块:
    • 从步骤 A req_order[] 过滤出 module_id 匹配的 REQ 列表(保持步骤 A 算出的模块内 REQ 顺序)。
    • 对每个 REQ 从 docs/01-需求清单/<module>/<req_id>.md 读出其一句话标题(REQ 卡片标题行或 goal 字段)。
    • 渲染 {{req_checklist}} 块:每行 - [ ] <req_id> <title>(4 空格缩进对齐 - 功能:)。
    • 渲染整条 bullet,填充 module_id / module_name / depends_on / path_scopes / req_checklist

注意一:docs/08 § 二 的模块行序不决定分发顺序——分发以 docs/02 § 二 REQ 清单为准,此处模块按字母序仅方便查找。 注意二:REQ 子项的 [ ]feature-reviewverdict=approve 时自动勾选,作为功能级进度可视化;模块完成判定仍由 MR: 字段 + GitLab API state 单独决定,不依赖子项勾选状态。

  1. 所有模块行拼成一段文本,用 Editdocs/08-模块任务管理.md## 二、Coding 阶段(按模块循环) 标题后插入(定位用下一行注释"(A5 填入后..."作为锚,把模块清单插到该注释之前)。

E. docs/10 — 验收清单

  1. Read 读取 ${CLAUDE_SKILL_DIR}/templates/docs-10-header-template.md,写入 docs/10-验收检查清单.md 头部。
  2. 对每个模块:用 Read 读取 ${CLAUDE_SKILL_DIR}/templates/docs-10-module-template.md,填充 reqs[]data_checks[]ui_checks[],追加。

F. 验证 + 勾选 docs/08 进度 + 终止 Plan 阶段

  1. 一致性检查:

    • docs/01 的每个 REQ 都出现在 docs/05(作为接口,如适用)、docs/10(作为验收项)。
    • docs/02 § 二 开发顺序清单的 module_id 集合 = docs/08 § 二module_id 集合(数量、ID 全等);不相等 → 报错停下,列出差集。
  2. 最终占位符扫描(覆盖 Plan 阶段全部产出:docs/01-需求清单/index.md + docs/01-需求清单/*/_module.md + docs/01-需求清单/*/REQ-*.md + docs/02 / docs/03 / docs/05 / docs/06 / docs/10):

a. TBD → CC 自动补齐:用 Grep 搜索 TBD(A3 自动补)TBD(A5 自动补)。有命中则按 A3 / B2 同样逻辑就地补填(A3 查 docs/03 填 依赖表:;A5 查 docs/05 按 REQ-ID 填 依赖接口:),再 Grep 确认 0 命中;仍残留报错停下。

b. 【人工填写:...】 → QA 循环等用户补(与 A2 skeleton-gen 步骤 E 同风格):

  循环执行直到两个条件**同时满足**:(1) 扫描 0 命中;(2) 用户选「继续」

  - **b.1 扫描**:用 `Grep` 在上述所有文件中搜索 `【人工填写:`,得到命中数 N 和残留位置清单(`<文件:行号> — <行内容摘要>`)
  - **b.2 打印汇总横幅**:
    ```
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
     [downstream-gen] 最终占位符审阅

     <N=0 时打印:✅ 全部填完>
     <N>0 时打印:⚠️ 还有 N 处待填:
                  <文件:行号> — <行内容摘要>
                  ...>

     需要调整 → 直接编辑对应文件
     填完后   → 回来选「继续」放行完成 Plan
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    ```
  - **b.3 弹出 QA**:用 `AskUserQuestion` 询问:
    - **question**: `最终占位符补填完毕?`
    - **options** (每项为对象):
      - `{"label": "继续", "description": "放行完成 Plan 阶段"}`
      - `{"label": "有疑问想先沟通", "description": "需要讨论或修改"}`
  - **b.4 路由**:
    - 选「有疑问想先沟通」→ 回答用户问题后**回 b.1 重新扫描**
    - 选「继续」→ **回 b.1 重新扫描验证**:
      - 新 N = 0 → 进入步骤 3
      - 新 N > 0 → 用户以为填完其实还有残留,回 b.2 打印新横幅并再次弹出 QA

  关键:**每次弹出 QA 前都重新扫描一次**,用户看到的 N 始终是最新的;只有 N = 0 且选「继续」才放行。
  1. Editdocs/08-模块任务管理.md 勾选 7 个 checkbox(A5 的 6 个子项 + A5 父项):

    • - [ ] docs/02 开发计划已生成[x]
    • - [ ] docs/05 API 契约已生成[x]
    • - [ ] docs/06 § 五 页面清单已填入[x]
    • - [ ] docs/10 验收清单已生成[x]
    • - [ ] 下方模块列表已填入[x]
    • - [ ] REQ 卡片依赖接口已回填[x]
    • - [ ] A5 下游文档生成 — downstream-gen[x]
  2. origin 远程派生 GitLab 凭据并回填 .env.local

仅当对应字段仍是 TBD(A5 自动补) 或空时回填;用户已手填为别的值一律不动。

a. 取远程 URL:Bash: git remote get-url origin 2>/dev/nullREMOTE_URL。为空 → 跳过本步骤,仅打印提示"未配置 origin 远程,GITLAB_* 留给用户手填"。 b. 解析: - PROJECT_PATH = 去掉 <scheme>://<host>/git@<host>:ssh://<host>/ 前缀 + 去掉末尾 .git(如 zhuzc/test) - PROJECT_ID_ENC = PROJECT_PATH/ 替换为 %2F(如 zhuzc%2Ftest) - HOST = 远程主机名(如 git.xlyprint.cn) - SCHEME 三分支(绝不把 SSH remote 降级成 http——Private Token 不能走明文): - REMOTE_URLhttps:// 开头 → https - REMOTE_URLhttp:// 开头 → http(明确声明,非推断) - 其他(git@ / ssh:// / 异常)→ https(默认走安全侧) - API_URL_GUESS = <SCHEME>://<HOST>/api/v3 c. 用 Read.env.local: - 若 GITLAB_PROJECT_ID 当前值为 TBD(A5 自动补) 或空 → 用 Edit 改为 GITLAB_PROJECT_ID=<PROJECT_ID_ENC> - 若 GITLAB_API_URL 当前值为 TBD(A5 自动补) 或空 → Edit 改为 GITLAB_API_URL=<API_URL_GUESS> - 上两字段若已被用户手改过(非 TBD(A5 自动补) 也非空)→ 不覆盖,仅核对打印 - GITLAB_TOKEN 派生不了,占位保持 【人工填写:...】,用户去 GitLab Profile → Account → Private token 手填 d. 打印回填摘要: [downstream-gen] GitLab 凭据自动派生(从 origin 远程): GITLAB_PROJECT_ID = <回填值 或「保留占位: 已手填 xxx」> GITLAB_API_URL = <回填值 或「保留占位: 已手填 xxx」> GITLAB_TOKEN = 保持占位 — 请去 GitLab Profile → Account → Private token 手填 SCHEMEhttp,额外提示:"⚠️ 本次派生用 http://——Private Token 将走明文;仅当你的 GitLab 部署真的没上 HTTPS 时可以保留,否则请手动改为 https://。" 若 SCHEME 是推断来的 https(来自 ssh/git@ remote),额外提示:"API URL 的 scheme 是从 SSH remote 默认推成 https;如实际 API 走 http,请手动改 GITLAB_API_URL。"

  1. 输出:downstream-gen: 写入 4 个文件 + <N> 个模块 + <M> 处 REQ 依赖接口回填。

  2. 打印 Plan 阶段终止横幅并停下(不自动进入 B 阶段):

   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    [downstream-gen] ✅ Plan 阶段(A0~A5)全部完成

    所有规划文档已就绪,docs/08 § 一 全部勾选。

    ⚠️ 进入 B 阶段前必须完成:
      1. 人工通读 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/*

      2. 把全部 Plan 产物 commit:
           git add -A && git commit -m "chore: plan phase A0~A5 done"

      3. 推到远程,按仓库状态二选一:

         情况 A — 远程仓库是全新的(尚无 main / master):
           git remote add origin <gitlab-url>   # 若尚未添加
           git -c core.hooksPath=/dev/null push -u origin master
           # 首次 push 本地 DB 尚未就位、scripts/test.sh 必然失败。
           # `-c core.hooksPath=/dev/null` 只对本次 push 临时指向空的 hooksPath,
           # 跳过 .githooks/pre-push,不动 repo 的 core.hooksPath 配置,
           # 也不用 --no-verify(后者被 CC 的 deny-no-verify hook 硬拦)。
           # push 完成后到 GitLab UI 把 master(或 main)设为 protected

         情况 B — 远程已有 main 需要走 MR 审核:
           git checkout -b plan-init
           git push -u origin plan-init
           # 在 GitLab 打开 plan-init → main 的 MR,审核并合并

      4. 补齐 `.env.local`:
         - `GITLAB_TOKEN`(必填):去 GitLab Profile → Account → Private token 生成后粘贴
         - `GITLAB_API_URL` / `GITLAB_PROJECT_ID`:步骤 4 已从 origin 远程自动回填(若仍显示
           `TBD(A5 自动补)`,说明 origin 没配,手动填;若 scheme 错请改 http↔https)
         B 阶段 mr-create 用 token 通过 curl 创建 MR。

      5. main(或 master)就绪后,再运行 /erp-workflow:coding-start
         进入 B 阶段。届时 .env.local 应指向本地 MySQL(非共享远程 DB),
         否则 B 阶段每次测试闸门都会因 setup-test-db.sh 的防护失败。
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

参考

  • ${CLAUDE_SKILL_DIR}/templates/docs-02-template.md
  • ${CLAUDE_SKILL_DIR}/templates/docs-05-header-template.md
  • ${CLAUDE_SKILL_DIR}/templates/docs-05-endpoint-template.md
  • ${CLAUDE_SKILL_DIR}/templates/docs-06-module-pagelist-template.md
  • ${CLAUDE_SKILL_DIR}/templates/docs-08-module-row-template.md(模块 bullet 行模板)
  • ${CLAUDE_SKILL_DIR}/templates/docs-10-header-template.md
  • ${CLAUDE_SKILL_DIR}/templates/docs-10-module-template.md