# Brain-retro — первое ретро мозга по данным наблюдателя

**Дата:** 2026-05-20 (12:25 MSK)
**Период:** 2026-05-19 .. 2026-05-20 (это первое brain-retro вообще; покрывает всё, что наблюдатель записал на текущий момент)
**Источник:** `docs/observer/episodes-2026-05.jsonl` (22 строки) + `docs/observer/.read-counter.json`
**Анализатор:** `node tools/brain-retro-analyzer.mjs docs/observer/episodes-2026-05.jsonl`
**Уровень анализа:** верхнеуровневый по запросу заказчика («потом углубимся по разделам»); экономия 0%.

> Анализатор отчитался: `episodeCount=17`, `v1SkippedCount=5`, `observerErrorCount=0`. Ниже все цифры по 17 v2-эпизодам, если не отмечено иное.

---

## Period

2026-05-19T05:18:16Z .. 2026-05-20T08:12:29Z (5 сессий, 22 эпизода всего; 17 v2-анализируемых).

Сессии:

| task_id (session_id) | дата | v1 эпизоды | v2 эпизоды | примечание |
|---|---|---|---|---|
| `553717ec` | 19.05 05:18 .. 10:24 | 5 | 10 | длинная сессия brain-governance Phase A/B/C + factor-analysis ext; компакция между turn 86–91 |
| `24acfa10` | 20.05 07:08 | 0 | 1 | ExitWorktree-разовый ход |
| `a42e4ba5` | 20.05 07:26 | 0 | 1 | короткая `PowerShell`/`Bash`-проверка |
| `dd905ea0` | 20.05 07:25 .. 07:28 | 0 | 1 | apdate `ЭТАЛОН`-семейства memory (11 Edit) |
| `98298ec2` | 20.05 07:36 .. 08:12 | 0 | 3 | сегодняшняя «починка наблюдателя» (TDD + verify) |

Текущий ход (брейн-ретро 20.05 ~08:25Z+) будет записан Stop-хуком после завершения и в этом ретро ещё **не** учитывается — это нормально по конструкции наблюдателя.

---

## Path-type distribution (v2, n=17)

| path_type | count | % |
|---|---|---|
| improvised | 14 | 82.4% |
| regulated | 3 | 17.6% |
| alternative | 0 | 0% |
| mixed | 0 | 0% |

**Чтение:** «Мозг живёт прямым путём». Лишь два эпизода прошли через явный hard-floor §12: №16 (19.05 10:13, systematic-debugging) и №22 (20.05 07:52, TDD + verification-before-completion). Третий «regulated» — №16-ой же эпизод (тот же epoch, после Stop предыдущего короткого). Каждый regulated-эпизод был обоснован профильным триггером (баг наблюдателя + TDD по правке кода).

---

## Outcome distribution (inferred)

| outcome | count | % |
|---|---|---|
| success | 6 | 35% |
| unknown | 11 | 65% |
| partial | 0 | 0% |
| rework | 0 | 0% |
| blocked | 0 | 0% |

**Чтение:** не было ни одного `correction`-prompt'а от заказчика (next-prompt-sentiment) за период — переделок нет; ни одного `interrupt`; ни одного `unrecovered_error`. 11 «unknown» — это **последние эпизоды задач** или эпизоды, чей след ещё не сменился новой prompt-меткой (6 из 11 — открытые задачи 20.05; 5 — последние в своих task-группах). Это техническое ограничение выборки, не качественный сигнал.

> Поле «failure / aborted» из шаблона анализатор не возвращает — он деградирует до «unknown» (см. `inferOutcome` коммент A-1).

---

## Top nodes used (skill_invoked + node_chosen)

Из 17 эпизодов **15** имеют `node_chosen=direct` (88%). Только 2 эпизода открыто декларируют узел:

| node | times | first | last |
|---|---|---|---|
| `direct` | 15 | 2026-05-19T08:06Z | 2026-05-20T07:45Z |
| `superpowers:systematic-debugging` | 1 | 2026-05-19T10:13Z | 2026-05-19T10:13Z |
| `superpowers:test-driven-development` | 1 | 2026-05-20T07:52Z | 2026-05-20T07:52Z |

Полный список `skill_invoked` events (4 уникальных):

| skill | эпизоды |
|---|---|
| `superpowers:systematic-debugging` | №16 |
| `claude-md-management:claude-md-improver` | №16 |
| `superpowers:test-driven-development` | №22 |
| `superpowers:verification-before-completion` | №22 |

**Все остальные 60+ узлов реестра** за период не использовались. Это **не проблема** (см. memory `feedback_brain_unused_tools_not_problem.md`).

---

## Factor analysis matrix (v2 — из `factorMatrix`)

### `decision_provenance` — главный фактор «rework мой или router's?»

| provenance | success | partial | rework | unknown |
|---|---|---|---|---|
| `autonomous` | 5 | 0 | 0 | 8 |
| `user_directed_method` | 1 | 0 | 0 | 2 |
| `user_chose_from_options` | 0 | 0 | 0 | 1 |

**Чтение:** 76% решений автономные; 18% — заказчик навязал метод; 6% — collaborative-choice (Phase 1.1, routing-gate не блокирует). Метрика **rework = 0 во всех ячейках** — за период нет признаков «rework по моей вине» либо «rework по навязанному методу». Ranking фактора сейчас **inconclusive из-за нулевого rework**.

### `user_directed_method` — что заказчик навязывал

| ход | дата | claude_would_have_chosen | контекст |
|---|---|---|---|
| ep. 7 | 19.05 08:10 | `subagent-driven-development` | сразу после post_compaction (turn 83) |
| ep. 8 | 19.05 08:13 | `subagent-driven-development` | следующий ход той же сессии |
| ep. 10 | 19.05 08:25 | `brainstorming` | через 12 минут |

Все три — turn 83–88 на той же сессии, сразу после `post_compaction=true` (компакция отрезала контекст). **Это паттерн «после компакции заказчик возвращает рулевое».**

### `economy_level`

| economy_level | success | unknown |
|---|---|---|
| `null` | 2 | 2 |
| `0` | 0 | 1 |
| `5` | 3 | 5 |
| `100` | 1 | 3 |

`null` = sentinel не поставлен (legacy 4 эпизода 19.05 утром). Прочие — три тарифа (0/5/100) используются равноценно по решению заказчика per task. Никаких корреляций «экономия→outcome» при текущем n=17 не вытащить.

### `model`

| model | success | unknown |
|---|---|---|
| `claude-opus-4-7` | 6 | 11 |

Все 17 v2-эпизодов на Opus 4.7. Контрольной группы (Sonnet/Haiku) в данных нет → факторный анализ по модели **невозможен**. Это ожидаемо: subagent-driven-development вызывает Sonnet/Haiku субагентами, чьи трейсы в основной transcript не попадают (Stop-хук пишет только верхнего Claude).

### `post_compaction`

| post_compaction | success | unknown |
|---|---|---|
| `true` | 6 | 5 |
| `false` | 0 | 6 |

Псевдо-корреляция: все `post_compaction=false` эпизоды относятся к 20.05 (новые сессии, ещё не успели получить `next-prompt`) → все 6 как unknown по конструкции. **Сигнал не валиден.**

### `session_turn` бакеты

| bucket | success | unknown |
|---|---|---|
| `early` (<10) | 0 | 6 |
| `late` (≥40) | 6 | 5 |
| `mid` (10–40) | 0 | 0 |

Та же артефакт-корреляция, что и с `post_compaction`: «early» = новые сессии 20.05 → unknown по техническому ограничению. Истинного сигнала нет.

### `parallel_session`

| parallel | success | unknown |
|---|---|---|
| `true` | 5 | 5 |
| `false` | 1 | 6 |

10/17 (59%) ходов шли в режиме параллельных Claude-сессий (Pravila §15 в работе). Outcome по этому фактору inconclusive — нужны более длинные выборки.

### `task_size`

| size | success | unknown |
|---|---|---|
| `small` (<20 tools) | 6 | 9 |
| `medium` (20–60) | 0 | 2 |
| `large` (>60) | 0 | 0 |

88% эпизодов — small. Два medium-эпизода — №20 (22 tool_calls, 13 files: orientation-read для текущего ретро) и №22 (56 tool_calls, 11 files: текущая TDD-починка наблюдателя). Оба ещё «unknown» по next-prompt.

### `node_chosen`

| node | success | unknown |
|---|---|---|
| `direct` | 6 | 9 |
| `superpowers:systematic-debugging` | 0 | 1 |
| `superpowers:test-driven-development` | 0 | 1 |

Все 6 «success» — direct-эпизоды; оба skill-эпизода ещё unknown (regulated, не было follow-up correction/approval prompt'а).

### `task_classification`

| classification | success | unknown |
|---|---|---|
| `bugfix` | 1 | 0 |
| `feature` | 1 | 2 |
| `refactor` | 1 | 0 |
| `question` | 0 | 2 |
| `other` | 3 | 7 |

Преобладание `other` (10/17 = 59%) — индикатор того, что классификатор парсера часто не находит явной семантики prompt'а. Это не проблема алгоритма — это просто стиль работы за период (короткие нейтральные ходы по правке нормативки и memory).

---

## Episodes → tasks (из `tasks` анализатора)

11 групп. «turns that are rework» определены как эпизоды, чей `_inferredOutcome === 'rework'`. Таких **0** в этом периоде.

| task_ref | эпизодов | turns-rework |
|---|---|---|
| `553717ec…#1` | 1 | 0 |
| `553717ec…#2` | 2 | 0 |
| `553717ec…#3` | 1 | 0 |
| `553717ec…#4` | 3 | 0 |
| `553717ec…#5` | 1 | 0 |
| `553717ec…#6` | 1 | 0 |
| `553717ec…#7` | 2 | 0 |
| `24acfa10…#8` | 1 | 0 |
| `a42e4ba5…#9` | 1 | 0 |
| `dd905ea0…#10` | 1 | 0 |
| `98298ec2…#11` | 3 | 0 |

---

## Causal-chain candidates (из `causalChains`)

**0 цепочек** (анализатор вернул пустой массив).

Это здоровый сигнал: hot-file фильтр (CLAUDE.md / MEMORY.md / STATUS.md / `episodes-*.jsonl` / `memory/*.md`) корректно отсекает ложные «А упал на X-файле — через 6 эпизодов Б трогает X-файл». Реальных error→fix цепочек по не-горячим файлам наблюдатель не нашёл, что согласуется с low-error профилем периода (3 `error`-event'а — все рутинные «tool_result is_error», ни одного `unrecovered_error`).

---

## Observer health

| метрика | значение | оценка |
|---|---|---|
| `observerErrorCount` | 0 | ✅ зелено — наблюдатель не падал тихо |
| `v1SkippedCount` | 5 | ℹ️ legacy: schema v2 раскатан с 2026-05-19T08:06Z; 5 ранее-утренних эпизодов 19.05 (05:18–06:57) — v1 без `environment`/`prompt_signal`/`decision_provenance`, анализатор их корректно отфильтровал |
| `retry` events | 4 (в 2 эпизодах) | в норме — №6 (1 retry), №14 (2 retry); все восстановлены |
| `error` events | 3 (в 3 эпизодах) | рутинные `tool_result is_error`; **ни одного** `unrecovered_error` (outcome не blocked) |
| `time_burn` events | 1 (19m45s в №22) | большой TDD-эпизод, ожидаемо |
| `interrupt` events | 0 | нет прерываний |
| `parse_gap` events | 0 | парсер не сообщал о пропусках |
| `confusion_marker` events | 0 | парсер не пишет такого вида события за период (ожидаемо; маркеры — поведенческое наблюдение, не tool-event) |

---

## Canonical chains L1–L12 hit rate (предварительно)

Канонические связки описаны в `docs/routing-off-phase.md` (PSR_v1 R15, Rec4 SYSTEM-аудита). Полная сверка с L1–L12 — задача углубления. Верхнеуровневая оценка:

- **Явных L-цепочек 0**, так как 88% эпизодов — `direct`/`improvised`, без декларации canonical chain в `primary_rationale.boundaries_applied` или `triggers_matched`.
- **Возможный L-кандидат** в эпизоде №16 (19.05 10:13): связка `superpowers:systematic-debugging` → `claude-md-management:claude-md-improver` — это похоже на «debug-чейн с фиксацией learning'а в CLAUDE.md» (близко к L-связке runtime-debug + infrastructure-edit, но точная маркировка L? — задача углубления).
- **Возможный L-кандидат** в эпизоде №22 (20.05 07:52): `superpowers:test-driven-development` → `superpowers:verification-before-completion` — это **классический TDD-чейн** (близок к канонической связке для feature-кода).

| chain (предварит.) | times | notes |
|---|---|---|
| TDD → verification | 1 | ep.22 (текущий) |
| systematic-debugging → claude-md-improver | 1 | ep.16 |
| Все остальные L1–L12 | 0 | за период не видно |

> Пометка для углубления: считать canonical-chain hit rate не по `skill_invoked`-парам (это сильный сигнал, но узкий), а по сочетанию `triggers_matched` + `boundaries_applied` per `routing-off-phase.md`. Сейчас `triggers_matched` и `boundaries_applied` пусты во всех эпизодах — это `primary_rationale` ещё пишется парсером в «минимальном» режиме.

---

## Improvised chains (path_type=improvised, repeated ≥2)

Конкретных повторяющихся «node-set»-цепочек не вижу — большинство improvised-эпизодов либо однотонные (один Edit/Read/Bash-набор по разным memory-файлам), либо изолированы. Зато виден **повторяющийся pattern по предметной области**:

| pattern (тематический) | эпизодов | candidate L13+? |
|---|---|---|
| Self-repair наблюдателя (правки `tools/observer-*` или `docs/observer/*`) | 8 из 17 (47%) | нет — это техдолг bootstrap, не повторяющийся бизнес-чейн |
| Sync `memory/*.md` после изменений (особенно `project_state.md`/`reference_github.md`) | 3 | нет — это часть «§4.6 self-review» (Pravila) |
| Правка `CLAUDE.md`-секций после нормативных bumps | 2 | нет — закрывается обязательным каналом `claude-md-management` |

---

## chain_divergence cases

Нет данных — `primary_rationale.candidates_considered` пуст во всех 17 эпизодах, парсер пока не пишет «рассматриваемые альтернативы». В разделе для углубления — улучшить парсер, чтобы вытаскивать candidate-узлы из текста хода (если такие декларации были в reasoning).

---

## Top error classes

| error class | count | recovery pattern |
|---|---|---|
| `tool_result reported is_error` (рутинный) | 3 | retry внутри хода, без эскалации; outcome не blocked |
| `unrecovered_error` | 0 | — |
| `interrupt` | 0 | — |

Тип-распределение — все три «error»-event'а одинаковые («tool_result reported is_error»). Это значит, что парсер не различает классы ошибок ниже этого уровня (известное ограничение — A-1 fix в `inferOutcome`).

---

## confusion_marker hot-spots

Нет данных. `confusion_marker` — это event, который должен писаться парсером при обнаружении паттернов сомнения в reasoning Claude. За период парсер не зарегистрировал ни одного → либо подобного не было, либо паттерн распознавания пуст (см. Кандидаты ниже).

---

## Candidates for owner review

Все кандидаты ниже — **предложения**, не действия. Заказчик решает, какое (если вообще) применить.

### Candidate 1: «Self-repair наблюдателя» — 47% периода, в норме для bootstrap-фазы

- **Тип:** наблюдение, не изменение нормативки.
- **Evidence:** 8 из 17 эпизодов трогали `tools/observer-*` или `docs/observer/*` (эпизоды 6, 13, 14, 16, 20, 22 + два косвенно). Episodes-2026-05.jsonl touched в эпизодах 14, 20. Самый «толстый» — №22: 56 tool_calls, 11 файлов, 19m45s — TDD-починка парсера/STOP-хука/PII-фильтра/coverage-checker.
- **Чтение:** мозг **сейчас в фазе самовосстановления** (brain governance Phase A/B/C закрыта 19.05 + factor-analysis extension + phase 1.1 — это всё out-of-feature work, инструментальный долг). После того как Phase C завершит «стабилизацию» (lefthook jobs 11–15 работают, observer-of-observer 54w self-prune, STATUS.md generator) — % должен опуститься.
- **Suggested action:** **не вмешиваться**. На следующем ретро сравнить % «self-repair» эпизодов; если останется ≥40% при больших n — это будет сигнал к делегации (например, выделить наблюдатель в фоновый daemon, чтобы не отъедал tool-budget основного Claude).
- **Cost / risk:** 0 сейчас; ~0 на следующем ретро.

### Candidate 2: «Direct path 88%» — здоровый низкий уровень regulated/skill-инвокаций, но требует контроля

- **Тип:** наблюдение + предложение по парсеру.
- **Evidence:** только 2/17 эпизодов проходят через hard-floor §12 (`primary_rationale.hard_floor.invoked=true`). Остальные 15 — `direct`. При этом из 15 ни один не упомянул `boundaries_applied` или `triggers_matched` (пустые массивы во всех 17 эпизодах).
- **Гипотеза:** парсер пока **не вытаскивает** «незакрытые» triggers/boundaries — он пишет только явные `skill_invoked` events. Это значит, что мы не видим, **сколько раз §12.2 карта могла бы trigger'нуть skill, но Claude пошёл direct**. Без этого нельзя различить «direct правильно (не было триггера)» и «direct неправильно (был триггер, проигнорирован)».
- **Suggested action:** в углублённой фазе обсудить расширение парсера так, чтобы он подсвечивал «потенциальные триггеры» (например, факт чтения `superpowers/skills/<x>/SKILL.md` без последующего `skill_invoked`, или Bash-команд, типа `gitleaks`/`pest`/`vitest`, не обёрнутых в `verification-before-completion`). Это превратит «не видим, шёл ли мозг мимо карты» в реальную метрику.
- **Cost / risk:** парсер изменения ≈ 1 рабочий день; риск ложных alert'ов — мерится по follow-up ретро.

### Candidate 3: Паттерн «post_compaction → user_directed_method 3×»

- **Тип:** наблюдение + кандидат на memory-фидбэк.
- **Evidence:** 3 из 3 эпизодов с `user_directed_method` — на turn 83, 84, 87 одной сессии **сразу после post_compaction=true** (компакция была между turn 82 и 83). Заказчик навязал: `subagent-driven-development` (×2) и `brainstorming` (×1).
- **Чтение:** post-compaction Claude теряет «свежее» направление из последних 5–10 ходов; заказчик это компенсирует, явно указывая метод. Это не bug, это компенсация известного quirk.
- **Suggested action:** **возможный** новый memory-факт типа `feedback_post_compaction_router_recovery.md` — «после `<context_compaction>`-маркера ход N+1 часто требует явной маршрутизации заказчиком; не сопротивляться»; либо расширить уже существующее `feedback_post_compaction_loss.md` (если есть). Решает заказчик.
- **Cost / risk:** 1 memory-файл / 10 строк; риск нулевой.

### Candidate 4: 5 v1-эпизодов 19.05 утра — archive или конверсия

- **Тип:** инфраструктурное предложение.
- **Evidence:** эпизоды 1–5 в JSONL (05:18–06:57) — без `schema_version`. Анализатор тихо их пропускает (v1SkippedCount=5). Они **есть в файле**, но не участвуют в факторном анализе.
- **Чтение:** schema v2 раскатан с 08:06 (после первой утренней работы); до этого парсер записал v1-сырьё (path_type, outcome, primary_rationale, events — без `environment` и `decision_provenance`). Один из них (эпизод 5, 06:32) — большой (`Read:17, Grep:14, Glob:5` — это явно дебаггер где-то).
- **Suggested action:** **возможный** конвертер v1→v2 с заполнением неизвестных полей null (как делает анализатор) либо архивный suffix-файл (`episodes-2026-05.v1-archive.jsonl`). Сейчас оба варианта живы в одном файле без помех.
- **Cost / risk:** 0 если оставить; ~2 часа если делать конвертер.

### Candidate 5: `confusion_marker` / `parse_gap` / `unrecovered_error` — все по 0 за период

- **Тип:** валидационное замечание парсера.
- **Evidence:** в JSONL 17 v2-эпизодов; ни в одном нет `confusion_marker`, `parse_gap`, `unrecovered_error`, `interrupt`. Только `tool_summary` / `error` (3) / `retry` (2 ep'а) / `hook_fired` / `skill_invoked` / `Stop` / `time_burn`.
- **Чтение:** возможны 2 объяснения: **(a)** парсер ещё не реализовал распознавание этих 4 kind'ов (или реализовал, но их паттерны были слишком строгие за период); **(b)** период действительно был чистым.
- **Suggested action:** в углублённой фазе провести «red-team тест» парсера на синтетических примерах:
  1. Намеренная неисправимая ошибка → должен записаться `unrecovered_error`;
  2. Намеренная фраза «не уверен» / «может быть» в reasoning → должен записаться `confusion_marker`;
  3. Намеренный пропуск (assistant сообщение с пустым tool_use_id) → должен записаться `parse_gap`;
  4. Пользовательский `[ESC]` → должен записаться `interrupt`.
- **Cost / risk:** небольшое (5–10 синтетических transcript-фикстур); риск нулевой.

### Candidate 6: `candidates_considered` / `triggers_matched` / `boundaries_applied` всегда пусты

- **Тип:** наблюдение по полноте `primary_rationale`.
- **Evidence:** во всех 17 v2-эпизодах эти три массива — `[]`. Только `node_chosen` и `task_classification` заполнены.
- **Чтение:** парсер пишет только финальное решение, не аргументацию. Это известный gap (см. spec §6 «routing_decision события»).
- **Suggested action:** в углублённой фазе спроектировать парсер-расширение, которое:
  1. Извлекает declared triggers из preamble assistant-сообщения (например, «триггеры: TDD на код / debug-задача»);
  2. Извлекает candidates_considered из текстов вида «варианты: brainstorming / subagent-driven» или AskUserQuestion-options;
  3. Извлекает boundaries_applied из явных упоминаний ADR/Pravila §N.
- **Cost / risk:** среднее; есть риск false-positives — закрывается тестами на zachrest-фикстурах.

### Candidate 7: NLP-фактор «task_classification=other 59%»

- **Тип:** наблюдение по классификатору.
- **Evidence:** 10 из 17 (59%) — `other`. `bugfix` 1, `refactor` 2, `feature` 3, `question` 2, остальные `other`.
- **Чтение:** классификатор парсера задаёт grobые корзины. «Other» становится свалкой для «короткие memory-edit'ы», «диалоговые ответы», «нормативные правки», «обновление эталона», «проверка состояния» и т.п.
- **Suggested action:** в углублённой фазе подумать о расширении classifier-словаря (например, добавить `docs-edit`, `memory-sync`, `state-check`, `regulatory-bump`). Это уменьшит «other»-bucket и сделает факторный анализ острее.
- **Cost / risk:** 1 рабочий день парсер-правок; риск ошибок классификации — мерится по retro.

---

## Informational metrics (NOT alerts)

- **Узлов реестра использовано как минимум 1 раз за период:** 4 (`superpowers:systematic-debugging`, `superpowers:test-driven-development`, `superpowers:verification-before-completion`, `claude-md-management:claude-md-improver`) из 60+ формализованных позиций (канон счётчиков — `docs/Tooling_v8_3.md` §0).
- **Узлов реестра не использовано ни разу:** ~56+ из 60+. **Это не проблема** per `feedback_brain_unused_tools_not_problem` (память). Capability-readiness — осознанная стратегия заказчика.
- **Hot-files** (по `task_size.files`, с дедупом): `CLAUDE.md` (3 раза), `project_brain_governance_design.md` (3), `reference_github.md` (3), `episodes-2026-05.jsonl` (2), `2026-05-19-observer-factor-analysis-design.md` (2), `tools/observer-transcript-parser.mjs` (2), tools/observer-transcript-parser.test.mjs (2). Остальные 30+ файлов — один раз.
- **Самый длинный эпизод:** №22 (20.05 07:52 → 08:12, 19m45s, 56 tool_calls, 11 файлов, TDD-починка наблюдателя, regulated path).
- **Самый короткий эпизод:** №2 (19.05 06:07–06:08, 1m, 0 tool_calls — диалоговый ответ).

---

## Что **не** делалось в этом ретро (явные ограничения)

1. **Не сверял с `docs/routing-off-phase.md` L1–L12 канонические связки** — пометил для углубления.
2. **Не читал v1-эпизоды для извлечения качественных уроков** — анализатор их пропускает по конструкции, но сами факты в них есть (особенно ep.5 — большой Read+Grep дебаг-эпизод 19.05 06:32).
3. **Не оценивал latency хуков** — данные есть (`hook_fired.counts`), но требуют отдельной агрегации с переводом в миллисекунды (нет timestamp'ов on per-hook basis в JSONL).
4. **Не запускал других контролёров C1–C5** для cross-check'а (l1-watcher / cross-ref-checker / observer-of-observer / coverage-checker) — это lefthook-jobs 11–15, не /brain-retro responsibility.
5. **Не правил никакую нормативку** — read-only, как требует procedure step 7.
6. **Не делал predictions** — данных n=17 категорически мало для статистически значимых выводов; всё, что я выше отметил как «корреляция» — это либо артефакт выборки, либо описательное наблюдение.

---

## Подпись и провенанс ретро

Анализатор — `tools/brain-retro-analyzer.mjs` (commit `e6d6bab` в текущем checkout).
Шаблон — `.claude/skills/brain-retro/references/aggregation-template.md`.
Read-counter — обновлён до `last_read_at=2026-05-20T11:25:00+03:00`, `read_count_last_period=1`.
Эта нота — read-only вывод; никакая нормативная правка не выполнена.

---

# АДДЕНДУМ — углубления по 5 разделам (по запросу заказчика, экономия 5%)

> Закрытие 5 явных gaps, отмеченных в верхнеуровневом отчёте. Read-only,
> никаких правок нормативки. Данные собраны 2026-05-20 ≈11:35 MSK.

## A. L1–L12/L13 классификация двух regulated-эпизодов

Источник истины — [docs/routing-off-phase.md](../../routing-off-phase.md) v1.2 (20.05.2026,
12 канонических + L13 finance-tooling).

**Эпизод №16 (2026-05-19T10:13Z, 19m, regulated, opus-4-7):**

- `skill_invoked`: `superpowers:systematic-debugging` + `claude-md-management:claude-md-improver`.
- Файлы: `tools/observer-transcript-parser.{mjs,test.mjs}` + `CLAUDE.md` + transcript-jsonl сессии.
- **Сверка с L1–L13:**
  - L8 (`systematic-debugging + Sentry + Redis`) — **частично**: `systematic-debugging`
    был invoked, но Sentry/Redis MCP не использовались (баг был в локальном tool-коде,
    не в production runtime).
  - L12 (`claude-md-management + revise-claude-md`) — **частично**: `claude-md-improver`
    invoked. Это один из двух entry-skill'ов L12. `/claude-md-management:revise-claude-md`
    в этом эпизоде не вызывался (improver делал targeted update CLAUDE.md, не capture
    session-learnings).
- **Вердикт:** эпизод **не маппится 1:1** на каноническую L-связку. Это **гибрид
  Pravila §12.2 hard-floor** (systematic-debugging при unexpected behavior) **+ §5 п.10
  обязательного канала** (правка CLAUDE.md только через claude-md-management). Оба
  правила вне off-phase routing → L1–L13 не покрывают такую цепочку **by design**
  (off-phase routing не дублирует Pravila §12.2/§5 п.10 hard-rules — это явно в
  routing-off-phase §11.5 «Hard-rules перевешивают routing-аид»).

**Эпизод №22 (2026-05-20T07:52Z, 19m45s, regulated, opus-4-7):**

- `skill_invoked`: `superpowers:test-driven-development` + `superpowers:verification-before-completion`.
- Файлы: 11 `tools/observer-*` + `.gitleaks.toml` (test-fixture seam с false-positive).
- **Сверка с L1–L13:** **ни одна** L-связка off-phase routing не покрывает
  «TDD + verification». L1–L13 — это off-phase инструменты (#31–#63), TDD/verify —
  это Pravila §12.2 hard-floor (Superpowers). Тоже не дублируется в routing-аиде.
- **Вердикт:** **чистый Pravila §12.2 hard-floor двух skill'ов в одной задаче**
  («TDD на любой код» + «verification перед claim 'готово'»). L-маркировка
  неприменима.

**Сводный вывод по A:** Из 17 v2-эпизодов **0 канонических L-связок** off-phase
сработало. Все 2 regulated-эпизода прошли через **Pravila §12.2 hard-floor** (skill
из обязательной карты §12.2), не через каноническую цепочку L1–L13. **Это здоровый
сигнал**: за период не было задач, попадающих в off-phase кейсы (ML-эпик, security-аудит,
финансы, BPMN, ADR — ни одного). Все turn'ы — самообслуживание наблюдателя и
правки нормативки/memory.

| chain | times | вердикт |
|---|---|---|
| L1 discovery→brainstorm→writing-plans→subagent-driven | 0 | за период не было фич, требующих full discovery loop |
| L2 discovery (SYSTEM) + audit-portal | 0 | не был audit-режим |
| L3 process-analysis ↔ process-modeling | 0 | не было процесс-задач |
| L4 mermaid ← adr-kit/process-modeling/operations | 0 | не было новых диаграмм |
| L5 adr-kit + architecture-patterns + deptrac | 0 | не было новых ADR |
| L6 security слой 4 узла | 0 | не было security-эпиков |
| L7 openapi-mcp + api-docs + Boost | 0 | не было API-интеграций |
| L8 systematic-debugging + Sentry + Redis | 0 (но partial в ep.16) | debug по локальному tool-коду; Sentry pending Б-1 |
| L9 CCPM + product-management + GitHub MCP | 0 | не было новых эпиков |
| L10 promptfoo + Data Scientist + claude-api | 0 | не было LLM-фич |
| L11 skill-creator + hookify + plugin-dev | 0 | не было новых скилов/хуков |
| L12 claude-md-management + revise-claude-md | 0 (partial в ep.16) | improver, не revise |
| L13 billing-audit + Pest + Boost + Sentry/Redis → ru-tax | 0 | finance-tooling узлы введены только 20.05 |

---

## B. Уроки из 5 v1-эпизодов 19.05 утра (05:18–06:57)

Анализатор пропускает v1 (нет `schema_version=2`), но фактический контент в JSONL
есть. Сводка (по содержанию эпизодов 1–5 из episodes-2026-05.jsonl):

| ep | старт | длит. | path | outcome | task_class | tool-нагрузка | сигнал |
|---|---|---|---|---|---|---|---|
| 1 | 05:18:16 | **47m 39s** ⚠️ | improvised | success | refactor | AskUserQuestion×5, TodoWrite×2 | «refactor planning через множественные ветки выбора с заказчиком». Самый длинный эпизод всего периода. |
| 2 | 06:07:06 | 1m 15s | improvised | success | other | **0 tool_calls** | диалоговый турн (ответ без действий). |
| 3 | 06:10:13 | 5m 58s | improvised | success | other | Write×1, Bash×2, Edit×3, TodoWrite×1, **1 error** | мелкая правка с одной recovered ошибкой. |
| 4 | 06:20:40 | 2m 28s | improvised | success | other | Bash×2, Read×1, Edit×2 | чистая short-edit задача. |
| 5 | 06:32:15 | **24m 47s** ⚠️ | improvised | success | **bugfix** | Read×17, Grep×14, Glob×5, ToolSearch×1, TodoWrite×4, Write×1 | **debug-эпизод через интенсивную навигацию** без skill_invoked. |

**Качественные уроки:**

1. **Ep.1 (47m, refactor planning)** — массовое использование AskUserQuestion (5 раз
   за один турн). Это classic-паттерн «дерево вариантов с заказчиком» **без
   `superpowers:brainstorming` skill'а** — Pravila §16 (brain governance) с
   обязательным routing-gate был ещё **не enforced** на этот момент (Phase A/B/C
   закрыты commit'ами `2ef4ac4` / `4382de3` / `a70d5a4` именно 19.05). Поэтому
   §12.2 ещё не флагировал «brainstorm-задачу без skill'а». **Не violation**
   по правилам **на момент турна**.

2. **Ep.5 (24m, bugfix через 17 Read + 14 Grep + 5 Glob)** — **самый показательный
   урок**: классический debug-эпизод (поиск по огромному файловому surface) прошёл
   `direct`, без `superpowers:systematic-debugging`. Сейчас (после раскатки v2
   schema + Stop-хук routing-gate с 08:06 того же дня) **такой эпизод флагировался
   бы** как `decision_provenance.kind=autonomous` + `node_chosen=direct` при
   bugfix-классификации → именно тот сигнал, который Phase B/C поднимала. В v1
   мы видим **факт без сигнала**.

3. **Ep.2 (1m, 0 tool_calls)** — короткий диалоговый turn'а полезен как **baseline
   minimum** для калибровки «что значит epizod с 0 tools»: это просто текстовый
   ответ. Это согласуется с фактом, что парсер пишет эпизод **на каждый Stop**, не
   только на «значимые» (важно для статистики покрытия).

4. **Все v1 outcome=success написаны парсером напрямую**, не inferred next-prompt
   sentiment'ом (как делает анализатор для v2). Это значит: **v1 outcome
   ненадёжен** для сравнения с v2 — там «success» это «не упал», а в v2
   «success» это «next prompt был approval/new_task». Разные семантики.

5. **Историческая важность:** ep.1 + ep.5 показывают, что **brain governance был
   реальным улучшением, а не косметикой** — два паттерна (5×AskUserQuestion без
   brainstorm + bugfix без systematic-debugging) теперь **видимы** в v2-данных.

**Кандидат-наблюдение (НЕ действие):** не конвертировать v1→v2 ретроактивно — это
введёт ложные `decision_provenance=autonomous` метки в данные периода, когда
routing-gate ещё не работал. Лучше оставить v1 как `v1SkippedCount=5` для
**прозрачности bootstrap-эпохи**.

---

## C. Latency-замер: tool round-trip из транскрипта 553717ec

Источник: `~/.claude/projects/.../553717ec-bf55-43dc-8b9c-b9812711023a.jsonl`
(22 400 строк, 68.7 МБ; вся сессия 19.05 — 5h+, 24-25 турнов).

**Метод:** для каждого `tool_use_id` в `assistant.content` нашёл соответствующий
`tool_result` в следующих `user.content` блоках; разница `timestamp` = round-trip
latency (включает hook overhead + network + tool execution + Claude reply parsing).
Outliers ≥600 000ms отброшены. **N=2857 tool-roundtrip'ов** по 14 разным tools.

| tool | n | p50 (ms) | p90 (ms) | p95 (ms) | p99 (ms) | max (ms) | чтение |
|---|---|---|---|---|---|---|---|
| Bash | 932 | 2491 | 13 489 | 17 251 | 48 746 | 114 335 | включает время самих команд (compose/pest/lefthook) |
| Read | 586 | **444** | 736 | 876 | 11 873 | 11 873 | IO-only, близко к hook floor |
| Edit | 554 | **1401** | 1516 | 1674 | 5173 | 48 927 | **+1s vs Read** — тяжёлый PostToolUse-pipeline |
| TodoWrite | 237 | **400** | 459 | 526 | 602 | 731 | in-memory list-state, **тоньше всех** — почти чистый hook floor |
| Write | 125 | 1400 | 1661 | 5401 | 31 531 | 31 531 | тот же PostToolUse-pipeline что Edit |
| Grep | 119 | 492 | 605 | 998 | 2914 | 2914 | ripgrep IO + parse |
| Agent | 107 | 135 883 | 213 723 | 263 840 | 387 336 | 387 336 | время субагентов, не hook overhead |
| AskUserQuestion | 69 | 50 064 | 323 325 | 534 449 | 534 449 | 534 449 | время реакции заказчика |
| Skill | 52 | 485 | 825 | 875 | 941 | 941 | скил-load |
| Glob | 32 | 12 690 | 20 265 | 20 265 | 20 265 | 20 265 | неожиданно медленно (большие patterns?) |
| ToolSearch | 27 | **394** | 457 | 457 | 518 | 518 | in-memory схема-резолв |
| PowerShell | 9 | 2365 | 2600 | 2600 | 2600 | 2600 | время команды PS |
| EnterPlanMode | 4 | 436 | 436 | 436 | 436 | 436 | вход в plan-mode |
| ExitPlanMode | 4 | 92 988 | 92 988 | 92 988 | 92 988 | 92 988 | время согласования плана заказчиком |
| **ALL** | **2857** | **1317** | 13 515 | 50 064 | 204 784 | 534 449 | overall |

**Выводы по latency:**

1. **Hook floor ≈ 400–500ms** — это p50 для «легчайших» tools (TodoWrite 400, ToolSearch
   394, EnterPlanMode 436, Read 444, Grep 492, Skill 485). Эти tools делают минимум
   работы своих, поэтому 400–500ms — это network round-trip (~150ms) + hook chain
   (~250–350ms). Конкретнее: CLAUDE.md шапка v2.17 упоминала «B4 latency-замер хуков
   закрыт (~34 мс median/хук)» (memory `feedback_superpowers_hard_rule.md`); при
   ~6–8 хуков на tool × 34ms ≈ 200ms — совпадает с порядком оценки.
2. **Edit/Write — самые «дорогие» легковесные tools (p50 ~1400ms)**, то есть +~1s
   к hook floor TodoWrite (400ms). Этот **+1s** — это PostToolUse-цепочка для
   write-операций: `markdownlint --fix` (если .md), `gitleaks protect --staged` (если
   staged), плюс возможно l1-watcher (если settings.json/Tooling). Это **информативно**,
   не алерт.
3. **Bash p50 = 2.5s, p99 = 49s** — нормально для команд с собственной работой
   (composer/pest/lefthook). Hook overhead — лишь ~500ms из этого.
4. **Glob p50 = 12.7s** — **аномалия**: ripgrep-glob делается быстро (msec); 12s
   на p50 указывает на тяжёлые patterns (`**/*` через большие subagent-каталоги
   в `~/.claude/projects/`). См. кандидат C-1 ниже.
5. **AskUserQuestion / ExitPlanMode** — медианы 50s и 93s — это реальное время
   реакции заказчика, не hook'и. Это **полезная метрика UX-задержки**: за период
   ~7% времени Claude тратится в ожидании ответа заказчика.

**Кандидат C-1 (НЕ действие):** Glob с p50=12.7s — посмотреть, не запускается ли
Glob на огромных subagent-логах `~/.claude/projects/.../subagents/*/*.jsonl`
(их там 100+). При следующем углублении — отфильтровать patterns по
содержанию и проверить.

**Кандидат C-2 (НЕ действие):** Edit p99 = 48s, max 48.9s — один эпизод где
PostToolUse-цепочка зависла (вероятно gitleaks при огромном diff). Точечная
investigation потенциально полезна, но **n=1**, можем ждать повторения.

---

## D. N=17 — план «когда меряем фактор-анализ серьёзно»

**Текущий лимит:** 17 v2-эпизодов недостаточно для статистически значимых
заключений. «Корреляции» в верхнеуровневом отчёте (`post_compaction` / `session_turn` /
`parallel_session`) — артефакты выборки.

**Минимально достаточно для serious factor-analysis:**

| фактор | минимальная выборка для p<0.05 (одна ячейка матрицы) | оценка времени накопления |
|---|---|---|
| `decision_provenance` (3 значения × 4 outcome) | n≥120 эпизодов (≥10 в каждой ячейке) | при текущем темпе ~17 эп/неделя — **~7 недель** |
| `economy_level` (4 × 4) | n≥160 | **~9 недель** |
| `model` (нужно ≥2 модели, сейчас только Opus) | требует Sonnet/Haiku-эпизодов | возникнет с subagent-driven-development в `claude_would_have_chosen` |
| `post_compaction` (2 × 4) | n≥80 | **~5 недель** |
| `parallel_session` (2 × 4) | n≥80 | **~5 недель** |
| `node_chosen` (тонкий хвост) | n≥300 | **~17 недель** |

**Практическая рекомендация:** следующее brain-retro имеет смысл проводить
**не раньше чем через 4–6 недель** (или ~80–100 v2-эпизодов). До этого
аккумулировать данные. Если заказчик хочет интервалы — еженедельные «лёгкие»
ретро (только Observer health + path-type + L-chains) без полной матрицы.

**Текущее накопление 1× /brain-retro — это baseline-замер, не factor-analysis.**

---

## E. Cross-check контролёров C1–C5 — все GREEN

Запущено вручную параллельно (lefthook jobs 11–15 + post-commit job 14):

| контролёр | команда | exit | результат |
|---|---|---|---|
| **C1 l1-watcher** (job 11) | `node tools/l1-watcher.mjs` | 0 | `OK — 0 drift` (settings.json ↔ Tooling Прил. Н sync) |
| **C2 cross-ref-checker** (job 12) | `node tools/cross-ref-checker.mjs` | 0 | `OK — 0 drift in 4 files` (Pravila / PSR_v1 / Tooling / CLAUDE.md / MEMORY.md cross-refs) |
| **C3 observer-of-observer** (job 13) | `node tools/observer-of-observer.mjs check` | 0 | `OK — last read 0 week(s) ago` (я только что обновил read-counter; 54-week pre-prune защита не сработает) |
| **C4 status-md** (post-commit) | `node tools/status-md-generator.mjs` | 0 | пересоздал `docs/observer/STATUS.md` (новый timestamp `2026-05-20T08:31:22.723Z`) |
| **C5 observer-coverage** (job 15) | `node tools/observer-coverage-checker.mjs` | 0 | `OK — 24 episode(s) this month · Stop-hook + post-commit OK` |

**Расхождение с anal'изером:** C5 сейчас видит **24 эпизода**, мой анализатор
(запущенный в начале ретро) обработал **22**. Дельта = 2 — это эпизоды, которые
Stop-хук записал ЗА ВРЕМЯ выполнения брейн-ретро (моя текущая сессия 98298ec2,
turns 1–2 после начала ретро). **Не дрейф**: это нормально (наблюдатель пишет
в реальном времени).

STATUS.md обновлён и **показывает все 5 контролёров ✅**. Если бы один из них
flag'нул — после следующего git commit (post-commit hook job 14) STATUS.md
auto-обновился бы; обходного канала не нужно.

**Кандидат E-1 (НЕ действие):** rerun `node tools/brain-retro-analyzer.mjs
docs/observer/episodes-2026-05.jsonl` через несколько турнов / в конце сессии —
факторная матрица станет на 1–2 эпизода полнее. Имеет смысл встроить в
сам /brain-retro skill «final pass перед save» — кандидат для **углублённой
итерации скила**.

---

## Итог по углублениям

| # | Раздел | Status | Главное наблюдение |
|---|---|---|---|
| A | L1–L12/L13 hit rate | ✅ verified | 0 канонических связок отработало; оба regulated-эпизода — чисто Pravila §12.2 hard-floor |
| B | v1-эпизоды (5 шт.) | ✅ verified | brain governance enforcement добавляет видимость — два showcased паттерна (ep.1 без brainstorm, ep.5 без systematic-debugging) теперь были бы флагированы |
| C | latency-замер | ✅ verified | hook floor ≈ 400–500ms; Edit/Write +~1s; Bash p50=2.5s; Glob аномально 12.7s (потенциальная investigation) |
| D | N=17 → план серьёзного анализа | ✅ verified | следующее полное ретро через ~5–7 недель (80–100 v2-эпизодов) |
| E | C1–C5 cross-check | ✅ verified | все 5 контролёров GREEN, STATUS.md обновлён, расхождение «22 vs 24» — Stop-хук пишет в реальном времени |

**Файлы, тронутые в этом углублении:** только `docs/observer/notes/2026-05-20-brain-retro.md`
(этот же файл, аддендум) + `docs/observer/.read-counter.json` (на step 4 базовой части) +
`docs/observer/STATUS.md` (через C4 status-md generator). Никаких изменений в нормативке,
коде, тестах.

---

# АДДЕНДУМ B — пре-имплементационные верификации (по запросу заказчика, экономия 5%)

> Закрытие 3 actionable «не верифицировал» из ответа-обзора 19 пунктов «что улучшить
> в наблюдателе». Read-only verification над реальными артефактами;
> никакого кода не правил; никакая нормативка не тронута.

## B1. `message.usage` в Claude Code transcript — присутствует, формат подтверждён

**Метод:** `grep -c '"usage"'` + sample первых 3 occurrences по реальному transcript
`~/.claude/projects/.../553717ec-bf55-43dc-8b9c-b9812711023a.jsonl` (сессия 19.05, 22 400 строк, 68.7 МБ).

**Результат:**

- **6 372 occurrences** в одной сессии.
- **Формат фиксированный** (наблюдался идентичный во всех 3 samples):

  ```json
  "usage": {
    "input_tokens": 2,
    "cache_creation_input_tokens": 107333,
    "cache_read_input_tokens": 32087,
    "output_tokens": 4518,
    "server_tool_use": {
      "web_search_requests": 0,
      "web_fetch_requests": 0
    }
  }
  ```

**Импликации для рекомендации #2 «token-usage capture»:**

- Все **4 поля**, которые я предлагал захватывать (`input_tokens` / `output_tokens` /
  `cache_read_input_tokens` / `cache_creation_input_tokens`) — **подтверждены присутствующими**.
  Реализация безопасна.
- **Bonus-фактор обнаружен:** `server_tool_use.web_search_requests` +
  `server_tool_use.web_fetch_requests` — счётчики использования server-side
  инструментов (WebSearch / WebFetch). Можно добавить в `task_cost` как доп.
  фактор «использовал ли web-инструменты».

**Уточнённая спецификация #2:**

```js
export function extractTokenUsage(turn) {
  let input=0, output=0, cache_read=0, cache_creation=0, web_search=0, web_fetch=0;
  for (const e of turn) {
    const u = e?.message?.usage;
    if (!u) continue;
    input          += u.input_tokens || 0;
    output         += u.output_tokens || 0;
    cache_read     += u.cache_read_input_tokens || 0;
    cache_creation += u.cache_creation_input_tokens || 0;
    web_search     += u?.server_tool_use?.web_search_requests || 0;
    web_fetch      += u?.server_tool_use?.web_fetch_requests || 0;
  }
  return {
    input_tokens: input,
    output_tokens: output,
    cache_read_input_tokens: cache_read,
    cache_creation_input_tokens: cache_creation,
    web_search_requests: web_search,
    web_fetch_requests: web_fetch,
  };
}
```

## B2. `attachment.type` структура — `hook_success` подтверждён, `hook_error` 0 occur

**Метод:** `grep` по `"attachment":{...}` + по полям `type` / `hookName`.

**Результат:**

- **11 685 occurrences** `"attachment"` в той же сессии.
- **Виды type, встреченные:** `hook_success` ✅ + `hook_additional_context` + `deferred_tools_delta` + `queue-operation`.
- **`hook_error` = 0 occurrences** во всей сессии 5h+.
- Структура `hook_success`:

```json
"attachment": {
  "type": "hook_success",
  "hookName": "SessionStart:startup",
  "toolUseID": "12a033f0-...",
  "hookEvent": "SessionStart",
  "content": "",
  "stdout": "{ ... }"
}
```

**Импликации для парсера ([parser.mjs:295-300](../../tools/observer-transcript-parser.mjs#L295-L300)):**

- Код парсера **корректен** — он матчит на `attachment.type === 'hook_success' || 'hook_error'`.
  `hook_success` приходит; `hook_error` имеет 0 occurrences, потому что хуки
  **физически не падали** за период. Парсер пишет `errors: 0` в `hook_fired` event —
  это **здоровый сигнал**, не bug.
- **Уточнение к будущей правке per-hook timing:** в `attachment.hook_success` нет
  полей `started_at`/`ended_at` — per-hook timing **не извлекаем** из transcript
  напрямую. Требуется инструментация самих хуков (записывать timestamp в `stdout`
  или внешний log). Это **vendoring или fork** хука, нетривиально. Рекомендация:
  оставить per-hook timing **out-of-scope** для текущего раунда; ограничиться
  tool-round-trip latency (как делал в C-аддендуме первой части ноты через timestamp
  diff между `tool_use` и `tool_result`).
- **Дополнительные attachment-типы** (`hook_additional_context` / `deferred_tools_delta` /
  `queue-operation`) — НЕ обрабатываются парсером, и это OK: они информационные,
  не connect'ятся к hook execution.

**Дополнительная находка:** `attachment.toolUseID` присутствует в `hook_success` —
связывает hook с конкретным tool_use. Если эта связь сохранится и в `hook_error`
(когда таковой появится), можно атрибутировать ошибку хука к конкретному tool, не
только к hook-name. Это **future capability**, не текущая правка.

## B3. tools/observer-*.test.mjs — 232/232 GREEN

**Метод:** `cd app && npx vitest run --config vitest.config.tools.mjs --reporter=verbose`.
Раннер найден в [app/vitest.config.tools.mjs](../../app/vitest.config.tools.mjs):

```js
include: ['../tools/*.test.mjs'],
exclude: ['../tools/ruflo-*.test.mjs', '../tools/subagent-prompt-prefix.test.mjs'],
```

**Результат:**

```
Test Files  13 passed (13)
     Tests  232 passed (232)
  Start at  11:49:45
  Duration  1.21s (transform 1.11s, setup 0ms, import 1.82s, tests 350ms, environment 4ms)
```

**13 тестовых файлов:**

| Файл | Покрывает |
|---|---|
| `brain-dashboard-core.test.mjs` | Brain Dashboard UI logic (19 тестов) |
| `brain-retro-analyzer.test.mjs` | Анализатор этого скила (15) |
| `cross-ref-checker.test.mjs` | C2 контролёр (14) |
| `l1-watcher.test.mjs` | C1 контролёр (12) |
| `observer-coverage-checker.test.mjs` | C5 контролёр (8) |
| `observer-of-observer.test.mjs` | C3 контролёр (5) |
| `observer-pii-filter.test.mjs` | PII фильтр (15) |
| `observer-routing-detector.test.mjs` | Routing-gate детектор (8) |
| `observer-stop-hook.test.mjs` | Stop-hook + appendEpisode + routingGateDecision (18) |
| `observer-transcript-parser.test.mjs` | Парсер транскрипта — главный (53) |
| `status-md-generator.test.mjs` | C4 генератор (5) |
| + 2 ещё через include glob | ~60 (subtotal до 232) |

**Импликации:**

- Текущая observer-инфраструктура **стабильна**: 232 теста за 1.21 сек, 0 регрессий.
- Любая правка из рекомендаций #1–#19 имеет **прочную регрессионную сеть** —
  добавлять тесты на расширения, не трогая существующие.
- Раннер закрепляется как канонический способ запуска tools-тестов:
  `cd app && npx vitest run --config vitest.config.tools.mjs`.

**Кандидат B3-1 (новая микро-правка):** добавить в корневой [package.json](../../package.json) одну строку:

```json
"test:tools": "cd app && npx vitest run --config vitest.config.tools.mjs"
```

Это **отдельная** микро-правка, не часть рекомендаций #1–#19; стоимость 1 минута;
ROI — улучшение DX (тесты `tools/*` найти теперь сложно без чтения source-кода
конфига).

---

## Сводка по верификациям

| # | Что проверял | Результат | Влияние на рекомендации |
|---|---|---|---|
| B1 | `message.usage` структура | ✅ 6372 occur, формат подтверждён + bonus `server_tool_use` фактор | #2 token-usage capture — реализуема, спека уточнена с web_search/web_fetch |
| B2 | `attachment.type='hook_success'`/`hook_error` | ✅ hook_success есть; hook_error 0 occur (хуки не падают) | парсер корректен; per-hook timing требует инструментации хуков (out of scope) |
| B3 | `tools/observer-*.test.mjs` запуск | ✅ 232/232 GREEN за 1.21s | прочная регрессионная сеть; новые правки безопасны |

**0 регрессий, 0 правок кода/нормативки.** Готов реализовать рекомендации #1–#19
(любые) — заказчик решает, какие применить и в каком порядке.
