From 9fde407e4ebaf1d7bbf1534f70e7b9a3a971e5e0 Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 31 Aug 2026 03:40:36 +0300 Subject: [PATCH] docs: #398 spec revision 3 per SPEC-REVIEW-398-r2 User-Visible: no Issue: #398 --- docs/specs/398-sysmodules-guard-scope.md | 34 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/docs/specs/398-sysmodules-guard-scope.md b/docs/specs/398-sysmodules-guard-scope.md index 91787426..79261fda 100755 --- a/docs/specs/398-sysmodules-guard-scope.md +++ b/docs/specs/398-sysmodules-guard-scope.md @@ -3,7 +3,8 @@ - Issue: https://github.com/Matysh/houseplan-card/issues/398 - Приоритет: P2, infra/tests; полный трек — класс B (тесты и гейт), меняется правило, а не продуктовый код -- Ревизия: 2 (2026-08-31) — по SPEC-REVIEW-398-r1 (Medium: путь 2 несовместим с AC4) +- Ревизия: 3 (2026-08-31) — по SPEC-REVIEW-398-r2 (Medium: статический гвард + против самоочищающегося файла) ## Сценарий @@ -84,7 +85,19 @@ custom_components.houseplan.coordinate_canonicalization `custom_components` за время `exec_module`, — тогда после вызова остаются ровно пустышки conftest. Проверено исполнением, включая повторный вызов подряд. -Единственное исключение остаётся одно — `tests_backend/conftest.py`. +**Исключений в статическом гварде два, и это не возврат снятого пути.** +Гвард читает исходники, не исполняя их, и физически не может знать, что +`load_pure` возвращает `sys.modules` в исходное состояние сразу после +`exec_module`. По букве AC1 он обязан отклонить `pure_imports.py` — но тогда +файл, делающий ровно то, чего задача требует, оказывается вне закона. + +Поэтому исключений в списке два — `tests_backend/conftest.py` (условная +подмена, контракт #394) и `tests_backend/pure_imports.py` (запись, снимаемая +тем же вызовом). Разница со снятым «путём 2» существенная: там файл +исключался ВМЕСТО очистки, здесь — при обязательной очистке, которую держит +исполняемый AC4 и отдельный AC8. Статический список отвечает на вопрос «кому +разрешено писать в `sys.modules`», исполняемая проверка — на вопрос «осталось +ли что-нибудь после»; подменять второе первым и было исходной ошибкой #394. ## Скоуп / не-скоуп @@ -117,8 +130,13 @@ custom_components.houseplan.coordinate_canonicalization `sys.modules.setdefault(...)`/`update(...)` тоже отклоняются либо приводят к явному отказу «не могу доказать безопасность». Доказательство: по одному контракту на форму. -- **AC3**. `tests_backend/conftest.py` остаётся разрешённым и остаётся - единственным исключением; список зафиксирован тестом, его рост краснеет. +- **AC3**. Список статических исключений состоит ровно из двух поимённых + файлов — `tests_backend/conftest.py` и `tests_backend/pure_imports.py`; + любой третий краснеет. Доказательство: контрактный тест на список. +- **AC8**. Исключение не превращается в дыру: `pure_imports.py` обязан + восстанавливать `sys.modules` в `finally`, и это проверяется отдельно от + гварда. Доказательство: тест, который убирает восстановление и получает + красный AC4 (мутант `pure-imports-stops-cleaning`). - **AC4**. После прогона всего `tests_backend/` в `sys.modules` нет ключей `custom_components*` сверх тех, что положил conftest. Доказательство: исполняемая проверка, а не чтение исходников. @@ -136,7 +154,7 @@ custom_components.houseplan.coordinate_canonicalization 1. Синтетический файл с записью через переменную → гвард краснеет (AC1). 2. По контракту на каждую форму из AC2. 3. `conftest.py` со своей условной подменой → зелено (AC3). -4. Попытка добавить второй файл в список исключений → красный. +4. Попытка добавить третий файл в список исключений → красный (AC3). **Backend** (`tests_backend/test_backend_quality.py` или новый файл): @@ -145,8 +163,10 @@ custom_components.houseplan.coordinate_canonicalization **Мутант** (`scripts/mutation-gate.mjs`): -- `sysmodules-guard-blind-to-variable`: вернуть запись через переменную в - `pure_imports.py` → гвард красный. +- `sysmodules-guard-blind-to-variable`: добавить запись через переменную в + файл ВНЕ списка исключений → гвард красный. +- `pure-imports-stops-cleaning`: убрать восстановление `sys.modules` из + `finally` в `pure_imports.py` → исполняемая проверка AC4 красная (AC8). ## Риски