r1 of the code review:
- M1: a strip keeps its marker's value badge, passive, at the half-length
anchor on the card and on the static card (no icon core, pulse or slot).
- M2: one room resolver for the strip's Glow — an explicit valid room_id of
the marker wins over the anchor room; a stale one falls back (stripRoom).
- M3: the chain remembers the space it is drawn in; a space switch finishes
it there, never in the space shown next, and opens no picker over it.
- M4: visibility is decided before import(): a hidden marker or an
HA-disabled device loads no LED chunk (ledVisible, also checks the stored
marker so a just-hidden one does not slip through a stale device list).
- M6: the static card is a full light_pools × live_states browser matrix.
Unit tests for M2/M3, smokes for M1/M3/M4/M6, five registered mutants.
Issue: #780
User-Visible: yes
Stage 5 of #780.
- Import summary: «Strips left unbound after import: {n}» from the backend
`unbound_led_strips` count; «Optimize plans» reports strips passing
through walls per space and edits none (AC16).
- Linear field for long strips (ТЗ §13.2): pieces of at most the radius
along the polyline, emitters thinned to r/4, each piece clipped to the
visibility fans of its own emitters as separate clipPath children (no
boolean pass per piece), one floor clip for the whole layer, no fan at
all where nothing blocks within the radius; a grid index of body faces
and boxed inside tests; unchanged fields skip re-diffing. 50×50 on the
large house: first stable frame ~1.4 s, warm space ~1.1 s locally.
- led-strips-v1 profile: demo/benchmark_led_strips.mjs with the derived
large-house fixture (10×5, 50×50, none), absolute limits of the ТЗ table
in demo/performance/budgets-led-strips.json, exact counters (zero
recomputes on HA ticks/camera/colour, ≤50 cache entries, no growth over
20 cycles); added to the full performance workflow.
- Bundle: LAZY_LED_GZIP_CEILING 10 KiB, LAZY_LED_EDITOR_GZIP_CEILING 11 KiB
(measured + 10 %, rounded up); overlaps with the initial and editor
graphs refused; the lazy editor graph stays inside its ceiling.
- Smokes smoke_led_strip_draw/bind/glow, linked in smoke-links; 13 mutants
in the registry (7 browser guards in the inventory); config field registry
entry `spaces[].led_strips`.
- Golden: five new scenes on the `golden-led` space of the visual fixture
(`ledStrips` option, the designer's four strips on #868D94), matrix v71.
- Docs: LIGHT, DEVICE-PRESENTATION, USER-GUIDE (en/ru), UX-MODES,
ARCHITECTURE, ISOMETRIC, CONFIG-COMPATIBILITY, TOUCH-SUPPORT, demo/stand
README, performance README; docs/design/led-strips with the unchanged
designer archive, two paired frames and ACCEPTANCE.md; both changelogs.
Issue: #780
User-Visible: yes
The owner decided on 30.09 that the moon is not part of the "Follow the Sun"
environment but a switch of its own: with a static background (global or a
space's own) the card showed no moon even with the switch on, and the switch
said nothing about why the moon was missing right now.
With a static background there is no environment, so the moon stands in its
own layer, `.hp-moon-sky`: the first child of `.stage` / `.hp-static-stage`,
the whole scene, no z-index, filter or will-change, under the plan by DOM
order, fading with the #101 View weight. Inside is the very #661 element, so
place, size, art and fades are unchanged, and a background switch moves it to
its new parent in the same render without a flicker. The phase comes from the
same `resolveDayCycle`, computed only while the moon is on and on View; without
`sun.sun` both cards keep their 30 s clock ticker and re-render only when the
phase changes (the environment is still compared by its whole fingerprint).
General settings get a second caption line under the moon switch
(`data-moon-status`): one snapshot per opening, judged by the lazy chunk as if
the switch were on, first reason wins (no home, day, below 3°, under 3 %),
numbers rounded and clamped below the threshold they missed. `moonStatus`
decides "shown" with the same `moonShownAt` as the element. It lives in a
WeakMap beside the draft, so it never makes the dialog dirty; a closed
opening's result is dropped. The dialog loads the chunk through the gate's
loader (`withMoon`), now shared by every caller while a load is in flight, so
there is still one fingerprint check and one retry token.
Bundle (same build, against origin/dev): initial View 300 072 -> 300 248 B gzip
(+176 B, under the 500 B of the spec; budget and ceiling not raised); lazy
editor 238 558 -> 238 991 B (+433 B, the line and English strings); lazy moon
11 385 -> 11 712 B (+327 B, layer CSS and status). `src/moon.ts` stays out of
the initial and the editor graph; bundle-budget now refuses an editor/moon
overlap. Monolith metrics: hostRefs 4 885 -> 4 888 — the three `host.` reads of
`src/editors/moon-status.ts` (hass, `_settingsDialog`, requestUpdate) through
its own three-member interface, not the editor port; the other five metrics
are unchanged. houseplan-editor-runtime.ts grows by two lines (import, call).
Tests: AC9/AC10/AC15 and the sky layer in test/moon.test.mjs (the #661
"static -> nothing" check inverted), AC14 and the opening lifecycle in
test/moon-settings.test.mjs, smokes demo/smoke_moon_static.mjs (AC1-AC6; AC1
and AC3 were red on dev) and demo/smoke_moon_status.mjs (AC11/AC12), AC7 in
smoke_daycycle_layer_budget. Golden: two new scenes
(static-bg-moon-gibbous-white-light, static-bg-moon-crescent-south-dark,
matrix v70), the harness checks the moon's parent by background and waits for
the status line in the General settings frames. Four new mutants; the clock
ticker one is a browser guard (201 at the guideline of 200).
Issue: #718
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
- Vertical oblique projection: the floor matrix is the identity and a height
rises straight up by z·sin 20°, so the on-screen wall height is unchanged
and the cos 20° foreshortening of the plan, decor and anchors is gone.
- Device tiles and lock badges stand on the wall-top plane with one common
shift; the #651 placement search no longer runs in the live scene (its
removal is #714). Room names keep their Flat floor point.
- The 2.5D fit no longer reserves the 48 CSS px nudge budget.
- Switching projection keeps the camera when the previous projection was on
screen: saving the setting, entering an editor from 2.5D and adopting a warm
memo from the other projection re-read only the scalar zoom. A cold 2.5D
start still opens the 2.5D home.
- Opening faces are ordered along the oblique projector (s·y + z).
Witness: demo/smoke_iso_flat_parity.mjs (AC2–AC5, AC11) is red on the old code.
Issue: #713
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
At dawn, dusk and night the environment shows the moon in the top-left
corner of the scene, behind the plan: computed in the card from the home
coordinates and the browser clock (short Meeus series + topocentric
parallax, within 1.4° / 1.8 pp of JPL Horizons), one designer image under
a continuous phase mask with the lit side always on the left (owner
2026-09-29), a feathered terminator, 3°/3 % thresholds and the 2 s fade of
the window rays. Everything but a small gate lives in the lazy
moon-runtime chunk, with its own 30 s ticker. General settings: "Sun"
becomes "Sun and Moon" with one switch, on for new installations
(DEFAULT_CONFIG), off for existing ones.
The initial View graph sat 728 B under its budget: the gate is paid for by
moving fifteen dialog-only strings of General settings into the lazy
settings dictionary (#459) and by one build fingerprint literal instead of
three, so the graph ends 4 B above dev. Golden: two new moon scenes, and
the two General settings help frames show «Sun and Moon»; the WSL artifact
test fixture now models scenes whose first capture awaits acceptance.
Issue: #661
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
The Plan editor hid every device marker (display: none), so walls,
openings and stairs were placed against devices from memory. It now
shows them exactly as the Background editor does (#362): the same
markers as View, at the Background editor's effective opacity, fully
pointer-inert. The fade is filter: opacity(0.35) on each marker, not
on .devlayer, because room labels and room settings buttons share that
layer and stay opaque and interactive; filter multiplies the marker's
own opacity (.unavail -> 0.35 x 0.35), as the Background layer does.
Marker handlers already fail closed outside View/Devices.
demo/smoke_plan_device_landmarks.mjs proves AC1/AC2: same markers as
View, per-marker alpha equal to Background, core and capsule fall
through, a real click on a marker reaches the Walls tool and a room
settings button below, cursor and target unchanged, handlers inert, no
tabindex. Three mutants guard hiding, opacity override and the pointer
boundary. UX-MODES, DECOR-EDITOR, USER-GUIDE.ru mode table, changelogs.
Issue: #687
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
Лестница рисуется протяжкой, как прямоугольник или эллипс декора: длинная
ось протяжки — ось подъёма, стрелка смотрит от нажатия к отпусканию, клик
ставит размер по умолчанию. Выделенная лестница получает рамку декора в
верхнем оверлее карточки — выше тел стен, с узлами постоянного экранного
размера и курсорами по мировому направлению узла; винтовая — четыре узла на
касательных. Ресайз идёт от противоположной стороны, не зеркалит и не
поворачивает; магнит работает по сторонам: в пределах 6 клеток и 5° сторона
ложится на видимую грань стены, при перемещении лестница доворачивается не
больше чем на 5°, при ресайзе и черновике — никогда. Грани перегородок и
колонн получают внешнюю сторону по обходу многоугольника.
Клик, синтезированный после жеста, глушится: под «Лестницей» больше не
появляется копия, под «Выбором» не пропадает выделение. Диалог свойств
считает в сантиметрах лестницы (30…10000), а не в конвертере толщины стены;
нетронутое поле хранит число бит в бит, «Сохранить» без изменений ничего не
пишет. В Просмотре наведение на лестницу с корректной целью показывает
«Переход на этаж …» — ровно при том же условии, что делает её ссылкой.
Чистые функции слоя — в stairs-editor-model.ts с юнитами; смок лестниц
покрывает протяжку, курсоры, рамку над стенами, клик после жеста, диалог и
пять состояний подсказки; реестр мутантов получил шесть свидетелей.
Issue: #676
User-Visible: yes
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
Пятнадцать расхождений из #667. Бюджет initial View назван одним
источником (scripts/bundle-budget.mjs). CONTRIBUTING описывает бандл после
#657, HA-харнесс после #630 и релиз через release-contract и release.yml.
Мутанты указывают на реестр scripts/mutation-registry.mjs. STATUS и
ROADMAP больше не держат PR в HACS «в очереди» и инструкции прежней
песочницы. SCOPE называет три редактора, форматы плана и радар #485.
README выводят первую комнату через «Стены». Русское руководство сверено с
v1.78.0-beta.5, таблица «Источник плана» склеена. UX-MODES и STYLING-HOOKS
следуют коду: проёмы в просмотре инертны, space-card рисует проёмы и
декор-изображения. Починены якорь в DEVICE-PRESENTATION и пол зума 1/3.
ADR 089/122/160 и ISOMETRIC помечают активацию через hp_alpha исторической.
Из раскладки ARCHITECTURE убран несуществующий src/data. legacy/README не
называет docs/superpowers действующими спецификациями.
Паритет английского руководства (п. 8) выделен в #668.
Issue: #667
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
Прямые и винтовые лестницы получили отдельную модель, инструменты редактора, безопасную межэтажную навигацию, вычитание из чистой площади и плоское отображение в 2.5D.
Issue: #663
User-Visible: yes
- settings.volumetric_view (General settings › Display, third switch) replaces
the alpha entry, the header projection toggle and the phone-menu item; one
rule for card, sidebar page and kiosk; editors stay Flat; backend accepts
only a boolean, support package copies it.
- Raised tiles in 2.5D View: no ring, rounded body min(0.275 D, 0.3 h), edge
0.1 D with the lab filters, ×1.12 size also in layout/collision, lift
0.075 D, one floor-shadow layer under all markers (theme × floor table),
frames hugging tile and edge (Alert > Focus > Selected > Hover), forced
colours without edge and shadow (src/iso-tiles.ts, styles/iso-tiles).
- Soft sun wash instead of projected Flat wedges (src/iso-sun.ts): same gates
and windows as sun_rays, length from elevation, parallelogram along the sun,
tone and streaks by floor lightness, sill line.
- Walls take the user's wall colour (top opaque, sides × .77/.68/.60); theme
rules for walls/openings/labels removed; furniture uses the Flat stroke rule.
- Smokes smoke_iso_tiles/iso_sun/iso_theme_walls/volumetric_setting, 16 new
mutants, acceptance scenes STAGE6_ACCEPTANCE_SCENARIOS (golden with their
Linux CI baselines), ACCEPTANCE.md side by side.
Issue: #649
User-Visible: yes
На окне не шире 480 px шапка карточки и страницы бокового меню занимает одну
строку ≤ 56 px: вкладки пространств (без шестерёнок и «+», прокрутка вбок,
текущая вкладка докручивается в видимую область), масштаб, одна шестерёнка
«Действия и настройки» и у админа слот крестика #647. Заголовок скрыт.
Меню шестерёнки (src/header-menu.ts): редакторы, настройки и добавление
пространства, общие настройки, PDF, помощь, сводная панель и объёмный вид
(alpha) — в фиксированном порядке и с теми же условиями и действиями, что у
прежних кнопок. Пункт закрывает меню; Escape возвращает фокус на шестерёнку;
касание вне меню поглощается прозрачной подложкой. На > 480 px шапка прежняя,
в киоске меню нет.
- три кнопки шапки вынесены в renderHeaderActions — шапка карточки не растёт;
- сводная панель отдаёт свои пункты методом menuItems();
- i18n: title.header_menu (en/ru/de/fr);
- потолок initial View 291 000 → 292 600 (замер 291 635), база bundleBytes;
- смок smoke_mobile_view_header, юнит header-menu, 10 мутантов; смоки
support_feedback, gear_tabs, modes, toolbar_stable_width переведены на
меню на ≤ 480 px; подпись вкладки в span.tabtitle без pointer-events;
- USER-GUIDE.ru, UX-MODES, TOUCH-SUPPORT, STYLING-HOOKS, data-hp-contract,
оба changelog.
Issue: #616
User-Visible: yes
Транзитная геометрия Resize теперь только вычисляет центр для текущего кадра и не удаляет сессионную позицию. Окончательная геометрия по-прежнему очищает точку, если она действительно вышла за комнату.
Issue: #645
User-Visible: yes
Кнопка сохраняет положение в пределах комнаты только на время сессии редактора,
корректно отменяется при pinch/cancel и учитывается подписями Resize.
Issue: #645
User-Visible: yes
General settings colour tiles (room fills and glow) paint their colour at
its opacity over the checkerboard again, so "No light sources" at 0 % no
longer looks like solid grey. The label ink is chosen from the visible
colour (hex mixed with the checkerboard by alpha). Colour plates
(flat-swatch without cover-swatch) stay solid — now witnessed by the
- hp-color-opacity: one `_solidSwatch` condition drives background and
opacity; cover-swatch keeps the checkerboard
- editors/color-tile-ink.ts: pure isLightHex/colorTileInk, unit-tested
- smoke_dialog_polish_605: tileAlpha, defaultsDiffer, noneInkDark,
liveAlpha, platesFlat, plateSolid; oneSurface drops the opaque check
- mutants M-615-tile, M-615-plate, M-615-label
- USER-GUIDE (ru/en): "Colour tile" row; both changelogs
Issue: #615
User-Visible: yes
На вкладках «На плане» и «Скрытые» — флажки строк, «Выбрать все (N)» по
всему отфильтрованному набору (включая строки за «Показать ещё»), панель
«Выбрано: K · Скрыть/Показать выбранные (K) · Снять выбор» в отдельном
контейнере .device-inbox-batch вне .device-inbox-filters.
- src/device-inbox.ts: чистые inboxVisibilityAllowed (один источник для
canHide/canShow и выбора), selectableInboxRows, effectiveInboxSelection,
applyInboxVisibility — свёртка одиночных действий; одиночное = пакет из
одной строки.
- src/device-inbox-batch.ts (новый, lazy editor): выбор, панель, строка-
флажок и writeInboxVisibility — одна запись houseplan/config/set с
expected_rev на пакет, busy='__batch__' (inert), откат маркеров при отказе,
выбор сохраняется и сужается. Одиночный _setInboxHidden рантайма — тонкая
обёртка над той же записью; рантайм стал короче базы на 14 строк.
- сброс выбора при смене вкладки (клик и стрелка), поиска и «Только новые».
- i18n en/ru/de/fr: 9 ключей по ТЗ §8.
- смок demo/smoke_device_inbox_batch.mjs (AC1–AC8, AC10), юниты
test/device-inbox.test.mjs (AC2, AC4), 4 мутанта в реестре.
- docs: USER-GUIDE ru/en, FILTERING.md, оба changelog.
- monolith-baseline: hostRefs 4948→4959, bundleBytes 2500387→2505569 —
новая фича (состояние выбора и запись идут через порт хоста).
- bundle-budget: LAZY_EDITOR_GZIP_CEILING 245 400 → 246 600 (замер 245 610,
+1 190 Б gzip от базы 244 420); initial View 289 449 → 289 765 (+316 Б,
строки en-словаря), в пределах потолка.
Golden device-inbox-* изменятся ожидаемо — пересъёмка предрелизным гейтом
на Linux CI (§11.4).
Issue: #618
User-Visible: yes
План уходил base64 в WebSocket-кадре: файл больше ~3 МиБ давал кадр больше
4 МиБ, HA закрывал сокет до обработчика, и обещанные 8 МБ были недостижимы.
- бэкенд: HouseplanPlanUploadView (POST /api/houseplan/plans/upload), потоковый
предел MAX_PLAN_BYTES (read_bounded), общий writer store_plan_upload для view
и ws_plan_set (контракт WS без изменений);
- карточка: stagePlanFile/uploadPlanFile/renderPlanBackdropGuard в
backdrop-pick.ts для обоих рантаймов; PlanFilePayload хранит Blob вместо b64;
SVG больше предела — тост при выборе, растр — диалог #39 только с уменьшенной
копией, копия больше предела не попадает в staging, 413 называет предел;
- i18n toast.plan_too_large, backdrop.over_limit_body (en/ru/de/fr);
USER-GUIDE ru/en, CHANGELOG ru/en, docs/testing-notes (#617);
- тесты: test/plan-upload-limit.test.mjs, tests_backend/test_plan_upload.py,
test_ha_upload.py (#617), smoke_plan_upload_limit.mjs; три смока переведены
с b64/plan/set на blob/fetchWithAuth; мутанты plan-upload-*;
- база монолита: hostRefs +3 (общий хелпер плана вместо двух копий в рантаймах,
новый тост уменьшенной копии).
Issue: #617
User-Visible: yes
Publish corrected 0.4.1 pack, preserve old cactus objects as a read-only alias, and cover the catalog, renderers, editor, PDF and visual scene.
Issue: #606
User-Visible: yes
Make colour tiles solid, readable and fully clickable; align Data actions and device icon field with the form kit. Cover narrow Windows layouts and both icon-picker branches with a focused browser smoke.
Issue: #605
User-Visible: yes
Продолжение r1 M1: полные fill.* в RU («По силе зигби-сигнала», «По
температуре») не входили одной строкой в четыре колонки сегмента пространства
и переносились на вторую строку кнопками. Референс (§4.2 reference/app.js
fillOptions, §6.1) даёт короткие подписи Custom | Zigbee | Lights |
Temperature — они и стоят в обоих сегментах общими ключами ленивого словаря
fill.seg_* (en/ru/de/fr); room.fill_seg_* удалены. USER-GUIDE ru/en,
ACCEPTANCE.md и smoke_ux_fixes обновлены; changelog называет короткие подписи.
Три эталона меняются к лучшему и требуют повторной приёмки:
device-dialog-desktop-en и toggle-entity-dialog-desktop-en («Colour and
brightness» одной строкой вместо двух), space-room-color-popover-desktop-ru
(сегмент RU без разорванных слов); остальные десять сцен — passed на этом
дереве диагностически.
Issue: #600
User-Visible: yes
M1. Подпись сегмента больше не рвётся посреди слова: колонка `.hpf-seg label`
не сжимается ниже самого длинного слова (`min-width: fit-content`, для
браузеров без него — прежний `0` строкой выше), слова переносятся только по
пробелам (`overflow-wrap: normal`), не поместившийся сегмент переносится на
вторую строку целыми кнопками (`flex-wrap`). Новый свидетель
demo/smoke_dialog_segments_i18n.mjs — четыре диалога × en/ru/de/fr: у каждой
подписи число строк (по Range текстового узла: span — flex-элемент, у блока
один прямоугольник даже при переносе) не больше числа слов, текст внутри
кнопки. На разметке r1 краснеет на en/ru/fr комнаты («Temperature» в 2
строки, «По температуре» в 3). Мутант form-kit-segment-breaks-words
возвращает прежние две строки CSS. ACCEPTANCE.md — строка про подписи
сегмента переписана.
M2. Glow radius — не условие Save: как на dev, нечисло или неположительное
значение при сохранении молча становится общим радиусом. Удалены
glowRadiusValid, MarkerProblem marker.error_glow_radius, ключ в четырёх
локалях словаря settings, aria-invalid у поля; смок device_settings_form
утверждает обратное прежнему (badRadiusDoesNotBlockSave,
zeroRadiusKeepsSaveEnabled) и проверяет «Review N fields» на привязке в
create-режиме. Changelog EN/RU и USER-GUIDE ru/en больше не называют радиус
среди ошибок; таблица устройства говорит про молчаливый общий радиус.
M3. Свидетель дефекта №5 (поповер «?» обрезается границей диалога комнаты):
demo/smoke_dialog_help_clipping.mjs — короткое окно 900×620, диалог комнаты
скроллится, «?» нижней карточки прижат к краю; подсказка выходит за край
скроллера и остаётся hit-test'ируемой во всех пяти точках (elementsFromPoint
сквозь теневые корни — обрезанная часть не hit-test'ится). Четыре ветки:
нативный <dialog> и ha-dialog × Popover API и портальный fallback; ветка
ha-dialog — заглушка с ловушками настоящей mwc-поверхности (transform +
overflow: hidden + собственный скроллер), portal вне поверхности проверен
явно. Настоящий ha-dialog (#505, загрузка колеса HA-frontend) — отдельная
диагностическая приёмка владельца; так и записано в ACCEPTANCE.md, где
появилась таблица AC8 «девять дефектов — свидетель на каждый».
M4. ACCEPTANCE.md: эталонов 13, а не 11 — одиннадцать из ТЗ (список AC10
унаследован от #598 без комнаты) плюс две room-temperature-dialog-*.
L1. USER-GUIDE.ru.md: испорченная строка таблицы настроек пространства
(хвост удалённой строки про декоративный слой) — убран; пояснение «в
редакторе подложки остаются видимыми» вернулось в строку «Видимые слои»;
строка про солнце говорит «слой Двери, окна и ворота выключен» вместо
«Скрыть проёмы включено».
Фикстуры листа диалогов набора пересняты (панель — байт в байт).
Бюджет: initial 289 852 / 290 900; lazy editor 242 989 / 243 900.
Гейты: typecheck, build, bundle:sync (три копии), npm test 2794 pass по
четырём частям, no-new-any, mutation-gate --check, check-inputs --coverage,
смоки: segments_i18n, help_clipping, четыре формы, config_parity,
help_affordance, room_settings, settings_dialog_cards.
Issue: #600
User-Visible: yes
Завершение четырёх серий по референсу docs/design/600-settings-dialogs/:
- из eager-листов dialogs.styles.ts и chrome.styles.ts сняты стили прежней
разметки диалогов (.bindsel/.bindharow/.entcheck, .ctrlchip*, .markerhelp*,
.helpfieldlabel, .markerradios, .markerlightdisabled, .markerbadgetechnical,
.markerglowvalue, .fileupload, .descin, .pdfedit/.pdftag, .gsrow,
.ripple-*row, .radaradditional, .togglehint, футеры marker/room) — 5,8 КБ
исходника; с разметки сняты уже пустые классы;
- «Additional actions» карточки Details — подзаголовок и строка действий
набора, без раскрывашки (radar-section.ts, только additional-placement);
- Q3 уточнён по кадру: hp-device-preview стоит без обёртки — у него свой
заголовок «Display preview · Now», второй над ним удваивался; пустое
состояние — блок на тинте;
- §6.1: «?» у комнаты стоит у поля Home Assistant area, а не у заголовка
Basics; подписи сегмента заливки — None · Zigbee · Lights · Temperature ·
Custom (два коротких ключа room.fill_seg_*), источники — «Average of the
room’s sensors / Specific device or entity», режим свечения — «Colour and
brightness» (тексты прежних ключей укорочены под сегменты);
- подпись поля с «?» и без стоят на одной линии в сетке (min-height 32 по
кнопке hp-help); иконка внутри подсказки — inline;
- парные кадры: demo/capture_design_pairs_600.mjs снимает четыре диалога в
двух темах на 560 в состояниях макетов → docs/design/…/pairs/ (8 PNG,
палитра 256, ~0,5 МБ); ACCEPTANCE.md — статус по диалогам, расхождения с
референсом по каждому со ссылкой на пункт SPEC и принятые адаптации (шапка
hp-dialog по §3, тумблеры набора по К7, секции радара/пылесоса по §7.2);
- changelog EN/RU («Не выпущено»), USER-GUIDE ru/en: раздел «Как устроены
диалоги настроек», таблица настроек пространства по новым карточкам,
диалог комнаты, диалог устройства, ссылка «Как у пространства»;
- бюджет: стартовый граф 289 853 — потолок 291 600 → 290 900 (снятые стили);
ленивый редакторский 243 149 — итоговый потолок 243 900 с разбором по
сериям в bundle-budget.mjs.
Golden: 11 сцен диалогов (device-dialog-*, room-temperature-dialog-*,
toggle-entity-dialog-*, device-help-popover-light-ru,
general-color-popover-desktop-en, settings-help-zoom-200-*,
device-ripple-color-popover-mobile-ru, space-room-color-popover-desktop-ru)
прогнаны диагностически (capture, HP_ALLOW_FOREIGN_CAPTURE, только
artifacts/): харнесс проходит все пути без исключений, кадры «different» —
эталоны переснимаются на Linux CI / машине владельца по #455.
Скриншоты документации (06-device-editor, 06-device-display-preview и др.)
устарели по отпечатку — пересъёмка перед кандидатом беты (#479).
Гейты: typecheck, build, bundle:sync (три копии байт в байт), npm test
2793 pass по четырём частям, mutation-gate --check, check-inputs --coverage,
фокусные смоки четырёх форм и смоки диалога устройства.
Issue: #600
User-Visible: yes
«Общие настройки», «Пространство» и «Устройство на плане» были плоскими
столбцами. В «Общих» девять подписей-заголовков шли вперемешку с рядами и
абзацами; в «Устройстве» на 760 строк формы не было ни одного деления, и
«Название» лежало в одном списке с «Дополнительными действиями».
Теперь это карточки общего набора: семь в «Общих», четыре в «Пространстве»,
пять в «Устройстве». Набор взят как есть — он поднят в #594 и доказан на диалоге
комнаты и редакторе панели (#597).
Шесть пояснений переехали под «?» к своему заголовку, подзаголовку или строке;
текст не переписан. Два сообщения о СОСТОЯНИИ остались на виду — решение
владельца: `gs.sun_missing` и `marker.run_target_gone` говорят, что настройка
сломана прямо сейчас, а второе ещё и называет пропавшую цель подстановкой,
которой в статической подсказке взяться неоткуда.
`gs.hint` при этом переехал из ленивого support-словаря в основной: «?» читает
текст через `_t`, а не `supportT`. Тест мёртвых ключей сливает оба словаря, так
что копия в support удалена во всех четырёх локалях.
Классы-опоры сохранены намеренно: `.srcrow`, `.dispsection`, `.gsrow`,
`.colorrow`, `.namein`, `.areasel`, `.tempin` остались на своих элементах, а
карточки добавились вокруг. За эти имена держатся одиннадцать смоков, и потеря
любого превратила бы ревью в чтение переписанных свидетелей.
Изменён ровно один смок — `smoke_general_settings`, и изменён в сторону
строгости: вместо одного списка подписей он снимает теперь два (заголовки
карточек и подзаголовки внутри), требует непустоты каждой карточки, проверяет
что радиус свечения лежит ВНУТРИ карточки свечения (подпись такой границей не
была), что четыре переехавших пояснения доступны кнопкой и что их абзацев в
форме не осталось.
`src/houseplan-onboarding-runtime.ts` затронут двумя строками: он второй
потребитель переименованных ключей `space.hide_*_tip` → `space.hide_*.help`.
Один текст — один ключ; дублировать его ради неприкосновенности файла хуже.
Потолок стартового графа поднят 291 400 → 292 500. Выросли СТРОКИ, а не код:
12 новых ключей в четырёх локалях плюс переехавший `gs.hint`. Разметка диалогов
как была в ленивом редакторском графе, так там и осталась.
Свидетели: `state-callout-hidden-under-help` прячет сообщение о состоянии под
«?» и краснит новый блок `smoke_tap_run`; `dialog-card-loses-its-heading`
обнуляет заголовок карточки и краснит `smoke_general_settings` — карточка без
заголовка это снова плоский список, только с рамкой.
Issue: #598
User-Visible: yes
Встроенная библиотека мебели была смешанной: 44 предмета рисовал дизайнер, а
12 — код, прямоугольниками и эллипсами в unit box. Они выбивались из стиля, и
три категории меню (`computer`, `hood`, `oven`) вообще не открывались, потому
что вида сверху для них не было.
Пакет 0.4.0 перерисовывает все 56 существующих публичных ID и добавляет четыре
новых: `computer`, `hood`, `oven`, `cactus`. Каталог становится равен поставке:
60 символов, 32 непустые категории.
Совместимость. Ни один сохранённый план не мигрирует: положение, размер,
поворот, отражение, цвет, прозрачность и толщина линии не трогаются, а
default-размеры всех 56 прежних ID совпадают со старыми до числа — таблица
зашита в `test/furniture.test.mjs`, чтобы сдвиг было видно тестом, а не глазами.
Ловушка, ради которой примитивы удалены целиком. Библиотека собиралась союзом
каталога и `LEGACY_FURNITURE`, и обе половины несли ОДНИ И ТЕ ЖЕ публичные ID.
`BY_ID` — это `Map`, она оставляет последнюю запись, поэтому примитив молча
побеждал дизайнерский рисунок, который его заменял: задача выглядит выполненной,
12 предметов не обновились, ни один существующий тест этого не видит. Поэтому
`LEGACY_FURNITURE`, `RETAINED_IDS`, тип `Prim`, `box()` и `primitivePathD`
удалены, а на дубликаты в каталоге стоит тест и мутант.
Два продуктовых решения владельца (#593):
- `cactus` живёт в категории `plant`, а не `exercise`, как предлагал пакет:
плитка «Тренажёр» рисует тренажёр, и открывать её на кактус — врать о
содержимом палитры. `exercise` остаётся скрытой пустой категорией, непустых
становится 32, а не 33;
- при отказе ленивого чанка с рисунками не рисуется НИЧЕГО. Прежде 12
примитивов рисовались всегда, и `docs/FURNITURE.md` это обещал; обещание
снято. Это единственное место, где задача ухудшает наблюдаемое поведение, и
оно проверяется смоком, а не словом.
Бюджеты. Ревью ТЗ показало, что «бюджет ленивого графа защищён» было
заявлением без гейта: размеры печатались в отчёт и ни с чем не сравнивались.
Добавлены `lazyGraphCeilingViolation` и два настоящих потолка со своим мутантом.
Стартовый граф ушёл ниже полосы, поэтому `INITIAL_VIEW_GZIP_CEILING` опущен
292 400 → 291 400 — вторая половина храповика, без которой выигрыш отыграется.
Провенанс. Документ пакета воссоздан под 0.4.0: грант владельца, имя архива и
его SHA-256. Это единственная исполнимая защита от повторения истории #159, и
она не ослаблена — число проверок в тесте не уменьшилось, а мутант подменяет
хеш и обязан её покраснить.
Свидетели: пять новых мутантов, новая golden-сцена `furniture-new-symbols-light`
для четырёх новых ID, матрица 63 → 64. Шестой мутант правится, а не добавляется:
`furniture-placement-needs-art` ссылался на удалённое поле `g` и переставал
компилироваться — раннер честно отличал это от «пойман» и отчитывался
setup-failure. Ложь мутанта осталась прежней по смыслу, убрана только ветка про
примитив.
i18n: 7 ключей × 4 локали. `furn.cat_exercise` не добавлен — категория скрыта.
Issue: #593
User-Visible: yes
Шаг 1 эпика #591. Набор поднят из редактора боковой панели — он сделан по
макетам дизайнера и уже живёт в продукте, — а не собран заново по прототипу.
Стили набора выдаёт генератор, параметризованный именами: контейнер формы,
префикс классов, карточки, футер и токены цвета. Это не украшение архитектуры,
а условие переезда панели на общий источник: тест с замороженной фикстурой
показывает, что с параметрами панели генератор отдаёт её нынешние правила
дословно — карточку, обводку фокуса, выключенное состояние и ряд-переключатель.
После этого переезд станет подстановкой, а не редизайном, и пиксели панели не
дрогнут. Сама панель в этом коммите не тронута.
Диалог комнаты собран в четыре карточки: «Основное», «Заливка», «Источники»,
«Размеры шрифтов». Порядок полей прежний, записи прежние — те же ключи
черновика, те же условия «Сохранить», тот же `hp-color-opacity` с атомарной
записью цвета и прозрачности. Пояснения уехали под «?» у заголовков групп
(решение владельца 18.09), выбор источника стал сегментированным
переключателем — под капотом это по-прежнему радиогруппа, поэтому стрелки и
скринридер работают как раньше.
Два измерения определили форму решения, и оба стоило сделать до кода.
Первое: лист набора в синхронном графе дал +1459 Б gzip и пробил потолок
initial View на 415 Б. Диалоги — инструмент администратора, во View их не
открывают, поэтому лист уехал в ленивый редакторский граф и вносится в теневой
корень при открытии диалога — тем же способом, которым это делает боковая
панель. Потолок не поднимался.
Второе: `css` с интерполяцией запрещён минификатором сборки (он намеренно
падает, чтобы не зависеть от рантайма), поэтому лист собирается `unsafeCSS`.
Цена — несжатые пробелы генератора, замерено 59 Б gzip; копия литералом стоила
бы расхождения с генератором, то есть единого источника.
Оставшийся рост initial View — 291 356 → 291 872 Б (+516) — это четыре текста
подсказок в двух встроенных локалях. Тексты сокращены до одного предложения:
подсказка под «?» и должна быть короткой.
`_renderRoomSource` переехал в модуль комнаты вместе со стабом карточки: у него
остался ровно один потребитель.
Issue: #594
User-Visible: yes
`USER-GUIDE.ru.md`, `FILTERING.md` и `VACUUM.md` описывали «подсветку
убираемой комнаты» пылесоса как существующую фичу, которую подавляет
«Всегда статичный значок». Фичи нет: #12 закрыт `not_planned` 2026-08-27,
`room_highlight` встречается в `src/**` ровно один раз — объявлением поля
схемы в `types.ts:184`, без единого runtime-потребителя.
Формулировка уже утекла как факт в ТЗ #588 (ревью ТЗ r1, M2). Правится
источник: везде остаются только реально существующие слои — puck, след и
предупреждение о маршруте на несопоставленной карте. В `VACUUM.md` поле
схемы помечено как зарезервированное: оно принимается и сохраняется, чтобы
старые планы читались, но ничего не рисует.
Issue: #589
User-Visible: no
Пятый вариант «Отображение»: маркер показывает выбранное значение и при этом
никогда не меняет цвет — ни по состоянию, ни по тревоге, ни по недоступности,
ни под цвет RGB-света, ни по активности.
Четыре прежних режима задавали одним выбором две независимые вещи: что
нарисовано внутри маркера и красится ли он состоянием. Поэтому вместо
сравнения с одним токеном появились два производных предиката рядом с
`normalizeDeviceDisplay` — `displayWantsValue` и `displayIsNeutral`, — и их
спрашивают политика, слой пульсации, внешний бейдж, редактор и три ветки
живого слоя пылесоса.
Две ловушки, из-за которых режим не сводится к одной строке в словаре:
- быстрый путь «статичному маркеру источники не нужны» (`sourceDetails: false`)
— это основной путь рендера плана, карточки пространства и PDF. Он оставлен
только режиму без значения: иначе число пропало бы именно на плане и
осталось в предпросмотре редактора;
- три ветки живого пылесоса сравнивают режим строкой и не читают политику,
поэтому зелёная политика их не гарантирует. Свидетель держит две
конфигурации карты: сопоставленная доказывает puck и след, несопоставленная
— бейдж маршрута (при совпадающей калибровке маршрут `ready`, и бейджа не
было бы ни в одном режиме). Бейдж проверяется при наполненном буфере
позиций, иначе его отсутствие объяснялось бы первой веткой.
Потолок initial View перецентрирован 291_700 → 292_400 без изменения общего
бюджета: измеренный факт 291 346 Б оставлял под прежним центром 354 Б —
внутри шумовой полосы метрики.
Эталон: в `device-icon-state-table-{light,dark}` включённая RGB-лампа
переведена в новый режим. Кадры обязаны разойтись; приёмка — отдельным
коммитом класса D с полного линуксового артефакта Validate.
Issue: #588
User-Visible: yes
Режим заливки комнаты и её цвет — два поля конфига под одним переключателем.
«Как у пространства» снимало только режим; цвет оставался и продолжал
применяться, потому что `roomCustomFillOf` отдавал цвет комнаты независимо от
того, чей режим `custom` действует. Так возникало безымянное состояние «режим
наследую, цвет свой» — Cabinet на даче.
Теперь цвет комнаты участвует в раскраске только вместе с её собственным
`fill_mode: 'custom'` (одна функция — все поверхности: карточка, space-card,
PDF, черновик диалога). Диалог загружает цвет в черновик только при своём
режиме, обнуляет его при уходе с «Свой цвет» и показывает строку цвета только
под этим радио; сохранение пишет `custom_fill` только с `fill_mode: 'custom'`,
иначе удаляет — включая сироту от прежнего редактора. Чтение конфиг не
переписывает: застрявшие комнаты выздоравливают обновлением.
- `test/logic.test.mjs`: AC1 — сирота и любой чужой режим → цвет пространства
- `demo/smoke_room_settings.mjs` шаг 7: свой цвет → «Как у пространства» →
ни режима, ни цвета, во View цвет пространства; сирота открывается как
наследование, сохранение её удаляет (проверено красным на базе: 9 фактов)
- `demo/smoke_space_settings.mjs`: override с собственным режимом + сирота
- `demo/golden/harness.mjs`: `roomCustomFill` ставит комнате её режим —
кадры `lighting-custom-glow-*` не меняются
- мутант `room-orphan-colour-wins-again`
- docs: ARCHITECTURE (#56), CONFIG-COMPATIBILITY, USER-GUIDE ru/en, TESTING;
отпечаток скриншотов принят попиксельно (11 кадров)
Issue: #581
User-Visible: yes
Review pipeline (process.yml):
- concurrency moves from the workflow to the guard/review jobs and the guard
runs only for S4-spec-review / S7-code-review. Any other label used to enter
the issue's concurrency group and evict the pending review run (sample of
150 runs since 2026-09-01: 92 empty guard-only runs, 30 cancelled).
- the guard reads the issue's current labels instead of the event snapshot; a
label removed before the run starts is a withdrawn request, no comment.
- a green verdict is re-applied without calling the model when the latest
review document carries the pipeline-recorded verdict `green`/High 0 and the
tree differs from its anchor in nothing outside docs/reviews/** (#437 r4
re-reviewed an unchanged tree for 7 minutes). The verdict from
structured_output is now written into the anchor block for that purpose.
- the reviewer is pinned to the captured material SHA in the prompt; the
broken escaping in the "merge cancelled" comment (empty SHAs) is fixed.
Mutation gate: nine browser guards started with `npm run bundle:sync` although
the runner already builds the mutant bundle — a second rollup plus a
`tsc --noEmit` that fails on a non-strict mutant before the smoke even runs.
Prefix removed; `--check` refuses guards that build the bundle themselves.
Docs: SCOPE (Project v2 dropped, three editors), STATUS (#437 merged, HACS zip
automated), USER-GUIDE ru/en (static card shows live states; kiosk double tap
on free background fits all), #34 → #425 references, #367 named as closed in
bundle-budget messages, PROCESS §10.4 and AGENTS.md describe the controller.
Issue: #499
User-Visible: no