# Observer Missed Activations — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Расширить правило Pravila §16.4 «не использован ≠ проблема» симметрией missed activation: эпизоды с профильной классификацией без активации релевантного узла — показывать в STATUS.md (C5) и `/brain-retro` как сигнал (не блок). DEFERRED-узлы исключены.

**Architecture:** Hybrid mapping. Manual JSON `tools/observer-classification-map.json` декларирует `classification → recommended_node_ids[]` (single source, ~30 строк, явная семантика). Auto-extracted JSON `tools/.node-dormancy.json` фиксирует `dormant: true/false` из Tooling Прил.Н §3.5/§4.X через pre-commit генератор (прецедент `.l1-watcher-aliases.txt`). Matcher в `brain-retro-analyzer.mjs` детерминирован, чистый: эпизод matches профильную классификацию + `node_chosen === 'direct'` + хотя бы один non-dormant релевантный узел → counted as missed. Порог — одиночный эпизод (§16.4 condition). STATUS.md C5 показывает `missed_activations: N`, ⚠️ при N>0. /brain-retro выводит per-node breakdown.

**Tech Stack:** Node.js ESM, Vitest, lefthook, Markdown (Pravila/ADR/skills/CLAUDE.md).

**Branch:** `feat/observer-missed-activations` (worktree).

**Pre-flight gate (§15.2):** `git fetch origin && git log HEAD..origin/main --oneline` — нормативные файлы (Pravila/CLAUDE.md/Tooling/PSR_v1/MEMORY.md/Открытые_вопросы/docs/adr/*/db/schema.sql) должны быть синхронны с origin/main перед стартом и перед каждой нормативной правкой (Task 10-14).

---

## File Structure

**New files:**

- `tools/observer-classification-map.json` — manual mapping classification → node_ids
- `tools/extract-node-dormancy.mjs` — Tooling §3.5/§4.X parser → `.node-dormancy.json`
- `tools/extract-node-dormancy.test.mjs` — Vitest cases
- `tools/.node-dormancy.json` — generated artifact (committed, like `.l1-watcher-aliases.txt`)
- `tools/missed-activations.mjs` — pure matcher (`detectMissedActivations`)
- `tools/missed-activations.test.mjs` — Vitest cases
- `docs/adr/ADR-011-brain-governance.md` — amendment section (existing file edit)

**Modified files:**

- `tools/brain-retro-analyzer.mjs` — import matcher, inject into `analyze()` output
- `tools/brain-retro-analyzer.test.mjs` — extend coverage for `missedActivations` field
- `tools/observer-coverage-checker.mjs` — surface missed count to STATUS.md
- `tools/observer-coverage-checker.test.mjs` — coverage for new surfacing
- `tools/status-md-generator.mjs` — render `missed_activations` in C5 detail + ⚠️
- `tools/status-md-generator.test.mjs` — new render tests
- `lefthook.yml` — new pre-commit job `extract-node-dormancy`
- `docs/Pravila_raboty_Claude_v1_1.md` §16.4 + version bump v1.35→v1.36
- `.claude/skills/brain-retro/SKILL.md` — behavior reminder update
- `.claude/skills/brain-retro/references/aggregation-template.md` — Missed Activations section
- `CLAUDE.md` §0 cross-ref + §9 entry (via `/claude-md-management:claude-md-improver`)

**Outside-repo memory:**

- `memory/feedback_brain_unused_tools_not_problem.md` — refresh under new conditional rule

---

## Task 1: Classification → Node Mapping (data)

**Files:**

- Create: `tools/observer-classification-map.json`

- [ ] **Step 1: Write the mapping file**

```json
{
  "$schema_version": 1,
  "description": "Mapping from observer transcript-parser task_classification values to recommended Tooling Прил.Н node IDs. Source of truth for missed-activation detection (Pravila §16.4 conditional rule). 'other' deliberately empty — no recommendation, never counts as missed. DEFERRED-узлы filtered out by .node-dormancy.json at runtime.",
  "map": {
    "refactor":     ["#11", "#12", "#43", "#64", "#65"],
    "bugfix":       ["#18", "#34"],
    "feature":      ["#19"],
    "planning":     ["#19", "#41", "#42"],
    "memory-sync":  ["#33"],
    "monitoring":   ["#34", "#35"],
    "analysis":     ["#25", "#39", "#53"],
    "cleanup":      ["#11", "#12"],
    "question":     ["#60"],
    "other":        []
  }
}
```

Rationale per bucket (for reviewers, not in JSON):

- `refactor` — Pint (style) + Larastan (types) + deptrac (layer-fitness) + Rector (auto-refactor) + PHP Insights (complexity)
- `bugfix` — Pest (test-driven), Sentry MCP (production runtime errors)
- `feature` — Superpowers brainstorming (entry-point for creative work via §12)
- `planning` — Superpowers writing-plans (§12 hard-rule) + CCPM + product-management
- `memory-sync` — claude-md-management (§5 п.10 mandatory channel)
- `monitoring` — Sentry MCP + Redis MCP (debug-runtime category)
- `analysis` — Semgrep + Trail of Bits + process-analysis
- `cleanup` — Pint (format) + Larastan (find dead types)
- `question` — context7 (library docs first choice)
- `other` — empty (genuinely unclassifiable prompts; never counts as miss)

- [ ] **Step 2: Validate JSON syntax**

Run: `node -e "JSON.parse(require('fs').readFileSync('tools/observer-classification-map.json', 'utf-8'))"`
Expected: no output, exit 0.

- [ ] **Step 3: Commit**

```bash
git add tools/observer-classification-map.json
git commit -m "feat(observer): add classification→node mapping for missed-activation detection"
```

---

## Task 2: Tooling Dormancy Extractor (data)

**Files:**

- Create: `tools/extract-node-dormancy.mjs`
- Test: `tools/extract-node-dormancy.test.mjs`

- [ ] **Step 1: Write the failing test**

```javascript
// tools/extract-node-dormancy.test.mjs
import { describe, it, expect } from 'vitest';
import { extractDormancy } from './extract-node-dormancy.mjs';

describe('extractDormancy', () => {
  it('parses 9-attribute row with dormant=false', () => {
    const md = [
      '#### #10 Laravel Boost',
      '',
      '**Атрибуты:**',
      '',
      '| id | name | kind | phase | subcategory | triggers | boundaries | dormant | last-touched |',
      '|---|---|---|---|---|---|---|---|---|',
      '| #10 | Laravel Boost | composer-dep | 1 | — | «SQL, Eloquent» | replaces #1 PG MCP | false | 2026-05-19 |',
    ].join('\n');
    expect(extractDormancy(md)).toEqual({ '#10': false });
  });

  it('parses dormant=true (DEFERRED node)', () => {
    const md = '| #17 | pg_partman | binary-dep | 1 | — | «partition mgmt» | none | true | 2026-05-19 |';
    expect(extractDormancy(md)).toEqual({ '#17': true });
  });

  it('handles multiple nodes in one pass', () => {
    const md = [
      '| #44 | Figma MCP | mcp-server | off-phase | design-tooling | «figma extract» | extract-only | true | 2026-05-17 |',
      '| #45 | Universal Icons MCP | mcp-server | off-phase | design-tooling | «svg search» | non-Lucide | false | 2026-05-17 |',
    ].join('\n');
    expect(extractDormancy(md)).toEqual({ '#44': true, '#45': false });
  });

  it('ignores header/separator rows', () => {
    const md = [
      '| id | name | kind | phase | subcategory | triggers | boundaries | dormant | last-touched |',
      '|---|---|---|---|---|---|---|---|---|',
    ].join('\n');
    expect(extractDormancy(md)).toEqual({});
  });

  it('ignores non-numeric ids (template placeholders)', () => {
    const md = '| #NN | <name> | <kind> | <phase> | <subcat or —> | «<triggers>» | <ADR-NNN or none> | false | 2026-05-19 |';
    expect(extractDormancy(md)).toEqual({});
  });
});
```

- [ ] **Step 2: Run test to verify it fails**

Run: `cd app && npx vitest run ../tools/extract-node-dormancy.test.mjs`
Expected: FAIL with "Cannot find module './extract-node-dormancy.mjs'"

- [ ] **Step 3: Write minimal implementation**

```javascript
// tools/extract-node-dormancy.mjs
#!/usr/bin/env node
/**
 * Tooling Прил.Н dormancy extractor — emits {id: dormant_bool} JSON for
 * the missed-activation matcher (Pravila §16.4 conditional rule).
 * Parses 9-attribute table rows; ignores headers/separators/templates.
 *
 * Security Guidance #40: pure parsing — no exec/execSync.
 */
import { readFileSync, writeFileSync } from 'fs';

const ROW_RE = /^\|\s*#(\d+)\s*\|[^|]+\|[^|]+\|[^|]+\|[^|]+\|[^|]+\|[^|]+\|\s*(true|false)\s*\|[^|]+\|$/gm;

export function extractDormancy(md) {
  const out = {};
  for (const m of md.matchAll(ROW_RE)) {
    out[`#${m[1]}`] = m[2] === 'true';
  }
  return out;
}

if (process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('/extract-node-dormancy.mjs')) {
  const src = readFileSync('docs/Tooling_v8_3.md', 'utf-8');
  const dormancy = extractDormancy(src);
  writeFileSync('tools/.node-dormancy.json', JSON.stringify(dormancy, null, 2) + '\n');
  console.log(`[extract-node-dormancy] OK — ${Object.keys(dormancy).length} nodes`);
}
```

- [ ] **Step 4: Run test to verify it passes**

Run: `cd app && npx vitest run ../tools/extract-node-dormancy.test.mjs`
Expected: PASS 5/5.

- [ ] **Step 5: Generate the artifact and verify shape**

Run: `node tools/extract-node-dormancy.mjs`
Expected stdout: `[extract-node-dormancy] OK — N nodes` (N ≥ 60).
Verify: `tools/.node-dormancy.json` contains `"#17": true`, `"#44": true`, `"#50": true`, `"#54": true`, `"#67": true` and `"#10": false`.

- [ ] **Step 6: Commit**

```bash
git add tools/extract-node-dormancy.mjs tools/extract-node-dormancy.test.mjs tools/.node-dormancy.json
git commit -m "feat(observer): node-dormancy extractor + initial JSON snapshot"
```

---

## Task 3: Lefthook Job for Dormancy Regen

**Files:**

- Modify: `lefthook.yml`

- [ ] **Step 1: Find the existing pre-commit jobs section**

Run: `grep -n "extract-node-triggers\|pre-commit\|jobs:" lefthook.yml | head -20`

- [ ] **Step 2: Add a new job after the L1-watcher / chain-map-checker block**

```yaml
  17_extract_node_dormancy:
    glob: "docs/Tooling_v8_3.md"
    run: node tools/extract-node-dormancy.mjs && git add tools/.node-dormancy.json
```

(Job number 17 follows the existing 1-16 sequence. Confirm exact insertion point by reading `lefthook.yml` at the pre-commit section first.)

- [ ] **Step 3: Smoke-test the hook**

Run: `npx lefthook run pre-commit --files docs/Tooling_v8_3.md`
Expected: `17_extract_node_dormancy` runs, `tools/.node-dormancy.json` regenerated (no diff vs. Task 2 output).

- [ ] **Step 4: Commit**

```bash
git add lefthook.yml
git commit -m "build(lefthook): job 17 extract-node-dormancy on Tooling changes"
```

---

## Task 4: Missed-Activation Matcher (pure logic)

**Files:**

- Create: `tools/missed-activations.mjs`
- Test: `tools/missed-activations.test.mjs`

- [ ] **Step 1: Write the failing test**

```javascript
// tools/missed-activations.test.mjs
import { describe, it, expect } from 'vitest';
import { detectMissedActivations } from './missed-activations.mjs';

const map = {
  refactor: ['#11', '#12', '#43'],
  bugfix: ['#18', '#34'],
  feature: ['#19'],
  other: [],
};
const dormancy = { '#11': false, '#12': false, '#43': false, '#18': false, '#34': false, '#19': false };

function ep(classification, node_chosen) {
  return {
    schema_version: 2,
    primary_rationale: { task_classification: classification, node_chosen },
  };
}

describe('detectMissedActivations', () => {
  it('counts an episode with profile classification + node_chosen=direct as missed', () => {
    const result = detectMissedActivations([ep('refactor', 'direct')], map, dormancy);
    expect(result.totalMissed).toBe(1);
    expect(result.byNode).toEqual({ '#11': 1, '#12': 1, '#43': 1 });
  });

  it('does NOT count episode when the recommended node IS chosen', () => {
    const result = detectMissedActivations([ep('refactor', '#11')], map, dormancy);
    expect(result.totalMissed).toBe(0);
  });

  it('does NOT count episode when classification=other (empty list)', () => {
    const result = detectMissedActivations([ep('other', 'direct')], map, dormancy);
    expect(result.totalMissed).toBe(0);
  });

  it('excludes dormant (DEFERRED) nodes from recommendations', () => {
    const dorm = { ...dormancy, '#43': true };
    const result = detectMissedActivations([ep('refactor', 'direct')], map, dorm);
    expect(result.byNode).toEqual({ '#11': 1, '#12': 1 });
    expect(result.totalMissed).toBe(1);
  });

  it('returns totalMissed=0 when ALL recommended nodes are dormant', () => {
    const dorm = { '#11': true, '#12': true, '#43': true };
    const result = detectMissedActivations([ep('refactor', 'direct')], map, dorm);
    expect(result.totalMissed).toBe(0);
    expect(result.byNode).toEqual({});
  });

  it('ignores schema v1 episodes (no factor analysis)', () => {
    const v1 = { schema_version: 1, primary_rationale: { task_classification: 'refactor', node_chosen: 'direct' } };
    const result = detectMissedActivations([v1], map, dormancy);
    expect(result.totalMissed).toBe(0);
  });

  it('ignores observer_error markers', () => {
    const err = { observer_error: true };
    const result = detectMissedActivations([err], map, dormancy);
    expect(result.totalMissed).toBe(0);
  });

  it('ignores unknown classification (not in map)', () => {
    const result = detectMissedActivations([ep('unknown-bucket', 'direct')], map, dormancy);
    expect(result.totalMissed).toBe(0);
  });

  it('aggregates byClassification breakdown for the report', () => {
    const eps = [
      ep('refactor', 'direct'),
      ep('refactor', 'direct'),
      ep('bugfix', 'direct'),
    ];
    const result = detectMissedActivations(eps, map, dormancy);
    expect(result.byClassification).toEqual({ refactor: 2, bugfix: 1 });
    expect(result.totalMissed).toBe(3);
  });
});
```

- [ ] **Step 2: Run test to verify it fails**

Run: `cd app && npx vitest run ../tools/missed-activations.test.mjs`
Expected: FAIL with "Cannot find module './missed-activations.mjs'".

- [ ] **Step 3: Write minimal implementation**

```javascript
// tools/missed-activations.mjs
#!/usr/bin/env node
/**
 * Missed-activation matcher (Pravila §16.4 conditional rule).
 * Pure deterministic — read-only, no exec, no fs.
 *
 * An episode is "missed" iff:
 *   1. schema_version === 2 (v1 lacks factor data)
 *   2. NOT observer_error
 *   3. primary_rationale.task_classification ∈ map AND map[c].length > 0
 *   4. primary_rationale.node_chosen === 'direct' (no explicit node)
 *   5. AT LEAST ONE recommended node is non-dormant
 *
 * Threshold: single episode (per Pravila §16.4 v1.36).
 * DEFERRED-узлы filtered via dormancy registry.
 */

export function detectMissedActivations(episodes, classificationMap, dormancy) {
  const byNode = {};
  const byClassification = {};
  let totalMissed = 0;

  for (const e of episodes) {
    if (!e || e.observer_error) continue;
    if (e.schema_version !== 2) continue;
    const pr = e.primary_rationale || {};
    const cls = pr.task_classification;
    const chosen = pr.node_chosen;
    if (!cls || chosen !== 'direct') continue;

    const recommended = classificationMap[cls];
    if (!Array.isArray(recommended) || recommended.length === 0) continue;

    const live = recommended.filter((id) => dormancy[id] === false);
    if (live.length === 0) continue;

    totalMissed += 1;
    byClassification[cls] = (byClassification[cls] || 0) + 1;
    for (const id of live) {
      byNode[id] = (byNode[id] || 0) + 1;
    }
  }

  return { totalMissed, byNode, byClassification };
}
```

- [ ] **Step 4: Run test to verify it passes**

Run: `cd app && npx vitest run ../tools/missed-activations.test.mjs`
Expected: PASS 9/9.

- [ ] **Step 5: Commit**

```bash
git add tools/missed-activations.mjs tools/missed-activations.test.mjs
git commit -m "feat(observer): missed-activation matcher (pure, deterministic)"
```

---

## Task 5: Integrate Matcher into brain-retro-analyzer

**Files:**

- Modify: `tools/brain-retro-analyzer.mjs:196-216` (the `analyze()` function and CLI block)
- Modify: `tools/brain-retro-analyzer.test.mjs` (extend existing suite)

- [ ] **Step 1: Write the failing test (append to existing file)**

```javascript
// tools/brain-retro-analyzer.test.mjs — new describe block at the end
describe('analyze() — missedActivations integration', () => {
  it('includes missedActivations in the result', () => {
    const eps = [
      {
        schema_version: 2,
        task_id: 't1',
        timestamps: { started_at: '2026-05-21T00:00:00Z' },
        primary_rationale: { node_chosen: 'direct', task_classification: 'refactor' },
        events: [],
      },
    ];
    const map = { refactor: ['#11'], other: [] };
    const dormancy = { '#11': false };
    const result = analyze(eps, { classificationMap: map, dormancy });
    expect(result.missedActivations).toBeDefined();
    expect(result.missedActivations.totalMissed).toBe(1);
    expect(result.missedActivations.byNode).toEqual({ '#11': 1 });
  });

  it('returns missedActivations.totalMissed=0 when no map/dormancy provided', () => {
    const eps = [{ schema_version: 2, task_id: 't1', timestamps: { started_at: 'x' }, primary_rationale: { node_chosen: 'direct', task_classification: 'refactor' }, events: [] }];
    const result = analyze(eps);
    expect(result.missedActivations.totalMissed).toBe(0);
  });
});
```

- [ ] **Step 2: Run test to verify it fails**

Run: `cd app && npx vitest run ../tools/brain-retro-analyzer.test.mjs -t "missedActivations integration"`
Expected: FAIL (result.missedActivations undefined).

- [ ] **Step 3: Implement — extend imports and analyze()**

Edit `tools/brain-retro-analyzer.mjs`:

After the existing `import { readFileSync, existsSync } from 'fs';` line, add:

```javascript
import { detectMissedActivations } from './missed-activations.mjs';
```

Replace the `analyze` function signature and return block (lines 196-216) with:

```javascript
/** Full deterministic aggregation: dedup → infer outcomes → group → chains → matrix → missed activations. */
export function analyze(episodes, options = {}) {
  const deduped = dedupeEpisodes(episodes);
  const allNormal = deduped.filter((e) => !e.observer_error);
  const normal = allNormal.filter((e) => e.schema_version === 2);
  const v1SkippedCount = allNormal.length - normal.length;
  for (const eps of bySessionSorted(normal).values()) {
    eps.forEach((episode, i) => {
      episode._inferredOutcome = inferOutcome(episode, eps[i + 1]);
    });
  }
  const classificationMap = options.classificationMap || {};
  const dormancy = options.dormancy || {};
  return {
    episodeCount: normal.length,
    v1SkippedCount,
    observerErrorCount: deduped.length - allNormal.length,
    tasks: groupEpisodesToTasks(normal),
    causalChains: findCausalChains(normal),
    factorMatrix: buildFactorMatrix(normal),
    missedActivations: detectMissedActivations(normal, classificationMap, dormancy),
  };
}
```

In the CLI block at the bottom of the file (line 235), update to load both JSONs:

```javascript
if (process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('/brain-retro-analyzer.mjs')) {
  const classificationMap = (() => {
    try {
      return JSON.parse(readFileSync('tools/observer-classification-map.json', 'utf-8')).map || {};
    } catch { return {}; }
  })();
  const dormancy = (() => {
    try {
      return JSON.parse(readFileSync('tools/.node-dormancy.json', 'utf-8'));
    } catch { return {}; }
  })();
  const result = analyze(loadEpisodes(process.argv.slice(2)), { classificationMap, dormancy });
  console.log(JSON.stringify(result, null, 2));
  process.exit(0);
}
```

- [ ] **Step 4: Run tests to verify pass**

Run: `cd app && npx vitest run ../tools/brain-retro-analyzer.test.mjs`
Expected: PASS for all existing + 2 new cases.

- [ ] **Step 5: CLI smoke-test on real data**

Run: `node tools/brain-retro-analyzer.mjs docs/observer/episodes-2026-05.jsonl | grep -A 10 missedActivations`
Expected: prints `"missedActivations": { "totalMissed": N, "byNode": {...}, "byClassification": {...} }` where N reflects May classifications.

- [ ] **Step 6: Commit**

```bash
git add tools/brain-retro-analyzer.mjs tools/brain-retro-analyzer.test.mjs
git commit -m "feat(observer): wire missed-activation matcher into analyze()"
```

---

## Task 6: Surface Missed Count to STATUS.md via C5

**Files:**

- Modify: `tools/observer-coverage-checker.mjs:79-85` (`runCoverageChecker`)
- Modify: `tools/observer-coverage-checker.test.mjs`
- Modify: `tools/status-md-generator.mjs:107-130` (CLI block; render function unchanged in this task)

- [ ] **Step 1: Extend observer-coverage-checker to compute missed count**

Edit `tools/observer-coverage-checker.mjs`:

Add imports at top:

```javascript
import { detectMissedActivations } from './missed-activations.mjs';
import { dedupeEpisodes } from './brain-retro-analyzer.mjs';
```

After `countEpisodes`, add a helper:

```javascript
function loadEpisodes(root) {
  const month = new Date().toISOString().slice(0, 7);
  const file = join(root, 'docs', 'observer', `episodes-${month}.jsonl`);
  if (!existsSync(file)) return [];
  const out = [];
  for (const line of readFileSync(file, 'utf-8').split('\n')) {
    const t = line.trim();
    if (!t) continue;
    try { out.push(JSON.parse(t)); } catch { /* skip */ }
  }
  return out;
}

function loadClassificationMap(root) {
  try {
    return JSON.parse(readFileSync(join(root, 'tools', 'observer-classification-map.json'), 'utf-8')).map || {};
  } catch { return {}; }
}

function loadDormancy(root) {
  try {
    return JSON.parse(readFileSync(join(root, 'tools', '.node-dormancy.json'), 'utf-8'));
  } catch { return {}; }
}
```

Replace `runCoverageChecker`:

```javascript
export function runCoverageChecker(root = process.cwd()) {
  const settings = readSettings(root);
  const hookRegistered = isObserverStopRegistered(settings);
  const coverage = checkCoverage(countEpisodes(root), hookRegistered);
  const registration = checkRegistration(settings, existsSync(join(root, '.git', 'hooks', 'post-commit')));
  const episodes = loadEpisodes(root).filter((e) => e && e.schema_version === 2 && !e.observer_error);
  const missed = detectMissedActivations(
    dedupeEpisodes(episodes),
    loadClassificationMap(root),
    loadDormancy(root)
  );
  return { coverage, registration, missed };
}
```

- [ ] **Step 2: Update CLI block in observer-coverage-checker.mjs**

```javascript
if (process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('/observer-coverage-checker.mjs')) {
  const { coverage, registration, missed } = runCoverageChecker();
  if (!coverage.ok) console.warn(`[observer-coverage-checker] WARN — coverage: ${coverage.detail}`);
  if (!registration.ok) console.warn(`[observer-coverage-checker] WARN — registration: ${registration.detail}`);
  if (missed.totalMissed > 0) {
    console.warn(`[observer-coverage-checker] WARN — missed activations: ${missed.totalMissed} (see /brain-retro)`);
  }
  if (coverage.ok && registration.ok && missed.totalMissed === 0) {
    console.log(`[observer-coverage-checker] OK — ${coverage.detail}; ${registration.detail}`);
  }
  process.exit(0); // warn-only — never blocks a commit
}
```

- [ ] **Step 3: Write failing test for observer-coverage-checker**

```javascript
// tools/observer-coverage-checker.test.mjs — new describe block
import { describe, it, expect } from 'vitest';
import { runCoverageChecker } from './observer-coverage-checker.mjs';
// ... existing imports

describe('runCoverageChecker — missed surfacing', () => {
  it('returns a missed field with totalMissed', () => {
    const { missed } = runCoverageChecker();
    expect(missed).toBeDefined();
    expect(typeof missed.totalMissed).toBe('number');
  });
});
```

- [ ] **Step 4: Run tests**

Run: `cd app && npx vitest run ../tools/observer-coverage-checker.test.mjs`
Expected: PASS (existing + new).

- [ ] **Step 5: Commit**

```bash
git add tools/observer-coverage-checker.mjs tools/observer-coverage-checker.test.mjs
git commit -m "feat(observer): C5 surfaces missed-activation count via runCoverageChecker"
```

---

## Task 7: Render Missed Activations in STATUS.md

**Files:**

- Modify: `tools/status-md-generator.mjs:11-41` (`renderStatus`) + CLI block lines 107-130
- Modify: `tools/status-md-generator.test.mjs`

- [ ] **Step 1: Write failing test**

```javascript
// tools/status-md-generator.test.mjs — new describe block
describe('renderStatus — missed activations', () => {
  it('shows ✅ and missed=0 when there are no misses', () => {
    const md = renderStatus(baseInputs({ missed: { totalMissed: 0, byNode: {}, byClassification: {} } }));
    expect(md).toContain('| C5 Observer-coverage | ✅');
    expect(md).toContain('missed_activations: 0');
  });

  it('shows ⚠️ and the count when misses occur', () => {
    const md = renderStatus(baseInputs({
      missed: { totalMissed: 3, byNode: { '#11': 2, '#12': 1 }, byClassification: { refactor: 3 } },
    }));
    expect(md).toContain('| C5 Observer-coverage | ⚠️');
    expect(md).toContain('missed_activations: 3');
  });

  it('replaces the static «не проблема» line with the conditional rule', () => {
    const md = renderStatus(baseInputs());
    expect(md).toContain('Неиспользованные узлы — не алерт');
    expect(md).toContain('если профильной задачи не было');
    expect(md).toContain('feedback_brain_unused_tools_not_problem');
  });
});
```

(Update existing `baseInputs()` helper at the top of the file to accept an optional `missed` override; if not provided, default to `{ totalMissed: 0, byNode: {}, byClassification: {} }`. Also update the existing `mentions the capability-readiness behavioral rule` test — assertion text now includes the conditional phrase.)

- [ ] **Step 2: Run test to verify it fails**

Run: `cd app && npx vitest run ../tools/status-md-generator.test.mjs`
Expected: FAIL on the new describe block.

- [ ] **Step 3: Modify renderStatus**

Edit `tools/status-md-generator.mjs`:

Update `renderStatus` parameter destructuring (line 12):

```javascript
const { now, c1, c2, c3, c5, observer, lastRetroDaysAgo } = inputs;
const missed = inputs.missed || { totalMissed: 0, byNode: {}, byClassification: {} };
```

Update the C5 row icon logic — `c5.status` should already be `warn` when `missed.totalMissed > 0` (set in CLI block, Step 4 below). No row-template change needed except detail extension.

Update the detail string for C5 in the CLI block (Step 4).

Update the metrics paragraph (line 35):

```javascript
- Использование узлов: см. \`/brain-retro\` (раз в спринт). missed_activations: ${missed.totalMissed}. **Неиспользованные узлы — не алерт, если профильной задачи не было** (Pravila §16.4 v1.36; см. memory \`feedback_brain_unused_tools_not_problem\` — outside-repo memory store).
```

- [ ] **Step 4: Update CLI block in status-md-generator.mjs**

```javascript
if (process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('/status-md-generator.mjs')) {
  const cov = runCoverageChecker();
  const c5ok = cov.coverage.ok && cov.registration.ok && cov.missed.totalMissed === 0;
  const c5detail = [
    cov.coverage.detail,
    cov.registration.detail,
    cov.missed.totalMissed > 0 ? `${cov.missed.totalMissed} missed activation(s) — see /brain-retro` : null,
  ].filter(Boolean).join(' · ');
  const inputs = {
    now: new Date().toISOString(),
    c1: runControllerNode(['tools/l1-watcher.mjs']),
    c2: runControllerNode(['tools/cross-ref-checker.mjs']),
    c3: runControllerNode(['tools/observer-of-observer.mjs', 'check']),
    c5: { status: c5ok ? 'ok' : 'warn', detail: c5detail },
    c6: runControllerNode(['tools/observer-chain-map-checker.mjs']),
    observer: {
      episodeCount: countEpisodes(),
      observerErrors: countObserverErrors(),
      piiMatches: countPiiMatches(),
      v1Episodes: countV1Episodes(),
    },
    missed: cov.missed,
    lastRetroDaysAgo: lastRetroDaysAgo(),
  };
  const md = renderStatus(inputs);
  writeFileSync('docs/observer/STATUS.md', md);
  console.log(`[status-md-generator] OK — wrote docs/observer/STATUS.md`);
}
```

- [ ] **Step 5: Run tests + CLI smoke**

```bash
cd app && npx vitest run ../tools/status-md-generator.test.mjs && cd ..
node tools/status-md-generator.mjs
cat docs/observer/STATUS.md | head -40
```

Expected: tests PASS; STATUS.md contains the new `missed_activations` line and the conditional phrasing.

- [ ] **Step 6: Commit**

```bash
git add tools/status-md-generator.mjs tools/status-md-generator.test.mjs docs/observer/STATUS.md
git commit -m "feat(observer): render missed_activations in STATUS.md C5"
```

---

## Task 8: Pravila §16.4 Conditional Rule (v1.36)

**Files:**

- Modify: `docs/Pravila_raboty_Claude_v1_1.md:985-987` + version-bump header

- [ ] **Step 1: Re-run pre-flight sync (§15.2)**

```bash
git fetch origin
git log HEAD..origin/main --oneline -- docs/Pravila_raboty_Claude_v1_1.md
```

Expected: no commits ahead. If there are, halt and reconcile.

- [ ] **Step 2: Replace §16.4 body**

Replace lines 985-987 with:

```markdown
### 16.4. Поведенческое правило «не использован ≠ проблема» (условное)

Узел «мозга», не задействованный в реальной работе, **не** считается проблемой и **не** подлежит автоматической пометке **при условии, что профильной задачи для него в эпизодах не было**. Это — capability-readiness, осознанная стратегия заказчика.

**Симметричное правило (missed activation):** если в эпизодах присутствует **хотя бы один** эпизод с `primary_rationale.task_classification`, соответствующим набору рекомендуемых узлов из `tools/observer-classification-map.json`, при этом `primary_rationale.node_chosen === 'direct'` и среди рекомендуемых узлов есть хотя бы один non-dormant (по `tools/.node-dormancy.json`, экстракт `dormant`-флага из [Tooling Прил.Н §3.5/§4.X](Tooling_v8_3.md)) — это **сигнал**, кандидат на разбор. Surface в STATUS.md (C5: `missed_activations: N`, ⚠️ при N>0) и в выводе `/brain-retro`. Не блок коммита, не auto-edit.

**Исключения:** DEFERRED-узлы (на момент v1.36 — #17 pg_partman, #44 Figma MCP, #50 Jupyter MCP, #54 n8n-mcp, #67 NightOwl, признак `dormant: true` в Прил.Н) — для них «не активирован» = ожидаемое состояние, в missed activations не учитываются.

См. `memory/feedback_brain_unused_tools_not_problem.md`.
```

- [ ] **Step 3: Bump version header**

In the file header (top of Pravila):

- Bump `v1.35` → `v1.36`
- Date → `21.05.2026`
- Headline note: `§16.4 расширен симметрией missed activation (условное правило); хранение mapping в tools/observer-classification-map.json + .node-dormancy.json`

- [ ] **Step 4: Sync CLAUDE.md §0 cross-ref**

Through `/claude-md-management:claude-md-improver` (CLAUDE.md §5 п.10) — update row `Pravila v1.35` → `v1.36` and §9 entry. (This is a separate session/skill invocation; for plan tracking treat it as part of this task.)

- [ ] **Step 5: Commit**

```bash
git add docs/Pravila_raboty_Claude_v1_1.md CLAUDE.md
git commit -m "docs(normative): Pravila §16.4 v1.36 — conditional missed-activation rule"
```

---

## Task 9: ADR-011 Amendment

**Files:**

- Modify: `docs/adr/ADR-011-brain-governance.md`

- [ ] **Step 1: Append an Amendment section at the end of the ADR**

```markdown
## Amendment 2026-05-21: Conditional missed-activation rule (§16.4 v1.36)

The original §16.4 stated unconditionally that an unused node is not a problem. Real-world episodes show this is too permissive: when a profile-classified task (e.g. `refactor`) runs with `node_chosen === 'direct'` and a relevant non-dormant node exists in Tooling Прил.Н, the absence of activation IS a signal (router miss, not a problem in the node itself).

The rule now reads:

- **Unused + no profile task** → still not an alert (capability-readiness).
- **Unused + profile task present** → "missed activation", surfaced in STATUS.md C5 and `/brain-retro`. Not a commit block.

**Mapping artefacts:**
- `tools/observer-classification-map.json` — manual mapping `classification → recommended_node_ids[]` (single SoT).
- `tools/.node-dormancy.json` — generated from Прил.Н by `tools/extract-node-dormancy.mjs` (pre-commit job 17 in lefthook).
- `tools/missed-activations.mjs` — pure deterministic matcher.

**Detection threshold:** single episode (per user decision 2026-05-21).

**DEFERRED exclusion:** nodes with `dormant: true` (currently #17 / #44 / #50 / #54 / #67) are filtered before counting.

**Linkage:** Pravila §16.4 v1.36, plan `docs/superpowers/plans/2026-05-21-observer-missed-activations.md`.
```

- [ ] **Step 2: Commit**

```bash
git add docs/adr/ADR-011-brain-governance.md
git commit -m "docs(adr): ADR-011 amendment — conditional missed-activation rule"
```

---

## Task 10: brain-retro Skill + Aggregation Template

**Files:**

- Modify: `.claude/skills/brain-retro/SKILL.md:41`
- Modify: `.claude/skills/brain-retro/references/aggregation-template.md`

- [ ] **Step 1: Update SKILL.md behavior reminder**

Replace line 41 with:

```markdown
- **«Не использован ≠ проблема» (условное)** — when reporting node usage counts, distinguish two cases:
  1. **Unused + no profile task in episodes** → capability-readiness, do NOT flag.
  2. **Unused + profile task present (missed activation)** → mandatory section in the report. Cite `tools/observer-classification-map.json` for the mapping and `tools/.node-dormancy.json` for DEFERRED exclusions.
- **No auto-edit** — every regulatory suggestion is a candidate, not an action.
```

- [ ] **Step 2: Read existing aggregation-template.md to find the right insertion point**

Run: `head -40 .claude/skills/brain-retro/references/aggregation-template.md`

- [ ] **Step 3: Insert a new "Missed Activations" section after the factor-matrix template**

```markdown
## Missed Activations (Pravila §16.4 v1.36)

Surface candidates where a profile-classified task ran with `node_chosen === 'direct'` and at least one non-dormant recommended node was available.

**Pulled from:** `analyze(episodes, { classificationMap, dormancy }).missedActivations`.

| Node | Episodes missed | Classifications |
|---|---|---|
| #NN | N | refactor (a), bugfix (b) |

**Interpretation guide:**

- High count on one node → router miss pattern; suggest the node in `/brain-retro` aggregation as a candidate update to `observer-classification-map.json` or as a workflow nudge.
- Spread across many nodes with classification=other-leaning → classification dictionary may need refinement (separate concern, not a missed activation).
- All zero → either no profile work this period, or the router is operating cleanly.

**NOT to be auto-applied:** these are candidates for human review in retro, not commits or hook blocks.
```

- [ ] **Step 4: Commit**

```bash
git add .claude/skills/brain-retro/SKILL.md .claude/skills/brain-retro/references/aggregation-template.md
git commit -m "docs(brain-retro): conditional rule + Missed Activations section"
```

---

## Task 11: Outside-Repo Memory Refresh

**Files:**

- Modify: `C:\Users\Administrator\.claude\projects\c---------------------crm-------------\memory\feedback_brain_unused_tools_not_problem.md`

- [ ] **Step 1: Read current memory file**

Run: Read tool on `C:\Users\Administrator\.claude\projects\c---------------------crm-------------\memory\feedback_brain_unused_tools_not_problem.md`.

- [ ] **Step 2: Rewrite under new conditional rule**

```markdown
---
name: feedback-brain-unused-tools-not-problem
description: Conditional rule — unused brain nodes are not a problem only when no profile task was attempted; with a profile task they become missed-activation candidates surfaced in STATUS.md C5 and /brain-retro
metadata:
  type: feedback
---

**Rule:** A brain node not activated in real work is NOT a problem **only if there was no profile task for it in the episodes**. With a profile task present (`task_classification` matching the node per `tools/observer-classification-map.json`) and `node_chosen === 'direct'`, it becomes a **missed activation** — surfaced in STATUS.md C5 (⚠️) and `/brain-retro`.

**Why:** Original v1.0 of the rule (2026-05-19) was unconditional. 2026-05-21 user clarification — capability-readiness applies only to genuinely-not-needed nodes; when a profile task ran without the relevant node, the router missed.

**How to apply:**

- Use `tools/missed-activations.mjs` for detection (pure matcher).
- DEFERRED-узлы (#17 / #44 / #50 / #54 / #67) excluded via `tools/.node-dormancy.json`.
- Threshold: single episode (no smoothing).
- Surface, do not block — never an auto-edit, never a commit gate.

See [[pravila-16-4]] (Pravila §16.4 v1.36) and ADR-011 Amendment 2026-05-21.
```

- [ ] **Step 3: Update memory MEMORY.md entry if outdated**

Read `MEMORY.md`; if the line for `feedback_brain_unused_tools_not_problem` needs the description tightened to the conditional form, update it (one line under 200 chars).

- [ ] **Step 4: No git commit — outside-repo memory is not tracked**

Just verify the file was written.

---

## Task 12: Regression + Push

- [ ] **Step 1: Run full local regression**

```bash
cd app && npx vitest run ../tools/ && cd ..
node tools/extract-node-dormancy.mjs
node tools/status-md-generator.mjs
git diff docs/observer/STATUS.md
```

Expected: all Vitest GREEN; STATUS.md diff shows new `missed_activations` line and conditional phrasing.

- [ ] **Step 2: Run lefthook full pre-commit**

```bash
npx lefthook run pre-commit
```

Expected: all jobs GREEN incl. new job 17.

- [ ] **Step 3: Verify no normative file lag (§15.2 final)**

```bash
git fetch origin
git log HEAD..origin/main --oneline -- docs/Pravila_raboty_Claude_v1_1.md docs/Tooling_v8_3.md CLAUDE.md docs/Plugin_stack_rules_v1.md
```

Expected: no commits ahead.

- [ ] **Step 4: Push to origin/main**

```bash
git push origin feat/observer-missed-activations:main
```

(Or open PR if user prefers PR-flow.)

---

## Self-Review

**Spec coverage** — verified against original requirements:

- Conditional rule formulation → Task 8 (Pravila §16.4 v1.36).
- DEFERRED exclusion (#44/#50/#54/#67/+#17) → Task 2 (dormancy extract) + Task 4 (matcher filters live = non-dormant).
- Single-episode threshold → Task 4 (matcher counts every qualifying episode, no smoothing).
- STATUS.md proactive surface → Task 6 (C5 detail) + Task 7 (rendering + ⚠️).
- `/brain-retro` retro section → Task 5 (analyzer output) + Task 10 (template).
- Hybrid C architecture (manual classification map + auto-generated dormancy) → Tasks 1, 2, 3.
- Lefthook automation → Task 3.
- ADR amendment → Task 9.
- Skill update → Task 10.
- Memory refresh → Task 11.
- Regression + push → Task 12.

**Placeholder scan** — no TBD/TODO/«similar to»/«handle edge cases». Every code step contains real code.

**Type consistency** — `detectMissedActivations(episodes, classificationMap, dormancy)` signature stable across Tasks 4, 5, 6. Return shape `{ totalMissed, byNode, byClassification }` stable across all consumers. `analyze(episodes, options)` second-arg matches Task 5 test and Task 7 CLI.

**Open items for human review during execution:**

- Job number 17 in lefthook — confirm by reading actual `lefthook.yml` (estimate; may need to be 18 if 16 is taken).
- Classification map (Task 1) — initial bucket-to-nodes assignment is a judgment call; reviewer may want to adjust before merging.

---

## Execution Handoff

Save complete. Two execution options:

**1. Subagent-Driven (recommended for this multi-task plan)** — fresh subagent per task, two-stage review, controller (Opus) verifies each commit per Pravila §15.1.

**2. Inline Execution** — execute in this session via `superpowers:executing-plans`, batch with checkpoints.

Which approach?
