**High-1.** Комнаты хранятся в тех же координатах, что и перегородки:
`roomPoly` отдаёт сырой полигон конфига. Обе обвязки делили его на `NORM_W`
ещё раз, комната уезжала в область ~0.0001, и `junctionAt` не находил ни
одного совпадения. Узел на T-стыке к середине стены комнаты — тот самый
случай, ради которого ТЗ прошло два раунда ревью, — молча исчезал.
Воспроизведено вызовом `optimizePlans`: `partitionsMerged === 1` там, где
ожидается 0.
**High-2.** Завершаемая цепочка к моменту слияния ещё лежит в `room_drafts`:
каждый клик персистит её через `_persistActiveDraftSegment`, а удаляется
черновик строкой ниже вызова слияния. Собственные концы цепочки считались
чужим примыканием, и стык с существующей стеной не срастался. Активный
черновик теперь исключается — ровно так же, как это делает
`plan-snap-overlay` (`activeDraftId`).
Дыры в тестах, которые это пропустили, закрыты по существу, а не заплаткой:
- `demo/smoke_wall_chain_merge.mjs` рисует продолжение реальными кликами через
`_markupClick`, а не присваиванием `_path`, — то есть исполняет тот путь, на
котором дефект и жил. Клики задаются в координатах плана и переводятся через
живой view box, иначе смок целится мимо только что нарисованной стены.
- `test/plan-optimizer.test.mjs` получил комнату с примыканием к середине
стороны: юниты модуля этого не ловили, потому что передают полигон уже в
согласованном масштабе, минуя обвязку.
- Мутанты `partition-merge-rescales-rooms` и `chain-merge-sees-own-draft`
сторожат оба места: проверены применением патча, 1 и 2 падения.
Issue: #229
User-Visible: no
Рисование прямой стены в несколько кликов оставляло по записи на каждый
отрезок. Швы невидимы, пока их не тронешь: выделение хватает кусок,
перетаскивание ломает стену пополам, толщина задаётся пофрагментно. У стен
комнат этого давно нет — `normalizeWallIntervals` схлопывает каждый сплошной
участок одной толщины. Независимые перегородки жили по другому правилу.
Новый чистый модуль `src/wall-merge.ts` даёт им то же правило:
- `mergeCollinearPartitions` сращивает соседей одинаковой толщины и
направления до неподвижной точки, но только там, где узел никому не нужен.
Узел остаётся, если в него приходит третья перегородка, стена комнаты
(стороной, а не только вершиной), колонна или конец сохранённого черновика.
- Направление выжившей записи канонизируется лексикографически: иначе одна и
та же физическая стена выходила то a→b, то b→a в зависимости от порядка
входа, и каждый host.t вдоль неё переворачивался вместе с ней.
- `applyOpeningMoves` переносит проёмы на выжившую запись: и авторитетный
`host`, и legacy-проекцию `x/y/angle`, которую рисует старый читатель
конфига (docs/CONFIG-COMPATIBILITY.md, #132). Проекция здесь не кэш —
канонизация направления разворачивает угол на 180°.
Рисование сращивает только свою цепочку и то, чего она коснулась (§8.6 ТЗ):
молча править чужие швы в стороне оно не вправе — для этого есть
«Оптимизировать планы» с предпросмотром, отчётом и отменой. Оптимизация
проходит по всему пространству без seed-ограничения и отдельной строкой
сообщает, сколько записей исчезло.
Issue: #229
User-Visible: yes