--- name: erp-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 *) --- **所有输出必须使用中文。** # erp-downstream-gen ## 前置条件 - `docs/01-需求清单/*.md` 完整 REQ 卡片已就绪(A1 生成 + 人工审阅过)。 - `docs/03-数据库设计文档.md` 已就绪(A4 生成)。 - `docs/05` / `docs/06` / `docs/10` / `sql/seed-data.sql` 等下游文件尚不存在(本 skill 创建)。 ## 执行步骤 ### 步骤 0:打印当前位置流程图 向用户展示当前在 A 阶段流程中的位置(A-only,`▶` 标在 A5): ``` ┌──────────────────────────────────────────────────────┐ │ 📋 阶段 A:规划(一次性) │ │ │ │ A0 初始化项目 → A1 锁范围(REQ 卡片) │ │ ↓ │ │ ⏸ 等你审阅 REQ,重新运行 /erp-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 README 中的显式 `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`,并在**参与环的模块里第一个 REQ** 的 `note` 字段填入原因(如 "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 卡片 依赖接口 1. 用 `Glob` 列出 `docs/01-需求清单/*.md`(排除 `README.md`)。 2. 用 `Grep` 在这些文件中搜索 `TBD(A5 自动补)` 的行号(不读全文)。 3. 对每个命中的 REQ: - 用 `Read` 仅读取该 REQ 卡片所在的片段(REQ 标题行前后 20 行),提取 `req_id`。 - 在步骤 B 刚生成的 `docs/05-API接口契约.md` 里 Grep 属于该 REQ 的 endpoint(通过 REQ 标签或 req_id 关联)。 - 用 `Edit`:`依赖接口: TBD(A5 自动补)` → `依赖接口: POST /api/xxx, GET /api/yyy`(具体 method + path 列表,多个用 `,` 分隔)。 4. 打印回填统计:`A5 回填 N 处 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 § 二 — 追加模块清单 docs/08 已由 A0 erp-project-init 创建(含 Plan 进度骨架)。本步骤只往 § 二 追加模块行,**不重写整个文件**。 1. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/docs-08-module-row-template.md`(单模块 bullet 行模板)。 2. **按 `module_id` 字母序**对每个模块:渲染一行 bullet,填充 `module_id` / `module_name` / `depends_on` / `path_scopes`(不含 REQ 列表——REQ 级顺序以 docs/02 § 二 为准;模块完成信号由 `MR:` 字段 + GitLab API `state` 判定)。 > 注意:docs/08 § 二 的行序**不决定分发顺序**——分发以 docs/02 § 二 为准,此处按字母序仅方便查找。 3. 所有模块行拼成一段文本,用 `Edit` 在 `docs/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-需求清单/*.md` + `docs/02` / `docs/05` / `docs/06` / `docs/10` + `sql/seed-data.sql`): a. **`TBD` → CC 自动补齐**:用 `Grep` 搜索 `TBD(A4 自动补)` 和 `TBD(A5 自动补)`。有命中则按 A4 / B2 同样逻辑就地补填(A4 查 docs/03 填 `依赖表:`;A5 查 docs/05 按 REQ-ID 填 `依赖接口:`),再 Grep 确认 0 命中;仍残留报错停下。 b. **`【人工填写:...】` → QA 循环等用户补**(与 A2 erp-skeleton-gen 步骤 E 同风格): 循环执行直到两个条件**同时满足**:(1) 扫描 0 命中;(2) 用户选「继续」 - **b.1 扫描**:用 `Grep` 在上述所有文件中搜索 `【人工填写:`,得到命中数 N 和残留位置清单(`<文件:行号> — <行内容摘要>`) - **b.2 打印汇总横幅**: ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [erp-downstream-gen] 最终占位符审阅 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 且选「继续」才放行。 3. 用 `Edit` 在 `docs/08-模块任务管理.md` 勾选 7 个 checkbox(A5 的 6 个子项 + A5 父项): - ` - [ ] docs/02 开发计划已生成` → `[x]` - ` - [ ] docs/05 API 契约已生成` → `[x]` - ` - [ ] docs/06 § 五 页面清单已填入` → `[x]` - ` - [ ] docs/10 验收清单已生成` → `[x]` - ` - [ ] 下方模块列表已填入` → `[x]` - ` - [ ] REQ 卡片依赖接口已回填` → `[x]` - `- [ ] A5 下游文档生成 — erp-downstream-gen` → `[x]` 4. **从 `origin` 远程派生 GitLab 凭据并回填 `.env.local`** 仅当对应字段仍是 `TBD(A5 自动补)` 或空时回填;用户已手填为别的值一律不动。 a. 取远程 URL:`Bash`: `git remote get-url origin 2>/dev/null` → `REMOTE_URL`。为空 → 跳过本步骤,仅打印提示"未配置 origin 远程,`GITLAB_*` 留给用户手填"。 b. 解析: - `PROJECT_PATH` = 去掉 `:///` 或 `git@:` 或 `ssh:///` 前缀 + 去掉末尾 `.git`(如 `zhuzc/test`) - `PROJECT_ID_ENC` = `PROJECT_PATH` 的 `/` 替换为 `%2F`(如 `zhuzc%2Ftest`) - `HOST` = 远程主机名(如 `git.xlyprint.cn`) - `SCHEME` 三分支(**绝不把 SSH remote 降级成 http**——Private Token 不能走明文): - `REMOTE_URL` 以 `https://` 开头 → `https` - `REMOTE_URL` 以 `http://` 开头 → `http`(明确声明,非推断) - 其他(`git@` / `ssh://` / 异常)→ `https`(默认走安全侧) - `API_URL_GUESS` = `:///api/v3` c. 用 `Read` 读 `.env.local`: - 若 `GITLAB_PROJECT_ID` 当前值为 `TBD(A5 自动补)` 或空 → 用 `Edit` 改为 `GITLAB_PROJECT_ID=` - 若 `GITLAB_API_URL` 当前值为 `TBD(A5 自动补)` 或空 → `Edit` 改为 `GITLAB_API_URL=` - 上两字段若已被用户手改过(非 `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 手填 ``` 若 `SCHEME` 是 `http`,额外提示:"⚠️ 本次派生用 `http://`——Private Token 将走明文;仅当你的 GitLab 部署真的没上 HTTPS 时可以保留,否则请手动改为 `https://`。" 若 `SCHEME` 是推断来的 `https`(来自 ssh/git@ remote),额外提示:"API URL 的 scheme 是从 SSH remote 默认推成 `https`;如实际 API 走 http,请手动改 `GITLAB_API_URL`。" 5. 输出:`downstream-gen: 写入 4 个文件 + 个模块 + 处 REQ 依赖接口回填。` 6. 打印 Plan 阶段终止横幅并**停下**(不自动进入 B 阶段): ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [erp-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 # 若尚未添加 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 阶段 erp-mr-create 用 token 通过 curl 创建 MR。 5. main(或 master)就绪后,再运行 /erp-workflow:erp-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`