27 KiB
CODE-REVIEW-521-r2
Issue: #521 «fix: alignment guides follow the live gesture again»
Материал ревью: 9a3af808d95684b32df8f102da4211f3f800703c (ветка
issue/521-align-guides-live). Диапазон делты этого раунда —
0715321fdc69a2955d7850f3f384c1a0fdc25311..9a3af808 (материал r1 → материал
r2): один коммит deebcfb0 (публикация CODE-REVIEW-521-r1.md конвейером,
0 изменений кода) и один продуктовый/тестовый коммит 9a3af808 «fix: hide the
settled guides copy for the whole gesture, and prove it».
Заход: r2 · блокирующих циклов израсходовано 1 из 4 (r1 — жёлтый).
Скоуп
Единственная находка r1 (Medium, в скоупе) — AC5 заявлял доказательство
(«снятие подавления осевой копии слоя даёт 2 группы .alignguides — смок
красный»), которого прежний свидетель не давал: осевая копия
.hp-editor-only-layer в сценарии смока всегда была пуста, потому что смок ни
разу не форсировал осевую отрисовку посреди самого жеста. Дельта r1→r2 —
исправление ровно этого: переписанный сценарий devices в
demo/smoke_align_guides.mjs форсирует настоящую осевую отрисовку
(c._hdrH += 0.01) в середине перетаскивания, проверяет, что она
действительно произошла, что владение слоем перешло к осевшей сцене с
правильной (живой) точкой, что видимая группа ровно одна на каждом шаге
передачи, и что следующее движение возвращает владение живому художнику; плюс
новый мутант live-editor-keeps-the-settled-guides-visible в
scripts/mutation-gate.mjs, откатывающий подавление обратно в ветку plan
(как было до задачи).
Продуктовый код (src/live-editor.ts) в этой дельте не изменился по существу:
единственная правка — комментарий, объясняющий измеренный механизм передачи
слоя (было общее «плановый режим уже это делал», стало «владение чередуется
само, _commitLiveEditor() … измерено: две .alignline …»). Логика
makeTransparent(...) вне ветки plan осталась той же, что была принята и
проверена в r1 — сама защита не менялась, менялось только доказательство её
работы.
Дельта локальна по критериям §2.10: не ребейз (единственный промежуточный
коммит — публикация документа предыдущего раунда), не смена контракта
поведения (контракт п.3/AC5 переформулированы под тот же механизм, а не
заменены другим), не новая подсистема, объём (17 строк комментариев в
live-editor.ts, 16 строк нового мутанта, ~90 строк в свидетеле) далеко не
сопоставим с исходной задачей. Полный повторный разбор всей задачи не
требуется — по AC1–AC4, AC6–AC9 переверяю только то, что могла задеть дельта
(сам свидетель), остальное наследую из r1.
Отдельная находка: тело issue правилось после зелёного ревью ТЗ (#517)
Хеш нормализованного тела issue на момент зелёного SPEC-REVIEW-521-r3.md
(«Материал раунда», конец документа) — 1e7f8d192f340e80e878aec0d6de383c982263254b408f8fc928f383130f5684.
Текущий хеш (пересчитан дважды независимо: gh issue view --json body и
gh api repos/.../issues/521 -q .body, оба дают одинаковый нормализованный
текст) — d117679d25abeab7ebfad3ed7902a8222625fc2ad43d0396b56d6fdacc12b5d3.
Хеши расходятся — тело менялось после зелёного ревью ТЗ. Дельту GitHub не
хранит, поэтому проверяю не разницу, а текущий текст целиком (см. «Что
проверено» ниже).
Что именно изменилось, восстанавливается по признанию автора в комментарии
«Исправление по код-ревью r1 → заход r2»: «ТЗ: пункт контракта 3 и AC5
переписаны под измеренный механизм». Прочитал текущий пункт 3 «Контракта» и
строку AC5 в теле issue — они дословно описывают именно тот механизм
(чередование владения через _commitLiveEditor(), измеренные «две
.alignline, осевая отстаёт на шаг»), который выяснился при разборе r1 и
теперь доказан переписанным свидетелем. Текст не расходится с кодом и тестами
этого раунда — я сверил это построчно (см. «Что проверено»), а не принял на
слово автора.
Оценка: это находка процесса — правка раздела ## ТЗ, уже прошедшего
зелёное ревью, должна по идее возвращаться на новый цикл S4-spec-review, а
не вноситься по ходу код-ревью. Но по существу правка не меняет ни персону, ни
сценарий, ни объём видимого изменения, ни один AC-номер — она уточняет
формулировку контракта и доказательства AC5 под механизм, который сам же
код-ревью r1 и вскрыл, и делает это прозрачно (объяснено в комментарии, а не
тихой правкой). Отдельного цикла ревью ТЗ эта поправка не заслуживает: именно
для такого случая существует делегирование «технический спор решает ревью
кода» (PROCESS.md §7.1) — здесь спора и нет, есть согласие с находкой r1,
выраженное в тексте. Классифицирую как Low, снимаю решением ревьюера:
текст ТЗ проверен целиком и корректен, действие не требуется.
Как проверялось
Валидация на материале раунда (9a3af808) зелёная
(https://github.com/Matysh/houseplan-card/actions/runs/34526718586), включая
шесть параллельных джобов «Мутанты по диффу» — я прочитал логи всех шести
шардов напрямую (gh run view --job <id> --log), а не поверил статусу
«success» на слово:
| Гейт | Прогнал | Результат |
|---|---|---|
Validate на 9a3af808 (tsc, test, build+bundle-sync, no-new-any) |
нет, зачтено по зелёному прогону CI (см. ссылку) | success |
| Логи 6 шардов «Мутанты по диффу» в этом прогоне | да, прочитаны построчно | все 5 мутантов задачи пойманы: live-editor-devices-drops-align-guides (шард 5/6), live-editor-decor-drops-align-guides (4/6), live-editor-plan-drops-align-guides (1/6), align-point-reads-frozen-snapshot (2/6), live-editor-keeps-the-settled-guides-visible (6/6) — новый мутант этого раунда, «тест покраснел, как обязан» |
npm run bundle:sync (пересборка + сверка dist/custom_components/.../frontend/demo/srv/assets) |
да, локально (нужно было поднять demo/srv/assets, он не коммитится) |
чисто, git status не показал diff после сборки — включает tsc --noEmit |
node demo/smoke_align_guides.mjs (полный сценарий, все 34 поля) |
да | все true, OK |
node scripts/mutation-gate.mjs --id=live-editor-keeps-the-settled-guides-visible |
да, независимо от CI, в рабочем дереве материала | «поймано 1 из 1» |
Ещё 4 мутанта задачи по одному (--id=…) |
да, все 4 | все «поймано 1 из 1» |
node --test test/smoke-harness-contract.test.mjs (AC8) |
да | 7/7 |
node scripts/check-docs.mjs |
да | зелёный, 7 файлов, 12 внешних ссылок |
node scripts/smoke-select.mjs --base 0715321f --head HEAD (выборка по дельте раунда, не по всей задаче) |
да | 1 прямое совпадение — demo/smoke_align_guides.mjs (символ _alignPoint); других смоков дельта не касается |
node demo/smoke_isometric_live_touch.mjs, node demo/smoke_touch_tips.mjs |
да (продуктовый код не менялся между r1/r2, но риск ТЗ по touch не был явно закрыт в r1 — закрываю здесь) | оба зелёные, все поля true |
cmp dist/houseplan-card.js custom_components/.../houseplan-card.js, то же для houseplan-panel.js и houseplan-assets.json |
да | побайтово идентичны |
git show -s --format=full 9a3af808 (трейлеры) |
да | Issue: #521, User-Visible: no — верно: дельта не меняет видимого поведения (сама правка уже была User-Visible: yes в 53585b45, эта дельта — доказательство и комментарий) |
Хеш тела issue (issueBodyDigest из scripts/review-doc-guard.mjs, два независимых способа получения текста) |
да | не совпадает с зафиксированным в зелёном SPEC-REVIEW-521-r3.md — находка выше |
Не прогонял: golden:verify (дельта раунда не меняет визуал — только
комментарий, тестовый файл и конфиг мутанта; видимый рендер не тронут),
pytest tests_backend (Python не тронут), полный набор из 239 смоков (не
обосновано ни диффом, ни smoke-select), performance-профили (см. ниже),
WSL/HA-харнесс.
Перф (AC9). Не перезамерял. Диф src/live-editor.ts между материалом r1 и
материалом r2 — только комментарии (проверил построчно: if/makeTransparent
вызовы в paintHouseplanEditor и editorTemplate идентичны байт в байт по
существу, разница — только текст комментариев). Комментарии не выполняются,
поэтому число операций в кадре жеста не изменилось ни на одну со времён r1, где
я независимо (не на слово автора) прогнал benchmark:large-house-interaction
benchmark:compareс собственной базой наfa7ac02cи получил зелёный отчёт без регрессии (см.docs/reviews/CODE-REVIEW-521-r1.md, таблица гейтов). Этот вывод наследуется без повторного прогона.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
Medium: AC5 заявлял «снятие подавления даёт 2 группы .alignguides — смок красный», но смок никогда не форсировал осевую отрисовку посреди жеста, поэтому осевая копия всегда была пуста и мутация не проверялась |
Сценарий devices смока форсирует настоящую осевую отрисовку через c._hdrH += 0.01 (свойство вне liveProperties/hoverProperties/gestureProperties — проверил по src/live-editor.ts:80-86, оно не маршрутизируется в живой путь ни при каком состоянии жеста), проверяет, что рендер действительно случился (handoverHappened, счётчик willUpdate), что слой перешёл сцене с правильной живой точкой (handoverGivesTheLayerBackToTheSettledScene, handoverGuideStaysOnTheLivePoint), что видимая группа ровно одна (handoverKeepsExactlyOneGuide), и что следующее движение возвращает владение живому художнику (nextMoveTakesTheLayerBack). Новый мутант live-editor-keeps-the-settled-guides-visible откатывает makeTransparent(...) обратно в ветку plan — смок красный именно на nextMoveTakesTheLayerBack |
demo/smoke_align_guides.mjs:162-197; собственный прогон смока — все поля true; собственный прогон mutation-gate.mjs --id=live-editor-keeps-the-settled-guides-visible — «поймано 1 из 1»; то же самое независимо подтверждено логом шарда 6/6 в Validate на 9a3af808 |
| (побочно устранённая слабость свидетеля, не отдельная находка r1) счёт направляющих по числу узлов в DOM вместо видимых — гашение делает копию прозрачной, а не удаляет | visible(selector) фильтрует по layer.style.opacity !== '0' вместо сырого querySelectorAll; проверил, что makeTransparent (src/live-editor.ts:157-163) действительно ставит element.style.opacity = '0' строкой, а restore() снимает через removeProperty('opacity') |
demo/smoke_align_guides.mjs:27-36, src/live-editor.ts:157-163, :213-220 |
Находка закрыта по существу: не «сделали AC5 неопровержимым переписыванием слов», а заставили смок действительно воспроизвести сценарий (внешний осевой рендер посреди активного жеста), который единственный делает защиту наблюдаемой, и подтвердили мутацией, что без защиты сценарий ловится.
Унаследовано из r1
Без повторной проверки в этом раунде приняты выводы
docs/reviews/CODE-REVIEW-521-r1.md (материал:
0715321fdc69a2955d7850f3f384c1a0fdc25311), поскольку дельта r1→r2 их не
задевает:
- диагноз и причинность регресса (
c0d61ca3/#451, оба слома — слой и замороженная точка) — не менялись, дельта их не касается; - AC1 (направляющая в трёх живых жестах), AC2 (живая точка), AC3, AC4 —
свидетель для
decor/plan-сценариев не менялся вовсе в этой дельте (правки смока — только в сценарииdevices, между строками 162 и 197); мутантыlive-editor-devices-drops-align-guides,live-editor-decor-drops-align-guides,live-editor-plan-drops-align-guides,align-point-reads-frozen-snapshotперепрогнаны мной заново тем не менее (дёшево, один и тот же смок теперь длиннее) — все 4 по-прежнему «поймано 1 из 1»; - AC6 (один расчёт кандидатов на кадр) — код и проверка не менялись в этой дельте, наследуется техническое заключение r1 («проверено чтением», без отдельного мутанта — осталось так же, это не регрессия дельты);
- AC7 (старые гарантии, #400) — не менялся код и не менялся тот участок смока;
- AC8 (свидетель не фабрикует состояние) — тест-линт
test/smoke-harness-contract.test.mjsне менялся; перепрогнал (7/7) на новом более длинном смоке — линт по-прежнему проверяет отсутствие_deviceDrag =/_decorDraft =, новые строки (c._hdrH = …,c._layout = …уже был в старой версии) под запрет не подпадают; - AC9 (перф) — см. «Как проверялось» выше, база
fa7ac02cи зелёный отчёт r1 наследуются без повторного прогона, так как дельта не трогает исполняемый код; - бюджет
core-file-budgetи мягкость_renderAlignGuidesна карточке (this._editorRuntime?._renderAlignGuides() ?? nothing) — не менялись, проверено в r1 чтением; - трейлеры и changelog коммита
53585b45— не менялись,User-Visible: yesподтверждён в r1.
Технический раздел «Чего не проверял» из r1 (golden, pytest, полная матрица смоков, WSL/HA-харнесс) остаётся в силе тем же образом — дельта раунда их не задевает и не расширяет их необходимость.
Находки
Low-1 (снимается, см. выше) — тело issue правилось после зелёного ревью ТЗ без нового цикла S4-spec-review
См. раздел «Отдельная находка» в «Скоупе». Текст пункта 3 «Контракта» и AC5
переписан автором вместе с исправлением по код-ревью r1, без возврата в
S3-spec/S4-spec-review. Проверено: новая формулировка точно описывает
реализованный и теперь доказанный механизм, персона/сценарий/объём/номера AC
не изменились, изменение раскрыто в комментарии автора. Действия не требую;
записываю, чтобы хеш в следующем зелёном документе (материал раунда ниже)
зафиксировал новое значение и цепочка не молчала.
Других находок нет.
Что проверено и корректно
- Механизм передачи владения слоем воспроизведён чтением независимо от
комментария автора.
updated()(houseplan-card.ts:4157) на каждом осевом рендере зовёт_commitLiveEditor()→commitHouseplanEditor(live-editor.ts:439-454): отменяет незавершённыйraf, вызываетrestore(state)(снимаетopacity/visibility, выставленныеmakeTransparent/dim/hide) и очищает живой корень (render(nothing, target)). Значит после любого settled-рендера слой физически переходит к осевшей сцене чистым, без «памяти» о прежнем подавлении. _hdrHгарантированно не маршрутизируется в живой путь. ПрочиталliveProperties/hoverProperties/gestureProperties(live-editor.ts:80-86) иrouteHouseplanEditorUpdate(live-editor.ts:120-145) —_hdrHне входит ни в один набор, значитnamedLiveложно и осевой рендер происходит независимо от активного жеста. Форсирование смоком (c._hdrH += 0.01) действительно моделирует внешний settled-рендер (приходhass, ресайз), а не подглядывает во внутреннее состояние жеста._alignPoint— геттер, а не хранимое состояние (houseplan-card.ts:12879), поэтому и живой, и осевой путь при вызове_renderAlignGuides()во время самого settled-рендера читают одну и ту же живую точку — вот почему «отставание на шаг» проявляется только НА СЛЕДУЮЩЕМ живом кадре после хендовера, а не в момент самого хендовера; смок проверяет обе фазы раздельно (handoverGuideStaysOnTheLivePoint, затемnextMoveTakesTheLayerBack), и это совпадает с объяснением автора.- Мутант
live-editor-keeps-the-settled-guides-visibleпатчит именно тот код, который сейчас в файле — сверилfind-строку патча (scripts/mutation-gate.mjs) с текущимpaintHouseplanEditor(live-editor.ts:357-360) посимвольно, совпадает; применимость и результат подтверждены собственным прогоном и логом CI. - AC8 не сломан новыми строками смока.
c._hdrH = …,c._layout = …(уже было раньше),c.requestUpdate()не запрещены тест-линтом — запрещены только присваивания_deviceDrag =/_decorDraft =; перепрогнал линт, 7/7. - Touch-контракт не задет (риск ТЗ явно требовал: «существующие
touch-смоки обязаны остаться зелёными») —
smoke_isometric_live_touch.mjsиsmoke_touch_tips.mjsзелёные на материале раунда; в r1 эти два смока не были явно названы прогнанными, закрываю пробел здесь, а не переношу его дальше. - «Одно число — один источник» не применимо. Направляющая — линия и точка, не число; пользователю в этом диффе не показывается ни одна величина дважды.
- Бандл и трейлеры.
bundle:syncпересобрал дерево с нуля,dist/custom_components/.../frontend/demo/srv/assetsпобайтово совпали с закоммиченными файлами;git show -s --format=full 9a3af808— трейлерыIssue: #521/User-Visible: noкорректны, changelog не тронут и не должен быть (дельта раунда не меняет видимое поведение сверх уже опубликованного в53585b45).
Чего не проверял
npm run golden:verify— дельта раунда не меняет визуал (комментарий + тестовый файл + конфиг мутанта), в r1 видимый рендер тоже не менялся кроме уже принятого identical-скриншота.python -m pytest tests_backend— Python не тронут ни в одном коммите задачи.- Полная матрица из 239 смоков — не обоснована ни диффом раунда, ни
smoke-select.mjs(одно прямое совпадение); полный набор — предрелизный гейт. - Не перезамерял
benchmark:large-house-interaction— обосновано выше (дельта раунда не исполняемый код). - Не запрашивал у владельца дополнительного цикла
S4-spec-reviewпо поводу правки ТЗ после зелёного вердикта r3 — see Low-1: решил вопрос по существу сам, поскольку это чисто техническая правка формулировки под уже найденный в код-ревью механизм, а не продуктовый вопрос из числа тех, что задаются владельцу (§7.1).
Вердикт
Единственная находка предыдущего раунда закрыта по существу и подтверждена воспроизведением (собственный прогон смока и мутанта плюс независимое чтение логов CI). Новых блокирующих находок нет; единственная новая находка — процедурная (Low), снята решением ревьюера с записью выше. High: 0. Задача готова к очереди на пре-релиз.
Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0 → в задаче
Документ: docs/reviews/CODE-REVIEW-521-r2.md
Материал раунда
- Ветка:
issue/521-align-guides-live, коммит9a3af808d956— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
20fbfc5c7f020a361c04f252900511c2e383cebagit log --all --format='%H %T' | grep 20fbfc5c7f02 - Тело issue:
d117679d25abeab7ebfad3ed7902a8222625fc2ad43d0396b56d6fdacc12b5d3 - Вердикт конвейера:
green· High 0