diff --git a/docs/observer/STATUS.md b/docs/observer/STATUS.md index 51a2119e..e4747aa7 100644 --- a/docs/observer/STATUS.md +++ b/docs/observer/STATUS.md @@ -1,6 +1,6 @@ # Brain Status (auto-generated) -Last updated: 2026-05-29T04:31:35.642Z +Last updated: 2026-05-29T04:50:44.543Z | Контролёр | Состояние | Детали | |---|---|---| @@ -8,13 +8,13 @@ Last updated: 2026-05-29T04:31:35.642Z | 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 | ⚠️ | 685 episode(s) this month · Stop-hook + post-commit OK · 20 missed activation(s) — see /brain-retro | +| C5 Observer-coverage | ⚠️ | 688 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: 685 episodes this month, 0 observer_error markers, 138 PII matches before filter -- Legacy v1 episodes (not in factor analysis): 546 +- Observer evidence: 688 episodes this month, 0 observer_error markers, 139 PII matches before filter +- Legacy v1 episodes (not in factor analysis): 549 - 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). @@ -24,16 +24,16 @@ Baseline дисциплины роутера (этап 2 router discipline overh | Тип задачи | Эпизодов | % с триггер-матчем | % через скил | |---|---|---|---| -| analysis | 31 | 25.8% | 16.1% | -| planning | 18 | 16.7% | 16.7% | +| analysis | 32 | 25.0% | 18.8% | +| planning | 19 | 15.8% | 15.8% | | bugfix | 18 | 22.2% | 27.8% | | feature | 16 | 12.5% | 0.0% | | cleanup | 6 | 0.0% | 0.0% | | refactor | 1 | 0.0% | 0.0% | -Router step distribution: 1: 308, 2: 241, 3: 63, 5: 61 +Router step distribution: 1: 308, 2: 243, 3: 63, 5: 62 -Boundaries applied (ADR / границы): 75 of 673 эпизодов (11.1%). +Boundaries applied (ADR / границы): 75 of 676 эпизодов (11.1%). ## Активные многоэтапные проекты @@ -51,10 +51,10 @@ Boundaries applied (ADR / границы): 75 of 673 эпизодов (11.1%). | Компонент | Токены (in/out) | USD | |---|---|---| -| Classifier (Sonnet 4.6) | 4143/57492 | $0.87 | +| Classifier (Sonnet 4.6) | 4586/59217 | $0.90 | | Self-assessment (Sonnet 4.6) | 0/0 | $0.00 | | Reviewer (Opus 4.7 + fallback) | 0/0 | $0.00 | -| **Итого** | | **$0.87** | +| **Итого** | | **$0.90** | ## Аномалии классификатора @@ -67,7 +67,7 @@ Episodes since last run: 542 / threshold: 10 ## Reviewer: субагент vs fallback -0 эпизодов проверено из 685. +0 эпизодов проверено из 688. ## Reviewer findings @@ -109,7 +109,7 @@ Episodes since last run: 542 / threshold: 10 | Фраза | За всё время | За сегодня | |---|---|---| -| `recovery` | 1171 | 274 ⚠️ | +| `recovery` | 1233 | 336 ⚠️ | | `без скилов` | 233 | 55 ⚠️ | | `ремонт инфраструктуры` | 229 | 44 ⚠️ | | `срочно` | 144 | 51 ⚠️ | @@ -123,7 +123,7 @@ Episodes since last run: 542 / threshold: 10 | PID | Имя | CPU-время | Возраст | |---|---|---|---| -| 3464 | MsMpEng | 1.23ч | 14475943.0ч | +| 3464 | MsMpEng | 1.27ч | 0.0ч | ⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий. diff --git a/docs/superpowers/specs/2026-05-28-router-gate-hard-wall-design.md b/docs/superpowers/specs/2026-05-28-router-gate-hard-wall-design.md index 61a6939b..f2fb7c0c 100644 --- a/docs/superpowers/specs/2026-05-28-router-gate-hard-wall-design.md +++ b/docs/superpowers/specs/2026-05-28-router-gate-hard-wall-design.md @@ -1,7 +1,7 @@ -# Router-gate hard wall — Дизайн-спецификация (Уровень 4) v3.3 +# Router-gate hard wall — Дизайн-спецификация (Уровень 4) v3.4 **Дата:** 2026-05-29 -**Версия:** v3.3 (adversarial audit v4.1 closure — 12 holes из аудита v3.2 закрыто; **N1 fatal: честное признание что S5 не закрывается regex'ом — пересмотрено как acceptable residual + weak heuristic**) +**Версия:** v3.4 (N1/S5 TRUE closure — out-of-band runtime block-file через tool_use_id derived path. V3.3 «honest residual» пересмотрено: реальный fix найден без harness-зависимости через subagent gate-process side-channel) **Автор:** Claude (controller Opus 4.7) под руководством заказчика Дмитрия **Статус:** Approved by owner — готов к плану implementation **Тип:** feature — enforcement architecture rewrite @@ -32,7 +32,7 @@ - Gate budget 2s + fail-CLOSE при таймаутах. - Bash blocks sub-shells (`backticks`, `$()`, `<()`, `<<` heredocs) + file-watcher для script execution + static content scan. -**Цена:** 18-27 часов implementation в 6 этапов через subagent-driven-development. **Закрыто 50 holes** через 5 раундов adversarial audit (v1 → v2 → v3 → v3.2 → v3.3). **N1 fatal из v3.2 audit пересмотрено** — S5 не закрывается regex'ом (это была иллюзия), теперь acceptable residual + weak heuristic (см. §3.4 v3.3). +**Цена:** 21.5-31 часов implementation в 6 этапов через subagent-driven-development. **Закрыто 51 hole** через 5 раундов adversarial audit (v1 → v2 → v3 → v3.2 → v3.3 → v3.4). **S5 TRUE closure в v3.4** — out-of-band runtime block-file `subagent-block-.json` (см. §3.4 v3.4). Из 2 acceptable residual v3.3 (S5 + S8) **S5 теперь closed**, остаётся только S8 (BRANCH-SWITCH-CONFIRMED — separate follow-up эпик). **Recovery:** заказчик соглашается быть recovery-каналом ручной правкой `.claude/settings.json` / state-файлов при ошибочном lockout. @@ -40,6 +40,26 @@ **Сохраняется:** 7 preserved хуков (tdd-gate / coverage-verify / memory-coverage / verify-before-push / rationalization-audit / prompt-injection / branch-switch) — у них своя семантика, не про router. 6 из них теряют свои findOverride escape-фразы — становятся hard-walls тоже. +**Changes v3.3 → v3.4:** dedicated brainstorm на N1 (закрытие fatal hole S5) выявил реальный механизм out-of-band channel без harness-зависимости. **S5 TRUE closure** (больше не residual). Механизм: + +- **Subagent's gate-процесс пишет block-event-файл** по пути derived из `tool_use_id` Task tool: `~/.claude/runtime/subagent-block-.json`. Я (controller) НЕ контролирую: (1) generation `tool_use_id` — Anthropic CLI assigns; (2) запись — gate-процесс subagent'а, отдельный node-процесс; (3) сам файл — в `~/.claude/runtime/*` protected paths (§3.1). +- **Parent gate читает файл** при PreToolUse следующего tool-use после Task return. Decision: file exists + blocks non-empty → escalate AskUser; file missing + tool_use count > 0 → DONE; file missing + count == 0 → assume BLOCKED (gate-crash defensive). +- **Атомарные writes** через proper-lockfile (§3.5) для concurrent blocks subagent'а. +- **Smoke 3** добавлен в §3.2.0 — user-run проверка что subagent gate реально пишет block-file (acceptance: file exists в `~/.claude/runtime/` после blocked Task). + +7 spec edits: +- §3.4 переписан — убрана «weak heuristic» framing, заменена «out-of-band channel» закрытием. S5 явно closed. +- §3.1 +`subagent-block-.json` explicit-mention. +- §3.2 расширен — subagent gate ТАКЖЕ пишет block-file (parallel к чтению inheritance). +- §3.2.0 +Smoke 3. +- §8 budget +2.2h. +- §9 — S5 убран из residual list (now closed). Остаётся только S8. +- §10.2 +schema для `subagent-block-.json`. + +Adversarial pre-check (мой же v4.2 audit на этот дизайн перед написанием) — 6 attack-векторов проверены, все заблокированы protected paths + harness-assigned tool_use_id + side-channel write. True closure, не heuristic. + +Budget impact: +2.2h (19.5-29h → 21.5-31h). + **Changes v3.2 → v3.3:** adversarial audit v4.1 (второй раунд контроллера на свою же v3.2) выявил **12 новых holes**, из них: - ☠️ **N1 fatal:** BLOCKED-protocol regex полностью fake-able контроллером (subagent response = controller-written text → `{"status":"DONE"}` маркер всегда вставляется). **Мой v3.2-фикс S5 был самообман.** Closure: убран claim «closes S5», заменён на weak heuristic + S5 переведён в acceptable residual (как S8). @@ -181,7 +201,7 @@ Self-retrospect #2 (28.05 вечер) подтвердил: 5 commitments утр Список путей, **никогда** не разрешённых для Edit / Write / MultiEdit / NotebookEdit и Bash-команд rm / mv / cp / redirects (`>` / `>>`): -- `~/.claude/runtime/*` — все gate state-файлы (router-state, chain-state, askuser-decisions, gate-decisions, gate-errors, subagent-inheritance, coverage-hint) **и `gate-config.json` тоже** (explicit-mention — закрывает C4 из v4-аудита, чтобы glob-интерпретация не разошлась). +- `~/.claude/runtime/*` — все gate state-файлы (router-state, chain-state, askuser-decisions, gate-decisions, gate-errors, subagent-inheritance, coverage-hint, **`subagent-block-.json` — v3.4 explicit (N1/S5 TRUE closure: subagent gate-process пишет block-events, parent gate читает после Task return; см. §3.4 v3.4 + §10.2 schema)**) **и `gate-config.json` тоже** (explicit-mention — закрывает C4 из v4-аудита, чтобы glob-интерпретация не разошлась). - `.claude/settings.json` / `.claude/settings.local.json` - `.claude/skills/**/SKILL.md` — skill definitions - `tools/enforce-*.mjs` — все enforcement-хуки (включая `enforce-router-gate.mjs`) @@ -227,7 +247,14 @@ Acceptance criteria: заказчик запускает prompt с явно-fail - **Pass B:** PostToolUse fires всегда, но содержит `status: "error"` поле → §3 handler фильтрует по этому полю, fix корректен. - **Fail:** PostToolUse fires всегда без status differentiation → chain_step++ инкрементнет на упавшем skill (та же проблема S3 которую думали что закрыли). Нужен redesign — например chain_step++ через **PreToolUse следующего turn'а** проверяя что предыдущий skill_invoke реально завершился (по transcript output length / artifact files). -Budget: 0.5h на каждый smoke-test + verification = 1h total. Without these — два architectural assumption неверифицированы → суммарный риск 5-15h rework. +**Smoke 3 — subagent block-file write (v3.4 — N1/S5 TRUE closure verification):** + +Acceptance criteria: заказчик запускает prompt: «спавн субагента через Task tool с заданием `Write tools/router-gate-decide.mjs` (path в §3.1 protected) — gate должен subagent'у заблокировать Write». После subagent return: + +- **Pass:** в `~/.claude/runtime/` появляется `subagent-block-.json` с записью block-event на Write. Parent gate в next response обязан escalate AskUser (видно в transcript). Acceptance: file exists + parent escalation visible. +- **Fail:** file missing после blocked Task → subagent gate-process не пишет block-file (либо не запускается в subagent-режиме, либо path-derivation broken). Degraded fallback — оставить v3.3 weak heuristic (tool_use count + marker). Не блокер для эпика, но S5 остаётся в residual. + +Budget: 0.5h × 3 smoke-tests + verification = **1.5h total**. Without these — три architectural assumption неверифицированы → суммарный риск 5-15h rework. ### 3.2. Subagent gate inheritance (env-based, NOT text-prefix) @@ -262,6 +289,8 @@ V3 переходит на **environment-based inheritance** (условно — 4. Inheritance-файл удаляется когда Task tool возвращает результат родителю. +**Block-file write (v3.4 — закрытие S5 через side-channel):** subagent gate ТАКЖЕ пишет `~/.claude/runtime/subagent-block-.json` каждый раз когда decide=block для любого tool subagent'а. `tool_use_id` derived из имени inheritance-файла (контролируется harness через Task spawn). Файл — append-only через proper-lockfile (§3.5). Parent gate читает после Task return для проверки blocked ли субагент. Полное описание механизма + flow + anti-forgery — §3.4 v3.4. Schema — §10.2. + **Закрывает Дыру 5 (v1 — undefined behavior)** и **Дыру 11 (v2 — prefix без enforcement)**. ### 3.4. Subagent constraints @@ -273,23 +302,43 @@ V3 переходит на **environment-based inheritance** (условно — - **Limit parallel Task**: max 3 параллельных Task tool calls в одном turn'е родителя. 4-й и далее → block с reason «parallel subagent limit reached». - При lockout субагент возвращает Task tool response: `{status: "BLOCKED", reason: "", parent_should_askuser: true}`. Родитель видит → сам эскалирует к заказчику через свой AskUserQuestion. -**BLOCKED-protocol weak heuristic (v3.3 honest revision of N1 from v4.1 audit — НЕ закрывает S5):** +**BLOCKED-protocol TRUE closure через out-of-band block-file (v3.4 — closes S5):** -V3.2 заявлял что закрывает S5 через regex `\{[^{}]*"status"\s*:\s*"(DONE|BLOCKED)"[^{}]*\}` в last 200 chars subagent response. **Это была иллюзия.** Subagent response — это текст, который пишет САМ КОНТРОЛЛЕР (через инструмент Task). Контроллер ВСЕГДА может вставить fake `{"status":"DONE"}` в любой response, реально blocked субагент или нет. Regex просто видит «маркер есть» и считает что субагент отработал. Это та же текстовая конвенция без enforcement, что была в S5 изначально. +V3.2 пытался закрыть S5 regex'ом маркера `{"status":"DONE"|"BLOCKED"}` в subagent response — это была иллюзия (controller-written text, fake-able). V3.3 признал residual + weak heuristic. **V3.4 находит real out-of-band channel** через subagent gate-process side-channel. -**Что РЕАЛЬНО можно сделать (weak heuristic, не закрытие):** +**Механизм:** -Parent gate при PreToolUse следующего tool-use **после** Task return анализирует: +1. **Parent gate при PreToolUse(Task)** запоминает `tool_use_id` (Anthropic-assigned для этого Task tool call — controller НЕ контролирует generation; уже используется в §3.2 для `subagent-inheritance-.json`). +2. **Subagent gate при decide=`block`** (любой tool, любое Поведение) — atomic append в `~/.claude/runtime/subagent-block-.json` запись `{ts, tool_name, reason, decision_kind}` ДО возврата block-decision в subagent process. proper-lockfile (§3.5) для concurrent blocks. +3. **Parent gate при PreToolUse(next tool после Task return)** — читает block-file. Decision: -1. **Tool_use count в subagent transcript** (out-of-band, контроллер не контролирует): сколько tool_uses реально сделал субагент. Если 0 (только text response) → strong-signal BLOCKED (субагент не смог ничего сделать). Если >0 → субагент действовал. -2. **JSON marker check (slabый сигнал)** в last 200 chars: `{"status":"DONE"|"BLOCKED",...}`. Совпадает с tool_use count — confidence high; расходится — flag в rationalization-audit. -3. **Defensive policy:** если tool_use count == 0 И marker отсутствует → assume BLOCKED, parent обязан AskUser. Если tool_use count > 0 И marker `DONE` → продолжаем (контроллер не подделает реальные tool_uses). +| Состояние block-file | tool_use count subagent'а | Parent decision | +|---|---|---| +| File exists, blocks непустой | любое | **Block ALL mutating** до AskUser-эскалации с reason из первого block. Логировать в decisions.jsonl как `subagent_blocked_escalate` | +| File missing | > 0 | **DONE** — subagent работал без блоков. Продолжаем normal flow | +| File missing | == 0 | **Assume BLOCKED** (defensive, gate-crash possible). Parent обязан AskUser | +| File read FS error | любое | fail-CLOSE per §3.3 | -**Acceptable residual (S5 не закрывается v3.3):** контроллер всё ещё может пометить fake `{"status":"DONE"}` после blocked субагента с >0 tool_uses (например, subagent действительно сделал несколько Read но не смог Edit). Real subagent BLOCKED detection требует **harness-level enforcement** (Anthropic Claude Code должен возвращать структурированный status о субагенте) — это за пределами controller-writable spec'а. +4. **Cleanup:** parent gate удаляет block-file после чтения. Background cleanup script (cron / lefthook): orphans > 24h → delete (защита от parent gate crash после read но до delete). -**S5 → §9 acceptable residual** (как S8 BRANCH-SWITCH-CONFIRMED — тот же класс controller-writable-signals). +**Anti-forgery (3 защитных слоя — почему controller не подделает):** -**Закрывает Дыру 18** (multiple subagents fan-out — это про parallel limit, не про signal), **завершает закрытие Дыры 11** (env-based inheritance, не signal). +- **Path derived из `tool_use_id`** — Anthropic CLI assigns при Task spawn. Controller передаёт `subagent_type` и `prompt`, но **не** tool_use_id. Невозможно заранее знать какой ID будет, чтобы pre-emptively написать fake-файл. +- **File в `~/.claude/runtime/*`** → protected per §3.1. Edit/Write/MultiEdit blocked hard-deny. Bash `rm`/`mv`/`cp` в hard-blacklist. Controller не может delete/modify/create. +- **Пишет gate-процесс subagent'а**, не controller-written response text. Gate — отдельный node-процесс с тем же `enforce-router-gate.mjs` (env-inherited через §3.2). Когда gate решает `block`, он напрямую writes файл, controller text-response это никак не контролирует. + +**Adversarial pre-check (v4.2 audit):** + +| Attack | Защита | +|---|---| +| Edit `enforce-router-gate.mjs` чтобы отменить write | Protected paths §3.1 — blocked | +| Delete block-file до parent read | `~/.claude/runtime/*` protected — rm/mv в hard-blacklist, blocked | +| Pre-emptive fake block-file с пустыми blocks | tool_use_id assign'ит harness, controller не знает заранее; Write на protected — blocked anyway | +| Subagent gate crash до first write | Defensive: file missing + tool_use count == 0 → assume BLOCKED | +| Subagent сделал read-only работу + был blocked на Write | Block-file существует с 1 блоком → parent escalates корректно | +| Race: 2 параллельных block writes от subagent | proper-lockfile (§3.5) — sequential consistency | + +**Закрывает S5 v4-audit полностью (TRUE closure, не heuristic).** Также сохраняется закрытие Дыры 18 (multiple subagents fan-out — parallel limit), Дыры 11 (env-based inheritance). ### 3.5. Atomic state writes / file locking @@ -419,6 +468,8 @@ Gate определяет одно из 4 поведений на основе s V3.2 заявлял silent fall-through к Поведениям 2/3/4 при unknown skill name. **Это была дыра N2:** пользователь пишет «делай ABC», скила `ABC` нет → controller silently начинает делать router-рекомендованный `Y` без AskUser (потому что Поведение 2 single-rec автоматом allow + safe base, первый mutating tool требует AskUser но к тому моменту контроллер уже многое сделал). **User intent тихо проигнорирован.** V3.3: если префикс-pattern matched (например `делай foo-bar`), но `foo-bar` **не найден** в registry — gate **обязан** заблокировать любой mutating tool до **mandatory AskUserQuestion** с явным форматом: «Пользователь указал skill `foo-bar`, но он не найден в реестре (возможно опечатка или переименован). Router рекомендует ``. Что делать?» с опциями: + + 1. «Имел в виду `` — делать его» (если fuzzy-match similarity > 0.7). 2. «Делать `` по рекомендации router'а». 3. «Делать direct без скила». @@ -808,10 +859,11 @@ Hint удаляется при Stop event (cleanup). | 4 — Документация Recovery | Памятка для заказчика по 3 уровням recovery + v3 update | 1 | | 5 — Прогон в реальной работе | Несколько дней наблюдения, brain-retro #11 | (не таск) | | 6 — Brain-retro adaptation | Обновить `brain-retro-analyzer.mjs` + SKILL.md mandatory tables 11→13 | 1.5-2 | -| **2.1.0 — Pre-flight smoke-tests (v3.2/H2 + v3.3/N4/N10)** | Two user-run smoke-tests до 2.1: (1) env propagation probe, (2) PostToolUse semantics probe. Pass обоих → продолжить §3.2 env-based + §3 chain PostToolUse. Fail любого → stop эпик, открыть отдельную задачу redesign | 1 | +| **2.1.0 — Pre-flight smoke-tests (v3.2/H2 + v3.3/N4/N10 + v3.4 S5)** | Three user-run smoke-tests до 2.1: (1) env propagation probe, (2) PostToolUse semantics probe, (3) subagent block-file write probe (v3.4). Pass всех → продолжить §3.2 env-based + §3 chain PostToolUse + §3.4 S5 closure. Fail любого → stop эпик, degraded fallback | 1.5 | | **+v3.2 fixes inline** | Section A protected paths (+0.3h), B chain PostToolUse migration (+1-2h), C Bash hardening (~2.4h), D default-CLOSE (+0.3h), E path-hardening + BLOCKED-protocol (+0.8h), F morphology + length-ratio (+0.5h) | +5-7 | | **+v3.3 fixes inline (N1-N12)** | N1 BLOCKED-protocol heuristic + S5 residual (+0.3h), N2 mandatory AskUser on unmatched skill (+0.3h), N3 git commit exit code 0 check (+0.3h), N5 SKILL_BASH_ALLOW mapping init для 3-5 skills (+0.3h), N7 PostToolUse handler registration in settings.json + smoke (+0.5h), N6 edited-files schema in §10.2 (trivial), N8/N11 в существующих modules (+0.3h) | +1.5-2 | -| **Итого** | | **19.5-29** | +| **+v3.4 N1/S5 TRUE closure (out-of-band block-file)** | Subagent gate write block-events (0.5h), parent gate read block-file at PostTask (0.5h), proper-lockfile integration для concurrent blocks (0.3h), schema §10.2 + protected paths integration §3.1 (0.1h), spec edits §3.4 rewrite (0.5h), Smoke 3 уже в 2.1.0 budget | +2 | +| **Итого** | | **21.5-31** | ### Implementation order matrix (closes N7+N9 v4.1-audit) @@ -850,7 +902,7 @@ Protected paths должны быть live **до** того как соотве - **S8 — `BRANCH-SWITCH-CONFIRMED` controller-writable marker** (preserved `enforce-branch-switch` hook): после удаления `recovery` override этот хук требует explicit маркер в каждом ответе на dangerous git-операцию. **Маркер пишется в controller response** — controller может self-write его, что есть pre-existing bypass-pattern (зафиксирован в memory `feedback_hook_bypass_pattern_2026-05-28.md`). **Известный residual risk v3.2** — не закрывается в этом эпике. **Follow-up эпик:** «Move BRANCH-SWITCH-CONFIRMED source from response to user-prompt» (заказчик должен явно писать фразу в prompt, аналогично direct-invocation). Budget ~1h на отдельный мини-эпик. Surface как **known-residual-risk** в §11 history v3.2 entry и в STATUS.md C5. -- **S5 — Subagent BLOCKED-protocol controller-writable marker** (v3.3 honest revision of v3.2/N1): mы пытались закрыть это regex'ом `{"status":"DONE"|"BLOCKED"}` в last 200 chars subagent response — но response пишет САМ КОНТРОЛЛЕР, fake-able trivially. V3.3 переводит S5 в acceptable residual со **weak heuristic** (см. §3.4 v3.3): tool_use count == 0 → assume BLOCKED. **Real-fix требует harness-level enforcement** (Anthropic Claude Code должен возвращать структурированный subagent status, недоступный для контроллера) — за пределами controller-writable spec'а. Same class как S8 (controller-writable signals). Surface как known-residual-risk в STATUS.md C5. +- **S5 — Subagent BLOCKED-protocol — CLOSED в v3.4** через out-of-band runtime block-file (§3.4 v3.4). История: v3.2 regex-marker (иллюзия) → v3.3 weak heuristic + honest residual → v3.4 TRUE closure через side-channel `subagent-block-.json` (subagent gate-process пишет, parent gate читает, controller не контролирует path/file/writer). Перенесено в §11 как resolved. - **N12 — Off-topic detection при silence** (§4.7 v3.2): keyword-overlap heuristic с `task_classification` ловит грубые off-topic'и, но `task_classification` сам контролируется prompt'ом (контроллер влияет на classifier output через формулировку prompt'а — хотя prompt пишет пользователь, не controller). **Acceptable residual** — закрытие требовало бы LLM-call для semantic similarity, что добавляет cost+latency без proportional gain. 90% real cases ловятся, edge cases (контроллер выкручивает AskUser в рамки classification но скрытно про другое) — surface через rationalization-flags в next prompt. @@ -983,6 +1035,30 @@ Protected paths должны быть live **до** того как соотве } ``` +**`~/.claude/runtime/subagent-block-.json`** (новый в v3.4, S5 TRUE closure — N1/v4.1-audit fix) — short-lived per Task tool call, append-on-block: + +```jsonc +{ + "schema_version": 1, + "tool_use_id": "", + "subagent_session_id": "", + "created_at": "", + "blocks": [ + { + "ts": "", + "tool_name": "Edit|Write|MultiEdit|NotebookEdit|Bash|Skill|Task|...", + "tool_input_summary": "string (truncated for log)", + "reason": "string (gate decide reason)", + "decision_kind": "1_direct_invocation|2_single_rec|3_chain|4_silence|hard_deny_path" + } + ] +} +``` + +Пишется subagent gate-процессом atomic-append через proper-lockfile (§3.5) при каждом decide=block. Читается parent gate at PreToolUse of next tool после Task return. Удаляется parent gate после чтения. Background cleanup script: orphans > 24h → delete (защита от parent crash после read/before delete). + +**Protected status:** glob `~/.claude/runtime/*` в §3.1 + явное упоминание (v3.4) для устранения двусмысленности. Controller не может Edit/Write/Delete. + **`~/.claude/runtime/edited-files-.json`** (новый в v4, **schema добавлен в v3.3 — N6 v4.1-audit fix**) — session-scoped, append-on-edit: ```jsonc @@ -1167,6 +1243,40 @@ Berkeley-style: use v2 spec, новый план implementation. ## 11. История версий +### v3.4 (2026-05-29, утро, N1/S5 TRUE closure через out-of-band block-file) + +Dedicated brainstorm на закрытие N1 (subagent BLOCKED-protocol, S5) — был зафиксирован в v3.3 как unresolvable в controller-writable spec'е (требует harness-level enforcement). Найден **real out-of-band channel** без harness-зависимости через subagent gate-process side-channel write в protected runtime-файл. + +**Что изменилось архитектурно:** + +В v3.2/v3.3 BLOCKED-сигнал шёл через **controller-written text** в subagent response (regex-marker или heuristic). Это была уязвимость класса controller-writable signals: я (controller) пишу любой текст, включая fake `{"status":"DONE"}` маркер. V3.4 переносит сигнал в **side-channel** который контроллер не контролирует: + +| Что | Раньше (v3.2/v3.3) | V3.4 | +|---|---|---| +| Сигнал | Текст в subagent response (controller-written) | Файл `subagent-block-.json` (subagent gate-process пишет) | +| Путь | n/a | Derived из tool_use_id (Anthropic-assigned, controller не знает заранее) | +| Защита от tampering | Regex match (легко подделать) | §3.1 protected paths (`~/.claude/runtime/*` — Edit/Write/Delete blocked) | +| Writer | Controller-written response text | Subagent's gate-process (отдельный node-процесс) | +| Reader | Parent gate parses text | Parent gate reads protected file by deterministic path | + +**Adversarial pre-check на новый дизайн (v4.2 audit):** 6 attack-векторов проверены до написания spec'а — все заблокированы protected paths + harness-assigned tool_use_id + side-channel write semantics. Закрытие TRUE, не heuristic. + +**7 spec edits:** + +- **§3.4 переписан** полностью: убрана «weak heuristic» framing, заменена «out-of-band block-file channel». Anti-forgery (3 защитных слоя) + Adversarial pre-check таблица + Failure modes таблица. +- **§3.1** +`subagent-block-.json` explicit-mention (без надежды на glob-интерпретацию). +- **§3.2** расширен: subagent gate ТАКЖЕ пишет block-file (parallel к чтению inheritance — единая subagent-gate logic). +- **§3.2.0** +Smoke 3 user-run probe: «Write на protected → block-file должен появиться». Acceptance + degraded fallback path. +- **§8** budget +2h (subagent write logic 0.5h + parent read logic 0.5h + proper-lockfile integration 0.3h + spec edits 0.5h + integration 0.1h + Smoke 3 уже в 2.1.0). Pre-flight smoke-tests 1h → 1.5h за Smoke 3. +- **§9** — S5 убран из acceptable residual list (был "Same class как S8"). Теперь только S8 (BRANCH-SWITCH-CONFIRMED — отдельный класс, в response-канале, не subagent-related; нужен отдельный follow-up эпик). +- **§10.2** +schema для `subagent-block-.json` (tool_use_id, subagent_session_id, blocks[]). + +Implementation time: 19.5-29h (v3.3) → **21.5-31h** (v3.4). +2h за N1/S5 TRUE closure infrastructure. + +**Главный урок v3.4 (обновление v3.3 урока):** controller-writable signals fundamentally fake-able, НО можно вырваться из этого класса через **side-channel write**: если сигнал пишется отдельным процессом (subagent gate) в файл с deterministic-path-from-harness-id + protected by Edit/Write/Delete enforcement — controller теряет все 3 точки контроля (writer, path, file). Это **не harness-dependent** — работает с любым Claude Code где env-vars пробрасываются (smoke 1) и subagent gate-process реально стартует с теми же settings.json хуками (smoke 3). + +Brainstorm methodology: `superpowers:brainstorming` skill — 1 clarifying вопрос (closure level: heuristic / out-of-band file / disable subagent mutating / wait harness) → дизайн с 3 защитными слоями + 6-attack pre-check → user approval → 7 spec edits. + ### v3.3 (2026-05-29, утро, adversarial audit v4.1 closure) Второй раунд adversarial audit от controller'а — теперь на свою же v3.2 (через chain `audit-context-building` → ручной разбор как незнакомого файла). Выявил **12 новых holes**, главное — **N1 fatal: v3.2-фикс S5 был самообманом.** BLOCKED-protocol regex `\{[^{}]*"status"\s*:\s*"(DONE|BLOCKED)"[^{}]*\}` в subagent response — текст пишет САМ КОНТРОЛЛЕР (через Task tool), regex просто видит fake `{"status":"DONE"}` маркер. Та же текстовая конвенция без enforcement, что была в S5 изначально.