spec(router-gate): v3.1 clarification pass for writing-plans handoff

Comprehensive analysis of v3 found ~24 minor issues (no critical bypasses).
V3.1 closes most via clarifications to prepare for writing-plans skill in
next session.

Additions:
- TL;DR at top — fast orientation for implementer
- 10.1 Function and registry references (nodeMatches source,
  registry source docs/registry/nodes.yaml, SDD-skill impact,
  coverage-hint to recovery resolution)
- 10.2 State file schemas (8 files: router-state, chain-state,
  askuser-decisions, router-gate-decisions, subagent-inheritance,
  coverage-hint, gate-errors, gate-config)
- 10.3 Test strategy: ~150 unit + 10-15 integration + 10-15 golden
  snapshot + 5-7 smoke
- 10.4 Success metrics: quantitative (override drops to 0,
  gate-decisions growing) + qualitative (lockout < 5/100,
  correct% > 60%) + acceptance criteria
- 10.5 Rollback plan (3 levels: hook off / revert commits / v2 baseline)
- 10.6 Stages and parallelism: 6-9h wall-clock with SDD parallelism
  vs 13.5-20h sequential

No architectural changes — v3.1 only clarifies what implementer needs
to know without making implicit decisions.

Spec versions in git:
- v1: ae5ea663
- v2: 32cdf352
- v3: 31774f65
- v3.1: this commit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-05-29 06:41:48 +03:00
parent 98a540ab76
commit 25492a8eb6
2 changed files with 319 additions and 13 deletions
+11 -11
View File
@@ -1,6 +1,6 @@
# Brain Status (auto-generated)
Last updated: 2026-05-29T03:32:43.655Z
Last updated: 2026-05-29T03:41:09.334Z
| Контролёр | Состояние | Детали |
|---|---|---|
@@ -8,13 +8,13 @@ Last updated: 2026-05-29T03:32:43.655Z
| C2 Cross-ref consistency | ✅ | [cross-ref-checker] OK — 0 drift in 4 files |
| C3 Observer-of-observer | ✅ | [observer-of-observer] OK — last read 0 week(s) ago |
| C4 Сигнальный статус | ✅ | This file (self-reference) |
| C5 Observer-coverage | ⚠️ | 669 episode(s) this month · Stop-hook + post-commit OK · 20 missed activation(s) — see /brain-retro |
| C5 Observer-coverage | ⚠️ | 671 episode(s) this month · Stop-hook + post-commit OK · 20 missed activation(s) — see /brain-retro |
| C6 Chain map sync | ✅ | [chain-map-checker] OK — 16 chains in sync |
## Метрики (информационные, не алерты)
- Observer evidence: 669 episodes this month, 0 observer_error markers, 131 PII matches before filter
- Legacy v1 episodes (not in factor analysis): 530
- Observer evidence: 671 episodes this month, 0 observer_error markers, 131 PII matches before filter
- Legacy v1 episodes (not in factor analysis): 532
- Last /brain-retro: 2 day(s) ago
- Использование узлов: см. `/brain-retro` (раз в спринт). missed_activations: 20. **Неиспользованные узлы — не алерт, если профильной задачи не было** (Pravila §16.4 v1.36; capability-readiness; см. memory `feedback_brain_unused_tools_not_problem` — outside-repo memory store).
@@ -31,9 +31,9 @@ Baseline дисциплины роутера (этап 2 router discipline overh
| cleanup | 6 | 0.0% | 0.0% |
| refactor | 1 | 0.0% | 0.0% |
Router step distribution: 1: 300, 2: 237, 3: 63, 5: 60
Router step distribution: 1: 302, 2: 237, 3: 63, 5: 60
Boundaries applied (ADR / границы): 75 of 660 эпизодов (11.4%).
Boundaries applied (ADR / границы): 75 of 662 эпизодов (11.3%).
## Активные многоэтапные проекты
@@ -51,10 +51,10 @@ Boundaries applied (ADR / границы): 75 of 660 эпизодов (11.4%).
| Компонент | Токены (in/out) | USD |
|---|---|---|
| Classifier (Sonnet 4.6) | 3628/48891 | $0.74 |
| Classifier (Sonnet 4.6) | 3660/49643 | $0.76 |
| Self-assessment (Sonnet 4.6) | 0/0 | $0.00 |
| Reviewer (Opus 4.7 + fallback) | 0/0 | $0.00 |
| **Итого** | | **$0.74** |
| **Итого** | | **$0.76** |
## Аномалии классификатора
@@ -67,7 +67,7 @@ Episodes since last run: 542 / threshold: 10
## Reviewer: субагент vs fallback
0 эпизодов проверено из 669.
0 эпизодов проверено из 671.
## Reviewer findings
@@ -112,7 +112,7 @@ Episodes since last run: 542 / threshold: 10
| `recovery` | 914 | 17 ⚠️ |
| `ремонт инфраструктуры` | 229 | 44 ⚠️ |
| `без скилов` | 201 | 23 ⚠️ |
| `срочно` | 118 | 25 ⚠️ |
| `срочно` | 131 | 38 ⚠️ |
| `memory dump` | 17 | 0 |
| `direct ok` | 6 | 0 |
| `быстрый коммит` | 3 | 0 |
@@ -123,7 +123,7 @@ Episodes since last run: 542 / threshold: 10
| PID | Имя | CPU-время | Возраст |
|---|---|---|---|
| 3464 | MsMpEng | 1.07ч | 0.0ч |
| 3464 | MsMpEng | 1.08ч | NaNч |
⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий.
@@ -1,12 +1,45 @@
# Router-gate hard wall — Дизайн-спецификация (Уровень 4) v3
# Router-gate hard wall — Дизайн-спецификация (Уровень 4) v3.1
**Дата:** 2026-05-28
**Версия:** v3 (closes 10 holes found in v2 adversarial audit)
**Версия:** v3.1 (clarification pass — TL;DR, schemas, test strategy, success metrics, cross-refs)
**Автор:** Claude (controller Opus 4.7) под руководством заказчика Дмитрия
**Статус:** Approved by owner — готов к плану implementation
**Тип:** feature — enforcement architecture rewrite
**Предшественник:** [docs/superpowers/plans/2026-05-28-router-discipline-level-1-2.md](../plans/2026-05-28-router-discipline-level-1-2.md) (Уровни 1+2, merged ранее в день)
---
## TL;DR
**Цель:** Закрыть все обходы router-рекомендаций. Controller (Claude) физически не может действовать без явного разрешения заказчика.
**Решение:** Один PreToolUse-хук `tools/enforce-router-gate.mjs` заменяет 5 старых хуков и vocab.json. На каждый tool call gate резолвит «разрешено / заблокировано» по 4 поведениям:
1. **Direct invocation** заказчика (strict whitelist: slash-cmd / `Skill(X)` / `используй #N` / `делай <exact-name>`) → allow.
2. **Single router-рекомендация** → allow только matching Skill/Task + read-only baseline; mutating требует AskUserQuestion approval.
3. **Chain router-рекомендация** → allow только текущий шаг chain (state persists across turns, TTL 24h); progression after matching skill invoked.
4. **Silence** (router молчит) → mutating tools блокируются до AskUserQuestion с 1/2+/0-формат предложениями.
**Безопасная база (всегда разрешено):** Read / Grep / Glob / LS / TodoWrite / AskUserQuestion (с лимитом 2 per turn) / read-only MCP. Bash — отдельный whitelist (§5.1).
**Ключевые защиты:**
- AskUserQuestion НЕ unlock'ает turn автоматически — gate парсит ответ заказчика и unlock'ает только для approved action.
- Protected paths (`~/.claude/runtime/*`, `tools/enforce-*.mjs`, etc) — hard-deny независимо от unlock.
- Subagent inheritance через env vars (`CLAUDE_GATE_INHERIT=true`), не text-prefix.
- Subagent constraints: нет AskUser, нет recursive Task, max 3 parallel.
- Atomic file writes + `proper-lockfile` для race-free state.
- Gate budget 2s + fail-CLOSE при таймаутах.
- Bash blocks sub-shells (`backticks`, `$()`, `<()`, `<<` heredocs) + file-watcher для script execution + static content scan.
**Цена:** 13.5-20 часов implementation в 6 этапов через subagent-driven-development. **Закрыто 20 holes** через 3 раунда adversarial audit (v1 → v2 → v3).
**Recovery:** заказчик соглашается быть recovery-каналом ручной правкой `.claude/settings.json` / state-файлов при ошибочном lockout.
**Удаляется:** 5 хуков (chain-recommendation / classifier-match / graph-first / semgrep-security / override-limit) + `enforce-override-vocab.json` (7 фраз обхода больше не работают).
**Сохраняется:** 7 preserved хуков (tdd-gate / coverage-verify / memory-coverage / verify-before-push / rationalization-audit / prompt-injection / branch-switch) — у них своя семантика, не про router. 6 из них теряют свои findOverride escape-фразы — становятся hard-walls тоже.
**Changes v2 → v3:** §3.2 переписан под env-based subagent inheritance (вместо text-prefix). Добавлены §3.4 Subagent constraints (no AskUser, no recursive Task), §3.5 Atomic state writes / file locking, §3.6 Gate budget / timeout (2s hard limit, fail-CLOSE), §5.2 Static content scan для node/python/vitest. §3.1 расширен path normalization (resolve + realpath + case-fold). §4.5 ограничен max 2 AskUserQuestion per turn. §4.7 при silence использует task_classification вместо recommendation. §5.1 добавлены file-watcher + broad sweep hard-blacklist для sub-shells. §7 добавлен coverage-hint coordination layer. §8 пересчитан (13.5-20h эпик).
**Changes v1 → v2:** добавлены §3.1 (protected paths), §3.2 (subagent inheritance), §3.3 (failure modes), §4.5 (AskUserQuestion answer parsing), §4.6 (post-skill partial unlock), §4.7 (question quality detector), §5.1 (Bash content rules). §4 Поведение 1 переписан со strict whitelist.
@@ -663,10 +696,283 @@ Hint удаляется при Stop event (cleanup).
- Pravila §16 brain governance, §17 universal skill-coverage — будет обновляться отдельной задачей через `claude-md-management` после этого эпика.
- Текущая enforcement-архитектура: 5 PreToolUse хуков + vocab.json в `tools/`.
### 10.1. Function and registry references
Концепции упомянутые в спеке и их источники:
- **`nodeMatches(recommendation, toolUse)`** — pure function для сравнения router-рекомендации (`#19`, `superpowers:writing-plans`) с tool invocation (Skill/Task). Существует в текущем `tools/enforce-classifier-match.mjs:42-66`. В реализации Уровня 4 переносится в `tools/router-gate-decide.mjs` как exported function. Обрабатывает aliases через registry: `#NN``node.id`, `superpowers:X` / `skill:X` через canonical lookup в registry.
- **Registry источник** — `docs/registry/nodes.yaml` — единственный source of truth для skill/node names, aliases, slugs. Gate использует `tools/registry-load.mjs` для чтения. Direct-invocation strict whitelist (§4 Поведение 1) сравнивает с `node.name` и `node.slug` из этого реестра.
- **Subagent_type matching** — для Task tool gate сравнивает `tool_input.subagent_type` (например `coder-agent`) с recommendation в формате `slug` или `name` через тот же `nodeMatches()` с alias resolution.
- **Coverage-hint cross-ref recovery (§7.1 ↔ §6):** при coverage mismatch flag попадает в rationalization-flags и сюрфейсится в next prompt. Заказчик видит «прошлый turn нарушил coverage hint» — это уже surface, не блок. Если coverage-hint критичен (например я аннотировал `direct` но gate ожидал `skill:X`), заказчик использует §6 recovery уровень 3 (правка router-state) чтобы выровнять.
- **SDD-skill impact** — `superpowers:subagent-driven-development` skill активно использует Task tool. С §3.4 constraints (no AskUser в субагенте, no recursive Task, max 3 parallel) — sub-skill будет работать но с ограничениями: контроллер не сможет делегировать больше 3 субагентов параллельно (раньше можно было N), и subagent при lockout эскалирует через parent's AskUser. Skill сам **не правится** в этом эпике; impact документируется как known-behavior-change в release notes.
### 10.2. State file schemas
Все state-файлы — JSON или JSONL. Структуры:
**`~/.claude/runtime/router-state-<sess>.json`** (существует, не меняется в v4):
```jsonc
{
"schema_version": 1,
"ts": "<iso-8601>",
"session_id": "<sess>",
"classification": {
"task_type": "feature|bugfix|analysis|planning|conversation|memory-sync|ambiguous|unknown",
"confidence": 0.0,
"recommended_node": "#19|null",
"recommended_chain": ["#55", "#19", "#56"],
"triggers_matched": ["string"],
"primary_rationale": "string"
},
"source": "prefilter|regex|llm|cache"
}
```
**`~/.claude/runtime/chain-state-<sess>.json`** (новый в v4):
```jsonc
{
"schema_version": 1,
"chain_active": ["#55", "#19", "#56"],
"chain_step": 1,
"initialized_at": "<iso-8601>",
"last_step_at": "<iso-8601>"
}
```
**`~/.claude/runtime/askuser-decisions-<sess>.jsonl`** (новый в v4) — append-only:
```jsonc
{
"ts": "<iso-8601>",
"session_id": "<sess>",
"turn_id": "<id>",
"question": "string",
"options": ["string"],
"chosen_label": "string",
"chosen_text": "string",
"gate_interpretation": "stop_remain_locked|approve_specific_tool|approve_direct_no_skill|ambiguous_requires_followup",
"approved_tool": "Skill|Edit|Bash|null",
"approved_action_pattern": "string|null"
}
```
**`~/.claude/runtime/router-gate-decisions.jsonl`** (новый в v4) — append-only:
```jsonc
{
"ts": "<iso-8601>",
"session_id": "<sess>",
"turn_id": "<id>",
"tool_name": "Edit|Write|Skill|Task|Bash|...",
"tool_input_summary": "string (truncated for log)",
"decision": "allow|block|unlock",
"reason": "string",
"behavior_branch": "1_direct_invocation|2_single_rec|3_chain|4_silence",
"state_snapshot": {
"rec_node": "string|null",
"rec_chain": ["string"],
"chain_step": 0,
"askuser_called_this_turn": false,
"askuser_count_this_turn": 0,
"skill_invoked_matching": false,
"is_direct_invocation": false,
"edited_files_this_turn": ["path"]
},
"gate_latency_ms": 0
}
```
**`~/.claude/runtime/subagent-inheritance-<task-id>.json`** (новый в v4) — short-lived:
```jsonc
{
"schema_version": 1,
"parent_session_id": "<parent-id>",
"parent_router_state_path": "absolute path",
"parent_chain_state_path": "absolute path",
"allowed_actions": ["pattern"],
"subagent_constraints": {
"can_use_askuser": false,
"can_spawn_task": false,
"max_parallel": 1
},
"created_at": "<iso-8601>"
}
```
**`~/.claude/runtime/coverage-hint-<sess>.json`** (новый в v4) — short-lived per turn:
```jsonc
{
"ts": "<iso-8601>",
"session_id": "<sess>",
"turn_id": "<id>",
"expected_coverage": "skill:X|direct:Y|chain:[...]",
"reason": "string",
"first_mutating_tool": "Skill|Edit|...",
"first_mutating_tool_ts": "<iso-8601>"
}
```
**`~/.claude/runtime/gate-errors.jsonl`** (новый в v4) — append-only:
```jsonc
{
"ts": "<iso-8601>",
"session_id": "<sess>",
"error_type": "state_missing|state_malformed|gate_internal|timeout|lock_contention",
"error_message": "string",
"stack_trace": "string (optional)",
"tool_attempted": "string"
}
```
**`~/.claude/runtime/gate-config.json`** (новый в v4) — tuning параметры:
```jsonc
{
"max_decision_time_ms": 2000,
"state_cache_ttl_ms": 5000,
"transcript_lookback_turns": 5,
"max_askuser_per_turn": 2,
"max_parallel_subagents": 3,
"chain_state_ttl_hours": 24,
"lock_timeout_ms": 1000
}
```
### 10.3. Test strategy
Покрытие тестами по слоям:
**Pure decision-функции (unit, vitest):**
- `decide()` — 4 поведения × ~10 вариаций = ~40 тестов (§4).
- `parseAskUserAnswer()` — stop/skill/direct/freeform classification = ~15 тестов (§4.5).
- `detectDirectInvocation()` — strict whitelist patterns + negative cases = ~15 тестов (§4 Поведение 1).
- `bashCommandClassify()` — whitelist/blacklist/sub-shell sweep + tokenizer edge cases = ~25 тестов (§5.1).
- `staticContentScan()` — fs.write / exec patterns + protected paths = ~15 тестов (§5.2).
- `pathNormalize()` — resolve/realpath/case/env = ~10 тестов (§3.1).
- `questionQualityCheck()` — missing-stop / leading / off-topic = ~12 тестов (§4.7).
- `chainStateUpdate()` — progression / TTL / replace-on-new-chain = ~10 тестов (§3 chain-state).
**Integration тесты (vitest + temp fs):**
- Atomic state writes — concurrent simulated writers, no corruption = ~10 тестов (§3.5).
- File locking — lock acquire/release + timeout fail-CLOSE = ~5 тестов (§3.5).
- Gate budget enforcement — slow decision → fail-CLOSE = ~5 тестов (§3.6).
- State cache invalidation на mtime change = ~5 тестов (§3.6).
**Subagent inheritance (вместе с расширением subagent-prompt-prefix):**
- Env-vars set + inheritance file write/read = ~8 тестов (§3.2).
- Subagent constraints — AskUser blocked / Task nested blocked / parallel > 3 blocked = ~8 тестов (§3.4).
**Golden snapshot tests (decision traces):**
- 10-15 end-to-end сценариев записанных как input → expected decision trace JSON. Покрывают типичные real workflows (writing-plans → Edit → Bash вышеуказанной vitest, chain progression across turns, silence + AskUser + Edit).
**Smoke тесты (полная Claude сессия):**
- Запустить Claude с тестовым prompt'ом, проверить что новый gate активен и старые не fire'ятся = 5-7 manual scenarios.
**Регрессия после каждого этапа:** `npx vitest run --exclude=".claude/**" --exclude="app/**" --exclude="tools/ruflo-*"` — должна оставаться GREEN.
**Целевое покрытие тестами:** ~150 новых unit-тестов + 10-15 integration + 10-15 golden snapshots + 5-7 smoke.
### 10.4. Success metrics
После реализации и прогона ~1 неделю под Уровнем 4:
**Метрики которые должны drop'нуться к нулю (quantitative):**
- `~/.claude/runtime/override-usage.jsonl` — пустеет полностью (хука нет, vocab нет).
- `hook-outcomes.jsonl` Cut 11 buckets (`passed-inline-override`, `passed-global-override`) — не пишутся.
- Brain-retro Table 5 bucket `direct_ignored_rec` (Skill should have been used but wasn't) — 0 (gate physically блокирует).
- Rationalization flags `rationalization-phrase: временно` и подобные — должны drop'нуться значительно (но не до нуля — soft detector).
**Метрики которые должны вырасти (quantitative):**
- `router-gate-decisions.jsonl` entries per day — fresh source данных.
- `askuser-decisions-<sess>.jsonl` entries — visible user approval pattern.
- Brain-retro Table 12-new `approved-alternative-skill` — показывает где я ловлю плохие рекомендации router'а.
**Качественные метрики (brain-retro #11 после прогона):**
- Brain-retro Table 13-new `Lockout incidents` показывает <5 incidents per 100 episodes — gate помогает, не раздражает.
- Self-retrospect #3 (через ~50 episodes под Уровнем 4) подтверждает: attitude commitments больше не нужны — гибридная архитектура enforce'ит.
- Reviewer Table «node_quality» percentages: correct % > 60% (vs 41% on 28.05), wrong_node < 5% (vs 11%).
- Time-to-first-action в типичной задаче не превышает baseline +30% (acceptable friction от AskUser).
**Acceptance criteria для перехода в production:**
- 150+ unit tests GREEN.
- 10+ integration tests GREEN.
- 5 smoke scenarios passing.
- Manual prod test: 1 час real workflow без false-positive блокировок.
- Brain-retro #11 (через 1 неделю) показывает 0 missed activations, 0 bypass attempts via gate.
### 10.5. Rollback plan
Если v4 ломает critical workflows и заказчик хочет откатиться:
**Уровень 1 — выключить новый gate, сохранить остальное:**
В `.claude/settings.json` секции `PreToolUse` удалить запись про `tools/enforce-router-gate.mjs`. Все остальные 7 preserved хуков продолжают работать. Эффект: возврат к dynamics v3 без router-routing enforcement (vocab.json уже удалён, нет escape-фраз).
**Уровень 2 — revert all v4 commits:**
```bash
git revert <gate-implementation-commit-range>
```
Восстанавливает 5 удалённых хуков + vocab.json. Полный возврат к pre-эпику состоянию.
**Уровень 3 — checkout v2 spec baseline (для rebuild с нуля):**
```bash
git checkout b510a758 -- docs/superpowers/specs/2026-05-28-router-gate-hard-wall-design.md
```
Berkeley-style: use v2 spec, новый план implementation.
### 10.6. Этапы и параллелизм
Большинство этапов и подэтапов в §8 могут идти параллельно через subagent-driven-development:
**Sequential обязательно:**
- Этап 1 (pure decision module) → ВСЕ другие подэтапы (1.1-1.8 расширяют этот модуль).
- Этап 2 (удаление хуков) → Этап 3 (регистрация в settings.json — нужно знать какие удалить).
- Этап 6 (brain-retro adaptation) → после Этапа 3 (нужны real data из нового gate).
**Параллелизуемое:**
- Этапы 1.1 / 1.2 / 1.3 / 1.4 / 1.5 / 1.6 / 1.7 / 1.8 — независимые подмодули, можно делегировать одновременно (max 3 параллельных Task tool вызовов per §3.4).
- Этап 2.1 (subagent-prompt-prefix расширение) и 2.2 (subagent constraints) — параллельны с этапом 2 (удаление хуков).
**Оценочное wall-clock с параллелизмом:** 6-9 часов вместо 13.5-20 sequential. Но это требует чёткой координации между субагентами и хорошего decomposition в writing-plans.
---
## 11. История версий
### v3.1 (2026-05-28, поздний вечер, после комплексного анализа)
Clarification pass — не новые архитектурные решения, а **уточнения для implementer'а**:
- TL;DR в начале спека для быстрого orientation.
- §10.1 — явные cross-refs: `nodeMatches()` source, registry source (`docs/registry/nodes.yaml`), SDD-skill impact, coverage-hint ↔ recovery resolution.
- §10.2 — JSON Schema-ish определения всех 7 state-файлов (router-state / chain-state / askuser-decisions / router-gate-decisions / subagent-inheritance / coverage-hint / gate-errors / gate-config).
- §10.3 — test strategy: ~150 unit + 10-15 integration + 10-15 golden snapshot + 5-7 smoke.
- §10.4 — success metrics: quantitative (override drops to 0, gate-decisions growing) + качественные (lockout incidents < 5/100 ep, correct% > 60%) + acceptance criteria.
- §10.5 — rollback plan (3 уровня от выключения хука до polnogo revert).
- §10.6 — этапы и parallelism: что sequential, что параллелизуемо через SDD. Wall-clock 6-9h с parallelism вместо 13.5-20h sequential.
Этот pass — для подготовки к writing-plans skill в next session: implementer должен иметь всё необходимое чтобы не делать implicit decisions.
### v3 (2026-05-28, поздний вечер)
Adversarial audit спека v2 от controller'а выявил 10 новых дыр, из них: