feat(req-test-gate): 集成 Harness Engineering 工程约束方法论

将项目级的 Ratchet/约定检测方法论融入 req-test-gate 技能,
通过 /req 流程三个节点自动触发(dev 环境检测、cr 约定建议、test Gate 0B),
无需手动记忆执行。

新增文档:harness-engineering.md、ratchet-pattern.md、convention-flow.md、
project-bootstrap.md 及 4 个模板(ratchet/convention 脚本、GATES.md、pre-commit)。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-26 11:34:42 +10:30
parent e3924e6b2b
commit b9c808cce0
13 changed files with 1333 additions and 14 deletions

View File

@@ -47,6 +47,7 @@ analysis → design → dev → review → testing → [待部署池] → releas
- **操作前先确认实际 ID** — 从 URL 提取 ID`/requirements/897` → ID=897
- **务实路线关闭也必须补全关联任务** — 每个进度条阶段需创建关联任务
- **阶段内容门禁(防空转)** — `/req next` 时检查关键阶段任务是否有实质内容
- **Harness 环境检测** — `/req dev` 启动开发前,快速检测项目基础设施(`.husky/` 存在?`scripts/check-*.sh` 存在。若两者都不存在Level 0输出一行提示「项目无质量护栏建议先运行 `/harness init`」。检测结果不阻塞开发,仅提示
## 禁止直接调用(必须走命令流程)
@@ -92,13 +93,15 @@ Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD
6. /req next → 推进到 design/dev 阶段
7. /req dev → 启动开发(创建【开发】任务, delivery_stage=dev
8. /req next → 推进到 review 阶段
9. /req cr → 代码评审(创建【代码评审】任务
9. /req cr → 代码评审 + 约定检查 + bug约定建议⭐ harness 自动
10. /req next → 推进到 testing 阶段
11. /req test → 测试验收(5-Gate 流程
11. /req test → 测试验收Gate 0B 约定检查 ⭐ + Gate 1-5
12. /req deploy → 批量部署build-and-push.sh → Jenkins ai-proj
13. /req done → 归档 (archived) + git commit + push
```
> ⭐ 标记的步骤是 Harness Engineering 自动嵌入的,无需手动调用。
**5 阶段文档**
| 阶段 | 文档名 | 技能 |
@@ -287,10 +290,19 @@ Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD
2. `git diff` / `find` 确定变更范围(文件数、行数)
3. 读取所有变更文件源码(非 test 文件)
4. **五视角扫描**:逐一用攻击者/泄露者/并发者/边界者/依赖者视角审查
5. `ai-proj task create` 创建【代码评审】任务并关联需求linkRole=code_review
6. `ai-proj create-and-attach` 附加 CR 报告文档(必须含五视角扫描结果)
7. 展示发现摘要AskUserQuestion 确认是否创建 bug 修复任务
8. 若有 High/Critical 发现 → `ai-proj task create` 创建关联修复任务
5. **约定检查**:运行所有 `scripts/check-*.sh --ci`,将结果写入 CR 报告
6. **⭐ Bug 约定建议**bug 类需求自动触发category=bug或标题含 fix/修复/bug或描述含根因/复现步骤):
- 分析本次 bug 的根因模式
- 判断可检测性:能写 grep 找到同类问题?(规则见 convention-flow.md「可检测性判断」
- 不可检测 → 仅在 CR 报告记录根因,跳过
- 可检测 → 检查已有 `scripts/check-*.sh` 是否已覆盖 → 已覆盖则跳过
- 扫描代码库统计同类模式数量 N
- AskUserQuestionN=0: 防未来复发N>0: 防恶化+逐步清理)
- 是 → 执行 convention flow → 产出物**单独 commit**`chore: 建立 {name} 约定`
7. `ai-proj task create` 创建【代码评审】任务并关联需求linkRole=code_review
8. `ai-proj create-and-attach` 附加 CR 报告文档(必须含五视角扫描结果 + 约定检查结果)
9. 展示发现摘要AskUserQuestion 确认是否创建 bug 修复任务
10. 若有 High/Critical 发现 → `ai-proj task create` 创建关联修复任务
**`/req test [REQ-ID]`** — 测试(前置:代码评审通过),遵循 dev-test 技能的 5-Gate 流程
@@ -332,6 +344,7 @@ ai-proj task append-doc --id 5214 --content "# PRD: 用户认证功能\n..."
| design | 【评审】技术设计: {需求标题} | design |
| dev | 【开发-后端】{需求标题}、【开发-前端】{需求标题} | implementation |
| review | 【代码评审】{需求标题} | code_review |
| review | 【约定】{约定名称}bug 类需求convention flow 自动创建) | documentation |
| testing | 【测试】集成测试: {需求标题} | test |
| deploy | 由 `/req deploy` 批量创建 | deploy |
@@ -398,3 +411,4 @@ ai-proj req tasks --id <id> # 查看关联任务
| `req-prd` | PRD 文档编写 + 评审方法论 |
| `req-prototype` | Stitch 原型生成 + 迭代 |
| `dev-test` | 测试 + 质量门禁 |
| `req-test-gate` (harness) | 工程约束方法论。Gate 0-2 的约定检查由项目本地脚本定义,方法论见 `/harness` 命令 |

View File

@@ -1,6 +1,6 @@
{
"name": "req-test-gate-plugin",
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5,含前后端联调+视觉验证)、部署级验证(Deploy Gates)、持续回归(Regression)。",
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论Ratchet、约定建立、门禁层级。",
"version": "1.0.0",
"author": {
"name": "qiudl"

View File

@@ -1,6 +1,7 @@
---
name: req-test-gate
description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5,含前后端联调+视觉验证)、部署级验证(Deploy Gates)、持续回归(Regression)。
description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论Ratchet、约定建立、门禁层级。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活
arguments: <subcommand> [args]
---
# 测试与质量门禁制度 (req-test-gate) v2.1
@@ -36,12 +37,13 @@ Gate 5: 回归贡献检查(按 scope 区分贡献形式)
通过 → 允许 /req next 推进到待部署池
```
## Gate 0: Scope 分级(自动)
## Gate 0: Scope 分级 + 约定检查(自动)
**从 `git diff` 自动推断变更范围,决定后续 Gate 的裁剪策略。**
**两件事**:① `git diff` 推断变更范围;② 运行项目内所有约定检查脚本。
### 0A: Scope 分级
```bash
# 自动推断逻辑
git diff main...HEAD --name-only | classify_scope
```
@@ -56,6 +58,43 @@ git diff main...HEAD --name-only | classify_scope
**scope 影响全部后续 Gate**,在测试报告中必须标注。
### 0B: 约定检查Harness 自动执行)
**自动发现并运行项目内所有 `scripts/check-*.sh` 脚本。**
```bash
# 自动执行逻辑
for script in scripts/check-*.sh; do
[ -x "$script" ] && bash "$script" --ci
done
```
| 结果 | 处理 |
|------|------|
| 全部 PASS | Gate 0 通过,继续 Gate 1 |
| 有 FAIL | **阻塞**,按下方「失败处理」修复后重新 `/req test` |
| 无 scripts/check-*.sh | 跳过(项目未建立约定检查) |
**失败处理**(区分脚本类型):
| 脚本类型 | 判断方法 | 修复方式 |
|---------|---------|---------|
| Hard wall纯检测 | 脚本无 baseline 文件 | 修改代码消除违规 |
| Ratchet基线对比 | 脚本有 `.*-baseline.json` | 修改代码降低违规数,**或**确认是故意增加 → `./scripts/check-{name}.sh baseline` 更新基线 + AskUserQuestion 确认 |
> 更新基线是有意识的决策(如重构导致临时增加),不能静默执行。
**报告格式**
```
### 约定检查 (Gate 0B)
| 脚本 | 结果 | 详情 |
|------|------|------|
| check-architecture.sh | ✅ PASS | 5 rules, all within baseline |
| check-modal-safety.sh | ✅ PASS | 0 violations |
```
> 这样 Harness 建立的约定脚本会在每次 `/req test` 时自动运行,无需手动执行 `/harness report`。
---
## Gate 1: 前置条件检查
@@ -437,9 +476,10 @@ DG6: 生产部署(不可跳过) → 健康检查+文档
### `/req test` 增强
```
v1: Gate 1 → Gate 2 → Gate 3 → Gate 4
v1: Gate 1 → Gate 2 → Gate 3 → Gate 4
v2: Gate 1 → Gate 2(2A→2B→2C→2D→2E) → Gate 3 → Gate 4 → Gate 5
v2.1: Gate 0(scope) → Gate 1 → Gate 2(2A→2B→2C→2D→2E→2F按scope裁剪) → Gate 3 → Gate 4 → Gate 5
v2.1: Gate 0(scope) → Gate 1 → Gate 2(2A→2B→2C→2D→2E→2F) → Gate 3 → Gate 4 → Gate 5
v3: Gate 0A(scope) + 0B(约定检查) → Gate 1 → Gate 2(按scope裁剪) → Gate 3 → Gate 4 → Gate 5
```
### `/req next` testing→staging 门禁
@@ -449,3 +489,52 @@ v2.1: Gate 0(scope) → Gate 1 → Gate 2(2A→2B→2C→2D→2E→2F按scope
### `/req deploy` 部署门禁
在 req-deploy 流程前增加 Deploy Gates (DG1-DG6) 检查。
---
## Harness Engineering — 工程约束方法论
**项目级基础设施**的建立和维护方法论,与上述需求级门禁互补。
> 详见 [harness-engineering.md](harness-engineering.md) 完整文档。
### 核心理念
**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题。**
三大支柱:
1. **Ratchet** — 遗留违规数只能减不能增 → [ratchet-pattern.md](ratchet-pattern.md)
2. **约定建立** — 每个 bug 修复后立即建立约定和检测 → [convention-flow.md](convention-flow.md)
3. **门禁层级** — 按项目成熟度逐级引入 → [project-bootstrap.md](project-bootstrap.md)
### `/harness` 命令集(手动可用 + 自动嵌入)
| 命令 | 手动 | 自动触发 |
|------|------|---------|
| `/harness assess` | 随时 | 首次 `/req dev` |
| `/harness init [level]` | 随时 | — |
| `/harness convention [name]` | 随时 | `/req cr` + bug 类型需求 |
| `/harness report` | 随时 | `/req test` Gate 0B |
### 模板
`templates/` 目录提供可复用的脚本模板:
- [ratchet-script.md](templates/ratchet-script.md) — Ratchet 脚本模板check/baseline/report 三命令)
- [convention-script.md](templates/convention-script.md) — 约定检测脚本模板grep 模式匹配)
- [gates-doc.md](templates/gates-doc.md) — GATES.md 文档模板
- [pre-commit-config.md](templates/pre-commit-config.md) — husky + lint-staged 配置Node.js/Go/Monorepo
### 与需求级门禁的关系
```
Harness项目级护栏 req-test-gate需求级门禁
├── .husky/ pre-commit ├── Gate 0A: scope 分级
├── CI lint workflow ├── Gate 0B: 运行 harness 的 check-*.sh ←─ 连接点
├── scripts/check-*.sh ├── Gate 1: 前置条件
├── CLAUDE.md 约定 ├── Gate 2: 测试执行
│ ├── Gate 3: 质量验证
│ ├── Gate 4: 文档持久化
│ └── Gate 5: 回归贡献
```
> Harness 不使用 Gate 编号。它的产出物check-*.sh 脚本)通过 Gate 0B 接入 req-test-gate 流程。

View File

@@ -0,0 +1,222 @@
# 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 <req_id> --task-ids <task_id> # linkRole=documentation
ai-proj create-and-attach --id <task_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 flowStep 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. 输出摘要:约定名称、策略、违规数、文件清单
```

View File

@@ -0,0 +1,113 @@
# Harness Engineering — 工程约束方法论
## 概述
Harness Engineering 是一套**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题**的工程方法论。
**三大支柱**
| 支柱 | 核心机制 | 说明 |
|------|---------|------|
| **Ratchet** | 基线 + 单调递减 | 遗留违规数只能减不能增,新代码零容忍 |
| **约定建立** | Bug → 文档 → 自动检测 | 每个 bug 修复后立即建立约定和检测脚本 |
| **门禁层级** | Level 1-3 递进 | 按项目成熟度逐级引入基础设施 |
## 子文件索引
| 文件 | 内容 |
|------|------|
| [ratchet-pattern.md](ratchet-pattern.md) | Ratchet 模式详解Ratchet vs Hard Wall、基线格式、三命令接口 |
| [convention-flow.md](convention-flow.md) | 三步约定建立流程:修 bug → 文档化 → 自动化检测 |
| [project-bootstrap.md](project-bootstrap.md) | `/harness assess` 检测逻辑 + `/harness init` 分级实施指南 |
| [templates/](templates/) | 脚本模板ratchet、约定检测、GATES.md、pre-commit 配置 |
## 命令集与自动触发
| 命令 | 手动触发 | 自动触发时机 |
|------|---------|-------------|
| `/harness assess` | 用户显式调用 | **`/req dev`** 启动时快速检测(`.husky/` + `scripts/check-*.sh` 都不存在 → 提示 Level 0 |
| `/harness init [level]` | 用户显式调用 | 不自动触发(创建基础设施需用户确认) |
| `/harness convention [name]` | 用户显式调用 | **`/req cr` + category=bug** 时自动建议(分析根因 → 建议约定 → 用户确认后执行) |
| `/harness report` | 用户显式调用 | **`/req test` Gate 0B** 自动运行所有 `scripts/check-*.sh` |
### 自动触发流程图
```
/req dev (每次)
└→ 快速检测: .husky/ 存在? scripts/check-*.sh 存在?
└→ 都不存在 → 提示「Level 0建议 /harness init」不阻塞
└→ 有任一 → 静默通过
/req cr (bug 类型需求)
└→ 五视角扫描
└→ 运行 scripts/check-*.sh → 写入 CR 报告
└→ ⭐ 分析 bug 根因 → 可自动检测? → 建议建立约定
└→ 用户确认 → convention flow文档化 → 脚本 → CLAUDE.md
/req test
└→ Gate 0A: scope 分级
└→ Gate 0B: 运行所有 scripts/check-*.sh
└→ 有 FAIL → 阻塞,必须修复
└→ 全 PASS → 继续 Gate 1-5
```
> 手动命令仍然可用。自动触发只是让你"不用记得去跑"。
## 成熟度分级
| Level | 基础设施 | 适用场景 |
|-------|---------|---------|
| **0** | 无自动化检查 | 新项目/原型 |
| **1** | husky + lint-staged + formatter | 所有活跃项目 |
| **2** | + ratchet 脚本 + GATES.md + CLAUDE.md 质量段落 | >10k LOC 或 >1 开发者 |
| **3** | + CI workflow + 架构强制 + 约定检测 | 有 PR 流程和受保护分支的项目 |
## 技术栈检测矩阵
| 技术栈 | 检测标志 | Level 1 工具 | Level 2+ 工具 |
|--------|---------|-------------|--------------|
| **Go** | `go.mod` | gofmt + go vet | golangci-lint, architecture ratchet |
| **React/TS** | `package.json` + react 依赖 | ESLint + Prettier | tsc --noEmit, convention scripts |
| **Vue/TS** | `package.json` + vue 依赖 | ESLint + Prettier | vite build check |
| **Monorepo** | 多个 `package.json` / `go.mod` | 各子项目独立 lint | 统一 CI + 分模块 ratchet |
## CLAUDE.md 约定文档格式
在项目的 CLAUDE.md 中添加约定时,必须遵循以下标准格式:
```markdown
### {类别}: {约定名称}
{原因描述 — 为什么需要这条约定}
**规则:{一句话规则描述}**
\`\`\`{语言}
// WRONG — {错误原因}
{错误代码示例}
// CORRECT — {正确做法}
{正确代码示例}
\`\`\`
**检查命令**`./scripts/check-{name}.sh`CI 自动运行)
```
## 与其他技能的关系
Harness 和 req-test-gate 是**互补关系**,不要混淆两者的 Gate 编号:
| 维度 | Harness | req-test-gate |
|------|---------|--------------|
| **作用域** | 项目级(所有 PR 共享) | 需求级(每个需求独立) |
| **触发方式** | git commit / PR / `/req test` Gate 0B | `/req test` Gate 1-5 |
| **管什么** | pre-commit、lint、ratchet 脚本、约定检测 | 测试执行、质量验证、文档持久化、回归 |
| **产出物** | `.husky/``scripts/check-*.sh`、CLAUDE.md 约定 | 测试报告、回归用例 |
**⚠️ 注意**:项目的 `docs/quality/GATES.md` 中有自己的 Gate 编号Gate 0=pre-commit, Gate 1=CI lint, Gate 2=ratchet...),这是**项目文档的编号**,与 req-test-gate 的 Gate 0-5 是两套独立体系,不要混用。
| 技能 | 职责 |
|------|------|
| **harness**(本文档) | 建立和维护项目级护栏脚本、约定、CLAUDE.md |
| **req-test-gate** | 需求级测试门禁Gate 0-5其中 Gate 0B 自动调用 harness 建立的脚本 |
| **dev-coding** | 遵守 harness 建立的 CLAUDE.md 约定,编码时不违反 ratchet |

View File

@@ -0,0 +1,194 @@
# Project Bootstrap — 工程约束基础设施引导
## `/harness assess` 检测逻辑
扫描项目结构自动判断成熟度等级0-3列出缺口和建议。
### 检测项清单(每项附实际执行的命令)
| 检测项 | 执行命令 | 判定 | Level |
|--------|---------|------|-------|
| 技术栈 | `Glob("package.json", "go.mod", "Podfile", "*.csproj")` | 存在即识别 | 基础 |
| Pre-commit hooks | `Glob(".husky/pre-commit")` | 文件存在 | 1 |
| Lint 配置 | `Glob(".eslintrc*", ".golangci.yml") + Grep("lint", "Makefile")` | 任一存在 | 1 |
| Formatter | `Glob(".prettierrc*") + Grep("gofmt\\|prettier", ".husky/pre-commit")` | 任一存在 | 1 |
| lint-staged | `Grep("lint-staged", "package.json") + Glob(".lintstagedrc*")` | 任一存在 | 1 |
| 约定脚本 | `Glob("scripts/check-*.sh")` | 至少 1 个 | 2 |
| 基线文件 | `Glob("scripts/.*-baseline.json")` | 至少 1 个 | 2 |
| 门禁文档 | `Glob("docs/quality/GATES.md")` | 文件存在 | 2 |
| CLAUDE.md 质量段落 | `Grep("检查命令\\|check-.*\\.sh", "CLAUDE.md")` | 有匹配 | 2 |
| CI workflow | `Grep("check-.*\\.sh", ".gitea/workflows/*.yml", ".github/workflows/*.yml")` | 有匹配 | 3 |
### 等级判定
```
Level 0: 无上述任何基础设施
Level 1: 有 pre-commit hooks + lint + formatter
Level 2: Level 1 + 有 ratchet/约定脚本 + GATES.md
Level 3: Level 2 + CI 自动运行 + 受保护分支
```
### 输出格式
```markdown
## Harness Assessment: {项目名}
**技术栈**: Go + React (monorepo)
**当前等级**: Level 2
### 已有基础设施
- [x] husky pre-commit hooks
- [x] ESLint + Prettier (frontend)
- [x] gofmt + go vet (backend)
- [x] check-architecture.sh (ratchet, 5 rules)
- [x] check-modal-safety.sh (hard wall)
- [x] GATES.md
### 缺口(升至 Level 3
- [ ] CI workflow 未调用 check-architecture.sh
- [ ] CI workflow 未调用 check-modal-safety.sh
### 建议
1. 在 .gitea/workflows/quality-gates.yaml 中添加 check-architecture.sh check
2. 在 .gitea/workflows/quality-gates.yaml 中添加 check-modal-safety.sh --ci
```
---
## `/harness init [level]` 分级实施
### Level 1: 基础代码格式化
**适用**:所有活跃项目。
#### Node.js 项目
```bash
# 1. 安装 husky + lint-staged
npm install -D husky lint-staged
npx husky init
# 2. 配置 lint-stagedpackage.json
# 参见 templates/pre-commit-config.md — Node.js 变体
# 3. 配置 pre-commit hook
echo "npx lint-staged" > .husky/pre-commit
```
#### Go 项目
```bash
# 1. 安装 husky需要 package.json
npm init -y
npm install -D husky lint-staged
npx husky init
# 2. 配置 lint-stagedpackage.json
# 参见 templates/pre-commit-config.md — Go 变体
# 3. 配置 pre-commit hook
echo "npx lint-staged" > .husky/pre-commit
```
#### Monorepo
```bash
# 根目录安装 husky各子项目配置 lint-staged
# 参见 templates/pre-commit-config.md — Monorepo 变体
```
### Level 2: 约定与 Ratchet
**前置**Level 1 已完成。
#### Step 1: 创建目录结构
```bash
mkdir -p scripts docs/quality
```
#### Step 2: 创建 GATES.md
参见 [templates/gates-doc.md](templates/gates-doc.md),填入项目实际的 lint 工具和 CI 系统。
#### Step 3: 约束发现(关键步骤)
AI 必须探索项目代码库,按以下清单逐项检查,找出适合建 ratchet 的约束:
| 检查项 | 检测方法 | 发现约束时 |
|--------|---------|-----------|
| **分层架构** | 读 `go.mod` / 目录结构,判断是否有 handler/service/repository 分层 | 检查跨层 import → ratchet |
| **禁止 import** | `grep -r "import" --include="*.go"` 找不应出现的包引用 | 统计违规数 → ratchet 或 wall |
| **前端 API 调用规范** | `grep -r "fetch\|axios" --include="*.tsx"` 看是否有直接调用(应走 service 层) | 统计违规数 → ratchet |
| **console.log 残留** | `grep -r "console.log" --include="*.tsx" --include="*.ts"` | 统计数量 → ratchet |
| **硬编码配置** | `grep -rn "localhost\|127.0.0.1\|:3000\|:8080" --include="*.ts" --include="*.go"` 排除测试和配置文件 | 统计数量 → ratchet |
| **TODO/FIXME** | `grep -rn "TODO\|FIXME" --include="*.go" --include="*.ts"` | 统计数量 → ratchet仅追踪不阻塞 |
**执行流程**
```
1. 读取项目目录结构ls -R 前两层)
2. 识别技术栈和架构风格
3. 按上表逐项 grep 扫描
4. 筛选:违规数 > 0 的项列为候选规则
5. AskUserQuestion展示候选规则表让用户勾选要建哪些
6. 对每条选中的规则:
a. 用 ratchet-script 模板生成 scripts/check-{name}.sh
b. 运行 baseline 记录初始值
c. 在 CLAUDE.md 添加约定段落
```
> 如果扫描后没有发现任何候选规则代码量很小或结构简单Level 2 只创建 GATES.md + CLAUDE.md 质量段落,不创建 ratchet 脚本。后续通过 convention flow修 bug 时)逐步积累。
#### Step 4: CLAUDE.md 质量段落
参见 [harness-engineering.md](harness-engineering.md) 的「CLAUDE.md 约定文档格式」。
#### Step 5: 提交
```bash
./scripts/check-{name}.sh baseline # 每个新脚本都要 baseline
git add scripts/ docs/quality/ CLAUDE.md
git commit -m "chore: establish harness engineering Level 2"
```
### Level 3: CI 强制
**前置**Level 2 已完成 + 项目有 CI 和受保护分支。
```yaml
# .gitea/workflows/quality-gates.yaml 示例
name: Quality Gates
on:
pull_request:
branches: [main, develop]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Architecture Check
run: ./scripts/check-architecture.sh check
- name: Convention Checks
run: |
for script in scripts/check-*.sh; do
[ "$script" = "scripts/check-architecture.sh" ] && continue
bash "$script" --ci
done
```
---
## `/harness init` 执行流程
```
1. 运行 /harness assess 确定当前等级
2. 确定目标等级(用户指定或默认 Level 1
3. 如果当前等级 ≥ 目标等级,提示"已满足"
4. 按目标等级模板生成文件:
- Level 1: .husky/ + package.json lint-staged
- Level 2: scripts/ + docs/quality/ + CLAUDE.md 段落
- Level 3: CI workflow
5. 运行验证husky hook 是否生效、脚本是否可执行)
6. 输出文件清单和下一步建议
```

View File

@@ -0,0 +1,96 @@
# 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 中的正反示例

View File

@@ -0,0 +1,120 @@
# Convention Detection Script Template
泛化的约定检测脚本模板,基于 new-ai-proj `check-modal-safety.sh` 提取。用于检测代码中违反编码约定的模式。
## 使用方法
1. 复制下方脚本到 `scripts/check-{NAME}.sh`
2. 替换所有 `{PLACEHOLDER}` 为项目特定值
3. 运行 `chmod +x scripts/check-{NAME}.sh`
4. 验证:`./scripts/check-{NAME}.sh` 输出 PASSED
## 脚本模板
```bash
#!/usr/bin/env bash
#
# {CONVENTION_NAME} Check — detects {WHAT_IT_DETECTS} in {TARGET_SCOPE}.
#
# {PROBLEM_DESCRIPTION}
#
# Usage:
# ./scripts/check-{NAME}.sh # Check and report
# ./scripts/check-{NAME}.sh --ci # Exit code 1 on violations
#
set -euo pipefail
TARGET_DIR="$(cd "$(dirname "$0")/../{TARGET_DIRECTORY}" && pwd)"
CI_MODE=false
[[ "${1:-}" == "--ci" ]] && CI_MODE=true
violations=0
# ── detection logic ─────────────────────────────────────────────────
# Strategy A: Simple grep pattern (file-level)
# Find files matching a bad pattern, optionally excluding false positives.
#
# while IFS= read -r file; do
# rel_path="${file#"$TARGET_DIR"/}"
# echo "WARNING: $rel_path — {VIOLATION_MESSAGE}"
# violations=$((violations + 1))
# done < <(grep -rl '{BAD_PATTERN}' "$TARGET_DIR" --include='{FILE_GLOB}' 2>/dev/null \
# | while read f; do grep -L '{FALSE_POSITIVE_PATTERN}' "$f" 2>/dev/null; done)
# Strategy B: Line-level with context (multi-condition)
# Find a primary pattern, check nearby lines for secondary pattern.
#
# while IFS= read -r file; do
# while IFS= read -r line_num; do
# # Check context around the match
# has_mitigation=$(sed -n "${line_num},$((line_num + {CONTEXT_LINES}))p" "$file" \
# | grep -c '{MITIGATION_PATTERN}' || true)
# if [[ "$has_mitigation" -eq 0 ]]; then
# # Additional condition: check if problematic follow-up exists
# after_end=$((line_num + {LOOKAHEAD_LINES}))
# has_problem=$(sed -n "$((line_num + 1)),${after_end}p" "$file" \
# | grep -cE '{FOLLOW_UP_BAD_PATTERN}' || true)
# if [[ "$has_problem" -gt 0 ]]; then
# rel_path="${file#"$TARGET_DIR"/}"
# echo "WARNING: $rel_path:$line_num — {VIOLATION_MESSAGE}"
# violations=$((violations + 1))
# fi
# fi
# done < <(grep -n '{PRIMARY_PATTERN}' "$file" | cut -d: -f1)
# done < <(grep -rl '{PRIMARY_PATTERN}' "$TARGET_DIR" --include='{FILE_GLOB}' 2>/dev/null)
# ── results ─────────────────────────────────────────────────────────
if [[ "$violations" -gt 0 ]]; then
echo ""
echo "Found $violations {CONVENTION_NAME} violation(s)."
echo "Fix: {FIX_GUIDANCE}"
echo "Docs: see CLAUDE.md '{CLAUDE_MD_SECTION}'"
$CI_MODE && exit 1
else
echo "{CONVENTION_NAME} check PASSED."
fi
```
## Placeholder 说明
| Placeholder | 含义 | 示例 |
|-------------|------|------|
| `{NAME}` | 脚本短名 | `modal-safety`, `sql-injection` |
| `{CONVENTION_NAME}` | 约定显示名 | `Modal safety`, `SQL injection prevention` |
| `{WHAT_IT_DETECTS}` | 检测内容 | `potential modal overlap patterns` |
| `{TARGET_SCOPE}` | 目标范围描述 | `frontend code`, `Go handlers` |
| `{TARGET_DIRECTORY}` | 扫描目录 | `frontend/src`, `backend` |
| `{PROBLEM_DESCRIPTION}` | 问题描述 | `Ant Design Modal.success is non-blocking...` |
| `{FILE_GLOB}` | 文件匹配 | `*.tsx`, `*.go` |
| `{PRIMARY_PATTERN}` | 主匹配模式 | `Modal\.\(success\|info\)` |
| `{MITIGATION_PATTERN}` | 缓解模式(有则排除) | `onOk` |
| `{FOLLOW_UP_BAD_PATTERN}` | 后续违规模式 | `set\w*Open\(true\)` |
| `{CONTEXT_LINES}` | 上下文行数 | `25` |
| `{LOOKAHEAD_LINES}` | 前瞻行数 | `20` |
| `{BAD_PATTERN}` | Strategy A 主匹配模式 | `console\.log` |
| `{FALSE_POSITIVE_PATTERN}` | Strategy A 排除模式(匹配到则非违规) | `// eslint-disable` |
| `{VIOLATION_MESSAGE}` | 违规消息 | `Modal without onOk followed by setState` |
| `{FIX_GUIDANCE}` | 修复指导 | `add onOk callback to Modal` |
| `{CLAUDE_MD_SECTION}` | CLAUDE.md 章节名 | `Frontend: Modal 安全规则` |
## 两种检测策略
### Strategy A: 文件级检测
适用于:整个文件不应出现某模式(或出现即违规)。
例子:
- `console.log` 在生产代码中
- `TODO` / `FIXME` 计数
- 缺少 license header
### Strategy B: 行级 + 上下文检测
适用于:模式本身不违规,需要检查周围上下文。
例子:
- `Modal.success()` 不违规,但后面紧跟 `setState` 才违规
- `db.Exec()` 不违规,但参数用字符串拼接才违规
- `fmt.Println()` 不违规,但在 handler 中输出敏感数据才违规

View File

@@ -0,0 +1,102 @@
# GATES.md Template
项目质量门禁文档模板。放置于 `docs/quality/GATES.md`
## 使用方法
1. 复制下方模板到项目的 `docs/quality/GATES.md`
2. 替换 `{PLACEHOLDER}` 为项目特定值
3. 根据项目实际情况增删 Gate
## 模板
```markdown
# Quality Gates
Quality gates are automated checks that code must pass before it can be merged or deployed.
## Gate Overview
| Gate | Name | Where | What it catches |
|------|------|-------|-----------------|
| 0 | Pre-commit | Local (husky) | Formatting, lint errors |
| 1 | CI Lint | {CI_SYSTEM} | {LINT_TOOLS} |
| 2 | Convention & Architecture | {CI_SYSTEM} | New violations of established rules |
| 3 | Unit Tests | CI / local | Logic bugs, regressions |
| 4 | Security Scan | CI | Dependency vulnerabilities |
| 5 | Build | CI | Compilation errors |
## Gate 0: Pre-commit (Local)
Runs automatically on every `git commit` via husky + lint-staged.
{FRONTEND_SECTION — include if applicable}
**Frontend** (`{FRONTEND_GLOBS}`):
- {LINT_TOOL} with {LINT_FLAGS}
- {FORMATTER}
{BACKEND_SECTION — include if applicable}
**Backend** (`{BACKEND_GLOBS}`):
- {FORMAT_TOOL} (auto-format)
- {ANALYSIS_TOOL} (static analysis)
**Bypass** (emergency only): `git commit --no-verify`
## Gate 1: CI Lint
Runs on pull requests via {CI_SYSTEM}.
- **Backend**: {BACKEND_LINT_COMMANDS}
- **Frontend**: {FRONTEND_LINT_COMMANDS}
## Gate 2: Convention & Architecture Checks
Runs on pull requests via {CI_SYSTEM}.
{LIST_EACH_CHECK_SCRIPT}
- `./scripts/check-{name}.sh check` — {description}
See project CLAUDE.md and docs/architecture/ for rule details.
## Gates 3-5: Testing, Security, Build
These gates are enforced via the `/req test` workflow:
1. **Gate 3**: Unit test generation and execution
2. **Gate 4**: Security regression scan
3. **Gate 5**: E2E smoke test + final report
See the dev-test skill documentation for details.
## Local Commands
\`\`\`bash
# Gate 0: Format & Lint
{LOCAL_LINT_COMMANDS}
# Gate 2: Convention checks
{LOCAL_CHECK_COMMANDS}
# Gate 3: Unit tests
{LOCAL_TEST_COMMANDS}
\`\`\`
```
## Placeholder 说明
| Placeholder | 含义 | 示例 |
|-------------|------|------|
| `{CI_SYSTEM}` | CI 系统名称 | `Gitea Actions`, `GitHub Actions` |
| `{LINT_TOOLS}` | lint 工具列表 | `golangci-lint, ESLint, TypeScript errors` |
| `{FRONTEND_GLOBS}` | 前端文件 glob | `*.ts, *.tsx`, `*.vue` |
| `{BACKEND_GLOBS}` | 后端文件 glob | `*.go` |
| `{LINT_TOOL}` | lint 工具 | `ESLint` |
| `{LINT_FLAGS}` | lint 参数 | `--fix --max-warnings 0` |
| `{FORMATTER}` | 格式化工具 | `Prettier`, `gofmt` |
## 注意事项
- **此文档的 Gate 编号是项目级的**(描述项目基础设施层级),与 req-test-gate 技能的 Gate 0-5需求级测试门禁是两套独立体系
- Gate 0-2 由 harness engineering 方法论管理pre-commit → CI lint → ratchet/约定检测)
- Gate 3-5 由 `/req test` 流程管理(单元测试 → 安全扫描 → 构建验证)
- 两者互补,不要混淆编号

View File

@@ -0,0 +1,135 @@
# Pre-commit Configuration Template
husky + lint-staged 配置模板三种变体Node.js 项目、Go 项目、Monorepo。
---
## 变体 1: Node.js 项目React / Vue / 纯 TS
### 安装
```bash
npm install -D husky lint-staged prettier eslint
npx husky init
echo "npx lint-staged" > .husky/pre-commit
```
### package.json
```json
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix --max-warnings 0",
"prettier --write"
],
"*.{css,less,scss}": [
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
]
}
}
```
---
## 变体 2: Go 项目
### 安装
```bash
# Go 项目需要 package.json 来驱动 husky
npm init -y
npm install -D husky lint-staged
npx husky init
echo "npx lint-staged" > .husky/pre-commit
```
### package.json
```json
{
"lint-staged": {
"*.go": [
"gofmt -s -w",
"bash -c 'go vet ./...'"
]
}
}
```
### 可选golangci-lint更严格
```json
{
"lint-staged": {
"*.go": [
"gofmt -s -w",
"bash -c 'go vet ./...'",
"bash -c 'golangci-lint run --fix'"
]
}
}
```
---
## 变体 3: MonorepoGo + Node.js
### 安装(根目录)
```bash
npm install -D husky lint-staged
npx husky init
echo "npx lint-staged" > .husky/pre-commit
```
### package.json根目录
```json
{
"lint-staged": {
"frontend/**/*.{ts,tsx}": [
"bash -c 'cd frontend && npx eslint --fix --max-warnings 0'",
"bash -c 'cd frontend && npx prettier --write'"
],
"frontend/**/*.{css,less,json}": [
"bash -c 'cd frontend && npx prettier --write'"
],
"backend/**/*.go": [
"gofmt -s -w",
"bash -c 'cd backend && go vet ./...'"
]
}
}
```
### 注意事项
- `bash -c 'cd xxx && ...'` 确保命令在正确的子目录中执行
- lint-staged 传递的文件路径是相对于根目录的,部分工具(如 `go vet`)需要在子目录执行
- 如果前端和后端各自有 `package.json`lint-staged 仍然在根目录配置
---
## 验证配置
```bash
# 修改一个文件后测试 pre-commit hook
echo "// test" >> some-file.ts
git add some-file.ts
git commit -m "test: verify pre-commit hook"
# 应该看到 lint-staged 执行 ESLint + Prettier
# 如果有 lint 错误commit 会被阻止
```
## 常见问题
| 问题 | 原因 | 解决 |
|------|------|------|
| lint-staged 不执行 | `.husky/pre-commit` 内容不对 | 确认内容为 `npx lint-staged` |
| ESLint 报错太多 | 旧项目首次启用 | 先 `npx eslint --fix` 全量修复,再启用 hook |
| gofmt 修改后 commit 内容不一致 | lint-staged 自动 format 后需要 re-stage | lint-staged 自动处理,无需手动 |
| Monorepo 路径错误 | lint-staged 传递根目录相对路径 | 使用 `bash -c 'cd sub && ...'` 包装 |

View File

@@ -0,0 +1,189 @@
# Ratchet Script Template
泛化的 ratchet 脚本模板,基于 new-ai-proj `check-architecture.sh` 提取。
## 使用方法
1. 复制下方脚本到 `scripts/check-{NAME}.sh`
2. 替换所有 `{PLACEHOLDER}` 为项目特定值
3. 运行 `chmod +x scripts/check-{NAME}.sh`
4. 运行 `./scripts/check-{NAME}.sh baseline` 记录首次基线
5. 提交脚本和基线文件
## 脚本模板
```bash
#!/usr/bin/env bash
#
# {DESCRIPTION} Ratchet — ensures new code doesn't increase {WHAT} violations.
#
# Usage:
# ./scripts/check-{NAME}.sh check # CI / pre-push: fail if violations increased
# ./scripts/check-{NAME}.sh baseline # Record current counts (commit the result)
# ./scripts/check-{NAME}.sh report # Pretty-print current vs baseline
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BASELINE_FILE="$SCRIPT_DIR/.{NAME}-baseline.json"
TARGET_DIR="$SCRIPT_DIR/../{TARGET_DIRECTORY}"
# ── rule definitions ────────────────────────────────────────────────
# Add/remove rules as needed. Each rule has:
# - A name (used as JSON key)
# - A type: "ratchet" (baseline can decrease) or "wall" (must be 0)
# - A count function
RULES=({RULE_NAMES}) # e.g. ("rule_one" "rule_two" "rule_three")
TYPES=({RULE_TYPES}) # e.g. ("ratchet" "ratchet" "wall")
# ── counting functions ──────────────────────────────────────────────
count_rule() {
local rule="$1"
case "$rule" in
{RULE_NAME_1})
# Example: count files in {DIR} that import {PACKAGE}
grep -rl '{PATTERN_1}' "$TARGET_DIR/{SUBDIR_1}/" 2>/dev/null | wc -l | tr -d ' '
;;
{RULE_NAME_2})
grep -rl '{PATTERN_2}' "$TARGET_DIR/{SUBDIR_2}/" 2>/dev/null | wc -l | tr -d ' '
;;
# Add more rules...
*)
echo "0"
;;
esac
}
# ── helpers ─────────────────────────────────────────────────────────
current_counts() {
echo "{"
local first=true
for rule in "${RULES[@]}"; do
local count
count=$(count_rule "$rule")
$first || echo ","
printf ' "%s": %s' "$rule" "${count:-0}"
first=false
done
echo ""
echo "}"
}
json_val() {
local key="$1" file="$2"
grep "\"${key}\"" "$file" | head -1 | sed 's/[^0-9]//g'
}
json_val_str() {
local key="$1" json="$2"
echo "$json" | grep "\"${key}\"" | head -1 | sed 's/[^0-9]//g'
}
# ── commands ────────────────────────────────────────────────────────
cmd_baseline() {
echo "Recording {NAME} baseline..."
current_counts > "$BASELINE_FILE"
echo "Baseline written to $BASELINE_FILE"
cat "$BASELINE_FILE"
}
cmd_report() {
if [[ ! -f "$BASELINE_FILE" ]]; then
echo "No baseline found. Run: $0 baseline"
exit 1
fi
local current
current=$(current_counts)
printf "\n%-35s %10s %10s %8s\n" "Rule" "Baseline" "Current" "Status"
printf "%-35s %10s %10s %8s\n" "---" "---" "---" "---"
for i in "${!RULES[@]}"; do
local rule="${RULES[$i]}"
local type="${TYPES[$i]}"
local base cur status
base=$(json_val "$rule" "$BASELINE_FILE")
cur=$(json_val_str "$rule" "$current")
if [[ "$type" == "wall" ]]; then
[[ "$cur" -eq 0 ]] && status="PASS" || status="FAIL"
else
[[ "$cur" -le "$base" ]] && status="PASS" || status="FAIL"
fi
printf "%-35s %10d %10d %8s\n" "$rule" "$base" "$cur" "$status"
done
echo ""
}
cmd_check() {
if [[ ! -f "$BASELINE_FILE" ]]; then
echo "ERROR: No baseline found. Run: $0 baseline"
exit 1
fi
local current
current=$(current_counts)
local failed=0
for i in "${!RULES[@]}"; do
local rule="${RULES[$i]}"
local type="${TYPES[$i]}"
local base cur
base=$(json_val "$rule" "$BASELINE_FILE")
cur=$(json_val_str "$rule" "$current")
if [[ "$type" == "wall" ]]; then
if [[ "$cur" -ne 0 ]]; then
echo "FAIL [hard wall] $rule: expected 0, got $cur"
failed=1
fi
else
if [[ "$cur" -gt "$base" ]]; then
echo "FAIL [ratchet] $rule: baseline=$base, current=$cur (+$((cur - base)))"
failed=1
fi
fi
done
if [[ "$failed" -eq 1 ]]; then
echo ""
echo "{NAME} check FAILED. New violations detected."
echo "If this is intentional, update the baseline: $0 baseline"
exit 1
fi
echo "{NAME} check PASSED."
}
# ── main ────────────────────────────────────────────────────────────
case "${1:-check}" in
baseline) cmd_baseline ;;
report) cmd_report ;;
check) cmd_check ;;
*)
echo "Usage: $0 {check|baseline|report}"
exit 1
;;
esac
```
## Placeholder 说明
| Placeholder | 含义 | 示例 |
|-------------|------|------|
| `{NAME}` | 脚本短名 | `architecture`, `import-rules` |
| `{DESCRIPTION}` | 一句话描述 | `Architecture`, `Import Boundary` |
| `{TARGET_DIRECTORY}` | 扫描目标目录(相对于 scripts/ | `backend`, `frontend/src` |
| `{RULE_NAMES}` | Bash 数组,规则名列表 | `"handler_db" "route_db"` |
| `{RULE_TYPES}` | Bash 数组,规则类型列表 | `"ratchet" "wall"` |
| `{PATTERN_N}` | grep 匹配模式 | `"my-pkg/database"` |
| `{SUBDIR_N}` | 目标子目录 | `handlers/`, `routes/` |