From a1f0db22c2f6c0b0436789b64e82f53bfa314327 Mon Sep 17 00:00:00 2001 From: Crimson Date: Tue, 1 Sep 2026 04:56:33 +0500 Subject: [PATCH] =?UTF-8?q?fix(build):=20=D0=B3=D0=B5=D0=B9=D1=82=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D1=81=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20reconfigure=20=D0=BE=D0=BF=D0=B8=D1=81=D1=8B?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20=D0=BF=D1=80=D0=B5=D0=B6=D0=BD=D1=8E=D1=8E?= =?UTF-8?q?=20=D0=B0=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проверка требовала литеральное `classifyReconfigureFailure(ownership)`, тогда как у функции давно два параметра. Второй появился вместе с типизированным распознаванием сработавшего guard: по владению он неотличим от обычного отказа smoke — тронут firewall, перезапущены сервисы, — но чинить надо другое, потому что сервер уже вернулся на ПРЕЖНИЙ firewall. То есть гейт утверждал не тот контракт, который назван в его же заголовке, и падал на коде, который этот контракт соблюдает. Поведенческие тесты при этом были и остаются зелёными: «текст ошибки на классификацию не влияет» и «сработавший guard опознаётся по типу ошибки». Проверка приведена к фактической форме, заголовок — к фактической архитектуре. `error instanceof FirewallGuardFiredError` намеренно не дублируется: тот же инвариант проверяется ниже, в «a fired guard forbids the durable commit», и для install, и для reconfigure. Заодно прогнаны ВСЕ гейты приёмки по текущему дереву, а не только упавший: 116 положительных литеральных проверок, 36 bun-блоков с текстовыми инвариантами и все отрицательные сканы. Кроме этого одного — расхождений нет. --- tools/build/lib/acceptance.sh | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) 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 и журналы.