Skip to content

Аудит warning-only гейтов → политика fail-closed (v15-failclosed-gate-audit)

Статус: принято (v1.5, эпик v15-evidence-attestation). Принцип заимствован у Walko: предохранитель, который только предупреждает, — это предохранитель, который выключен.

Единая политика

Fail-closed по умолчанию. Гейт, охраняющий инвариант качества, обязан блокировать. Warning-only допустим только по одному из трёх явных оснований:

  1. Adoption-path — блок наказывал бы за неполное внедрение фичи, а не за риск (нет ключа подписи, pre-миграционные строки, легаси-задачи без новых полей).
  2. High-FP — проверка эвристическая (ключевые слова, паттерны), и ложный блок дороже пропущенного предупреждения; ремедиация ложного срабатывания неочевидна.
  3. Availability — отказ инфраструктуры проверки (битая БД, недоступный git) не должен останавливать работу; для shared/CI-сред есть строгий режим.

Каждый hard-гейт обязан иметь: (а) дешёвую ремедиацию в тексте ошибки, (б) явный документированный opt-out (config), (в) тест на негативный сценарий.

Инвентарь (состояние на v1.5)

ГейтГдеБылоВердиктОснование
QG-0 goal/AC отсутствуютgate_qg0_checkhardhard ✓
QG-0 AC без негативного сценарияgate_qg0_checkhardhard ✓
QG-0 Rule 6 rollback_plan (явные medium/complex)gate_qg0_checkhard (v28)hard ✓opt-out нет, unset-complexity = warn (adoption)
QG-0 Rule 2 scope (явные medium/complex)gate_qg0_checkwarn → hard (v1.5)hard ✓qg0.scope_hard_gate=false
QG-0 scope_excludegate_qg0_checkwarnkeep warnHigh-FP: для многих задач exclude избыточен; scope_paths уже hard
QG-0 security-AC (ключевые слова)gate_qg0_checkwarnkeep warnHigh-FP: keyword-эвристика (SECURITY_KEYWORDS)
QG-0 <5/9 intent dimensionsgate_qg0_checkwarnkeep warnдиагностика полноты, не инвариант
QG-2 verify-first (нет свежего verify)service_gateshard (v1.4)hard ✓task_done.auto_verify=true
QG-2 receipt-signature (tamper/substitution)verify_receipt_checkhard (v1.5)hard ✓NULL receipt / нет ключа = warn (adoption)
QG-2 scoped-pytest все skippedservice_verificationhard (synth fail)hard ✓
QG-2 L3 при measured-high riskrisk_l3_triggerhard (v1.5)hard ✓risk.l3_block_on_high=false; thin coverage = pass (High-FP)
QG-2 Rule 7 root cause (defect-задачи)service_task_donewarn → hard (эта задача)hard ✓task_done.root_cause_hard=false; ремедиация = одна строка task log
QG-2 Rule 8 knowledge (complex/defect + --no-knowledge)service_task_donerefusal (v1.3.4)hard ✓прочие задачи = note (High-FP)
QG-2 rollback_plan на doneservice_task_donewarnkeep warnAdoption-path: pre-v28 задачи должны закрываться
QG-2 AC evidence markers 0/Nservice_task_donewarnkeep warnHigh-FP: парсер формата (✓/AC-N) строг, прозаичное evidence легитимно
QG-2 verification checklistservice_task_donenotekeep warnдиагностика, дублирует verify-first
Hook task_gate (нет активной задачи)hookshardhard ✓fail-open на ошибке БД; TAUSIK_HOOK_FAIL_SECURE=1
Hook scope_write_gate (вне ACL)hookshard (v1.5)hard ✓задача без ACL = свобода (adoption); fail-open БД + FAIL_SECURE
Hook memory_pretool_blockhookshardhard ✓bypass-маркер anchored (gotcha #132)
Hook secret_scanhookswarnopt-in strictHigh-FP: паттерны секретов ловят фикстуры; TAUSIK_SECRET_SCAN_STRICT=1
Hook bash_firewall (опасные команды)hookshardhard ✓
Hook git_push_gate (нет тикета)hookshardhard ✓tausik push-ok = ремедиация
Session capacity (Rule 9.2)service_taskhardhard ✓task start --force (логируется audit-событием)

Изменения этой задачи

  • Rule 7 root cause: warn → hard. Defect-задача, закрытая без причины, — это ровно тот fail-open, ради которого аудит затевался: дефект «исправлен», а знание о причине потеряно. FP-риск (формулировка без ключевых слов «root cause/причина/из-за…») купирован дешёвой ремедиацией (одна строка task log "Root cause: ...") и opt-out task_done.root_cause_hard=false.

Кандидаты будущего ужесточения (когда уйдёт основание)

  • AC evidence 0/N → hard после того, как структурированный --evidence-json станет основным путём (FP формата исчезнет).
  • secret_scan strict по умолчанию после введения allowlist для тестовых фикстур.
  • receipt no-key → hard если/когда tausik key init войдёт в bootstrap по умолчанию.