claude-harness/tests/test-hook-corpus.sh
andrey.epifantsev 3d2ce3f41d fix(retro): финальное ревью — сабагенты, период по записям, разбор Bash, redact до обрезки, --from (#29)
- C-1: транскрипты сабагентов (<сессия>/subagents/*.jsonl) вливаются в
  сессию-родителя — ходы, токены, инструменты, codegraph, блокировки,
  отказы; промпты, роль, стартовый контекст — только из основного.
  hook_blocks_export.py выгружает их блокировки с сессией-родителем.
- I-7: период режется по меткам времени записей; сессия на стыке
  делится между ретро, стартовый контекст — если первый ход в периоде.
- I-1: простые команды Bash — shlex с punctuation_chars, «|» в кавычках
  команду не делит. I-2: правило src/ ловит и сам каталог src.
- I-3: clip() — redact до обрезки (кандидаты, выгрузка), роль — redact.
- I-4: без --since период берётся из существующего --out; --from рисует
  «Цифры» из сохранённого среза. SKILL.md передаёт период явно.
- I-5: выгрузка ретро без разметки — сборка корпуса падает.
- I-6: сводка компакции — не промпт; «перечитай» — побуждение перечитать.
- 2026-10-11.metrics.json: несравнимое помечено (без сабагентов, широкий
  «перечитай»). Спека и design.md (Decisions) обновлены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:59:41 +04:00

65 lines
5.2 KiB
Bash
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.

# hook_corpus.py: регрессионный корпус хуков (B2) через источники hooks/ — assert на запись.
R="$ROOT/skills/harness-doctor/scripts/hook_corpus.py"
F="$ROOT/skills/harness-doctor/fixtures/hook-cases.jsonl"
assert_file "hook_corpus.py существует" "$R"
assert_file "корпус hook-cases.jsonl" "$F"
assert_eq "корпус совпадает со сборкой из выгрузки" "$(python3 "$ROOT/docs/retro/scripts/hook_cases_build.py" | cmp -s - "$F" && echo same)" "same"
while IFS=$'\t' read -r id why exp got st cmd; do
case "$id" in total*) continue ;; esac
case "$st" in
ok|KNOWN) ok "корпус $id ($why)" ;;
*) fail "корпус $id ($why): $cmd" "ожидалось $exp, получено $got ($st)" ;;
esac
done < <(python3 "$R" "$ROOT/hooks")
# сегменты и known (#29): синтетический корпус из двух сегментов с разными home
S="$TMP/seg"; mkdir -p "$S"
cat > "$S/c.jsonl" <<'EOF'
{"home": "/h1", "layout": [{"path": "m", "branch": "main"}]}
{"id": "a1", "why": "main", "tool": "Write", "file_path": "/h1/m/a.md", "cwd": "/h1/m", "expected": "block"}
{"id": "a2", "why": "known-x", "tool": "Write", "file_path": "/h1/m/b.md", "cwd": "/h1/m", "expected": "allow", "known": "RT-21"}
{"home": "/h2", "layout": [{"path": "r", "branch": "feat/x"}]}
{"id": "b1", "why": "branch", "tool": "Write", "file_path": "/h2/r/a.md", "cwd": "/h2/r", "expected": "allow"}
EOF
out=$(python3 "$R" "$ROOT/hooks" --summary --fixtures "$S/c.jsonl"); rc=$?
assert_eq "два сегмента и известная ошибка: exit 0" "$rc" "0"
assert_eq "итог с известными ошибками" "$out" "корпус хуков: 3/3, известных ошибок 1"
sed 's/"expected": "allow", "known"/"expected": "block", "known"/' "$S/c.jsonl" > "$S/f.jsonl"
out=$(python3 "$R" "$ROOT/hooks" --summary --fixtures "$S/f.jsonl"); rc=$?
assert_eq "known совпал с вердиктом: exit 1" "$rc" "1"
assert_grep "KNOWN-FIXED с id записи" '^KNOWN-FIXED a2' <(printf '%s' "$out")
# разметка ретро (#29): hook-labels-<дата>.json + hook-blocks-<дата>.jsonl → новый сегмент корпуса
B="$ROOT/docs/retro/scripts/hook_cases_build.py"
FX="$TMP/fx"; mkdir -p "$FX"; cp "$ROOT/docs/retro/fixtures/hook-blocks.jsonl" "$FX/"
cat > "$FX/hook-blocks-2026-11-01.jsonl" <<'EOF'
{"id": "2026-11-01-001", "session": "s", "ts": "2026-11-02T10:00:00Z", "cwd": "/h3/r", "branch": "main", "tool": "Bash", "command": "ls x 2>/dev/null", "file_path": null, "hook": "guard-main(Bash)", "message": "BLOCKED", "expected": null}
{"id": "2026-11-01-002", "session": "s", "ts": "2026-11-02T10:01:00Z", "cwd": "/h3/r", "branch": "main", "tool": "Bash", "command": "echo x > f", "file_path": null, "hook": "guard-main(Bash)", "message": "BLOCKED", "expected": null}
{"id": "2026-11-01-003", "session": "s", "ts": "2026-11-02T10:02:00Z", "cwd": "/h3/r", "branch": "main", "tool": "Bash", "command": "cat $X", "file_path": null, "hook": "guard-main(Bash)", "message": "BLOCKED", "expected": null}
EOF
cat > "$FX/hook-labels-2026-11-01.json" <<'EOF'
{"home": "/h3", "layout": [{"path": "r", "branch": "main"}],
"cases": {"2026-11-01-001": {"expected": "allow", "why": "devnull"},
"2026-11-01-002": {"expected": "block", "why": "main-redirect"},
"2026-11-01-003": {"skip": "цель в переменной, состояние неизвестно"}}}
EOF
out=$(python3 "$B" --fixtures-dir "$FX"); rc=$?
assert_eq "сборка с разметкой ретро: exit 0" "$rc" "0"
assert_eq "в корпусе два сегмента" "$(printf '%s\n' "$out" | grep -c '"layout"')" "2"
assert_eq "сегмент ретро: две записи" "$(printf '%s\n' "$out" | grep -c '"2026-11-01-00[12]"')" "2"
assert_eq "запись со skip не в корпусе" "$(printf '%s\n' "$out" | grep -c '2026-11-01-003')" "0"
printf '%s\n' "$out" > "$FX/cases.jsonl"
assert_eq "корпус с сегментом ретро проходит хуки" "$(python3 "$R" "$ROOT/hooks" --summary --fixtures "$FX/cases.jsonl" | tail -1)" "корпус хуков: 131/131"
python3 - "$FX/hook-labels-2026-11-01.json" <<'PY'
import json, sys
p = sys.argv[1]; d = json.load(open(p)); del d["cases"]["2026-11-01-002"]; d["cases"]["2026-11-01-009"] = {"skip": "x"}
json.dump(d, open(p, "w"))
PY
out=$(python3 "$B" --fixtures-dir "$FX" 2>&1 >/dev/null); rc=$?
assert_eq "разметка не совпадает с выгрузкой: exit 1" "$rc" "1"
assert_grep "сообщение называет недостающий и лишний id" '2026-11-01-002.*2026-11-01-009' <(printf '%s' "$out")
# I-5: выгрузка ретро без парной разметки — сборка падает, а не пропускает её молча
FX2="$TMP/fx2"; mkdir -p "$FX2"; cp "$ROOT/docs/retro/fixtures/hook-blocks.jsonl" "$FX2/"
head -1 "$FX/hook-blocks-2026-11-01.jsonl" > "$FX2/hook-blocks-2026-11-07.jsonl"
out=$(python3 "$B" --fixtures-dir "$FX2" 2>&1 >/dev/null); rc=$?
assert_eq "выгрузка без разметки: exit 1" "$rc" "1"
assert_grep "сообщение называет файл выгрузки" 'hook-blocks-2026-11-07\.jsonl' <(printf '%s' "$out")