- 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>
36 lines
5.6 KiB
Bash
36 lines
5.6 KiB
Bash
# hook_blocks_export.py: выгружаются только ошибки хуков, а не вывод команд со словом BLOCKED.
|
||
X="$ROOT/docs/retro/scripts/hook_blocks_export.py"
|
||
T="$TMP/transcripts"; mkdir -p "$T"
|
||
cat > "$T/s1.jsonl" <<'EOF'
|
||
{"type":"assistant","cwd":"/r","gitBranch":"main","timestamp":"t1","message":{"content":[{"type":"tool_use","id":"u1","name":"Bash","input":{"command":"echo x > f"}}]}}
|
||
{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"u1","is_error":true,"content":"PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED [guard-main]: запись в f — репозиторий /r на main."}]}}
|
||
{"type":"assistant","cwd":"/r","gitBranch":"main","timestamp":"t2","message":{"content":[{"type":"tool_use","id":"u2","name":"Bash","input":{"command":"cat hooks/require-plan.sh"}}]}}
|
||
{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"u2","content":"echo \"BLOCKED: editing $fpath without a plan. Run superpowers:brainstorming\" >&2"}]}}
|
||
{"type":"assistant","cwd":"/r","gitBranch":"main","timestamp":"t3","message":{"content":[{"type":"tool_use","id":"u3","name":"Edit","input":{"file_path":"/r/a.md"}}]}}
|
||
{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"u3","is_error":true,"content":[{"type":"text","text":"PreToolUse:Edit hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: you are on main. Create an isolated worktree first"}]}]}}
|
||
EOF
|
||
out=$(python3 "$X" "$T")
|
||
assert_eq "две записи: вывод cat с BLOCKED не выгружен" "$(printf '%s\n' "$out" | grep -c .)" "2"
|
||
assert_eq "новый префикс → guard-main(Bash)" "$(printf '%s\n' "$out" | sed -n 1p | python3 -c 'import json,sys; r=json.load(sys.stdin); print(r["hook"], r["command"])')" "guard-main(Bash) echo x > f"
|
||
assert_eq "старая фраза → guard-main(Edit/Write)" "$(printf '%s\n' "$out" | sed -n 2p | python3 -c 'import json,sys; r=json.load(sys.stdin); print(r["hook"], r["file_path"])')" "guard-main(Edit/Write) /r/a.md"
|
||
# --since и --id-prefix (B12, #29): только сессии не раньше даты, id <дата>-NNN; модуль импортируется без выгрузки
|
||
T2="$TMP/tr2"; mkdir -p "$T2"
|
||
blk='{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"u1","is_error":true,"content":"PreToolUse:Bash hook error: BLOCKED [guard-main]: запись в f"}]}}'
|
||
printf '%s\n%s\n' '{"type":"assistant","cwd":"/r","gitBranch":"main","timestamp":"2026-10-01T10:00:00Z","message":{"content":[{"type":"tool_use","id":"u1","name":"Bash","input":{"command":"echo a > f"}}]}}' "$blk" > "$T2/old.jsonl"
|
||
printf '%s\n%s\n' '{"type":"assistant","cwd":"/r","gitBranch":"main","timestamp":"2026-11-02T10:00:00Z","message":{"content":[{"type":"tool_use","id":"u1","name":"Bash","input":{"command":"echo b > f"}}]}}' "$blk" > "$T2/new.jsonl"
|
||
# C-1: блокировка сабагента — в выгрузку сессии-родителя
|
||
mkdir -p "$T2/new/subagents"
|
||
printf '%s\n%s\n' '{"type":"assistant","isSidechain":true,"cwd":"/r","gitBranch":"main","timestamp":"2026-11-02T11:00:00Z","message":{"content":[{"type":"tool_use","id":"u1","name":"Bash","input":{"command":"echo c > f"}}]}}' "$blk" > "$T2/new/subagents/agent-x.jsonl"
|
||
# I-7: длинная сессия на стыке — блок до периода не выгружается, в периоде — выгружается; I-3: секрет на границе обрезки
|
||
lblk='{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"u2","is_error":true,"content":"PreToolUse:Bash hook error: BLOCKED [guard-main] '"$(printf 'z%.0s' $(seq 235))"' fedcba9876543210fedcba9876543210fedcba98"}]}}'
|
||
printf '%s\n%s\n%s\n%s\n' '{"type":"assistant","cwd":"/r","gitBranch":"main","timestamp":"2026-10-01T10:00:00Z","message":{"content":[{"type":"tool_use","id":"u1","name":"Bash","input":{"command":"echo k > f"}}]}}' "$blk" '{"type":"assistant","cwd":"/r","gitBranch":"main","timestamp":"2026-11-03T10:00:00Z","message":{"content":[{"type":"tool_use","id":"u2","name":"Bash","input":{"command":"echo l > f"}}]}}' "$lblk" > "$T2/long.jsonl"
|
||
out=$(python3 "$X" --since 2026-11-01 --id-prefix 2026-11-05 "$T2")
|
||
assert_eq "--since по записям, сабагенты — в выгрузке, id <дата>-NNN" "$(printf '%s\n' "$out" | python3 -c 'import json,sys; print([(r["id"], r["session"], r["command"]) for r in map(json.loads, sys.stdin)])')" "[('2026-11-05-001', 'long.jso', 'echo l > f'), ('2026-11-05-002', 'new.json', 'echo b > f'), ('2026-11-05-003', 'new.json', 'echo c > f')]"
|
||
assert_eq "секрет на границе обрезки сообщения не утекает" "$(printf '%s\n' "$out" | python3 -c '
|
||
import sys
|
||
out, t = sys.stdin.read(), "fedcba9876543210fedcba9876543210fedcba98"
|
||
print(sum(t[i:i + 8] in out for i in range(len(t) - 7)))')" "0"
|
||
assert_eq "старая обрезка до redact утекла бы (проверка теста)" "$(python3 -c "import sys; sys.path.insert(0, '$ROOT/docs/retro/scripts'); import hook_blocks_export as h; m='PreToolUse:Bash hook error: BLOCKED [guard-main] ' + 'z'*235 + ' fedcba9876543210fedcba9876543210fedcba98'; print('fedcba98' in h.redact(m[:300]), 'fedcba98' in h.clip(m, 300))")" "True False"
|
||
assert_eq "без флагов поля id нет (выгрузка 12.10 не меняется)" "$(python3 "$X" "$T2" | python3 -c 'import json,sys; print(sum("id" in json.loads(l) for l in sys.stdin))')" "0"
|
||
assert_eq "импорт модуля не выгружает" "$(python3 -c "import sys; sys.argv=['x', '$T2']; sys.path.insert(0, '$ROOT/docs/retro/scripts'); import hook_blocks_export" 2>&1)" ""
|