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

@@ -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/` |