Files
John Qiu b9c808cce0 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>
2026-03-26 11:34:42 +10:30

190 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/` |