# Ratchet Pattern — 渐进式违规清理 ## Ratchet vs Hard Wall | 策略 | 适用场景 | 基线值 | CI 行为 | |------|---------|--------|---------| | **Ratchet** | 有遗留违规(>0) | 当前违规数 | 新值 > 基线 → FAIL | | **Hard Wall** | 必须为零 | 固定 = 0 | 值 ≠ 0 → FAIL | **选择依据**: - 遗留违规 = 0 → Hard Wall(绝对禁止) - 遗留违规 > 0 → Ratchet(允许存在但不能增加) ## 基线文件格式 基线以 JSON 格式存储,提交到 git: ```json { "rule_name_1": 108, "rule_name_2": 7, "rule_name_3": 0 } ``` **位置约定**:`scripts/.{检测名}-baseline.json`(如 `scripts/.architecture-baseline.json`) ## 三命令接口 每个 ratchet 脚本必须实现三个子命令: | 命令 | 用途 | CI 使用 | |------|------|---------| | `check` | 比较当前值与基线,增加则失败 | PR 门禁 | | `baseline` | 记录当前值为新基线 | 修复违规后手动执行 | | `report` | 打印当前值 vs 基线的对比表 | 人工审查 | ```bash ./scripts/check-{name}.sh check # CI: fail if violations increased ./scripts/check-{name}.sh baseline # After fix: record new lower baseline ./scripts/check-{name}.sh report # Human: see current vs baseline ``` ## 何时降低基线 ``` 1. 修复一个或多个违规 2. 运行 check → PASS(因为当前值 ≤ 旧基线) 3. 运行 baseline → 记录更低的基线 4. git commit 新的基线文件 5. 后续 PR 使用更低的基线 ``` **关键**:修复后一定要 `baseline` + commit,否则下次有人新增违规会被允许(因为基线还是旧的高值)。 ## 多规则 Ratchet 一个脚本可以管理多条规则,每条规则有独立的基线值和类型(ratchet/wall)。 ```bash # rules 数组定义规则名 rules=("handlers_import_database" "routes_import_database" "models_import_upper") # types 数组定义策略 types=("ratchet" "ratchet" "wall") # 循环检查每条规则 for i in "${!rules[@]}"; do rule="${rules[$i]}" type="${types[$i]}" # ... done ``` ## 参考实现 **new-ai-proj** 项目的架构 ratchet: - 脚本:`scripts/check-architecture.sh` - 基线:`scripts/.architecture-baseline.json` - 规则文档:`docs/architecture/LAYER_RULES.md` - 门禁文档:`docs/quality/GATES.md` 五条规则: | 规则 | 类型 | 含义 | |------|------|------| | `handlers_import_database` | ratchet | Handler 不应直接访问数据库层 | | `routes_import_database` | ratchet | 路由不应直接访问数据库层 | | `routes_import_services` | ratchet | 路由应通过 handler 委托 | | `models_import_upper` | hard wall | 模型层禁止反向依赖 | | `services_import_handlers` | hard wall | 服务层禁止反向依赖 | ## 设计 Ratchet 规则的原则 1. **可计数** — 规则违规必须能通过 grep/AST 精确计数 2. **无歧义** — 一个文件/行要么违规要么不违规,不能有灰色地带 3. **可修复** — 开发者必须知道怎么把违规改正确 4. **有文档** — 每条规则配 LAYER_RULES.md 或 CLAUDE.md 中的正反示例