diff --git a/CLAUDE.md b/CLAUDE.md index f73933c..db1e894 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,14 +38,15 @@ - Правишь разбор или вердикт — сначала случай в `test-hook-parse`/`test-hook-guard-main`; меняется ожидаемый вердикт реальной команды — разметка в `hook_cases_build.py` и пересборка корпуса. ## Текущая работа -Бэклог обвязки — `docs/retro/2026-10-11.md`, раздел 4 (B1–B12), решения владельца — раздел 5. Находки ретро маркируются `RT-n`. +Бэклог обвязки — `docs/retro/2026-10-11.md`, раздел 4 (B1–B12), и `docs/retro/2026-10-24.md`, раздел 5 (B13–B17, принят владельцем 24.10.2026); решения владельца — раздел 5 и раздел 6 соответственно. Находки ретро маркируются `RT-n`. - Сделано: B5+B6 часть 1 — слой ролей (PR #4, 0.3.0). Часть 2 (awp-ca) — issue #2, отложенные находки — issue #5. - Сделано: B1+B2 и отмена исключения openspec-only для guard-main — хуки по цели записи и корпус хуков (issue #6, PR #12, 0.4.0), спека — `openspec/specs/hook-guards/`. Отложенные находки — issue #13. - Сделано: B3 — allow-правило `git push origin --delete` печатают adopt/new-project, проверяет doctor (issue #22, 0.4.2). B4 закрыт в #6 (решение владельца 1). B10 — workspace-хук awp-ca, в части 2 issue #2. - Сделано: allow-правила цикла фичи ставит `install.sh` (issue #24, 0.5.0; пересмотр решения владельца 2). - Сделано: worktree — `EnterWorktree` в `/.claude/worktrees/`, вход без подтверждения (issue #25, 0.5.1). Для awp-ca вопрос открыт в части 2 issue #2. - Сделано: B12 — `/harness-retro`: срез метрик, поправки владельца, хвосты закрытия, корпус по сегментам (issue #29, 0.6.0), спека — `openspec/specs/harness-retro/`. -- Дальше: первое ретро `/harness-retro` по полигону — на машине с awp-ca; остальные пункты бэклога — по мере надобности. +- Сделано: первое ретро `/harness-retro` — по cc-harness (issue #33, `docs/retro/2026-10-24.md`), находки RT-21…RT-25. +- Дальше: B13 (поправки в записях с `tool_result`), B14 (советы по настройке — с проверкой по документации), B15 (рецепт команд в изолированной сессии), B16 (#31), B17 (ретро по awp-ca на той машине). ## OpenSpec и superpowers - Вход в любую задачу — superpowers:brainstorming. Skills openspec-propose и openspec-explore сами по себе не запускать. diff --git a/docs/retro/2026-10-24.md b/docs/retro/2026-10-24.md new file mode 100644 index 0000000..7950ca3 --- /dev/null +++ b/docs/retro/2026-10-24.md @@ -0,0 +1,154 @@ +# Ретроспектива обвязки claude-harness — 24.10.2026 + +Период: 2026-10-11 — 2026-10-24, полигон — репозиторий `cc-harness` на машине разработчика обвязки. +Предыдущая точка сравнения — `docs/retro/2026-10-11.md` (полигон awp-ca, другая машина). + +Источники: +- 2 сессии Claude Code (`-Users-andrejepifancev-Projects-cc-harness`, `…--claude-worktrees-retro-2026-10-24`), 69 МБ. Одна из них идёт с 15.10 по 24.10 — вся работа над #6, #19, #22, #24, #25, #29 — с одной компакцией; +- auto-memory проекта — пусто, 0 файлов; +- состояние репозитория (`retro_leftovers.py --fetch`); +- блокировки среза, размеченные в корпус: `docs/retro/fixtures/hook-labels-2026-10-24.json`, 6 записей. + +Количественная часть посчитана `docs/retro/scripts/retro_metrics.py` (первый прогон `/harness-retro`), качественная — по кандидатам в поправки, выгрузкам и истории задач в Gitea. + +Маркировка находок: `RT-n`, продолжение нумерации ретро 11.10. Приоритет: P1 — теряет время каждую неделю или ломает процесс; P2 — трение; P3 — наблюдение. + +**О сравнимости.** 11.10 — workspace awp-ca, 44 сессии; сейчас — cc-harness, 2 сессии. Абсолютные числа несравнимы. Сравнимы: +- доли (ложные блокировки, explore); +- стартовый контекст на сессию; +- наличие хвостов закрытия. + +Строки 11.10, которые считались иначе, помечены в таблице: по строкам транскрипта, без сабагентов, широким регэкспом. + +## 1. Цифры + +| Показатель | Сейчас | Прошлое ретро | Δ | +|---|---|---|---| +| Сессий | 2 | 44 | -42 | +| Промптов владельца | 43 | 613 | -570 | +| Прерываний | 2 | — | — | +| Ходов модели | 832 | 12 121 (по строкам транскрипта, не по сообщениям) | — | +| Вызовов инструментов | 1 200 | 6 583 (без сабагентов) | — | +| Запусков сабагентов | 6 | 61 | -55 | +| Токенов вывода | 928 148 | 18 300 000 (по строкам транскрипта) | — | +| Чтение из кэша, токенов | 287 121 430 | 3 820 000 000 (по строкам транскрипта) | — | +| Запись в кэш, токенов | 3 509 921 | 145 000 000 (по строкам транскрипта) | — | +| Компакций контекста | 1 | 4 | -3 | +| Стартовый контекст, медиана | 48 484 | 56 000 | -7 516 | +| Стартовый контекст, мин | 44 149 | 52 000 | -7 851 | +| Стартовый контекст, макс | 52 820 | 61 000 | -8 180 | +| Блокировок хуков | 6 | 76 (без сабагентов) | — | +| Ложных блокировок | 5 | 72 | -67 | +| Доля ложных блокировок | 0.83 | 0.95 | -0.12 | +| Ложных, уже исправлено | 5 | 72 | -67 | +| Ложных, ещё не исправлено | 0 | 0 | +0 | +| Отказов классификатора | 4 | 6 (без сабагентов) | — | +| Отказов владельца | 3 | 6 | -3 | +| codegraph init | 10 | 43 (без сабагентов) | — | +| Вызовов codegraph_explore | 4 | 15 (без сабагентов) | — | +| Чтений кода (Read/Grep/Glob/Bash) | 50 | — | — | +| Доля codegraph_explore | 0.07 | — | — | +| Кандидатов в поправки владельца | 4 | — | — | +| Промптов «перечитай память/контекст» | 0 | 9 (широкий регэксп, со сводками компакции) | — | +| MEMORY.md, байт | 0 | — | — | +| Файлов памяти | 0 | 47 | -47 | +| Память, байт | 0 | 183 296 | -183 296 | +| cd в основной checkout на main | — | — | не считается: хуки 0.4.0 не зависят от cwd (RT-2); считалось по именам репозиториев awp-ca, было 84 | + +- Инструменты: Bash 675, Edit 245, Write 108, Read 93, Skill 17, EnterWorktree 12, AskUserQuestion 11, ExitWorktree 10, WebFetch 10, Agent 6 +- Роли: dev 2 +- Сабагенты по моделям: не указана 4, opus 2 +- Skills: superpowers:brainstorming 5, role 2, superpowers:writing-plans 2, superpowers:executing-plans 2, superpowers:test-driven-development 2, superpowers:finishing-a-development-branch 2, harness-doctor 1, harness-retro 1 +- Блокировки по хукам: guard-main(Bash) 5, guard-main(Edit/Write) 1 +- Отказы классификатора: Self-Modification 4 + +Период 2026-10-11 — 2026-10-24; сессий 2; каталоги транскриптов: -Users-andrejepifancev-Projects-cc-harness, -Users-andrejepifancev-Projects-cc-harness--claude-worktrees-retro-2026-10-24; битых строк 0. + +**Как читать блокировки.** Все пять ложных случились до установки хуков 0.4.0 на эту машину (23.10, 19:06): четыре — 15.10, одна — 23.10 в 19:05. После установки за срез одна блокировка, и она настоящая: ручная проверка Write в main. Все пять ложных текущие хуки пропускают, и все шесть теперь записи корпуса — 135/135. Ложных после 0.4.0: 0 из 1. + +**Подсчёт вне скрипта** — `docs/retro/scripts` его пока не умеет: +- 22 отказа изоляции worktree Claude Code («This session is isolated in the worktree…»): 13 — 15.10, 2 — 16.10, 7 — 23.10; +- 2 промпта владельца, отправленных вместе с результатом инструмента; скрипт их не видит (RT-23). + +Хвосты чеклиста закрытия: + +Хвостов закрытия нет. + +## 2. Находки + +**RT-21 (P1, fixed). Шаги цикла фичи спрашивали подтверждение у владельца.** +- Что было: вход в worktree `../.wt/` спрашивал разрешение при каждом входе. Шаги `tea issues/pulls create/comments`, push фичевой ветки и удаление веток не были покрыты allow-правилами. +- Две поправки владельца подряд: «почему мне нужно это каждый раз подтверждать?» и «но опять спрашивает разрешение». +- Исправлено: + - #24 (0.5.0): `install.sh` ставит allow-правила цикла фичи; + - #25 (0.5.1): worktree живут в `/.claude/worktrees/` и открываются через `EnterWorktree` без запроса. +- После 0.5.1 входы в worktree шли без подтверждений. +- Источники: кандидаты в поправки, PR #26, #27. + +**RT-22 (P2). Советы по настройке Claude Code и внешних инструментов давались без проверки по документации.** За срез три таких совета, и каждый стоил владельцу лишнего круга: +- `brew install codegraph` — такой формулы нет, это ошибка в `harness-doctor/SKILL.md` (O-8, исправлено в #19); +- пользовательские правила в `~/.claude/settings.local.json` — такого пользовательского файла в иерархии настроек нет (O-13); +- allow-правило `EnterWorktree` — запрос при входе вне `.claude/worktrees/` оно не снимает (O-14). + +Каждый раз ошибку нашли только после проверки агентом `claude-code-guide` по документации. Источники: PR #20, #26, ledger #29. + +**RT-23 (P2). `retro_metrics.py` не видит промпт владельца, отправленный вместе с результатом инструмента.** +- Если владелец пишет, пока висит окно разрешения или работает инструмент, Claude Code пишет его текст в ту же запись, что и `tool_result`. Определение спеки «сообщение без `tool_result`» такую запись отбрасывает. +- В этом срезе пропущены 2 из 5 поправок по процессу — обе по RT-21, они найдены вручную. Число промптов тоже занижено на 2. +- Источник: прогон `--corrections`, сверка с транскриптом. + +**RT-24 (P2). Изоляция worktree Claude Code отказывает командам, где слово `git` стоит в «сложном» тексте.** +- Где: heredoc, `$VAR`-аргументы, `python3 -c`, `bash` с переменной пути, `git -C` в основной checkout. +- 22 отказа за срез, 13 из них в первый день работы в worktree. +- Обход, найденный опытным путём: скрипты в файлах, литеральные пути, по одной команде; `tea comments` с телом из файла — через скрипт-обёртку. +- Нигде в обвязке это не описано (O-5, #13). Код не наш, но рецепт — наш. +- Источник: подсчёт по транскриптам. + +**RT-25 (P3, наблюдение к решению владельца 3). Доля `codegraph_explore` — 0.07: 4 вызова против 50 чтений кода.** +- В cc-harness codegraph индексирует только 9 Python-файлов. Bash — хуки-обёртки, `install.sh`, `doctor.sh`, тесты — в индекс не попадает (O-11, #21). Поэтому низкая доля здесь ожидаема и о пользе codegraph не говорит. +- Решающий замер — ретро по awp-ca. +- Источник: метрика codegraph. + +## 3. Поправки владельца + +| Вид | Сколько | Пример (цитата, что было до неё) | Что вышло | Находка | +|---|---|---|---|---| +| Лишнее подтверждение на шаге процесса | 2 | «почему мне нужно это каждый раз подтверждать?», «но опять спрашивает разрешение» — после `EnterWorktree` в `../cc-harness.wt/` | allow-правила в `install.sh` (#24), worktree в `.claude/worktrees/` (#25) | RT-21 | +| Работа переложена на владельца | 1 | «сделай сам» — после «Что осталось вам: запустите `bash install.sh`» | агент поставил сам; признак «сделай сам» добавлен в `--corrections` (H-1, #29) | — (1 раз) | +| Варианты в окне вопроса без пояснения | 1 | прерывание и «повтори варианты опять» — после `AskUserQuestion` о месте worktree | варианты повторены текстом с плюсами и минусами, выбран вариант 1 | — (1 раз) | + +Два прерывания из `--corrections` к поправкам по процессу не относятся. Одно — пауза в brainstorming 15.10, после неё «продолжаю». Второе — то же событие, что «повтори варианты опять». + +## 4. Что сработало (сохранить) + +- **Хуки 0.4.0.** После установки ни одной ложной блокировки. Корпус ловит регрессии: каждый ложный блок среза стал записью корпуса, 135/135. +- **Чеклист закрытия.** Выполнялся целиком после каждого merge: архив отдельным PR, follow-up issue, удаление веток из worktree. Хвостов на конец среза нет — RT-3 из ретро 11.10 больше не воспроизводится. +- **Финальное ревью всей ветки на opus.** Находило то, чего не видели тесты плана: + - в #6 — 2 Important (`checkout main && … && push`, префиксы с опциями); + - в #29 — C-1 (сабагенты не читались, замер codegraph был бы пустым) и 7 Important. +- **Живые прогоны на реальных транскриптах до ревью.** Нашли O-18 (синтетическое сообщение об ошибке API давало стартовый контекст 0) и O-19 (рабочий каталог активного плана как хвост). +- **Одна длинная сессия.** 10 дней работы, одна компакция. Работа продолжалась по сводке, ledger и issue без потери места. +- **Стартовый контекст** — 44–53 К токенов против 52–61 К на 11.10. + +## 5. Бэклог улучшений (по приоритету) + +| # | Что | Находки | Оценка | +|---|---|---|---| +| B13 | `retro_metrics.py`: считать текстовые блоки промпта владельца и в записях с `tool_result`; поправить определение в спеке `harness-retro` | RT-23 | 0,5 дня | +| B14 | `roles/dev.md`: совет о настройке Claude Code, правах или установке внешнего CLI — сначала сверить по документации (агент `claude-code-guide`) или явно пометить как непроверенный | RT-22 | 0,25 дня | +| B15 | `HARNESS.md`: рецепт команд в изолированной сессии — скрипты в файлах, литеральные пути, одна команда на вызов, обёртка для `tea comments` из файла | RT-24 | 0,25 дня | +| B16 | Отложенные мелкие находки `/harness-retro` | #31 (M-1…M-13) | 1 день | +| B17 | Ретро по awp-ca — решающий замер codegraph (решение владельца 3) | RT-25 | на машине с полигоном | + +## 6. Решения владельца (24.10.2026) + +1. **Бэклог B13–B17 принят** в предложенном порядке (ревью PR #34). +2. Где жить правилу B14 — в `roles/dev.md` или в глобальном `CLAUDE.md` — решается при его реализации. По умолчанию — `roles/dev.md`: глобальный `CLAUDE.md` уже 3058 Б при пороге 3 КБ. + +## 7. Что не измерялось + +- Сторона Gitea чеклиста закрытия: открытые issue после слитого PR, пропущенные follow-up. На конец среза по Gitea их нет, проверено вручную. +- Промпты владельца в записях с `tool_result` (RT-23). Поправки по ним найдены вручную, счёт промптов занижен на 2. +- Память: auto-memory проекта на этой машине пуста. Память awp-ca (47 файлов на 11.10) здесь недоступна, поэтому сравнение строк памяти — между разными проектами. +- Даты среза — по меткам транскрипта в UTC (M-5, #31). +- Время, потерянное на каждую поправку и каждый отказ изоляции: в транскрипте есть факт, но нет длительности. diff --git a/docs/retro/2026-10-24.metrics.json b/docs/retro/2026-10-24.metrics.json new file mode 100644 index 0000000..65276ed --- /dev/null +++ b/docs/retro/2026-10-24.metrics.json @@ -0,0 +1,99 @@ +{ + "schema": 1, + "period": { + "since": "2026-10-11", + "until": "2026-10-24" + }, + "project": "/Users/andrejepifancev/Projects/cc-harness", + "sources": { + "dirs": [ + "-Users-andrejepifancev-Projects-cc-harness", + "-Users-andrejepifancev-Projects-cc-harness--claude-worktrees-retro-2026-10-24" + ], + "sessions": 2, + "bytes": 68711659, + "bad_lines": 0 + }, + "metrics": { + "sessions": 2, + "prompts": 43, + "interrupts": 2, + "turns": 832, + "tool_calls": 1200, + "tools": { + "Bash": 675, + "Edit": 245, + "Write": 108, + "Read": 93, + "Skill": 17, + "EnterWorktree": 12, + "AskUserQuestion": 11, + "ExitWorktree": 10, + "WebFetch": 10, + "Agent": 6, + "SubagentHandback": 6, + "mcp__codegraph__codegraph_explore": 4, + "ToolSearch": 3 + }, + "roles": { + "dev": 2 + }, + "subagents": 6, + "subagent_models": { + "не указана": 4, + "opus": 2 + }, + "skills": { + "superpowers:brainstorming": 5, + "role": 2, + "superpowers:writing-plans": 2, + "superpowers:executing-plans": 2, + "superpowers:test-driven-development": 2, + "superpowers:finishing-a-development-branch": 2, + "harness-doctor": 1, + "harness-retro": 1 + }, + "tokens": { + "output": 928148, + "cache_read": 287121430, + "cache_write": 3509921 + }, + "compactions": 1, + "start_context": { + "median": 48484, + "min": 44149, + "max": 52820 + }, + "hook_blocks": 6, + "hook_blocks_by_hook": { + "guard-main(Bash)": 5, + "guard-main(Edit/Write)": 1 + }, + "classifier_denials": 4, + "classifier_by_reason": { + "Self-Modification": 4 + }, + "owner_denials": 3, + "codegraph": { + "init": 10, + "explore": 4, + "code_reads": 50, + "explore_share": 0.07 + }, + "recall": 0, + "corrections": 4, + "memory": { + "index_bytes": 0, + "files": 0, + "bytes": 0 + }, + "false_blocks": { + "labelled": 6, + "skipped": 0, + "false": 5, + "share": 0.83, + "fixed": 5, + "known": 0 + } + } +} diff --git a/docs/retro/fixtures/hook-blocks-2026-10-24.jsonl b/docs/retro/fixtures/hook-blocks-2026-10-24.jsonl new file mode 100644 index 0000000..4a5d445 --- /dev/null +++ b/docs/retro/fixtures/hook-blocks-2026-10-24.jsonl @@ -0,0 +1,6 @@ +{"id": "2026-10-24-001", "session": "a0d0e55f", "ts": "2026-10-15T19:21:53.930Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "grep -rn \"npm i\\|npm install\\|brew install\\|plugin install\\|go install\" --include=\"*.md\" --include=\"*.sh\" . | grep -v \"^./docs/retro/2026\" | head -20", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (grep -rn \"npm i\\|npm install\\|brew install\\|plugin install\\|go install\" --includ). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": null} +{"id": "2026-10-24-002", "session": "a0d0e55f", "ts": "2026-10-15T19:22:13.088Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "grep -rn \"plugin add\\|marketplace\\|caveman@\\|superpowers@\" --include=\"*.md\" --include=\"*.sh\" README.md docs global install.sh 2>/dev/null | head -20", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (grep -rn \"plugin add\\|marketplace\\|caveman@\\|superpowers@\" --include=\"*.md\" --in). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": null} +{"id": "2026-10-24-003", "session": "a0d0e55f", "ts": "2026-10-15T19:36:14.904Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "git show origin/feat/6-guard-main-target:docs/retro/fixtures/hook-blocks.jsonl | python3 -c '\nimport sys,json,re,collections\nrs=[json.loads(l) for l in sys.stdin if l.strip()]\nc=collections.Counter()\nfor r in rs:\n cmd=r.get(\"command\") or \"\"; cwd=r.get(\"cwd\") or \"\"; fp=r.get(\"file_path\") or \"\"\n if r[\"tool\"]!=\"Bash\": c[\"edit/write: \"+(\"вне репо (.claude/ или tmp)\" if (\".claude/\" in fp or \"/tmp/\" in fp or \"/private/\" in fp) else \"в репо\")]+=1; continue\n if re.search(r\"\\d>{1,2}\\s*/dev/null|\\d>&\\d\",cmd): c[\"bash: N>/dev/null или N>&M\"]+=1\n if \"/.wt/\" in cwd: c[\"bash: cwd в .wt/\"]+=1\n if re.search(r\"\\bgit\\s+-C\\s+\\S+\\s+worktree\",cmd): c[\"bash: git -C … worktree\"]+=1\n if re.search(r\"mkdir\\s+-p\\s+\\S*\\.wt/\",cmd): c[\"bash: mkdir -p .wt/\"]+=1\n if re.search(r\"\\bsed\\s+-i\",cmd): c[\"bash: sed -i\"]+=1\n if re.search(r\"\\bgit\\s+(-C\\s+\\S+\\s+)?(commit|push)\\b\",cmd): c[\"bash: git commit/push\"]+=1\n if \"<<\" in cmd: c[\"bash: heredoc\"]+=1\n if \"/Users/andrey.epifancev/.claude/\" in cmd: c[\"bash: путь в ~/.claude\"]+=1\nfor k,v in c.most_common(): print(f\"{v:4} {k}\")\nprint(\"cwd roots:\",collections.Counter(re.sub(r\"^/Users/andrey\\.epifancev/\",\"~/\",r[\"cwd\"]).split(\"/.wt/\")[0] for r in rs).most_common(8))'", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (git show origin/feat/6-guard-main-target:docs/retro/fixtures/hook-blocks.jsonl |). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": null} +{"id": "2026-10-24-004", "session": "a0d0e55f", "ts": "2026-10-23T19:05:33.550Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "bash tests/run.sh > /private/tmp/claude-501/-Users-andrejepifancev-Projects-cc-harness/a0d0e55f-be61-459b-924f-66d15a515f97/scratchpad/main-final.log 2>&1; echo \"exit $?\"; tail -1 /private/tmp/claude-501/-Users-andrejepifancev-Projects-cc-harness/a0d0e55f-be61-459b-924f-66d15a515f97/scratchpad/main-final.log; OPENSPEC_TELEMETRY=0 openspec list 2>&1 | tail -3; OPENSPEC_TELEMETRY=0 openspec validate --specs --strict 2>&1 | tail -1; git status --short; ls ~/.claude/hooks/; git branch -a", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (bash tests/run.sh > /private/tmp/claude-501/-Users-andrejepifancev-Projects-cc-h). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": null} +{"id": "2026-10-24-005", "session": "a0d0e55f", "ts": "2026-10-23T19:07:43.623Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Write", "command": null, "file_path": "/Users/andrejepifancev/Projects/cc-harness/hook-check.tmp", "hook": "guard-main(Edit/Write)", "message": "PreToolUse:Write hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED [guard-main]: запись в hook-check.tmp — репозиторий /Users/andrejepifancev/Projects/cc-harness на main. Работай в worktree на ветке (superpowers:using-git-worktrees).", "expected": null} +{"id": "2026-10-24-006", "session": "a0d0e55f", "ts": "2026-10-15T19:34:55.903Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "grep -rn \"workspace\" --include=*.md -il . 2>/dev/null | head -20; ls ~/.claude/roles ~/.claude/harness 2>&1 | head -30", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (grep -rn \"workspace\" --include=*.md -il . 2>/dev/null | head -20; ls ~/.claude/r). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": null} diff --git a/docs/retro/fixtures/hook-labels-2026-10-24.json b/docs/retro/fixtures/hook-labels-2026-10-24.json new file mode 100644 index 0000000..8d86823 --- /dev/null +++ b/docs/retro/fixtures/hook-labels-2026-10-24.json @@ -0,0 +1,12 @@ +{ + "home": "/Users/andrejepifancev", + "layout": [{"path": "Projects/cc-harness", "branch": "main"}], + "cases": { + "2026-10-24-001": {"expected": "allow", "why": "grep-pattern"}, + "2026-10-24-002": {"expected": "allow", "why": "devnull"}, + "2026-10-24-003": {"expected": "allow", "why": "regex-text"}, + "2026-10-24-004": {"expected": "allow", "why": "outside-repo"}, + "2026-10-24-005": {"expected": "block", "why": "main"}, + "2026-10-24-006": {"expected": "allow", "why": "devnull"} + } +} diff --git a/skills/harness-doctor/fixtures/hook-cases.jsonl b/skills/harness-doctor/fixtures/hook-cases.jsonl index 9f5cc07..d8406dd 100644 --- a/skills/harness-doctor/fixtures/hook-cases.jsonl +++ b/skills/harness-doctor/fixtures/hook-cases.jsonl @@ -128,3 +128,10 @@ {"id": "l001", "session": "live-1015", "cwd": "/Users/andrey.epifancev/projects/cc-skills/claude-harness", "branch": "main", "tool": "Bash", "command": "grep -rn \"npm i\\|npm install\\|brew install\\|plugin install\\|go install\" --include=\"*.md\" --include=\"*.sh\" . | grep -v \"^./docs/retro/2026\" | head -20", "file_path": null, "hook": "guard-main(Bash)", "message": "", "expected": "allow", "why": "grep-pattern"} {"id": "l002", "session": "live-1015", "cwd": "/Users/andrey.epifancev/projects/cc-skills/claude-harness", "branch": "main", "tool": "Bash", "command": "grep -rn \"plugin add\\|marketplace\\|caveman@\\|superpowers@\" --include=\"*.md\" --include=\"*.sh\" README.md docs global install.sh 2>/dev/null | head -20", "file_path": null, "hook": "guard-main(Bash)", "message": "", "expected": "allow", "why": "devnull"} {"id": "l003", "session": "live-1015", "cwd": "/Users/andrey.epifancev/projects/cc-skills/claude-harness", "branch": "main", "tool": "Bash", "command": "git show origin/feat/6-guard-main-target:docs/retro/fixtures/hook-blocks.jsonl | python3 -c '\nimport sys,json,re,collections\nrs=[json.loads(l) for l in sys.stdin if l.strip()]\nc=collections.Counter()\nfor r in rs:\n cmd=r.get(\"command\") or \"\"\n if re.search(r\"\\d>{1,2}\\s*/dev/null|\\d>&\\d\",cmd): c[\"bash: N>/dev/null или N>&M\"]+=1\n if re.search(r\"mkdir\\s+-p\\s+\\S*\\.wt/\",cmd): c[\"bash: mkdir -p .wt/\"]+=1\n if re.search(r\"\\bsed\\s+-i\",cmd): c[\"bash: sed -i\"]+=1\nfor k,v in c.most_common(): print(f\"{v:4} {k}\")'", "file_path": null, "hook": "guard-main(Bash)", "message": "", "expected": "allow", "why": "regex-text"} +{"home": "/Users/andrejepifancev", "layout": [{"path": "Projects/cc-harness", "branch": "main"}]} +{"id": "2026-10-24-001", "session": "a0d0e55f", "ts": "2026-10-15T19:21:53.930Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "grep -rn \"npm i\\|npm install\\|brew install\\|plugin install\\|go install\" --include=\"*.md\" --include=\"*.sh\" . | grep -v \"^./docs/retro/2026\" | head -20", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (grep -rn \"npm i\\|npm install\\|brew install\\|plugin install\\|go install\" --includ). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": "allow", "why": "grep-pattern"} +{"id": "2026-10-24-002", "session": "a0d0e55f", "ts": "2026-10-15T19:22:13.088Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "grep -rn \"plugin add\\|marketplace\\|caveman@\\|superpowers@\" --include=\"*.md\" --include=\"*.sh\" README.md docs global install.sh 2>/dev/null | head -20", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (grep -rn \"plugin add\\|marketplace\\|caveman@\\|superpowers@\" --include=\"*.md\" --in). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": "allow", "why": "devnull"} +{"id": "2026-10-24-003", "session": "a0d0e55f", "ts": "2026-10-15T19:36:14.904Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "git show origin/feat/6-guard-main-target:docs/retro/fixtures/hook-blocks.jsonl | python3 -c '\nimport sys,json,re,collections\nrs=[json.loads(l) for l in sys.stdin if l.strip()]\nc=collections.Counter()\nfor r in rs:\n cmd=r.get(\"command\") or \"\"; cwd=r.get(\"cwd\") or \"\"; fp=r.get(\"file_path\") or \"\"\n if r[\"tool\"]!=\"Bash\": c[\"edit/write: \"+(\"вне репо (.claude/ или tmp)\" if (\".claude/\" in fp or \"/tmp/\" in fp or \"/private/\" in fp) else \"в репо\")]+=1; continue\n if re.search(r\"\\d>{1,2}\\s*/dev/null|\\d>&\\d\",cmd): c[\"bash: N>/dev/null или N>&M\"]+=1\n if \"/.wt/\" in cwd: c[\"bash: cwd в .wt/\"]+=1\n if re.search(r\"\\bgit\\s+-C\\s+\\S+\\s+worktree\",cmd): c[\"bash: git -C … worktree\"]+=1\n if re.search(r\"mkdir\\s+-p\\s+\\S*\\.wt/\",cmd): c[\"bash: mkdir -p .wt/\"]+=1\n if re.search(r\"\\bsed\\s+-i\",cmd): c[\"bash: sed -i\"]+=1\n if re.search(r\"\\bgit\\s+(-C\\s+\\S+\\s+)?(commit|push)\\b\",cmd): c[\"bash: git commit/push\"]+=1\n if \"<<\" in cmd: c[\"bash: heredoc\"]+=1\n if \"/Users/andrey.epifancev/.claude/\" in cmd: c[\"bash: путь в ~/.claude\"]+=1\nfor k,v in c.most_common(): print(f\"{v:4} {k}\")\nprint(\"cwd roots:\",collections.Counter(re.sub(r\"^/Users/andrey\\.epifancev/\",\"~/\",r[\"cwd\"]).split(\"/.wt/\")[0] for r in rs).most_common(8))'", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (git show origin/feat/6-guard-main-target:docs/retro/fixtures/hook-blocks.jsonl |). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": "allow", "why": "regex-text"} +{"id": "2026-10-24-004", "session": "a0d0e55f", "ts": "2026-10-23T19:05:33.550Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "bash tests/run.sh > /private/tmp/claude-501/-Users-andrejepifancev-Projects-cc-harness/a0d0e55f-be61-459b-924f-66d15a515f97/scratchpad/main-final.log 2>&1; echo \"exit $?\"; tail -1 /private/tmp/claude-501/-Users-andrejepifancev-Projects-cc-harness/a0d0e55f-be61-459b-924f-66d15a515f97/scratchpad/main-final.log; OPENSPEC_TELEMETRY=0 openspec list 2>&1 | tail -3; OPENSPEC_TELEMETRY=0 openspec validate --specs --strict 2>&1 | tail -1; git status --short; ls ~/.claude/hooks/; git branch -a", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (bash tests/run.sh > /private/tmp/claude-501/-Users-andrejepifancev-Projects-cc-h). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": "allow", "why": "outside-repo"} +{"id": "2026-10-24-005", "session": "a0d0e55f", "ts": "2026-10-23T19:07:43.623Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Write", "command": null, "file_path": "/Users/andrejepifancev/Projects/cc-harness/hook-check.tmp", "hook": "guard-main(Edit/Write)", "message": "PreToolUse:Write hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED [guard-main]: запись в hook-check.tmp — репозиторий /Users/andrejepifancev/Projects/cc-harness на main. Работай в worktree на ветке (superpowers:using-git-worktrees).", "expected": "block", "why": "main"} +{"id": "2026-10-24-006", "session": "a0d0e55f", "ts": "2026-10-15T19:34:55.903Z", "cwd": "/Users/andrejepifancev/Projects/cc-harness", "branch": "main", "tool": "Bash", "command": "grep -rn \"workspace\" --include=*.md -il . 2>/dev/null | head -20; ls ~/.claude/roles ~/.claude/harness 2>&1 | head -30", "file_path": null, "hook": "guard-main(Bash)", "message": "PreToolUse:Bash hook error: [\"$HOME/.claude/hooks/guard-main.sh\"]: BLOCKED: shell write on main (grep -rn \"workspace\" --include=*.md -il . 2>/dev/null | head -20; ls ~/.claude/r). Create an isolated worktree first (superpowers:using-git-worktrees). Only 'openspec archive' commits may land on main di", "expected": "allow", "why": "devnull"}