# 模块划分与校验清单

初始化时两处判断需要依据：`prds/` 下按什么分模块、落盘后怎么确认这仓真能用。本文只写这两件事。

## 一、prds/ 的功能模块怎么划

模块是 `prds/` 下的一层分组目录，**深度上限一层**。它解决的是「PRD 多了以后找不着」，不解决权限、不表达依赖。

### 两种主流分法

| 分法 | 形态 | 适合 | 实例 |
|---|---|---|---|
| **按能力域** | `01-cli/`、`02-agents/` | 产品长期演进、需求按功能归堆 | sdlc 自身的 context-repo |
| **按迭代/季度** | `26Q3需求/`、`26Q4需求/` | 需求按批次立项、一个季度一波 | cyt |

序号前缀（`01-`、`02-`）让目录有稳定顺序，不靠字母序。用中文名也可以——目录名不进 PRD id，不影响引用。

### 划分时问什么

模块划分是产品侧决策，**由用户定，不替他造**。缺信息时问这两句就够：

1. 「这个产品的需求平时按什么归堆——按功能模块（如订单、结算、账号），还是按迭代批次（如 26Q3）？」
2. 「先建哪几个模块？」

用户说不清、或产品刚起步没几个需求时，**建议 `prds/` 留空**：首个 PRD 立项时走 `ctx scaffold` 会触发 `PRD_MODULE_REQUIRED` 对话，届时按真实需求命名比现在凭空造准。凭空造出的空模块目录后续没人用，反而成噪音。

### 不该做的

- **别把模块名塞进 PRD id**。归属由目录位置表达，`slug` 里再写一遍等于同一信息两处维护，换模块时还要改 id。
- **别建二级模块**。CLI 只扫一层（`listModules`），二级目录会被当成 PRD 实体的父目录误判。
- **别为「将来可能有」的方向预建目录**。

## 二、产品名与 manifest

`manifest.yaml` 的 `product` 是 `ws init --product <name>` 的校验依据：装配时若 manifest 里的产品名与命令行给的不一致（且不在 `product_aliases` 里），装配会报错中止。

取值规则：

- **用 `~/.sdlc/config.yaml` 里 `products` 的 key**，而不是仓库目录名或中文名。key 才是跨机器稳定的那个串。
- 用户还没配 config 时，用他打算配的那个 key，并在报告里提醒「记得在 config 里用同名 key 注册」。
- 曾用过别名（老仓名、别的叫法）时写进 `product_aliases`，避免历史命令报不匹配。

schema 是 strict 的（`schema/repo-manifest.ts`），只认 `product`、`product_aliases`、`description` 三个字段。**多写一个字段会让整份 manifest 读取失败退化成 null**，产品名校验随之失效——所以别往里加 `repos`、`created_at` 之类看着合理的字段。

## 三、已有仓补齐：先看缺什么

对已有仓重跑脚本是安全的（缺失才补），但动手前先看清缺口，好在报告里说清改了什么。

| 检查 | 命令 | 缺了的后果 |
|---|---|---|
| 三层根目录 | `ls -d prds requirements assets` | 缺 `prds/` 或 `requirements/` → `ctx *` 报「未找到 context-repo 根」 |
| 导航真源 | `ls -la AGENTS.md CLAUDE.md .CLAUDE.md` | 缺 → AI 每次进仓都得重新猜目录约定 |
| 导航方向 | 同上，看箭头指向 | 反了（真源在 CLAUDE.md 一侧）→ 换 AI 工具时要动文件 |
| 根 manifest | `cat manifest.yaml` | 缺 → `ws init` 报 `REPO_MANIFEST_MISSING` 告警，产品名无从校验 |
| 空目录可提交 | `ls prds/.gitkeep` | 空目录进不了 git，clone 出来判根失败 |
| 需求产出形态 | `ls <某个 PRD>/prd/` 看有没有 `02_ai_output/` | 导航写错形态 → 读的人照错结构放文件 |

仓里已有 PRD 时，形态直接看得出来：有 `prd/02_ai_output/` 的是四段式（传 `--prd-layout standard`），没有的是散放形态（传 `loose`）。CLI 也是按这个逐个 PRD 判别的，所以同仓可以两种共存——一个仓里既有历史散放的 PRD、又有后来 `ctx scaffold` 建的四段式 PRD 是正常状态。导航按**多数形态**写，或按团队打算往哪走写。

### 导航文件的方向

规范是 **`AGENTS.md` 为真源、`CLAUDE.md` 软链指向它**。方向有讲究：`AGENTS.md` 是跨 AI 工具的通用约定，`CLAUDE.md` 只是 Claude 侧的入口名。真源放通用的那个，换工具时不用动文件。`sdlc-cli` 仓自身就是这个形态。

存量 context-repo 多是反的（真源在 `.CLAUDE.md` 或 `CLAUDE.md`，`AGENTS.md` 是软链）。脚本对这种仓**默认不翻**，只在 `skipped` 里报出来——它本身能用，翻方向会产生一次改名 diff，该由人决定要不要现在做。

要翻时重跑加 `--normalize-guide`：

```bash
python3 init_context_repo.py <repo> --product <name> --normalize-guide --dry-run   # 先看
python3 init_context_repo.py <repo> --product <name> --normalize-guide
```

它做三步：摘掉旧的 `AGENTS.md` 软链 → 把真源改名为 `AGENTS.md` → 建 `CLAUDE.md -> AGENTS.md`。

git 把这次改动记成三条：`D .CLAUDE.md`、`T AGENTS.md`（软链转普通文件）、`A CLAUDE.md`。**它不是一次干净的 rename**——因为 `AGENTS.md` 这个路径原本已被软链占着，git 无从配对。正文历史仍可用 `git log --follow -- AGENTS.md` 追到改名前。提交前 `git status` 核一遍这三条。

翻完还要 `grep -rn 'CLAUDE\.md' <repo>` 一遍：仓内文档引用旧文件名的地方脚本不改，需人工改指向。

`AGENTS.md` 或 `CLAUDE.md` 已是普通文件（不是软链）时脚本一律**原样保留**并报出来。里面可能有人手写的正文，删掉就没了；要不要并入真源由人判断。

## 四、落盘后的校验

跑完脚本，用这几条确认仓真能用：

```bash
# ① CLI 认得这个根（最关键的一条，其余都是文档层面）
cd <repo> && sdlc-cli ctx modules --json

# ② 产品名可被 ws init 校验
cat <repo>/manifest.yaml

# ③ 导航方向：AGENTS.md 是普通文件（真源），CLAUDE.md 是指向它的软链
ls -la <repo>/AGENTS.md <repo>/CLAUDE.md

# ④ 三层根目录都能进 git（有 .gitkeep 或已有内容）
ls -a <repo>/prds <repo>/requirements <repo>/assets
```

`ctx modules` 返回 `{"ok":true,...,"modules":[...]}` 说明判根成功——这是「这仓能用」最直接的证据，比逐个 `ls` 目录可靠。它报「未找到 context-repo 根」时，看 `prds/` 与 `requirements/` 是否都在。

模板里留下未替换的 `{{VAR}}` 占位符说明脚本参数漏了，`grep -r '{{' <repo>` 一遍能查出来。
