Pure deterministic Layer-4 aggregation module (spec §6) for the /brain-retro
skill. Exports: dedupeEpisodes, inferOutcome, groupEpisodesToTasks,
findCausalChains, buildFactorMatrix, analyze. Read-only — never writes JSONL.
11/11 tests green. CLI smoke: 10 real episodes → valid JSON with all 5 keys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12-task plan implementing the spec
docs/superpowers/specs/2026-05-19-observer-factor-analysis-design.md
in 4 layers (schema v2 + capture + enforcement + analysis) plus
normative sync. Each task has TDD steps with full code.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Design for making the brain governance observer rich enough for real
factor analysis. Surfaced during a discussion with the owner: the
observer is "paper-complete" but episodes lack the data factor analysis
needs — the outcome is a hardcoded "success", there is no decision
provenance (who chose the node — Claude autonomously, or the owner
forcing a method), no environment factors, no task grouping.
4-layer architecture:
- Layer 1 — episode schema v2: decision_provenance (+ counterfactual),
environment block, task_size, real outcome enum, task_ref.
- Layer 2 — capture: deterministic transcript parsing for all factors +
a one-line routing tag (owner-forced-method only).
- Layer 3 — two-sided enforcement: 3a routing-gate (Stop-hook blocks the
turn until the tag is present — unbypassable by Claude); 3b observer
self-discipline (silent failures become recorded observer_error
markers; coverage + registration verified by a controller).
- Layer 4 — analysis: /brain-retro infers real outcome from the next
episode's opening prompt, groups episodes into tasks, correlates
causal chains, builds the factor matrix.
Scope: everything except an independent agent-judge — that, plus
confusion_marker as a real judgment and real-time friction flags, is
phase 2 (separate spec).
Brainstormed via superpowers:brainstorming. Next: writing-plans.
Refs: ADR-011, spec 2026-05-19-brain-governance-design.md, Pravila §16.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The committed STATUS.md was stale (generated 2026-05-19T03:49, before
the C1/C2 strict-mode fixes and before the post-commit hook existed):
it showed C1/C2 🔴 and "0 episodes". Regenerated via the now-installed
post-commit hook (C4 status-md job) — C1/C2/C3/C4 all ✅, 5 episodes.
Context: `.git/hooks/post-commit` was never installed, so the C4
status-md job (lefthook post-commit) never ran automatically. Fixed
locally via `lefthook install --force` (installs pre-commit/post-commit/
pre-push). The hook files live in `.git/` and are not version-tracked —
re-run `lefthook install` after clone if hooks go missing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Stop-hook was writing empty-shell episodes (task_id "unknown-<ts>",
node_chosen "unknown", events []). Root cause: buildEpisodeFromContext
read fields from the Stop-event stdin that Claude Code never sends
(primary_rationale, node_chosen, ...) and the session field name was
wrong (ctx.sessionId camelCase vs Claude Code's session_id). The hook
never read transcript_path — the only real source of session data.
New tools/observer-transcript-parser.mjs — pure parseTranscript(text,
fallbackSessionId):
- Scopes to the last turn (from the last real user prompt to EOF) —
one episode == one prompt→response cycle. A tool_result-carrier user
message is not treated as a turn boundary.
- Extracts task_id (real sessionId), timestamps (real duration),
skill_invoked events, a tool_summary event with per-tool counts,
error events (tool_result is_error), node_chosen (first skill, else
"direct"), hard_floor (invoked when a superpowers:* skill is used),
path_type (regulated/improvised), task_classification (keyword
heuristic on the prompt).
- Reasoning fields triggers_matched/candidates_considered/
boundaries_applied stay [] — not recoverable from a transcript;
their capture is a separate ADR-011 follow-up.
observer-stop-hook.mjs: reads ctx.transcript_path + ctx.session_id
(camelCase fallback kept), readFileSync best-effort, delegates to
parseTranscript. No transcript → graceful fallback to ctx defaults.
Episode schema (5 mandatory + 7-field primary_rationale) unchanged —
no normative change. Stop-event is never blocked (exit 0 on any error).
TDD: 17 parseTranscript tests + 1 buildEpisodeFromContext transcript
test. Full tools Vitest 70/70 GREEN. CLI smoke against a real 575-entry
transcript: episode populated — real task_id, ~6.5 min duration,
tool_summary {Bash:5,Read:5,Grep:1,Edit:9,Write:1}, error event.
Refs: ADR-011 brain governance §6.2 (observer evidence loop).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Memory files (e.g. feedback_brain_unused_tools_not_problem.md) live
in C:/Users/.../memory/, OUTSIDE the git repo. Markdown link from
docs/observer/STATUS.md (relative path) resolved to non-existent
in-repo path → lychee broken-link error in pre-push gate.
Fix: plain-text mention of memory key (no markdown link), with
explicit note «outside-repo memory store». Generator updated
accordingly; 31/31 Vitest tests still GREEN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aggregates C1/C2/C3 outputs via execFileSync (Security Guidance #40
compliant — uses fixed args array, no shell injection surface) +
observer episode count. Behavioral rule embedded in metric copy.
Per ADR-011 + spec §6.4.
3 Vitest tests GREEN (31/31 total).
Smoke run rebuilds STATUS.md with current state:
- C1 🔴 (l1-watcher surfaces 9 plugins in settings not formalized
in Tooling Прил. Н by exact name@source — see commit 4382de3)
- C2 🔴 (cross-ref-checker surfaces noise from 'наследие' headers
— see commit a780959 DWC)
- C3 ✅ (0 weeks since last read)
- C4 ✅ (this file)
Both 🔴 states surface known pre-existing drift (not regressions).
C5 lefthook wiring will handle WARN-vs-FAIL semantics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified Stop event collision before B5 registration:
- User-level (~/.claude/settings.json): Stop hook = agent-type
Sonnet-4.6 economy compliance verifier (already wired in
6-component arch).
- Project-level (.claude/settings.json): Stop slot empty.
observer-stop-hook will register as command-type entry in
project-level Stop array. Independent slot from user-level agent;
no overwrite, no collision. Per Pravila ADR-010 HK1 hard-rule.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Empty infrastructure per ADR-011 + Pravila §16.2. Hook + generators
wire up in subsequent tasks (B2 PII filter, B3 Stop-hook, B5 register
in settings.json, C4 STATUS generator).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
+ §0.1 row template (one-time, ADR-011 mandated).
+ Атрибуты block for phase-0 nodes #1-#9. #1 PostgreSQL MCP dormant
(replaced by #10 Boost in phase 1).
Per spec §4.1, plan Task A3 sub-batch 1. Tooling header v2.16
remains; final v2.17 bump after all 6 sub-batches.
NB: file-layout adaptation — phase-0 nodes #1-#9 live in §2 tables
(not §4.X subsections); Атрибуты blocks placed in new §2.4
subsection. Plan-template "§4.1..§4.9" referenced the abstract
node-index, not file headings; subsequent sub-batches will follow
same pattern (§3.5 for phase-1 nodes #10-#18, etc.).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single SoT for task→node routing. Replaces implicit routing scattered
across Pravila/PSR_v1/Tooling/routing-off-phase.md. ADR-011.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Соответствует spec v1.1 (544c8f3). Изменения:
Task A1 (ADR-011 text inside plan):
- Decision #2 «Observer scope B» расширено: упоминание 5 mandatory
fields (включая primary_rationale 7 sub-fields) + routing_decision
events для цепочек + что это enables factor analysis.
Task B3 (observer-stop-hook.test.mjs + observer-stop-hook.mjs):
- REQUIRED_FIELDS расширен с 4 до 5 ('primary_rationale').
- Новая константа RATIONALE_FIELDS (7 полей) + validateRationale()
функция, вызываемая внутри appendEpisode после top-level validation.
- buildEpisodeFromContext возвращает primary_rationale (либо из ctx,
либо default с extracted hints из ctx.skill_id/triggers_matched/etc).
- Tests: было 5 → стало 8. Новые: «throws when primary_rationale
field missing», «persists routing_decision events with structured
fields», «preserves user-provided primary_rationale unchanged».
Все old fixtures обогащены primary_rationale: defaultRat().
Task B6 (aggregation-template.md):
- Новая большая секция «Factor analysis matrix (v1.1+)» с 5 осями
факторов + cross-tab factor×factor. Tables для каждой оси:
triggers_matched, candidates_dropped_because, boundaries_applied,
hard_floor.rules, task_classification.
Self-review:
- Spec coverage table +row для §5.2.1.
Связано: spec v1.1 (544c8f3), plan v1.0 (ca93cf7), spec v1.0 (dd5bded).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
iter8 (commit 9fcefa3) проставил 10 ruflo-узлам флаг
NODE_META.isolated=true, но это метаданные — рендер vis.js
флаг не читал, узлы рисовались обычным оранжевым цветом
группы ruflo. На карте изоляция была не видна.
Изоляция через group-level (переживает режимы карты —
теплокарту/фильтр, которые перезаписывают opacity/borderWidth,
но не color/shapeProperties):
- GROUPS.ruflo: оранжевый #ff8800 → серый #555555 +
shapeProperties.borderDashes [4,4] + приглушённый шрифт #8a8a8a
- легенда-фильтр: dot оранжевый → серый dashed, текст
«🌊 ruflo (оркестратор)» → «🔇 ruflo (изолирован 18.05)»
- hk_ruflo_queen: group 'hooks' → 'ruflo' (10-й изолированный
узел, был в hooks-кластере — теперь визуально в ruflo)
- CATEGORY_LABELS.ruflo: «оркестратор» → «изолирован»
Группа ruflo не опустела (все 9 её узлов изолированы) — фильтр
group:ruflo продолжает работать. NODE_META.isolated флаги
не трогались (data-слой корректен с iter8).
Верификация: JS-синтаксис проверен (vm.Script parse OK) +
stylelint GREEN (color-hex-length fix #888→#888888). Визуальный
рендер в браузере НЕ проверен — Playwright-профиль занят
параллельной Claude-сессией (тот самый mcp_pw↔sk_parallel
same-dir case). shapeProperties — документированная vis.js
group-опция, риск низкий.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7 задач TDD: миграция vid->nullable, rework SupplierCsvParser +
CsvReconcileJob, +3 метода SupplierPortalClient, scheduler 30 мин,
API + UI-экран «Интеграция с поставщиком».
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Резервный CSV-канал импорта лидов от crm.bp-gr.ru: страховка на случай
обрыва webhook. Сверка отчёта поставщика «Запрос номеров» (CSV 3 колонки
Name;Tag;Phone) каждые 30 мин + кнопка вручную; дедуп по phone+project;
recovery пропущенных лидов; drift-детект падения webhook.
Дизайн утверждён заказчиком. Ключевые решения: vid → nullable (CSV не
даёт vid), окно 2 кал. дня, rework SupplierCsvParser/CsvReconcileJob под
реальный async-флоу заказа отчёта.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ветка ребейзнута на parallel-sessions §15 — Pravila v1.27 и CLAUDE.md
v2.14 параллельно заняты §15-эпиком, перенумеровано Pravila→v1.28 /
CLAUDE.md→v2.15. Sync cross-refs: Tooling §0+§13 footer, PSR_v1 §0
entry, automation-graph rule-labels (pravila/claude_md узлы),
+rebase-девиация note в plan. Tooling v2.14 / PSR_v1 v3.13 — без
изменений (§15 их не трогал).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>