24 KiB
SPEC-REVIEW-306-r3
- Issue: #306 — Редактор плана: заменить виртуальные стены обычными стенами толщиной 0
- Этап: ревью ТЗ (PROCESS.md §2.4, повторный раунд — §2.10)
- Заход: r3 · блокирующих циклов израсходовано (до этого вердикта) 1 из 4
- ТЗ:
docs/specs/306-zero-thickness-walls.md, HEAD веткиissue/306-zero-thickness-walls=abbd4904641b196ab9c2ed841600e2b260c75653(«docs: adapt zero-wall spec to model v8») - Метка issue:
S4-spec-review(полный трек, неsmall/trivial)
Расхождение метаданных запуска с фактическим состоянием issue (процессная находка, не по ТЗ)
Входные метаданные этого запуска указывали «Заход: r1 · блокирующих циклов
израсходовано 0 из 4». Фактическое состояние по GitHub и репозиторию другое —
и это тот же класс расхождения, который уже фиксировал SPEC-REVIEW-306-r2.md
для своего запуска:
docs/reviews/SPEC-REVIEW-306-r1.md(SHA904c47e4) — вердикт жёлтый, High 0, Medium 3, опубликован комментарием issue (2026-08-25T16:49:42Z);docs/reviews/SPEC-REVIEW-306-r2.md(SHA2c801bf5) — вердикт зелёный, High 0, Medium 0 (2026-08-25T16:57:24Z), спущен цикл §4 не тратил;- после зелёного r2 задача ушла в разработку («Взял: Codex», 16:58:32Z), но
после rebase на
devреализация нашла продуктовый конфликт с только что слитым #282 (comment 2026-08-26T00:18:24Z: model v8 уже материализует каждый contour atom, включая старые тонкие стены, какwall_segments[].cm=0, и единый default стиля по этому единственномуcm:0бьёт по одной из двух групп старых планов); - владелец принял решение не вводить
zero_kind/compatibility-marker (2026-08-26T07:15:44Z) — стены неотличимы по происхождению, а не по решению спецификации; - автор переписал ТЗ под target model v9 на authoritative
wall_segments[]и явно передал «на независимое spec-review r3/4» (2026-08-26T07:22:47Z).
Возврат в работу между r2 и этим запуском не был вердиктом ревью с блокирующими находками — он вызван конфликтом, обнаруженным в реализации после слияния #282, поэтому по определению цикла (§4: «отправка на ревью → вердикт с блокирующими находками → возврат») бюджет циклов между r2 и этим запуском не тратится. Итого до этого вердикта: 1 цикл израсходован (жёлтый r1), r2 — зелёный, не тратит. Провожу разбор как r3 по фактическому состоянию GitHub, а не как первый заход. Это находка процесса запуска ревью, а не находка по содержанию ТЗ #306 — отдельный issue не заводится (параметр одного прогона, не продуктовый дефект).
Скоуп этого раунда
Дельта между r2 (2c801bf5) и текущим HEAD (abbd4904) — не локальна по
критерию §2.10: git diff 2c801bf5..abbd4904 -- docs/specs/306-zero-thickness-walls.md
даёт 136 добавленных / 87 удалённых строк (≈31% документа), и это правка,
которая задевает новую подсистему — целевую модель хранения меняет с
плоской space.walls[]/open_spans (v7) на authoritative
space.wall_segments[] со stable ID (v8, реализовано #282), а целевая версия
документа поднята с v8 до v9. Меняется контракт identity, миграции, лимитов и
нормализации — ровно тот случай, где §2.10 прямо предписывает полный разбор,
а не разбор по дельте. Провожу разбор целиком, с явным наследованием тех
частей r1/r2, которые модель v8/v9 не затронула по существу (см. раздел
«Унаследовано» ниже).
Как проверялось
Технические утверждения ТЗ о текущей системе проверены по коду и документам, не приняты со слов автора:
git show --stat/git log --followна историю issue #306: коммитыdc6d0aaf,41469d0a,0436850d,20070a75,abbd4904и все 11 комментариев issue прочитаны целиком (gh issue view 306 --json body,comments);custom_components/houseplan/const.py:54иsrc/plan-optimizer.ts:42—PLAN_MODEL_VERSION = 8подтверждён как текущий (не 7, как было на момент r1) — согласуется с заявленным ТЗ переходом «v8 → v9»;src/wall-thickness.ts:215—clampWallCmсуществует, используется в местах, которые ТЗ требует сделать zero-aware (АС2/АС11);custom_components/houseplan/validation.py:1394-1405—WALL_SEGMENT_SCHEMA.cmужеRange(min=0, max=100)(backend принимаетcm:0дляwall_segments[]уже сейчас, после #282);ROOM_DRAFT_SCHEMA(:1425) иPARTITION_SCHEMA(:1448) — всё ещёRange(min=1, max=100),WALL_COLUMN_SCHEMA(:1471) —Range(min=1, max=150). Формулировка §6.1 ТЗ («Backend принимает cm: 0..100 для wall_segments[], room_drafts[] и partitions[]») соответствует действительности как целевое состояние: часть уже верна, часть (room_drafts,partitions) требует правки — ТЗ не выдаёт частично готовое за полностью новое;custom_components/houseplan/validation.py:1038-1070— прямая проверка лимитов, см. находку M1 ниже;docs/specs/282-stable-wall-segment-identity.md(§6.5, AC7, разделы проwalls[]compatibility projection) — подтверждает заявления ТЗ §5.2 о том, чтоspace.walls[]уже сейчас генерируемая проекция, а не источник истины, и чтоwall_segments[]уже authoritative для identity;scripts/config-field-registry.mjs:206-219иdocs/CONFIG-COMPATIBILITY.md:37-46—spaces[].rooms[].open_toуже зарегистрирован какdeprecated-read; статусыdeprecated-read/migrate-on-write, на которые ссылается §6.2 ТЗ, существуют в реестре;docs/USER-GUIDE.ru.md— grepГраницаподтверждает, что инструмент документирован (строки 432, 483, 537, 1510 и др.) и требует правки, что ТЗ фиксирует в §16/§19;docs/TOUCH-SUPPORT.md— формулировки «desktop-first», «best effort» совпадают с §14 ТЗ;src/open-spans.ts(781 строк) иsharedBoundary(найден в 5 файлах, включаяsrc/logic.ts) существуют — ссылки §5.3/§10 п.2 на существующий resolver не выдуманы;node --test test/docs-accept.test.mjs test/process-gate.test.mjs— 40 passed, 0 failed, 0 skipped (Linux; ближе к последней ревизии автора, который называл 37/39 passed + ожидаемый Windows-skip — разница чисто по площадке и времени, не расхождение);ls demo/smoke_*.mjs | wc -l= 192 — для справки; новых смоков в этом ТЗ не требуется прогонять (спецификация, не код), упомянуто в §17/§18 ТЗ как будущий gate код-ревью.
Закрытие раунда r2
r2 (docs/reviews/SPEC-REVIEW-306-r2.md, SHA 2c801bf5) был зелёным,
High 0, Medium 0 — открытых находок к закрытию в этом раунде нет. Все три
находки r1 (M1 разделы «Сценарий»/«Что человек увидит», M2 отсутствие
USER-GUIDE.ru.md, M3 несуществующие статусы реестра) были закрыты уже в r2
и остаются закрытыми: соответствующий текст (§1-2, §16/§19 упоминания
USER-GUIDE.ru.md, §6.2 статусы deprecated-read/migrate-on-write)
присутствует и в текущей ревизии abbd4904 — переработка под model v8 не
откатила ни одну из трёх правок (проверено построчно по текущему файлу,
номера строк указаны в разделе «Унаследовано»).
Унаследовано из r1/r2
Без повторной проверки по существу в этом раунде принято — переработка под model v8 не касалась этих частей текстуально и по содержанию:
- продуктовые решения владельца по световому режиму (таблица dashed/solid,
запрет фиктивной толщины, единый resolver для Glow и солнца, инвалидация
кэшей при переключении, RU/EN copy под селектором) — документы
SPEC-REVIEW-306-r1.md/r2.md, полностью проверены на SHA904c47e4/2c801bf5, текст §4 п.4-7, §9.1, §15 текущей ревизии не отличается по смыслу; - соответствие
docs/SCOPE.md(J4/J6) и отсутствие конфликта с out-of-scope — раздел 1 ТЗ не менялся по сути между r2 и r3; - присутствие
docs/USER-GUIDE.ru.mdв §16/§19 (M2 r1) — строки 483-484, 682 текущей ревизии; - статусы compatibility-реестра
deprecated-read/migrate-on-write(M3 r1) — §6.2, строки 172-175 текущей ревизии, повторно подтверждены и в этом раунде прямой проверкой реестра (см. «Как проверялось»), а не только по наследству; - i18n-таблица §15 (ключи
space.zero_wall_*,toast.zero_wall_*,gs.zero_walls_migrated) — не менялась между r2 и r3, RU/EN парность есть; - touch/accessibility требования §14 — не менялись по существу.
Дельта r2→r3 не касается доказательств AC1, AC2, AC5, AC6, AC7, AC14, AC16 —
их текст либо не менялся, либо менялся только терминологически (ссылки на
wall_segments[] вместо walls[]) без изменения проверяемого поведения; они
не переразбирались заново по существу. AC3, AC4, AC8, AC9, AC10, AC11, AC12,
AC13, AC15, AC17 разобраны заново ниже, так как их доказательство прямо
зависит от модели v8/v9, миграции и лимитов.
Находки
M1 (Medium, в скоупе) — лимит wall_segments[] в ТЗ устарел и противоречит уже слитому #282
ТЗ трижды называет лимит записей числом 500:
- §10 шаг 10: «При превышении 500
wall_segments[]или любого лимита отказать целиком» (docs/specs/306-zero-thickness-walls.md:352); - §13: «Лимит записей остаётся 500; результат не truncates» (
:423); - §17 AC13: «Лимит 500, invalid span, opening conflict или revision conflict отклоняет весь candidate» (
:604); - §20, риск «Atomization превысит лимит» → «atomic failure, no truncation» — привязан к той же цифре.
Фактический предел MAX_WALL_SEGMENTS в custom_components/houseplan/validation.py:1048
равен 200 000, не 500. Это не опечатка автора, а устаревший факт: до #282
MAX_WALLS действительно был 500 (подтверждено git log -p -S "MAX_WALL_SEGMENTS", коммит b336eee9: -MAX_WALLS = 500 /
+MAX_WALLS = 200_000 / +MAX_WALL_SEGMENTS = 200_000, с комментарием в коде
«v8 atomises room boundaries» — лимит был поднят именно потому, что
атомизация контура увеличивает число записей). Ревью r1 (2026-08-25T16:49,
до слияния #282 в dev) корректно зафиксировало «лимиты MAX_* = 500» как
факт на тот момент. #306 переписан под model v8 (после слияния #282,
комментарий 2026-08-26T00:18) и в остальном тексте последовательно опирается
на wall_segments[] как authoritative-каталог (§5.2, §6.1, §16) — но
конкретно эту цифру ревизия «adapt to model v8» не обновила ни в одном из
четырёх мест.
Отдельно — MAX_OPEN_SPANS = 500 (:1056) действительно равен 500, но это
предел удаляемого этим же ТЗ поля open_spans, а не wall_segments[]; ни
docs/specs/282-stable-wall-segment-identity.md, ни код не подтверждают
цифру 500 для итогового каталога — собственный perf-бенчмарк #282 оперирует
масштабом 10 000 атомов (docs/specs/282-...md:431), на два порядка выше.
Почему это Medium, а не Low: формулировка не «уточнить», а прямое
техническое утверждение, встроенное в проверяемый AC13 и в шаг миграции §10 —
если реализовать буквально, миграция/Optimize начнёт отказывать легитимным
пространствам, которые #282 уже поддерживает (200 000 записей), то есть ТЗ,
как написано, предписывает регресс уже принятой возможности. Это не открытый
продуктовый вопрос (владельцу нечего решать — предел объективно другой), и не
блокирует понимание архитектуры документа — значит, не High. Правка
механическая: заменить «500» ссылкой на актуальный MAX_WALL_SEGMENTS
(200 000) или убрать конкретное число из §10/§13/§20 и оставить «действующий
лимит записей», сохранив только в AC13 существующее поведение «превышение
лимита отклоняет весь candidate целиком».
Снято с записью (Low, не требует правки)
Как и в r1, в документе нет одного консолидированного блока «принято предположительно, поменять свободно» (PROCESS.md §7.1) — часть технических допущений оформлена инлайн с пометками свободы («имя можно уточнить без изменения контракта», §5.3), часть — как явные решения с обоснованием. Ревью r1 уже приняло этот формат как достаточный при отсутствии открытых продуктовых вопросов; в этом раунде замечание не переоткрывается.
AC, разобранные заново (затронуты моделью v8/v9)
- AC3 (единая семантика всех
cm:0) — соответствует принятому владельцем 2026-08-26T07:15:44Z решению не вводитьzero_kind; текст АС и §5.2/§10 п.5 согласованы, критерий проверяемый (source-contract + смешанная v8 fixture). - AC4, AC8, AC9, AC11 — используют authoritative
wall_segments[]и lineage #282 корректно (§8.1, §8.3, §10 п.3-4); ссылки на «lineage #282» соответствуют реально реализованному identity barrier (docs/specs/282-...md§6.5 и далее). - AC10 — защита проёмов на миграции распространена на все итоговые
cm:0(§10 п.6), не только на бывшие legacy spans — корректно закрывает edge case, который отдельно требовала аналитика (comment 0, «Перевод физического участка с уже размещённым проёмом»). - AC12 — read-only и mutation-gate формулировка не изменилась по смыслу относительно v7-версии, применима к v8/v9 без правок.
- AC13 — проверена выше в M1: текст в остальном (atomic transaction, no partial apply, one-deep Undo) корректен и соответствует §10.1; конкретная цифра лимита — единственный дефект.
- AC15 — соответствует текущему backend-состоянию (см. «Как проверялось»):
wall_segments[]уже принимаетcm:0,room_drafts/partitionsтребуют правки диапазона — AC формулирует именно это, без завышения того, что уже сделано. - AC17 — бюджеты §13 корректно исключают лимит 500 из перф-раздела нет,
see M1; сам перф-контракт (fingerprint по
space id + geometry revision + zero_wall_style, отсутствие rebuild на HA state tick) сформулирован однозначно и проверяем benchmark-артефактом.
Что проверено и корректно
- Оба продуктовых раздела §7.1 (сценарий, что человек увидит) присутствуют и не описывают реализацию;
- открытых продуктовых вопросов к владельцу нет, оба его решения (комментарии
2026-08-25T15:10:53Z о световом режиме пространства и 2026-08-26T07:15:44Z
о запрете
zero_kind) полностью отражены в §4 и §5.2; - модель v8/v9 описана не как догадка, а с явной опорой на уже слитый #282, и эта опора проверена по факту (не по заявлению автора) в разделе «Как проверялось»;
- AC1-AC18 пронумерованы, у каждого указан способ доказательства (unit/backend/smoke/golden/perf), формулировки однозначны;
- release-артефакты (§19) включают оба changelog, оба user-guide, канонические документы подсистем (WALL-THICKNESS, LIGHT, CONFIG-COMPATIBILITY, ARCHITECTURE, STATUS) и явно требуют bundle sync — соответствует PROCESS.md §8 и §11.2 таблице класса D; i18n-таблица симметрична RU/EN, включает пояснение под селектором отдельным ключом (не только tooltip), что было прямым требованием владельца;
- откат (§12) отдельно разбирает forward compatibility и invalid-downgrade,
без Labs-флага — обоснование («две одновременно пишущие модели — больший
риск, чем флаг снимает») продуктово осмысленно и не противоречит
docs/CONFIG-COMPATIBILITY.md; - зависимости (§21) корректно называют #282 как реализованную основу и не претендуют на дублирование её identity-writer; #148/#173 помечены как требующие superseded-note, а не тихо игнорируются.
Чего не проверял
- Не прогонялись
npm run typecheck/npm test/npm run build— на этапе ревью ТЗ src не менялся, эти гейты относятся к код-ревью (PROCESS.md §2.7, §8) и здесь неприменимы; - не прогонялись browser-смоки, golden,
python -m pytest tests_backend— код отсутствует, нечего исполнять; упомянутые в ТЗ файлы тестов (test/zero-wall-migration.test.mjs,demo/smoke_zero_walls.mjsи др.) ещё не существуют, это ожидаемо для стадии спеки; - не проверялась математика perf-бюджетов §13 (p95 +10%/+5%) эмпирически — оценена только как формулируемая и измеримая величина, без числового прецедента на реальном large-house fixture;
- не проверялись все 21 раздел построчно на перенумерацию (в отличие от r2,
где это было предметом дельты) — в этом раунде перенумерации не было,
структура документа (1…21) стабильна между
2c801bf5иabbd4904, что подтвержденоgrep -n '^## 'без более глубокой сверки каждой внутренней§N-ссылки за пределами тех, что упомянуты выше.
Вердикт
Одна находка Medium (M1, в скоупе, устаревший лимит wall_segments[]),
High — нет. По PROCESS.md §2.4/§4 это жёлтый вердикт: автор правит текст в
трёх/четырёх местах (§10 п.10, §13, §17 AC13, §20), фикс проходит повторный
раунд ревью по дельте (§2.10, дельта в этом случае локальна — правка одной
цифры и её контекста, не новая подсистема).
Вердикт: жёлтый · заход r3 · блокирующих циклов 2/4 · High: 0 · Medium: 1 → в задаче