Phase 3 Task 19 partial — coverage announcement §4.9 deferred to a
separate commit (touches Pravila §17, requires §15.2 pre-flight sync).
- tools/brain-retro-sanity-generator.mjs (NEW, pure):
generateCandidateQuestions(episodes) returns ≤5 sanity questions
derived from per-classification volume (>10 episodes per task type
triggers a themed question: bugfix/feature/planning/refactor/security/
marketing) plus 2 meta questions about missed activations / direct
bypass. Reads task_type from classifier_output (v4) with fallback
to primary_rationale.task_classification (v2/v3). Spec §4.7.
- tools/brain-retro-sanity-generator.test.mjs (NEW): 6 tests
(bugfix >10 / feature >10 / max 5 / empty / legacy v2/v3 / strings).
- .claude/skills/brain-retro/SKILL.md:
+ description rewritten — "раз в 1-2 недели OR sanity-check threshold"
(cadence change per spec §4.7).
+ procedure +steps 5a (sanity questions via AskUserQuestion +
PII filter + sanity-checks/YYYY-MM-DD.json), 5b (reviewer-agent
Task() spawn + fallback to brain-retro-opus-reviewer.mjs), 9
(self-retrospect threshold check), 10 (cost report from
~/.claude/runtime/cost-daily.json), 11 (richer summary).
- .claude/skills/self-retrospect/SKILL.md (NEW) — stub skill;
full procedure wired in Task 20 (analyzer + STATUS.md surface the
threshold).
- docs/observer/.self-retrospect-counter.json (NEW): initial state
{last_run_at: null, episodes_since_last: 0}.
- docs/observer/sanity-checks/.gitkeep (NEW): directory placeholder
for sanity-answers JSON files.
Tests: 608 passed / 0 failed (+15 from Task 19 + prior). 4 pre-existing
file fails unchanged. Coverage announcement §4.9 (economy-mode.py +
Pravila §17 subsection + feedback memory + coverage-annotation-mode
flag) — deferred: touches Pravila which is in the §15.2 8-file SoT
list and needs pre-flight `git fetch origin && git log HEAD..origin/main`
before edit; flagging as Phase 3 follow-up commit.
Observer infrastructure
Passive evidence-loop for the Лидерра «brain» per ADR-011.
Files
episodes-YYYY-MM.jsonl— append-only JSONL, one line per Stop-event. Schema v2 (schema_version: 2): the 5 mandatory fields +decision_provenance(who chose the node),environment(economy_level / model / post_compaction / session_turn / parallel_session),task_size,task_ref,prompt_signal, and anoutcomethat isunknownat write time (refined by/brain-retro). On an internal hook failure a minimalobserver_errormarker line is written instead of a silent skip. Written bytools/observer-stop-hook.mjsviatools/observer-transcript-parser.mjs.notes/YYYY-MM-DD-<slug>.md— optional MD notes for sessions with qualitative history.STATUS.md— auto-generated dashboard. Regenerated per-commit bytools/status-md-generator.mjs..read-counter.json— C3 observer-of-observer counter. Updated on Read of observer files.dashboard.html+dashboard.js+dashboard-core.js— Brain Dashboard: visualises the episode log over the automation-graph topology (4 views — Карта / Разбор / Лента / Агрегат). Runnpm run brain:dashboard, open the printed localhost URL.dashboard-core.jsis pure logic, unit-tested intools/brain-dashboard-core.test.mjs.
Lifecycle
- Write: every Stop-event appends one JSONL line, parsed from the session transcript (Stop-hook).
- Aggregate:
/brain-retroskill reads JSONL each sprint, proposes regulatory candidates. - Surface:
STATUS.mdshows controllers + monthly stats. - Self-prune: C3 warns if 54 weeks pass without any read of observer files.
Routing-tag discipline
When the user dictates a specific method/node (e.g. «запусти discovery-interview»), Claude must emit one line in its response:
<!-- routing: provenance=user_directed_method node=<chosen> counterfactual=<node Claude would have chosen autonomously> -->
The Stop-hook routing-gate (tools/observer-routing-detector.mjs + routingGateDecision) detects a dictated method; if the tag is missing it returns decision: block, so the turn cannot end without the tag. The gate fires at most once per turn (stop_hook_active guard). This makes decision_provenance reliable — factor analysis can separate a router error from a user-dictated one.
Privacy
PII filter (phone numbers, emails, tokens) is applied before every write — see tools/observer-pii-filter.mjs. gitleaks pre-push also scans observer files as part of full-history sweep.
Don't
- Don't edit
episodes-*.jsonlmanually — it's append-only. - Don't write outside
docs/observer/notes/for hand-curated notes. - Don't change
.read-counter.jsonmanually — it's maintained by hooks.
HK1 pre-check (Pravila ADR-010) — verified 2026-05-19
Before registering tools/observer-stop-hook.mjs on Stop event (Task B5), verified collision against 6-component economy/skill-discipline architecture:
- User-level
~/.claude/settings.jsonalready has Stop hook: agent-type Sonnet-4.6 economy compliance verifier (analyzes transcript for claim-without-evidence violations). - Project-level
.claude/settings.json— Stop slot empty.
Result: no overwrite. observer-stop-hook will be added as command-type entry in project-level Stop array. Project + user scopes are independent slots in Claude Code 2.x — both run on the same Stop event without conflict. The agent verifier (user scope) and the JSONL appender (project scope) have non-overlapping responsibilities.