From 9dabf84652ace03cbf67d9eb14a3fda87683a894 Mon Sep 17 00:00:00 2001 From: Matysh Date: Sun, 6 Sep 2026 15:00:39 +0300 Subject: [PATCH] docs: specify removal of persisted room drafts Issue: #478 User-Visible: no --- docs/specs/478-remove-room-drafts.md | 531 +++++++++++++++++++++++++++ docs/specs/README.md | 1 + 2 files changed, 532 insertions(+) create mode 100644 docs/specs/478-remove-room-drafts.md diff --git a/docs/specs/478-remove-room-drafts.md b/docs/specs/478-remove-room-drafts.md new file mode 100644 index 00000000..8817d2c4 --- /dev/null +++ b/docs/specs/478-remove-room-drafts.md @@ -0,0 +1,531 @@ +# ТЗ #478 — отказ от persisted-сущности `room_drafts` + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/478 +- **Статус:** готово к независимому ревью; канонический статус задаётся метками issue +- **Тип / приоритет:** tech debt + migration / P2 +- **Трек:** полный — меняются каноническая модель, импорт/экспорт, backend validation, + редактор плана и контракт Undo/Redo +- **Оценка:** пользовательская ценность 7/10; архитектурная ценность 9/10; + сложность 9/10; риск 8/10 +- **Связано:** #173 (единый инструмент «Стены»), #248 (идемпотентный Optimize), + #282 (атомарная wall model), #314 (транзакционный geometry write), #461 + (быстрый commit клика), #477 (fixed point после преобразования) + +## 1. Сценарий + +Персона — администратор дома. Поверхность — desktop Plan editor, инструмент +«Стены». Пользователь ставит точки цепочки. Каждый завершённый отрезок уже +является обычной стеной. Если новый отрезок замкнул допустимую область, +редактор предлагает создать комнату; если пользователь прекратил рисование, +перешёл в другой режим или покинул карточку, сохранённые отрезки остаются +обычными независимыми стенами. + +После возвращения пользователь может поставить первую точку новой цепочки на +конце существующей стены благодаря обычному snap. Специального «продолжения +черновика» и скрытой группировки старых отрезков больше нет. + +Touch-редактор остаётся best effort согласно `docs/TOUCH-SUPPORT.md`. View и +kiosk не меняют свой interaction contract. + +## 2. Что человек увидит до и после + +**До:** незамкнутая цепочка хранится отдельной сущностью `room_drafts`. Она +может специальным образом возобновляться после reload, отдельно участвует в +snap, выборе, толщине, удалении, Optimize и миграциях. При создании комнаты +черновик превращается в masonry, а совпадающие обычные перегородки могут +остаться дубликатами. + +**После:** каждый принятый отрезок сразу сохраняется как обычная независимая +стена (`partition`). Пока текущая цепочка жива в памяти, её части могут +подсвечиваться действующим жёлтым active-stroke. Замыкание комнаты атомарно +заменяет совпадающие участки независимых стен каноническими стенами комнаты. +Незамкнутые участки остаются стенами без дополнительного действия. + +Панель, жесты, snap, Shift 45°, предложение комнаты и внешний вид законченной +геометрии не получают новой настройки или отдельной кнопки. + +## 3. Проблема + +`room_drafts` дублирует роль независимых стен и проходит почти через весь +продукт: TypeScript/Python wall model, schema, import/export, diagnostics, +Optimize, renderer, snap, limits, editor lifecycle и history. Это создаёт две +модели одного физического отрезка и делает простой пользовательский принцип +«клик — стена; замкнул — комната» зависимым от специального persisted carrier. + +Удалить только UI-понятие нельзя. Если просто записывать цепочку в +`partitions`, текущая логика создания комнаты добавит masonry поверх уже +сохранённых стен. Получатся совпадающие физические тела, неоднозначные IDs, +лишние линии и нестабильный Optimize. Поэтому переход включает новую версию +модели, однократную миграцию и атомарное поглощение совпадающих partitions. + +## 4. Решения владельца + +Зафиксированы в issue 6 сентября 2026 года: + +1. Специального resume после reload/remount нет. Новая цепочка начинается с + обычного snap к существующему торцу и использует текущую толщину панели. +2. Сохранённые незамкнутые контуры не имеют отдельной подсветки: это обычные + стены. +3. При принятии комнаты совпадающие independent walls поглощаются сразу; рядом + с masonry не остаются дублирующие partitions. +4. Модель меняется с v9 на v10. Legacy `room_drafts` мигрируют в partitions. + +## 5. Scope + +### 5.1 Входит + +1. Wall model v10 в TypeScript и Python. +2. Детерминированная миграция каждого legacy draft segment в partition с + удалением `room_drafts`. +3. Запись каждого нового принятого отрезка текущей цепочки непосредственно в + `partitions` с сохранением crash safety и быстрым локальным preflight #461. +4. Session-only состояние активной цепочки: точки, толщины и IDs её partitions. +5. Face detection на общей masonry/partition topology без persisted draft. +6. Атомарное поглощение совпадающих partitions при создании/разделении комнаты, + включая lineage, split-остатки и rehosting проёмов. +7. Undo/Redo, Cancel, Esc, смена инструмента/режима/пространства и лимиты. +8. Удаление текущих draft-specific типов, UI, renderer/snap веток, diagnostics, + import/export проекций и мёртвых переводов. +9. Unit, parity/migration, production-bundle smoke, mutation witnesses, + performance и документация. + +### 5.2 Не входит + +- изменение алгоритма выбора допустимых faces, их порядка или полей комнаты; +- автоматическое разделение HA Area либо комнаты независимой стеной; +- объединение коллинеарных partitions вне участков, поглощаемых принятой + комнатой; +- общий фикс задержки terminal click из #461 сверх сохранения его бюджета; +- новая кнопка Finish, новая настройка, отдельный цвет «незаконченных стен»; +- визуальный redesign инструмента «Стены», wall properties или room dialog; +- изменение physical geometry, glow, sun, area или opening semantics; +- восстановление grouping старого draft после миграции; +- поддержка downgrade записи v10 старой карточкой. + +Расширение любой из этих границ требует возврата issue в `S3-spec`. + +## 6. Каноническая data model v10 + +### 6.1 Текущая схема + +`SpaceModel` больше не содержит `room_drafts`; тип `RoomDraftCfg` удаляется. +Физические линейные объекты пространства представлены только: + +- room masonry (`rooms` + `wall_segments`); +- independent walls (`partitions`); +- `wall_columns`. + +Каждый partition имеет стабильный `id`, две конечные точки `a`/`b` и `cm` по +действующему контракту толщины. `cm: 0` сохраняет смысл нулевой стены. Поле +`room_drafts` не записывается, не экспортируется и не возвращается Optimize. + +### 6.2 Legacy read и stale write + +Schema/import boundary может прочитать `room_drafts` только как legacy input, +который должен быть мигрирован до текущей семантической проверки. Для документа +с `model_version >= 10` непустое или пустое явно переданное `room_drafts` +недопустимо. + +Если устаревший v9 frontend пытается записать `room_drafts` поверх уже +сохранённого v10 config, backend отклоняет весь write существующей ошибкой +`wall_model_client_outdated` с действующим требованием обновить карточку и +перезагрузить страницу. Поле нельзя молча отбросить, частично принять или +вернуть в current config. + +Current-model save после reload, import, copy, Optimize и editor commit всегда +остаётся v10 и не реанимирует legacy carrier. + +## 7. Миграция v9 и более старых документов + +### 7.1 Нормативное преобразование + +До v10 atomization для каждого draft в порядке массива и каждого ребра +`points[i] → points[i + 1]` создаётся ровно один partition: + +```text +partition.id = valid unique draft.segments[i].id + либо deterministic legacy fallback id +partition.a = draft.points[i] +partition.b = draft.points[i + 1] +partition.cm = normalized draft.segments[i].cm/current legacy fallback +``` + +После успешного преобразования `room_drafts` удаляется. Миграция не +объединяет, не дедуплицирует и не поглощает совпадения: одно legacy edge даёт +один partition, чтобы migration сама не меняла topology сверх carrier type. +Коллинеарные, совпадающие, обратные и нулевые стены сохраняются как отдельные +records. Действующие validators затем решают, допустим ли результат. + +### 7.2 Идентичность и parity + +- Валидный уникальный legacy segment ID переносится без изменения. +- Отсутствующий/невалидный/повторный ID получает детерминированный + collision-safe ID из space identity, draft identity, edge index и geometry. +- TypeScript и Python обязаны выдавать один и тот же ID и порядок записей для + одного JSON input. +- Generated ID не зависит от locale, object key order, runtime random/clock и + абсолютного пути. +- Unknown fields существующих partitions/rooms сохраняются; draft-only unknown + fields и grouping намеренно не переносятся. +- Некорректные points/segments обрабатываются одинаково с действующей legacy + политикой fail-closed; миграция не создаёт non-finite или zero-length record, + если такой input ранее был отклоняемым. + +### 7.3 Отчёт + +Migration/Optimize/import diagnostics отдельно сообщают число преобразованных +draft records и edge records. Повторный commit v10 возвращает нули и +byte-equivalent geometry: миграция строго однократна и идемпотентна. + +## 8. Контракт активной цепочки + +### 8.1 Session state + +Активная цепочка хранит только в памяти: + +- ordered points; +- per-edge `cm`; +- ordered IDs уже сохранённых partitions; +- gesture/face-offer state, необходимый текущему editor lifecycle. + +Первый клик ставит стартовую точку и не меняет config. Каждый следующий +принятый клик добавляет один partition и его ID в session state. Активный +жёлтый stroke может определяться этим списком IDs, но не persisted-флагом. + +После reload, remount, смены пространства или повторного входа в редактор +session state пуст. Сохранённые partitions остаются на плане. Клик в их торец +использует общий snap, но начинает новую цепочку; толщина берётся с панели в +момент нового сегмента и не наследуется скрыто. + +### 8.2 Быстрый commit + +Draft-specific fast path #461 заменяется общим wall-chain partition append +path. Для одного terminal click он обязан: + +1. проверить partition limit до mutation; +2. создать стабильный ID и candidate partition; +3. выполнить тот же bounded production physical/junction proof, что #461; +4. атомарно принять одну geometry/history/save transaction; +5. при любом отказе вернуть config, path, cm и ID arrays к `before`. + +Нельзя возвращать полный синхронный preflight всего пространства на каждый +клик. Fallback к общему barrier допустим только при тех же fail-closed условиях, +которые закреплены #461. Performance profile переименовывается по текущему +carrier, но исходные budgets не ослабляются. + +### 8.3 Завершение без комнаты + +Смена инструмента/режима/пространства, Esc по #294 и уход из editor только +завершают session chain: сохранённые partitions не преобразуются и не получают +дополнительную history/save запись. Одна несохранённая стартовая точка +отбрасывается. Команда «Оставить стенами» в room offer делает то же: закрывает +соответствующий offer/chain без копирования геометрии. + +## 9. Face detection и принятие комнаты + +### 9.1 Источники graph + +Face graph строится из current masonry и partitions, включая только что +сохранённый terminal partition. Active chain нельзя добавлять второй раз как +отдельный synthetic source. Provenance каждого graph atom содержит исходный +room-wall либо partition ID и его параметрический интервал. + +Предложение появляется по действующему click-only delta contract: новая face +должна быть создана последним принятым отрезком и пройти существующие проверки +simple polygon, overlap, limits, openings и batch ordering. + +### 9.2 Поглощение совпадающих partitions + +При подтверждении room batch система в одном `before → candidate` commit: + +1. создаёт/разделяет room по действующему алгоритму; +2. назначает канонические room wall IDs через + `fixedTopologyWallLineageHints` или эквивалентный общий helper; +3. находит все интервалы partitions, совпадающие с принятыми room-wall + intervals в пределах canonical epsilon; +4. удаляет только поглощённые интервалы; +5. сохраняет непоглощённые остатки как partitions; +6. rehost-ит partition openings на новый canonical carrier или нужный остаток; +7. валидирует candidate полным physical geometry/junction barrier; +8. добавляет ровно одну именованную history command и один save. + +Поглощаются как partitions текущей session chain, так и ранее сохранённые +partitions, реально образующие принятую границу. Простое совпадение с чужой +комнатной стеной не удаляет эту стену. Не связанные с принятой face объекты не +меняются. + +### 9.3 Split и lineage + +Если partition лишь частично совпал с новой masonry, он детерминированно +делится. Child, содержащий прежнюю `a`/наименьший canonical midpoint, сохраняет +старый partition ID; остальные children получают стабильные производные IDs. +Порядок и направление исходной записи не меняют итоговый canonical result. + +Openings целиком в поглощённом интервале переходят на room wall. Openings в +остатке остаются на соответствующем child. Opening, пересекающий точку split, +подчиняется существующей fail-closed политике reconciliation: ни один проём не +может исчезнуть, дублироваться или оказаться без carrier. + +После успешного room commit немедленный Optimize обязан сообщить: + +- `partitionsReconciled = 0`; +- `redundantDraftsRemoved = 0` либо поле полностью удалено из current report; +- второй Optimize не меняет geometry/config. + +Cancel, validation failure, save conflict и лимит оставляют исходные +partitions и session chain без partial room и без потерянных IDs/openings. + +## 10. Undo/Redo и lifecycle + +- Один успешный segment click — одна named history command. Пока session chain + активна, Ctrl/Cmd+Z удаляет последний сохранённый partition по его ID и + последнюю точку/толщину; Redo возвращает тот же ID/cm и синхронизирует chain. +- Когда session chain уже завершена, обычный geometry Undo/Redo работает с теми + же partition records без draft-specific восстановления. +- Принятие room batch — одна атомарная команда: Undo восстанавливает точный + набор поглощённых partitions/openings; Redo повторно создаёт тот же room-wall + identity без дублей. +- Cancel room dialog восстанавливает/сохраняет активную session chain и её уже + записанные partitions; он не создаёт новую геометрию. +- Esc при открытом диалоге сначала закрывает диалог по текущему modal contract; + следующий Esc завершает chain, не удаляя partitions. +- Failed preflight/save не оставляет history entry, redo entry, лишнюю точку, + stale active ID или partial config. + +History labels меняются с «сегмент черновика» на нейтральный «отрезок стены» во +всех локалях, где строка остаётся видимой. + +## 11. Limits, selection и свойства + +- `MAX_ROOM_DRAFTS` и `MAX_DRAFT_SEGMENTS` удаляются. +- Каждый новый segment заранее резервирует одну запись в `MAX_PARTITIONS`. + Достигнутый лимит отклоняет клик до mutation и показывает действующее + локализованное limit message. +- Поглощение комнаты проверяет итоговые room/wall/partition/opening limits на + candidate, а не по промежуточному пиковому числу records. +- После завершения chain каждый отрезок выбирается, перемещается, получает + толщину/свойства и удаляется ровно как существующий partition. +- Draft-specific whole-chain/segment dialog, delete confirmation и special hit + target удаляются. Удаление обычной стены сохраняет текущий UX. +- Snap/Align/Grid/Resize/physical selection/zero-wall/near-axis code читает + partitions и room walls; отдельного draft source kind в current model нет. + +## 12. Import, export, copy и diagnostics + +1. Current full/plan-only export v10 не содержит `room_drafts`. +2. Legacy full/plan-only import сначала мигрируется и валидируется; preview и + summary показывают итоговые partitions и migration counts. +3. Copy space работает только с v10 geometry после materialization и + remap-ит partition IDs/opening hosts обычным путём. +4. Diagnostics/support package/system health не перечисляют current drafts. + Допустим legacy migration counter, но не persisted current count. +5. Coordinate canonicalization, Align, optimizer, wall limits и integrity + visitors перестают иметь отдельную ветку `room_drafts`. +6. `redundantDraftsRemoved` удаляется из current optimizer type/report/UI либо + остаётся только как явно deprecated legacy migration counter, всегда 0 в + v10. Один вариант должен быть одинаков в TS/Python/docs/tests. +7. Unknown future wall model по-прежнему отклоняется без downgrade. + +## 13. UX, i18n и accessibility + +Новых контролов нет. Основной visible flow остаётся прежним: «Стены», active +chain, Shift 45°, room offer, «Создать комнату»/«Оставить стенами», Cancel и +Undo/Redo. + +Обязательные текстовые изменения: + +- удалить упоминания сохранённого/возобновляемого «черновика контура»; +- объяснить, что незамкнутые отрезки сразу являются независимыми стенами; +- заменить draft-specific history/toast copy нейтральным wall wording; +- синхронно обновить все зарегистрированные локали, если ключ остаётся в i18n + registry; мёртвые ключи удалить во всех локалях и из parity tests. + +ARIA pressed state, keyboard shortcuts, focus trap room dialog и touch target +не меняются. Session-only цвет не является единственным сигналом сохранения: +после завершения chain стена видна обычной masonry-графикой. + +## 14. Acceptance criteria + +- **AC1 (`unit` + TS/Python parity):** model v9 fixture с несколькими drafts, + разными cm, reverse/duplicate/zero walls и существующими partitions один раз + мигрирует в v10; каждый edge становится partition, валидные IDs сохранены, + generated IDs совпадают в TS/Python, `room_drafts` отсутствует. +- **AC2 (`unit` + backend):** повторный v10 commit/import/Optimize byte-stable; + current document с `room_drafts` и stale v9 write поверх v10 отклоняются + целиком с правильным outdated contract. +- **AC3 (`unit` + smoke):** первый click не пишет config; каждый следующий + accepted click создаёт ровно один partition/history/save с правильным ID/cm; + отказ/limit оставляет config и session bit-equivalent `before`. +- **AC4 (`performance` + mutation):** partition append использует bounded fast + path #461, не вызывает full-space barrier на нормальном current input и не + ослабляет timing/heap/full-check budgets исходного профиля. +- **AC5 (`smoke`):** open chain после смены tool/mode/space, Esc, reload и + remount остаётся набором ordinary partitions; special resume отсутствует, + новый segment использует snap и текущую toolbar thickness. +- **AC6 (`unit` + smoke):** room face определяется через masonry+partitions без + synthetic duplicate active source и только после terminal segment, который + действительно добавил face. +- **AC7 (`unit` + smoke):** создание standalone/adjacent/split room поглощает + все и только совпадающие partition intervals; итог содержит одну masonry, + нет coincident partitions, IDs/толщина/metadata каноничны. +- **AC8 (`unit` + smoke):** partial overlap детерминированно оставляет children, + сохраняет один исходный ID и rehost-ит room/partition openings без потери, + дубля и orphan host. +- **AC9 (`unit` + smoke):** Cancel/error/conflict/limit возвращают исходные + partitions/session; accepted batch имеет одну history/save transaction, Undo + восстанавливает точные partitions, Redo — тот же room result. +- **AC10 (`unit` + smoke):** active-chain Undo/Redo удаляет/возвращает один + terminal partition с тем же ID/cm и синхронизирует points; после finish + generic Undo/Redo не зависит от draft state. +- **AC11 (`unit`):** limits считают каждый segment как partition до mutation; + draft limits/types/source kinds/select/delete/renderer branches отсутствуют, + а обычные partition properties работают для бывшей chain geometry. +- **AC12 (`unit` + import smoke):** legacy full/plan-only import и space copy + дают валидный v10 output; current export/diagnostics/support package не + публикуют `room_drafts`; future model по-прежнему отклоняется. +- **AC13 (`unit` + optimizer witness):** Optimize сразу после принятия room + возвращает нулевую reconciliation работу и второй прогон byte-stable; мутант, + оставляющий coincident partition либо draft, краснит gate. +- **AC14 (`golden verify` + smoke):** View/Plan/static/hidden Iso, wall bodies, + openings, clean floor, glow и sun до/после carrier migration визуально + эквивалентны для одинаковой geometry; active chain остаётся читаемой. +- **AC15 (`typecheck` + `unit` + `build` + docs review):** implementation-loop + gates зелёные, три bundle-копии побайтно одинаковы, канонические документы, + RU/EN changelog и i18n соответствуют v10. + +## 15. Автотесты и обязательные доказательства + +### 15.1 Migration/parity fixtures + +Добавить минимум: + +1. v9 space с одним multi-edge draft и существующей partition; +2. несколько drafts с сохранёнными IDs; +3. pre-v8 draft без IDs и collision fixture; +4. reverse/order/object-key permutation; +5. `cm:0`, mixed cm и invalid/non-finite input; +6. repeat commit, import round-trip и TS/Python JSON parity; +7. stale v9 client write в current v10. + +### 15.2 Editor unit и production-bundle smoke + +Targeted smoke через реальный UI обязан: + +1. нарисовать open chain и проверить partition после каждого terminal click; +2. выполнить Undo/Redo, Esc, tool/mode/space switch и reload; +3. продолжить от старого endpoint обычным snap с новой toolbar thickness; +4. замкнуть собственную face, face из старых partitions и adjacent face; +5. проверить partial consumed partition и hosted opening; +6. выбрать/изменить толщину/удалить бывший незамкнутый segment; +7. отменить room dialog и воспроизвести save rejection/limit; +8. проверить отсутствие `room_drafts` в config/export/Optimize result. + +### 15.3 Red witnesses + +Обязательные тесты, которые падают на `origin/dev` или при целевом мутанте: + +- legacy `room_drafts` после materialization отсутствует и заменён partitions; +- замыкание по существующим partitions оставляет одну masonry и ноль + совпадающих partitions; +- immediate Optimize fixed-point; +- draw → leave editor → return → complete room сохраняет history/IDs и не + создаёт дублей; +- удаление partition fast path либо возврат full-space click path краснит + performance/mutation gate. + +### 15.4 Golden и performance + +Визуальный контракт намеренно не меняется, поэтому новые baselines по умолчанию +не принимаются. Выполняется `golden:verify`; любое отличие требует отдельного +объяснения и reviewed acceptance по процессу. Source fingerprint документации +обновляется только через Linux capture artifact, если его потребует +`check-docs`. + +Профиль #461 адаптируется с draft carrier на partition carrier и сохраняет +исходные budgets. Отдельно измеряется face acceptance/reconciliation, чтобы +поглощение не попало в pointermove и не стало частью каждого незамыкающего +клика. + +## 16. План реализации + +1. Добавить v10 migration/parity fixtures и красные тесты. +2. Реализовать одинаковую TS/Python миграцию и backend stale-write guard. +3. Удалить `RoomDraftCfg` из current model, visitors, import/export и reports. +4. Обобщить fast preflight/commit #461 на append обычной partition. +5. Перевести editor active chain на session points/cm/partition IDs. +6. Перевести graph, renderer, snap, selection, limits и Undo/Redo на partitions. +7. Встроить lineage/reconciliation совпадающих partitions в atomic room batch. +8. Удалить draft properties/delete/resume code и мёртвый i18n. +9. Пройти targeted smoke, optimizer/mutation/performance witnesses. +10. Обновить docs/changelog, собрать и синхронизировать bundles. + +Точные имена helpers и файлов не являются продуктовым контрактом. Общая pure +логика миграции/lineage предпочтительнее параллельных частичных реализаций. + +## 17. Риски и снижение + +| Риск | Вероятность / ущерб | Снижение | +|---|---|---| +| Миграция меняет wall identity | средняя / высокий | перенос valid segment ID, TS/Python parity fixtures | +| Созданная комната дублирует partition | высокая / высокий | atomic consume witness + immediate Optimize fixed point | +| Partial consume теряет остаток/проём | средняя / высокий | interval provenance, child-ID и opening-host matrix | +| Клик снова становится секундным | высокая / высокий | сохранить #461 fast path и budgets, mutation witness | +| Undo удаляет чужую совпадающую стену | средняя / высокий | session ordered IDs, exact before/after history | +| Старый frontend вернёт drafts | средняя / высокий | model v10 semantic rejection, outdated error smoke | +| Legacy malformed input расходится TS/Python | средняя / высокий | shared vectors и byte-level parity | +| Partition limit ломает длинную цепочку | средняя / средний | reserve до mutation, точный localized отказ | +| Удаление ветки ломает glow/sun/floor | низкая / высокий | physical parity + golden verify | +| Docs уже описывают целевое поведение частично | высокая / низкий | сверить оба guide с фактическим v10 contract | + +## 18. Откат + +Кодовый откат выполняется revert implementation commit, но уже сохранённый v10 +config нельзя безопасно записывать старой v9 карточкой. Поэтому operational +rollback — восстановить предыдущий release и одновременно запретить editor +writes для v10 с понятным outdated сообщением; автоматическая обратная миграция +partitions в drafts запрещена, поскольку grouping утрачен. + +До публичного релиза критический migration/parity/consume/optimizer gate +блокирует beta. Если дефект найден после beta, исправляется forward в v10; +понижение model version и синтетическое восстановление drafts не допускаются. + +## 19. Release-артефакты + +Изменение классифицируется как `User-Visible: yes`: внешний основной flow почти +тот же, но исчезает resume persisted draft и меняется поведение незамкнутых +отрезков после reload. + +Implementation commit обновляет: + +- `docs/CHANGELOG.md` и `docs/CHANGELOG.ru.md` со ссылкой на #478; +- `docs/ARCHITECTURE.md` — model v10 и session chain; +- `docs/CANVAS.md` — face graph provenance и active-chain rendering; +- `docs/WALL-THICKNESS.md` — partition-to-masonry lineage; +- `docs/CONFIG-COMPATIBILITY.md` — v10 migration/stale clients/downgrade; +- `docs/USER-GUIDE.md` и `docs/USER-GUIDE.ru.md` — незамкнутые стены и отсутствие + special resume; +- `docs/TESTING.md` — migration/parity/smoke/performance witnesses; +- `docs/STATUS.md` после фактической реализации; +- i18n registry/locales, если меняются видимые history/toast строки; +- три поставляемые bundle-копии. + +Нового screenshot baseline не ожидается. Перед beta обязательны exact-SHA +Linux Validate, targeted production smoke, `golden:verify`, performance smoke и +зелёное независимое code review. + +## 20. Принятые технические предположения — можно менять без продуктового ревью + +1. Session chain может храниться отдельным объектом вместо трёх массивов, если + остаётся неперсистентной и содержит ordered partition IDs. +2. `draft-live-preflight.ts`/`draft-live-commit.ts` можно переименовать либо + заменить общим модулем; важны boundary и budgets, а не имя. +3. Fallback ID может использовать действующий deterministic wall-ID helper, + если TS/Python parity и collision contract выполнены. +4. Reconciliation можно извлечь из `coincident-partitions.ts` либо выполнить + общим atomic helper до final wall-model commit. +5. Deprecated optimizer counter можно удалить либо оставить нулевым на один + compatibility cycle; решение должно быть единым во всех API/docs/tests. +6. Active-chain yellow style может адресоваться CSS/data-атрибутом по множеству + partition IDs без добавления persisted поля. +7. Открытых продуктовых вопросов нет: решения владельца полностью задают + resume, подсветку и поглощение совпадений. diff --git a/docs/specs/README.md b/docs/specs/README.md index 6eb1dbfc..16a33960 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -93,6 +93,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным | Issue | ТЗ | |---|---| +| [#478](https://github.com/Matysh/houseplan-card/issues/478) Отказ от persisted-сущности `room_drafts` | [478-remove-room-drafts.md](478-remove-room-drafts.md) | | [#10](https://github.com/Matysh/houseplan-card/issues/10) Roomba live position | [010-vacuum-roomba-live-position.md](010-vacuum-roomba-live-position.md) | | [#442](https://github.com/Matysh/houseplan-card/issues/442) Атомарный откат отклонённых записей маркера | [442-marker-write-rollback.md](442-marker-write-rollback.md) | | [#11](https://github.com/Matysh/houseplan-card/issues/11) Vacuum source health | [011-vacuum-source-health.md](011-vacuum-source-health.md) |