# SPEC-REVIEW-223-r3 - Issue: [#223](https://github.com/Matysh/houseplan-card/issues/223) «Оптимизировать планы» должна канонизировать координаты, а не консервировать floating-point шум - ТЗ: [`docs/specs/223-optimize-coordinate-canonicalization.md`](../specs/223-optimize-coordinate-canonicalization.md) на коммите `8c9e5feae3503c086cc9f987d57f676e5435e537` (HEAD) - Раунд: r3/4 - Вердикт: **зелёный** ## Скоуп ревью (по дельте, PROCESS.md §2.10) Round r2 получен на `965711ee2005b8ec2587b9c70fb9b14735c7756e` (SHA назван в самом документе `docs/reviews/SPEC-REVIEW-223-r2.md`, вердикт жёлтый, High: 0, Medium: 1). Предмет этого раунда — `git diff 965711e..HEAD`. Диапазон состоит из двух коммитов: - `981d3d6` — публикация документа `docs/reviews/SPEC-REVIEW-223-r2.md` (артефакт самого ревью, не правка ТЗ); - `8c9e5fe` («docs: clarify optimize report terminology») — единственная содержательная правка: 4 вставки / 4 удаления в `docs/specs/223-optimize-coordinate-canonicalization.md`, три места — §7 (текст диалога), строка AC6 и строка риска в §11. Меняется только слово «нормализовано»/«normalized» → «обновлено»/«updated» для существующего счётчика `c`; структура, семантика, доказательная матрица AC не затронуты. Дельта локальна текстом (§2.10): нет ребейза, нет смены контракта поведения, не задета новая подсистема, объём (8 строк) на порядок меньше и исходного ТЗ (~230 строк), и даже правки r2 (42 строки). Полный разбор не открывается. Отдельно проверено, не расширяет ли эта правка скоуп и не ломает ли AC, которые r1/r2 уже приняли (§2.10 п.5, регрессия по образцу #102) — нет: правка меняет исключительно пользовательскую метку одного и того же существующего показателя `c`, не его состав, не структуру `OptimizeReport`, не AC4/AC5/§6/§8. ## Как проверялось 1. Восстановлен вердикт r2 и его SHA командой `gh issue view 223 --json comments` — `965711e`, назван в документе r2 (в этом раунде замечание «SHA не назван» не применимо — второй раунд подряд его исправно называет). 2. `git show 8c9e5fe` — построчный разбор правки, единственная содержательная правка в диапазоне. 3. Проверено буквальное закрытие находки r2: `grep -in "нормализ|normali" docs/specs/223-optimize-coordinate-canonicalization.md` — осталось ровно два вхождения: (а) `§6`, «нормализованного числа» — легитимное употребление термина в его устоявшемся во всём проекте значении (координата 0..1 холста, никак не связано со счётчиком `c`); (б) `§11`, строка риска, где слово упомянуто явно как «занятый термин, не используемый для `c`» — то есть подтверждение отказа от него, а не остаточное использование. Нового употребления «нормализовано»/«normalized» как метки счётчика `c` не осталось нигде. 4. Проверено, не создаёт ли выбранная замена («обновлено»/«updated») новую коллизию того же класса, которую уже дважды находил этот цикл ревью (находка r1 №2 — «канонизировано» дважды; находка r2 — «нормализовано» занято координатной системой): - `grep -rn "\bupdated\b" docs/CANVAS.md docs/ARCHITECTURE.md` — ноль совпадений: слово не занято описанием координатной системы ни в одном каноническом документе; - `grep -rn "обновлен" docs/*.md` — единственное релевантное совпадение вне `CHANGELOG.ru.md` (журнал, не термин) — `toast.room_updated` / «Комната обновлена» (`src/i18n/ru.json:562`, `en.json:562`): это обобщённое подтверждение сохранения формы редактора комнаты, не термин геометрии и не соседствует с координатным текстом Optimize — коллизии смысла нет; - `docs/CANVAS.md` §9.5 (строка 421) — канонический документ фичи называет операцию, которую считает `c`, термином «exact open-span canonicalisation»; «обновлено»/«updated» нейтрально по отношению к этому термину и не конкурирует с ним за то же слово, в отличие от отвергнутых «канонизировано» и «нормализовано»; - выбранное слово — буквально один из двух вариантов по умолчанию, предложенных самим ревью r2 в качестве примера («например "обновлено"/"updated"»); автор использовал предложенный дефолт, а не самостоятельно ввёл новый термин, что снижает риск третьей итерации той же коллизии. 5. Сверено, что правка синхронна во всех трёх местах (§7, AC6, риск §11) и в обоих языках (RU/EN) — да, во всех трёх местах пара «обновлено»/«обновлённые»/«spaces updated» согласована, расхождений между местами нет. 6. Перечитаны комментарии issue #223 целиком на предмет незаявленного расширения скоупа — не найдено; хендофф r2→r3 (комментарий 2026-08-20 19:13:25Z) точно описывает сделанную правку. 7. Проверено происхождение семантики `c` в коде: `src/plan-optimizer.ts:404-500` — `canonicalized++` инкрементируется по каждому элементу цикла `for (let i = 0; i < config.spaces.length; i++)`, то есть ровно по пространствам (`space`), чьё сериализованное представление `open_spans`/`open_to`/`walls` изменилось. Формулировка «обновлено пространств: {c}» / «spaces updated: {c}» точно соответствует коду, который считает счётчик. Код не проверялся: реализация не написана, этап `spec`, ни один файл класса A/B в диапазоне `965711e..HEAD` не менялся (`git diff --stat` — только `docs/reviews/**` и `docs/specs/**`). ## Закрытие раунда r2 | Находка r2 | Чем закрыта | Где видно | |---|---|---| | Medium — замена «нормализовано»/«normalized» для `c` заново создаёт коллизию того же класса: слово устойчиво занято координатной системой 0..1 во всём проекте, включая канонический документ фичи (`CANVAS.md`), который к тому же называет саму операцию `c` термином «canonicalisation», а не «normalisation» | Термин «нормализовано»/«normalized» заменён на «обновлено»/«updated» во всех трёх местах — §7 (диалоговая строка), AC6 (доказательная формулировка), §11 (строка риска); слово выбрано ровно из набора дефолтов, предложенных самим ревью r2, и не пересекается ни с «канонизировано» (занято `p`), ни с «нормализовано» (занято координатной системой) | `docs/specs/223-optimize-coordinate-canonicalization.md` §7, AC6 (таблица §9), §11, коммит `8c9e5fe` | Закрытие точное и полное: `grep` не находит остаточного использования слова в роли пользовательской метки счётчика, а выбранная замена проверена на тот же класс коллизии, который дважды всплывал в предыдущих раундах (r1 №2, r2), и не воспроизводит его в третий раз. ## Находки Новых находок нет. High: 0, Medium: 0. ## Что проверено и корректно - Находка r2 закрыта буквально и без побочной коллизии (см. выше). - Три места правки (§7/AC6/§11) синхронны между собой и с новой пользовательской формулировкой; ни AC4/AC5/§6/§8/§9(остальные AC)/§10/§12/§13, ни структура `OptimizeReport`/`AlignReport` дельтой не задеты. - `c` семантически описан точно тому, что делает код (`plan-optimizer.ts:500`). - Скоуп задачи не расширился и не сузился; продуктовых вопросов по-прежнему нет, дельта чисто терминологическая. - Обязательные разделы ТЗ по PROCESS.md §7.1 присутствуют полностью (не повторная проверка формы — унаследовано, см. ниже, — но сам факт, что правка не удалила и не сломала ни один раздел, проверен диффом). ## Чего не проверял - Код реализации — не написан, этап `spec`. - Гейты (`typecheck`/`test`/`build`) — не запускались; дельта не затрагивает класс A/B (только `docs/specs/**` и `docs/reviews/**`), гейты для этапа `spec` не требуются. - Текст `docs/USER-GUIDE.ru.md`/`docs/CANVAS.md`/`docs/CHANGELOG*` после будущей реализации — они по плану переписываются в реализации (AC9, §12), не в ТЗ; r3 проверял только формулировку самого ТЗ. - Все AC и разделы, которых дельта `965711e..HEAD` не касается (AC1, AC2, AC3, AC4, AC5, AC7, AC8, AC9, AC10, §1–§6, §8, §10, §12, §13) — унаследованы из r2 без повторной проверки, ниже. ## Унаследовано из r2 Принято без повторной проверки в этом раунде, по документу `docs/reviews/SPEC-REVIEW-223-r2.md` на SHA `965711ee2005b8ec2587b9c70fb9b14735c7756e`: - скоуп по `docs/SCOPE.md` (J6) и выбор обычного трека — не изменился, дельта трек-критерии не затрагивает; - обязательные разделы ТЗ по PROCESS.md §7.1 присутствуют и по форме верны (подтверждено ещё в r1, дельта r2 и r3 разделов не удаляла); - подтверждённая причина и порядок величин шума (issue, §3 ТЗ) — арифметика `EPS`/`GRID_STEP_N` из `src/align-grid.ts:62-72`, не менялась ни в r2, ни здесь; - контракт снапа §6, идемпотентность (AC5), исключение проёмов из счётчика, отсутствие вызовов `snapN()` вне `align-grid.ts`, отсутствие роста `moved`/`maxShift*` от near-node замен — код и раздел §6 не входят в дельту ни r2, ни r3; - AC4/`partitions`/`wall_columns` (находка r1 №1) — закрыта в r2, дельта r3 этих разделов (§9 AC4, §10, §11 соответствующей строки риска) не касается; - AC1, AC2, AC3, AC4, AC5, AC7, AC8, AC9, AC10 — доказательство ни одного из них дельта r3 не задевает (правка только в §7-фрагменте, строке AC6 и строке риска §11, причём даже внутри AC6 меняется только слово, а не сама проверяемая формулировка контракта); - ссылка issue ↔ ТЗ в `docs/specs/README.md`, release-артефакты §12 — не входят в дельту; - отсутствие продуктовых вопросов владельцу — подтверждено r1 и r2, дельта r3 чисто терминологическая правка одной пользовательской строки, новых продуктовых вопросов не порождает. ## Вердикт Вердикт: зелёный · цикл r3/4 · High: 0 · Medium: 0 Обе находки предыдущих раундов (r1 №1, r1 №2/r2) закрыты по существу, третья попытка того же класса коллизии не воспроизвелась. ТЗ готово к переходу в «Готово к разработке».