# Convention Flow — 约定建立流程 ## 核心理念 **每个 bug 都是一个建立约定的机会。** 修复 bug 后,立即将教训固化为自动化检测,防止同类问题再次出现。 ## 流程总览 ``` Step 1: 修 Bug → 标准修复,代码层面解决问题 ↓ Step 2: 文档化约定 → 写入 CLAUDE.md(让 AI 和人都知道这条规则) ↓ Step 3: 自动化检测 → 创建 scripts/check-*.sh(让 CI 自动阻止违规) ↓ Step 4: 记录追踪 → 创建【约定】任务关联需求(仅 /req cr 自动触发时) ``` > Step 1-3 是核心流程(手动和自动都执行)。Step 4 仅在 `/req cr` 自动触发时执行,用于在 ai-proj 中留痕。 ### Step 1: 修 Bug 标准的 bug 修复流程。此步骤无特殊要求。 ### Step 2: 文档化约定 在项目的 CLAUDE.md 中添加约定段落,格式参见 [harness-engineering.md](harness-engineering.md) 的「CLAUDE.md 约定文档格式」。 **必须包含**: - 原因描述(为什么这是个问题) - 一句话规则 - 正例和反例代码 - 检查命令 ### Step 3: 自动化检测 创建 `scripts/check-{name}.sh` 脚本: 1. 扫描代码库查找违反约定的模式 2. 输出违规文件和行号 3. 支持 `--ci` 模式(退出码 0/1) 4. 零违规时输出 PASSED 消息 ### Step 4: 记录追踪(自动触发时) 当 convention flow 由 `/req cr` 自动触发时,产出物必须关联到当前需求: ``` ai-proj task create --title "【约定】{约定名称}" ai-proj req link --id --task-ids # linkRole=documentation ai-proj create-and-attach --id --content "..." # 约定详情文档 ``` **约定详情文档内容**: ```markdown ## 约定: {名称} | 字段 | 值 | |------|-----| | 来源需求 | REQ-XXXX | | 触发 bug | {bug 描述} | | 根因模式 | {代码模式} | | 策略 | {hard wall / ratchet(N)} | | 现有违规 | {N} 处 | ### 产出物 - CLAUDE.md 约定段落: ✅ 已添加 - 检测脚本: `scripts/check-{name}.sh` ✅ 已创建 - 首次运行结果: PASS ### 约定规则 {规则内容,与 CLAUDE.md 中一致} ``` > 这样在需求归档时能看到:这个 bug 不仅被修了,还产出了一条防止复发的约定。 ## Commit 策略 Convention flow 的产出物(CLAUDE.md 修改 + `scripts/check-*.sh` 新脚本)**必须单独提交**,不与 bug fix 代码混在一起: ```bash # 1. Bug fix 已经提交 git add src/... git commit -m "fix: 修复 Modal 重叠导致弹窗无法关闭" # 2. Convention 产出物单独提交 git add CLAUDE.md scripts/check-modal-safety.sh git commit -m "chore: 建立 Modal 安全约定和自动检测" ``` **原因**: - bug fix 可能需要 cherry-pick 到其他分支,约定脚本不应跟着走 - 约定提交的 review 焦点不同(规则是否合理 vs 代码是否正确) - `git log --oneline scripts/` 可以快速看到所有约定建立历史 ## 可检测性判断 `/req cr` 自动触发时,AI 必须先判断 bug 根因是否能自动检测。判断标准: **可检测(建议约定)** — 根因是一个**代码模式**,能用 grep/regex 在源码中匹配: | 模式类别 | 示例 | grep 可行性 | |---------|------|------------| | API 误用 | `Modal.success()` 后直接 `setState` | grep `Modal.success` + 上下文检查 | | 禁止导入 | handler 直接 import database 层 | grep import 路径 | | 缺失守卫 | SQL 拼接而非参数化查询 | grep 字符串拼接模式 | | 遗漏回调 | Promise 无 `.catch()` | grep `.then(` 无配对 `.catch` | | 硬编码 | 密钥/密码直接写在代码中 | grep 常见密钥模式 | **不可检测(仅记录根因)** — 根因是**逻辑/设计问题**,代码形态上看不出来: | 模式类别 | 示例 | 为什么不行 | |---------|------|-----------| | 算法错误 | 排序方向反了、边界条件漏了 | 代码语法正确,逻辑错误 | | 业务规则遗漏 | 退款应该校验订单状态但没校验 | 缺失的代码无法被 grep | | 竞态条件 | 两个请求并发修改同一资源 | 需要运行时才能暴露 | | 配置错误 | 环境变量填错、超时值设太小 | 值的正确性取决于上下文 | **快速判断法**:问自己「能不能写一条 grep 命令找到所有同类问题?」能 → 可检测,不能 → 不可检测。 ## 选择策略 修完 bug 后,根据当前违规数量选择不同策略: | 现有违规数 | 策略 | 脚本类型 | 说明 | |-----------|------|---------|------| | **0** | Hard wall | 纯检测脚本(有 → FAIL) | 刚修完最后一个违规,或首次建立 | | **1-10** | Ratchet + 排期清理 | Ratchet 脚本 + 基线 | 创建任务逐一清理遗留 | | **>10** | Ratchet only | Ratchet 脚本 + 基线 | 不立即清理,仅防止恶化 | | **不可计数** | 仅检测/告警 | 告警脚本(exit 0) | 如"代码风格"类软约定 | ## 实例 1: Modal 安全规则 **Bug**:Ant Design `Modal.success()` 非阻塞,后续 `setState` 立即执行导致两个 modal 重叠,第一个无法关闭。 **Step 1 — 修 Bug**: 将 `setNextModalOpen(true)` 移入 `onOk` 回调。 **Step 2 — 文档化**: 在 CLAUDE.md 添加: ```markdown ### Frontend: Modal 安全规则 Ant Design 的 `Modal.success()` / `Modal.info()` 是**非阻塞**调用。 **规则:`Modal.success/info/warning/error` 之后如果还有 UI 操作,必须放在 `onOk` 回调中。** // WRONG — 两个 modal 同时弹出 Modal.success({ title: '成功' }); setNextModalOpen(true); // CORRECT — 等用户确认后再打开 Modal.success({ title: '成功', onOk: () => setNextModalOpen(true) }); **检查命令**:`./scripts/check-modal-safety.sh` ``` **Step 3 — 自动化**: 创建 `scripts/check-modal-safety.sh`: - 扫描 `.tsx` 文件中的 `Modal.success/info/warning/error` 调用 - 检查 25 行内是否有 `onOk` 回调 - 如果没有且后续 20 行内有 `set*Open(true)`,标记为违规 - 策略:现有违规 = 0 → Hard wall ## 实例 2: 架构层级约束 **问题**:Handler 直接调用 database 层,绕过 service 层导致业务逻辑散落。 **Step 1 — 识别问题**: 发现 108 个 handler 直接 import database 的文件。 **Step 2 — 文档化**: 在 CLAUDE.md 和 `docs/architecture/LAYER_RULES.md` 中描述依赖矩阵和正反示例。 **Step 3 — 自动化**: 创建 `scripts/check-architecture.sh`(ratchet 脚本): - 5 条规则,3 个 ratchet + 2 个 hard wall - 基线记录各规则的当前违规数 - 策略:遗留 108 个 → Ratchet(只能减不能增) ## 执行流程 ### 自动触发(`/req cr` + bug 类需求) **触发条件**(满足任一): - `category == bug` - category 为空但标题含 `fix`、`修复`、`bug`、`问题`、`缺陷` - category 为空但 description 含 `根因`、`复现步骤` ``` 1. /req cr 五视角扫描完成后 2. 检测是否为 bug 类需求(按上述条件判断) 3. 分析本次 bug 的根因模式(从 git diff 提取) 4. 判断:根因是否可自动检测?(判断规则见上方「可检测性判断」) - 不可检测 → 仅在 CR 报告记录根因,不建议约定 → 结束 - 可检测 → 继续下一步 5. 检查是否已有覆盖此模式的脚本: - ls scripts/check-*.sh,读取每个脚本的注释头(# ... detects ...) - 已有覆盖 → CR 报告记录「已有约定: check-{name}.sh」→ 结束 - 无覆盖 → 继续下一步 6. 扫描代码库统计同类模式的现有数量(N) 7. AskUserQuestion(按 N 值调整措辞): - N=0: 「此 bug 模式已修复,当前代码无同类问题。建立约定可防止未来复发,是否建立?」 - N>0: 「代码库中还有 {N} 处同类模式。建立约定可防止恶化 + 逐步清理,是否建立?」 - 否 → 记录到 CR 报告「约定建议:用户跳过」 - 是 → 执行 convention flow(Step 2 + Step 3 + Step 4) 8. 约定产出物关联到当前需求(Step 4) 9. 约定检查结果写入 CR 报告 ``` ### 手动触发(`/harness convention [name]`) ``` 1. 询问约定名称和类别(如果未提供) 2. 分析刚修复的 bug,提取约定规则 3. 扫描代码库统计现有违规数量 4. 根据违规数量选择策略 5. 生成 CLAUDE.md 约定段落 6. 生成 scripts/check-{name}.sh 脚本 7. 运行脚本验证(应该 PASS) 8. 输出摘要:约定名称、策略、违规数、文件清单 ```