docs(brain): spec v1.0 → v1.1 — factor analysis amendment (routing_decision + primary_rationale)

User-requested перед запуском суб-агента: observer должен фиксировать
не только факт выбора узла, но и причину — чтобы был возможен
факторный анализ через /brain-retro.

Изменения §5.2:

- 4 обязательных поля → 5 (+primary_rationale на эпизод-уровне).
- Новое событие routing_decision в массиве events[] (1 на каждое
  решение роутера в сессии; для цепочки из N — N событий).
- Новая под-секция §5.2.1 — структура 7 полей (step / node_chosen /
  triggers_matched / candidates_considered / boundaries_applied /
  hard_floor / task_classification). primary_rationale — копия
  первого routing_decision для дешёвой агрегации без чтения events[].
- Полный JSON-пример эпизода с цепочкой из 2 узлов.

Изменения §5.5:

- /brain-retro aggregation расширен новой секцией «Факторная матрица»:
  таблица «узел × фактор × частота» + cross-tab «фактор × фактор».
  5 осей факторов: triggers / dropped_because / boundaries /
  hard_floor.rules / task_classification.

Эффект: /brain-retro теперь может выдавать утверждения уровня «#55
выбрался против #53 по ADR-009 7 раз и по triggers-match 5 раз», а
не просто «#55 использован 12 раз». Это closes гэп факторного
анализа.

Header bump v1.0 → v1.1. ADR-011 текст в плане Task A1 будет
обновлён следующим коммитом (план amendment).

Связано: dd5bded (spec v1.0), ca93cf7 (plan v1.0).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-05-19 04:45:18 +03:00
parent ca93cf7652
commit 544c8f3081
@@ -1,7 +1,8 @@
---
title: Brain governance design — router-only + observer scope B + 4 контролёра
date: 2026-05-19
status: design (approved by user — awaiting written-spec user review per brainstorming flow)
version: 1.1
status: design (approved by user; v1.1 amendment 2026-05-19 — structured routing_decision + primary_rationale for factor analysis per user request before subagent execution)
author: Дмитрий (заказчик) + Claude (Opus 4.7) via superpowers:brainstorming
related:
- docs/discovery/2026-05-18-system-audit-brain.md
@@ -188,7 +189,7 @@ Observer **только пишет evidence, не вмешивается**. Ни
**Action**: append одной JSONL-строки в `docs/observer/episodes-YYYY-MM.jsonl` (rotation помесячно — простое именование, никакой инфраструктуры).
**4 обязательных поля**:
**5 обязательных полей** (v1.1 — factor analysis amendment 2026-05-19):
| Поле | Тип | Описание |
|---|---|---|
@@ -196,18 +197,66 @@ Observer **только пишет evidence, не вмешивается**. Ни
| `timestamps` | `{started_at, ended_at}` ISO-8601 | Начало и конец сессии (для time-burn анализа) |
| `path_type` | enum: `regulated` / `improvised` / `alternative` / `mixed` | Пошёл ли роутер по канонической связке L1–L12, импровизировал, ушёл альтернативой или смешанно |
| `outcome` | enum: `success` / `partial` / `failure` / `aborted` | Итог сессии |
| `primary_rationale` | structured object (см. §5.2.1) | **Главное решение роутера на эпизод** — структурированные факторы выбора первичного узла. Дублирует первое `routing_decision` событие в `events[]` для быстрых агрегатов без чтения массива. |
**Структурированные события** (опционально, массив `events[]`):
| Тип | Описание |
|---|---|
| `routing_decision` | **Структурированный факт выбора узла** — 7 полей (см. §5.2.1). Записывается один раз на каждое решение роутера в сессии (если цепочка из N узлов — N событий с возрастающим `step`). Это основа факторного анализа. |
| `hook_fired` | Сработал хук (skill-marker, skill-check, state-guard, postcompact, verifier, economy-mode, security-guidance, ruflo-* — текущая 6+ компонентная архитектура) |
| `chain_divergence` | Роутер пошёл не по канонической связке, хотя совпадение триггеров было |
| `skill_invoked` | Инвокирован skill — записать `skill_id` + `reason` |
| `chain_divergence` | Роутер пошёл не по канонической связке, хотя совпадение триггеров было. Поля: `expected_chain` (L1L12 ID), `chosen_nodes[]`, `reason_summary`. Дополняет (не подменяет) `routing_decision` на этом же шаге. |
| `skill_invoked` | Инвокирован skill — поля `skill_id` + `reason` (free-text). Для факторного анализа выбора skill'а — используется отдельный `routing_decision` события с тем же `step`. |
| `error` | Возникла ошибка — записать `class` (например `quirk:72`) + `recovery_action` |
| `confusion_marker` | Заказчик/Claude пометил место как «запутано» (вручную через TODO-метку в промпте) |
| `time_burn` | Большой блок времени потрачен на одну операцию (порог: >5 минут на single tool-call) |
#### §5.2.1. Структура `routing_decision` и `primary_rationale`
Оба объекта используют **одинаковую схему 7 полей**`primary_rationale` это **копия первого `routing_decision`** в эпизоде, поднятая на уровень эпизода для дешёвой агрегации.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| `step` | int | yes (для events) / нет в primary_rationale | Номер решения в эпизоде (1, 2, 3 ...). Для primary_rationale — всегда 1. |
| `node_chosen` | string (`#NN` или `<name>`) | yes | Выбранный узел из Tooling Прил. Н §4.X. |
| `triggers_matched` | array of string | yes | Список триггеров из реестра, которые сработали (например `["discovery", "интервью", "JTBD"]`). Empty `[]` допустим если решение принято исключительно по hard-floor. |
| `candidates_considered` | array of `{node_id, dropped_because}` | yes | Узлы, которые роутер рассмотрел и отбросил. Каждый элемент содержит ID отброшенного узла и **строку причины** (ADR-ref / specificity / hard-floor / manual). Empty `[]` если альтернатив не было. |
| `boundaries_applied` | array of string | yes | ADR-ссылки или PSR_v1 R-ссылки, применённые для разруливания (например `["ADR-009", "PSR_v1 R15.3"]`). Empty `[]` если границ не применялось. |
| `hard_floor` | `{invoked: bool, rules: string[]}` | yes | Информация о hard-floor §12/§14/§15. Если применилось — `invoked: true` и в `rules` перечислены сработавшие правила (например `["Pravila §12"]`). |
| `task_classification` | string | yes | Категория задачи на момент решения (`discovery` / `brainstorm` / `writing-plan` / `TDD` / `debug` / `arch-decision` / `refactor` / `docs` / `sync` / `migration` / `commit` / `review` / `other`). |
**Пример** (запись эпизода с цепочкой из 2 узлов):
```json
{
"task_id": "2026-05-19-brain-governance-plan",
"timestamps": {"started_at": "2026-05-19T04:00:00+03:00", "ended_at": "2026-05-19T04:35:00+03:00"},
"path_type": "regulated",
"outcome": "success",
"primary_rationale": {
"step": 1,
"node_chosen": "superpowers:brainstorming",
"triggers_matched": ["brainstorm", "design"],
"candidates_considered": [
{"node_id": "superpowers:writing-plans", "dropped_because": "specificity: brainstorm предшествует writing-plans"}
],
"boundaries_applied": ["PSR_v1 R15.3"],
"hard_floor": {"invoked": true, "rules": ["Pravila §12"]},
"task_classification": "brainstorm"
},
"events": [
{"kind": "routing_decision", "step": 1, "node_chosen": "superpowers:brainstorming", "triggers_matched": ["brainstorm", "design"], "candidates_considered": [...], "boundaries_applied": ["PSR_v1 R15.3"], "hard_floor": {"invoked": true, "rules": ["Pravila §12"]}, "task_classification": "brainstorm"},
{"kind": "routing_decision", "step": 2, "node_chosen": "superpowers:writing-plans", "triggers_matched": ["writing-plans"], "candidates_considered": [], "boundaries_applied": [], "hard_floor": {"invoked": true, "rules": ["Pravila §12"]}, "task_classification": "writing-plan"},
{"kind": "skill_invoked", "skill_id": "superpowers:writing-plans", "reason": "terminal skill brainstorming-flow"}
]
}
```
**Граница**: `primary_rationale` НЕ дублирует все шаги цепочки — только **первое** решение. Полная цепочка читается через `events[].kind=="routing_decision"`. Это даёт два уровня агрегации:
1. **Дешёвая верхнеуровневая** (читай только `primary_rationale` каждого эпизода): «какой узел чаще всего открывает сессию», «какие факторы доминируют в первом решении».
2. **Глубокая факторная** (читай все `routing_decision` событий): «какие пары факторов чаще всего разруливают конкретные конфликты», «какие ADR-границы выходят на первый план».
### 5.3. Опциональные MD-заметки
**Путь**: `docs/observer/notes/YYYY-MM-DD-<slug>.md`. Создаются **только если у сессии есть качественная история, которая не помещается в JSONL**: длинный narrative разбора, скриншот ошибки, цепочка из 5+ узлов с границами и т.д.
@@ -239,6 +288,13 @@ Observer **только пишет evidence, не вмешивается**. Ни
- Топ-связки L1–L12 (использованы) + новые связки (path_type=improvised, повторившиеся ≥2 раз).
- Top `error` classes + recovery-патерны.
- `chain_divergence` cases — где роутер ушёл с канонической связки.
- **Факторная матрица** (v1.1+ amendment): по каждому узле в `routing_decision` событиях — частоты по 5 осям факторов:
- `triggers_matched` → какие триггеры чаще всего ведут к этому узлу
- `candidates_considered.dropped_because` → какие альтернативы чаще всего отбрасываются и по какой причине
- `boundaries_applied` → какие ADR-границы / R-rules чаще всего разруливают
- `hard_floor.rules` → как часто узел вынуждается hard-floor §12/§14/§15
- `task_classification` → в каких классах задач узел доминирует
Output — таблица «узел × фактор × частота» + cross-tab «фактор × фактор» для каждой пары факторов (например «ADR-009 ↔ triggers_matched=['discovery']» — 8 раз).
3. Возвращает **кандидатов на корректировку нормативки**:
- Новая связка L13+ (если improvised повторился ≥2 раз с success).
- Граница X ↔ Y нуждается в ADR-уточнении (если chain_divergence повторился).