diff --git a/tools/build/lib/acceptance.sh b/tools/build/lib/acceptance.sh index a3f3306..b79601f 100644 --- a/tools/build/lib/acceptance.sh +++ b/tools/build/lib/acceptance.sh @@ -1282,11 +1282,25 @@ run_single_owner_acceptance() { } ' || fail "acceptance: production-запись обязана объявлять владельца файла" - log_step "Acceptance: reconfigure classifies by ownership, not by message text" + log_step "Acceptance: reconfigure classifies by ownership and typed guard state, not by message text" + # Обычные операционные отказы классифицируются по ВЛАДЕНИЮ. Исключение ровно + # одно, и оно типизированное: сработавший guard по владению неотличим от + # обычного отказа smoke — тронут firewall, перезапущены сервисы, — но чинить + # надо другое, потому что сервер уже вернулся на ПРЕЖНИЙ firewall. Поэтому он + # опознаётся по типу ошибки и никогда по её тексту. + # + # Гейт требовал литеральное `classifyReconfigureFailure(ownership)` и потому + # утверждал не тот контракт, который назван в его же заголовке: второй + # параметр появился вместе с типизированным распознаванием guard, и с этого + # момента проверка описывала предыдущую версию архитектуры. + # + # `error instanceof FirewallGuardFiredError` здесь намеренно НЕ дублируется: + # тот же инвариант проверяется ниже, в «a fired guard forbids the durable + # commit», и для install, и для reconfigure. Один инвариант — один гейт. ! grep -qF '.test(message)' orchestrator/src/commands/reconfigure.ts \ || fail "acceptance: reconfigure must not classify failures by matching the error text" - grep -q 'classifyReconfigureFailure(ownership)' orchestrator/src/commands/reconfigure.ts \ - || fail "acceptance: reconfigure failure classification must be ownership-driven" + grep -q 'classifyReconfigureFailure(ownership, error)' orchestrator/src/commands/reconfigure.ts \ + || fail "acceptance: reconfigure failure classification must use ownership and the typed operation error" } # Каналы утечки секретов: Config API и журналы.