r2 H1: INDEX.md — снимок каталога, и ребейз ветки на dev, получивший чужие
документы ревью, устаревал его молча. Теперь `--commit-if-stale` пересобирает
и коммитит индекс коммитом конвейера после приведения к dev (process.yml) и
после ребейза кандидата (merge-candidate.mjs); тест «индекс свеж» сравнивает
закоммиченный файл с пересборкой и красит Validate при расхождении.
r2 M1: находка без заголовка — первый абзац секции, склеенный из перенесённых
строк, без маркера буллета и кода `**M1.**`; «не найдено», служебные скобки
«(унаследовано…)» — не находка. Нумерованные пункты тоже забирают перенесённые
строки. Мутант reviews-index-paragraph-tail. PROCESS.md §2.10 дополнен.
Issue: #635
User-Visible: no
r1 H1: parseCounts брал первое «High: N» по тексту (часто цитата чужого
раунда), parseFindings знал только «### H1 — …». Теперь: счётчик — своя
строка «Вердикт» → секция «## Вердикт» → единственное значение → подсчёт по
заголовкам; находки — «### Medium (…) — …», секции с «**M1. …**»,
«## Находка N (High…)», «### [High] …», «### Low L1 — …»; «— нет» не
находка. Новая колонка «Файлы» — пути из находок, чтобы grep по имени файла
отвечал на «что находили по X». Пересказ чужого вердикта строчными в шапке
не перебивает свой. INDEX.md пересобран; мутант reviews-index-counts-first-match.
Перезаход без изменения дерева: dispatch-прогон на 3719a06d покраснел
рассинхроном process.yml main/dev (исправлено зеркалом 0c933449), а
validate-gate считает завершённый красный dispatch на SHA окончательным.
Issue: #635
User-Visible: no
scripts/reviews-index.mjs собирает docs/reviews/INDEX.md: одна строка на
документ — issue, этап, раунд, вердикт (явная строка, раздел «Вердикт»,
свободная форма хвоста; 936 из 986 распознаны), High/Medium по строке вердикта
или заголовкам находок, до шести заголовков находок. Индекс детерминирован,
не индексирует сам себя, перечисляет файлы вне схемы имён; `--check` — гейт
свежести. process.yml публикует INDEX.md тем же коммитом, что документ ревью.
docs/LESSONS.md — датированные уроки со ссылками на источники (12 записей из
аудитов и разборов недели). PROCESS.md §2.10 — где искать решения.
Тесты: разбор имён, вердиктов, счётчиков, находок; фикстурный каталог;
живой каталог (100 % покрытие, >90 % вердиктов); контракт шага конвейера.
Мутанты reviews-index-skips-self-check, reviews-index-verdict-substring.
Issue: #635
User-Visible: no
У `contents` API потолок 1 000 записей с молчаливой обрезкой; каталог подошёл к
нему (986 файлов). Guard теперь спускается по дереву commit → docs → reviews и
трактует `truncated` как отказ листинга (счёт по файлам отключается, страховка
по комментариям остаётся). Предупреждение о потолке снято. Тесты: фикстура на
2 400+ имён со своими документами в хвосте; свидетель на проводке workflow.
Issue: #621
User-Visible: no
#608 поменял событие коммита числа в form-kit, а смоки форм комнаты и
устройства красными увидел только ночной реестр: smoke-select связывал
form-kit лишь с новым smoke_range_line_draft. Явная связь rangeLine/unitInput →
три смока форм, чтобы правка набора контролов гонялась на кандидате ревью.
Issue: #639
User-Visible: no
scripts/process-metrics.mjs — чистые функции над снимками GitHub: по issue
(таймлайн меток) вход по первой статусной метке, S4→S5, вход→S7, S7→S8,
повторные постановки S7; раунды ревью — по документам docs/reviews, не по
событиям метки (конвейер с #636 ставит S7 сам); прогоны Actions по workflow —
исходы, wall-time, события (прогоны конвейера сведены в одну строку); минуты
S4/S7 конвейера без skipped; при наличии jobs — job-минуты и доля «Мутанты».
Markdown-отчёт со сводкой и таблицей по issue; CLI на gh (только чтение).
process-metrics.yml — понедельник 05:00 UTC и по кнопке; отчёт в step summary
и артефакт на 90 дней; прав на запись нет.
Тесты на фикстурах, в т. ч. воспроизведение формы аудита 22.09 (30 issue:
15 r1/13 r2/2 r3 → 1,57; Validate 226/178/37/11). Мутанты
metrics-count-skipped-pipeline-runs, metrics-rounds-by-s7-events.
Issue: #637
User-Visible: no
Стадия prepare спала ≈ 28 минут на раунд, пока шёл Validate с мутантами на
материале (модель работает 10–12); за неделю ≈ 420–500 job-минут простоя и
потолок бюджета стадии 55 минут.
- validate-gate.mjs: `--no-wait` — гейт диспатчит прогон, убеждается, что тот
встал на материал (#539 сохранён), и возвращает `pending` (код 2) вместо
ожидания; завершённый зелёный/красный отдаёт сразу, как прежде.
- process.yml prepare: третий исход `proceed=pending`: запечатанный маркер
`review-pending-<issue>-<run>-<attempt>` (issue, stage, branch, material_sha,
validate run) и выход; модель и интеграция не запускаются; возврат автору —
только на явном `false`.
- process-resume.yml + scripts/process-resume.mjs: на `workflow_run: completed`
Validate по ветке issue/* — если метка S7 стоит, активного прогона нет и
последний прогон оставил маркер на этот SHA, переставить S7 (HP_PROCESS_TOKEN);
новый прогон находит завершённый dispatch сразу. Без маркера не будит.
- process-reconcile.mjs: читает маркер и состояние Validate на материале;
идёт — wait, завершился/пропал без продолжения — retry; без маркера — прежний
escalate. Общий loadSealedArtifact, экспорт processRuns/artifactNames.
- preflight сверяет process-resume.yml между main и dev наравне с process.yml.
- Тесты: validate-gate (4), process-resume (8, включая контракт трёх workflow),
process-reconcile (2); мутанты gate-no-wait-still-sleeps,
resume-wakes-round-without-marker, resume-ignores-active-run,
reconcile-wakes-pending-while-validate-active. PROCESS.md §10.4, AGENTS.md.
Issue: #636
User-Visible: no
Repair both incoming routes and safe target-reference recovery, with backend
and mutation coverage for foreign and same-instance imports.
Issue: #611
User-Visible: yes
Extend the authentic pinned-HA diagnostic with explicit light and dark palettes, a 32 px root-font case, one-scroller geometry assertions, and reviewed evidence images. Correct stale AC references in the mutation descriptions.
Issue: #609
User-Visible: no
The mobile mutant now changes both public height bounds, so the browser test observes the intended 48 px regression instead of CSS min-height precedence masking it.
Issue: #609
User-Visible: no
Use Home Assistant's public dialog tokens for the reviewed 560 px canvas, height cap, single scroller and mobile fullscreen layout. Keep generic dialogs and the native fallback unchanged, with authentic-HA evidence and mutation witnesses.
Issue: #609
User-Visible: yes
Promote the tested 1.77 beta line without new product behavior. Aggregate the stable notes since v1.76.0 and sync all version authorities and installable bundles.
Issue: #584
Issue: #588
Issue: #593
Issue: #594
Issue: #598
Issue: #600
User-Visible: yes
Release: v1.77.0
Reviewed the complete Linux Validate artifact 35606718247: one new furniture scene and eight expected dialog-polish changes; 166 other scenes remain unchanged.
Release: v1.77.0-beta.5
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/35606718247
Issue: #603
Issue: #605
Issue: #606
User-Visible: no
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
Шард 2/4 прогона 35565222849 снят по timeout-minutes: реестр вырос до 810
мутантов (~203 на шард), длительность за 11 дней 42 → 61 мин при потолке 60.
Отчёт назвал его «ok»: лог без строк FAIL считался зелёным, а обрыв по
таймауту строк FAIL не содержит. Агрегатор проверял identity, но не
завершённость — evidence шага `if: always()` было на месте.
- mutation-gate.yml: matrix из шести шардов, `--shard=i/6`, `--shards=6`;
шаг прогона получил id, его `outcome` пишется в evidence.
- mutation-gate-report.mjs: шард `ok` только с итоговой строкой
`поймано N из M`, N = M, без FAIL и с исходом шага `success`; лог без
итога или исход `cancelled`/`skipped` — `interrupted`, отказ; агрегатор
отвергает прерванный шард как неполный. `outcome` в evidence необязателен
ради старых артефактов, но, если назван, обязан быть из известного набора.
- тесты: обрыв → interrupted, исходы шага, evidence с outcome; делитель
шардов один во всех местах workflow; фикстуры зелёных логов получили итог.
- мутанты: mutation-report-truncated-log-is-ok,
mutation-evidence-ignores-cancelled-step.
- docs/TESTING.md: шесть шардов, итоговая строка.
Потолок 60 минут остаётся стражем от зависшего Chromium. Ledger в ночном
прогоне не включён: ночь гоняет всё.
Issue: #604
User-Visible: no
Count the Optimize action by its label instead of the removed alignall layout class, so the clean mutation guard still protects the action after the polish.
Issue: #605
User-Visible: no
The new smoke already names the changed color-picker symbols; an explicit registry link would duplicate the selector's direct evidence.
Issue: #605
User-Visible: no
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
Reviewed room settings desktop/mobile and the new 320px discard confirmation from Linux Validate 35566453055; 171 scenes unchanged and 106 environment witnesses.
Issue: #603
User-Visible: no
Release: v1.77.0-beta.4
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/35566453055
Package the dialog-polish release from #602 together with the validated CI optimization from #601; synchronize version authorities, generated bundles and bilingual release metadata.
Release: v1.77.0-beta.4
Issue: #601
Issue: #602
User-Visible: yes
С Linux CI Validate 35538133817 приняты ровно восемь визуально проверенных сцен: три базовых диалога устройства, три help/popover-сцены и два цветовых поповера. Остальные 165 сцен сохранены без изменений; строгий allowlist accept.mjs исключил необъявленные расхождения.
Issue: #602
User-Visible: no
Release: v1.77.0-beta.3
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/35538133817
`mutantsRequested` отвечает true лишь на PR и `workflow_dispatch mutants=true`
(конвейер ревью, слияние кандидата). Трейлер `Release:` и `full=true` включают
тяжёлые гейты — смоки, golden, performance_smoke — но не мутантов: к бете каждая
задача прогнана ими на ревью и на слитом после ребейза кандидате, ночь покрыта
полным реестром (mutation-gate.yml, #513), а ручной полный прогон ради
артефакта эталонов и приёмка эталонов с трейлером на ветке задачи платили
шестью job впустую. `schedule` мутантов тоже не запрашивает.
Политика release в ci-proof — `mutants: false`: иначе proof кандидата беты
без запрошенных mutant-jobs объявлялся бы stale. review и merge по-прежнему
требуют шесть исполненных job (#541).
Тесты: #510 AC1 переписан под новый список, ci-proof — release без мутантов
green, лёгкий stale, review/merge без запроса stale. Мутанты протокола:
`mutants-run-on-every-push` перепривязан, новые `mutants-run-on-beta-candidate`,
`mutants-run-on-full-dispatch`, `release-proof-demands-mutant-jobs`.
PROCESS.md §10.4, AGENTS.md, docs/TESTING.md, комментарии workflow.
Issue: #601
User-Visible: no
Package the designer-aligned settings dialogs from #600 as the next beta candidate and refresh the synchronized frontend bundles and bilingual release documentation.
Release: v1.77.0-beta.3
Issue: #600
User-Visible: yes
Съёмка — Linux CI, Validate 35515236776 на ec97fd95; приёмка — Windows
владельца с HP_ALLOW_FOREIGN_CAPTURE (#455).
Просмотрены toggle-entity-dialog-desktop-en и
space-room-color-popover-desktop-ru. Остальные 171 сцена совпали; accept.mjs
отказал бы при любом необъявленном расхождении.
Issue: #600
User-Visible: no
Release: v1.77.0-beta.3
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/35515236776
`node demo/capture_design_pairs_600.mjs` на дереве ec97fd95: room-* больше не
показывают «Temperatur/e» — ровно тот кадр, которым ревью r1 (M1) доказывало
дефект; сегменты остальных диалогов дали сдвиг в пределах строки. Все восемь
файлов меняются целиком из-за 256-цветной палитры, содержательные отличия —
только в сегментах.
Issue: #600
User-Visible: no
`npm run docs:accept -- --identical` (#512) после 8dc60aec и ec97fd95: все
11 документационных сцен попиксельно совпали с закоммиченными, принят только
отпечаток исходников. Ту же вершину снял Linux Docs Screenshots 35515141317 —
артефакт docs-screenshots лежит для сверки.
Issue: #600
User-Visible: no
Продолжение 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
Linux Docs Screenshots 35506234074 снял ветку после исправления скроллера.
Просмотрены space-create, device-editor и device-display-preview; кадр preview
отличается от обычного и показывает блок Display preview. Остальные восемь
сцен совпали побайтово.
Issue: #600
User-Visible: no
После #600 единственный скроллер диалога находится в shadow-root .content.
Съёмка продолжала менять scrollTop у старой .body, поэтому обычный кадр
устройства и кадр Display preview получались побайтово одинаковыми.
Тест фиксирует владельца скролла и запрещает возврат к неработающей .body.
Issue: #600
User-Visible: no
Съёмка — Linux CI, Validate 35504549764 на 504c1609; приёмка — Windows
владельца с HP_ALLOW_FOREIGN_CAPTURE (#455).
Объявлены ровно 13 сцен AC10; остальные 160 сцен совпали побайтово,
иначе accept.mjs отказал бы до записи.
Issue: #600
User-Visible: no
Release: v1.77.0-beta.3
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/35504549764
Единственный красный смок полного набора Validate #3417 (шард 2/3 и гард
мутанта в шарде 5/6): проверка localizedControl искала строку «Show the room
information window on hover» как label.srcrow с чекбоксом. С серии 2 это
строка-тумблер набора #gs-room-tooltip: название в .hpf-toggle-title, подпись
отдельно, переключатель — нативный чекбокс. Смок утверждает то же самое на
новой разметке.
Issue: #600
User-Visible: no
Гейт «новый код не добавляет any» (#342) поймал две строки диалога устройства,
унаследованные от прежней разметки: ключ подписи действия собирается как
I18nKey, событие ha-icon-picker — CustomEvent<{ value?: string }>.
Issue: #600
User-Visible: no
demo/smoke_dialog_config_parity.mjs: для каждого из четырёх диалогов одни и
те же правки делаются дважды — контролами набора с нажатием Save и прямой
записью в черновик с вызовом save-метода (путь прежних обработчиков и
существующих смоков); JSON.stringify(_serverCfg) совпадает побайтово, и оба
отличаются от исходного конфига. Заодно числовое поле размера шрифта
карточек в «Пространстве» получило id (space-card-font) — подпись for
раньше указывала в пустоту.
Issue: #600
User-Visible: no
Завершение четырёх серий по референсу 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
Срез «от export function segmented до конца файла» находил type="radio" у
radioRow и choiceCards ниже, и подмена радио на чекбокс внутри самого
сегмента проходила зелёной. Срез ограничен телом функции; мутант
form-kit-segment-drops-radio-semantics снова красит тест.
Issue: #600
User-Visible: no
§7 SPEC.md референса. Пять карточек #598 остались, менялось содержимое; все
ключи _markerDialog и все записи в них прежние (§7.2):
- Basics — баннеры привязки стали callout'ами; Name с подсказкой «Shown on
the plan»; привязка — сегмент [Virtual | From the HA list] (те же радио
bmode) и кнопка выбора #marker-binding с панелью в потоке (sourcePicker
набора), чекбокс «Show entities» — внутри панели рядом с поиском (решение
владельца Q8); Room селектом с подсказкой «по зоне / переопределяет»;
секции радара и пылесоса — своими модулями без изменений;
- Tap action — селект с подсказкой Target/Now, «Ask for confirmation» —
строка-тумблер только при toggle/run (Q8), Entity to toggle и What to run
теми же условиями (список целей — панель набора), «Controls other light
sources» — чипы набора и поиск с панелью в потоке;
- Light and glow — «Include the device temperature…» строкой-тумблером;
роль — сегмент [Auto | Always | Never] с подсказкой результата Auto;
Leading light entity по условиям; блок свечения (.hpf-block): подзаголовок
с «?», сегмент [From source | Set colour | Colour and brightness] с
отключёнными вариантами и aria-describedby на объяснение, плашка цвета
вокруг прежнего hp-color-opacity (Q5), яркость и радиус строками с
числом и единицей, при Never блок неактивен целиком (aria-disabled) с
callout'ом причины;
- Appearance — поле иконки с превью, очисткой и ссылкой Pin в подсказке;
Display селектом с подсказкой режима; пульс — плашка цвета и строка
размера; Value source по условиям; подраздел Value badge — компактный
тумблер, Value селектом с техническим идентификатором, Position сегментом
[Above | Right | Below | Left]; предупреждения бейджа — заметки и
callout'ы; Display preview — прежний hp-device-preview целиком в блоке на
тинте с тегом Now (решение владельца Q3); «Icon size and rotation» —
две строки range-line в сетке;
- Details — Model и Link в ряд, Description textarea набора, Manuals чипами
и строкой действий Attach…, «Additional actions» подзаголовком набора.
Футер: Hide и Delete слева, статус, Cancel и Save справа. К10: снимок
черновика при _openMarkerDialog (marker-form-state.ts поверх
dialog-baseline; транзиентные ключи — раскрытие и фильтры списков, busy,
объявление подсказки, autoIcon, uploadId); в edit Save только при
изменениях, закрытие с изменениями спрашивает через _confirmDanger. Прежние
запреты Save (busy, калибровка радара, привязка не выбрана/не подтверждена)
остались; привязка и радиус свечения названы словами под полем
(marker.error_binding, marker.error_glow_radius), «Review N fields» ведёт к
полю. Тумблеры диалога — строки набора (нативный чекбокс, К7), а не
ha-switch; слайдеры — прежний _rangeInput (ha-slider в HA).
Набор: sourcePicker получил toolbar и disabled; сегмент — value у радио,
disabled и describedBy у варианта; callout — role note и id; unitInput —
describedBy; rangeLine — id; новые классы .hpf-block/.hpf-disabled,
.hpf-toolbar/.hpf-check, .hpf-iconfield/.hpf-iconpreview, .hpf-mono,
.hpf-note, .hpf-hint-line, textarea.hpf-input. settingsCopy вынесен в
editors/settings-copy.ts (общий для пространства, комнаты, устройства).
Смоки переписаны осознанно, с пометкой #600: binding_ui (#marker-binding,
.hpf-panel, #marker-show-entities), tap_run (панель набора, #marker-tap-
confirm), value_face_source (.hpf-cand.sel), static_icon (callout'ы),
climate_temp (#marker-use-climate-temp), help_affordance (#marker-name,
.hpf-block.hpf-disabled вместо fieldset[disabled]; второй Escape в грязном
диалоге спрашивает — К10), size_angle_parity (сетка Appearance),
color_picker_consumers (подписи полей), device_preview_parity (сегмент
стороны), ha_controls (тумблеры набора — нативные чекбоксы по К7, ha-slider
прежний). Golden-harness device — .hpf-field/.hpf-block, скроллер .content.
Новый фокусный свидетель demo/smoke_device_settings_form.mjs: пять карточек
без старой разметки, оболочка, привязка/панель/Show entities, Q8, сегменты и
блок свечения, плашки, иконка, бейдж, превью на тинте, размеры, Details,
цели 44, Save/dirty/вопрос при закрытии, create-режим, семь полос экранов.
Мутанты: marker-save-enabled-without-changes, marker-discard-without-asking,
marker-confirm-row-shown-for-do-nothing, marker-never-keeps-glow-block-live,
marker-badge-position-forgets-touch; якоря same-binding-click-resets-source,
state-callout-hidden-under-help, form-kit-segment-drops-radio-semantics
переставлены на новую разметку.
Контрактные тесты: color-picker (два поля набора вместо flex-строк),
native-select-contract (сторона бейджа — сегмент), i18n dead-keys (34 → 38
подсказок), German=English (marker.name_label). Словарь i18n/settings: 21
ключ marker.* на четыре локали; в основном каталоге marker.name_label
«Name», marker.size_label «Icon size and rotation», удалены мёртвые
marker.room_override, marker.light_role_always/never. Фикстуры листа
диалогов набора пересняты намеренно (панель — байт в байт, AC11).
Бюджет: стартовый граф 290 489 (потолок 291 600 без изменений); ленивый
редакторский 243 173 — потолок 240 100 → 243 900 с разбором в
bundle-budget.mjs; старые стили диалога устройства снимаются в финале.
Гейты: typecheck, build, bundle:sync (три копии байт в байт), npm test
2790+ pass по четырём частям, mutation-gate --check, 39 смоков диалога
устройства + новый + три формы серий 1–3.
Issue: #600
User-Visible: no
§6 SPEC.md референса. Четыре карточки #594 остались, менялось содержимое:
- Basics — Display name и Home Assistant area в одну строку из двух полей
(#room-name, #room-area — контролы набора); подстановка имени из зоны при
пустом имени прежняя;
- Fill — «Как у пространства» стала строкой-тумблером (#room-fill-inherit,
решение владельца Q4): включена — подсказка «Следует за пространством:
режим»; выключена — сегмент из пяти режимов, который стартует с
действующего режима пространства, а не с «None». «Свой цвет» — плашка
вокруг прежнего hp-color-opacity (Q5): свотч, hex, число прозрачности,
Reset у своего цвета. Границы комфорта при эффективной «Температуре» —
два поля с единицей °C (#room-temp-min/-max) с плейсхолдерами границ
пространства, легендой Cold / Comfort / Hot, ошибкой под полями и ссылкой
«Как у пространства». Модуль room-temperature-controls.ts удалён — его
разметку заменили поля набора;
- Sensor sources — подзаголовки, прежний сегмент «среднее / конкретный», при
конкретном — кнопка выбора в стиле селекта и панель с поиском в потоке
карточки (sourcePicker набора, роль listbox/option; общая с диалогом
устройства в серии 4). Кандидаты и запись прежние;
- Font sizes — две строки «слайдер + число % + Reset to 100%» с концами шкалы,
подсказка про размер карточек пространства, образец карточки сохранён
внутри .hpf-tint (решение владельца Q2).
Футер: Cancel | статус | Save; в create — Keep as walls слева. К10: снимок
черновика при _openRoomEdit (room-form-state.ts поверх dialog-baseline),
в edit Save только при изменениях, закрытие с изменениями спрашивает через
_confirmDanger; в create Save по прежнему условию (имя или зона), отмена без
вопроса — она и раньше значила «не создавать» и возвращала контур на план.
Ошибки черновика: имя (edit) / имя-или-зона (create), нечисло в границах —
подпись под полем, «Review N fields» в футере ведёт к первому полю.
Из eager-листа dialogs.styles.ts удалены .roomtemprange*, .roomdialog .srcrow
и .dropbtn; `overflow-x: hidden` у .body убран — скроллер один (К3).
Смоки переписаны осознанно, с пометкой #600: smoke_room_settings (тумблер
вместо радио, #room-name/#room-area, .hpf-panel .hpf-cand, старт сегмента с
режима пространства), smoke_room_temperature_thresholds (.hpf-temprange,
ссылка сброса). Golden-harness room-temperature — те же селекторы. Новый
фокусный свидетель demo/smoke_room_settings_form.mjs: четыре карточки без
старой разметки, имя и зона в одну строку, оболочка 560 и один скроллер,
тумблер/сегмент/плашка/границы/источники/размеры пишут в свои ключи, Save и
вопрос при закрытии, create-режим, семь полос экранов.
Мутанты: room-save-enabled-without-changes, room-discard-without-asking,
room-fill-segment-starts-at-none, room-temp-min-writes-max. Якорь
form-kit-writes-to-a-neighbour-key прежний. smoke-links: символ
roomTemperatureControls заменён на renderRoomSettingsDialog.
Словарь i18n/settings: девять ключей room.* на четыре локали; в основном
каталоге room.area_label без «(unassigned)». Фикстуры листа диалогов набора
пересняты намеренно (панель — байт в байт, AC11).
Бюджет: стартовый граф 290 575 — потолок ОПУЩЕН 292 200 → 291 600 (ушли CSS
диалога комнаты и модуль границ); ленивый редакторский 239 391 — потолок
238 600 → 240 100 с разбором в bundle-budget.mjs.
Гейты: typecheck, build, bundle:sync (три копии байт в байт), npm test
2790+ pass по четырём частям, mutation-gate --check, смоки: room_settings,
room_settings_form, room_temperature_thresholds, editor_tabs, font_scales,
resize_labels, room_autoclose, binding_ui, color_picker_consumers,
lazy_editor_chunk, space_settings_form, general_settings_form, ux_fixes,
settings_dialog_cards, dialog_footer_width.
Issue: #600
User-Visible: no
§5 SPEC.md референса. Семь карточек #598 остались, менялось содержимое:
- Display — две полные строки с иконкой, подписью и тумблером справа; пояснение
радара по §5.2 стало подписью строки (текст прежний, ключ переехал в ленивый
словарь как gs.radar_show_live_hint, «?» у строки больше нет);
- Zigbee links — заголовок и «?» функции (topology.help) рисует карточка, а
блок hp-zigbee-topology-settings получил режим embedded: строка-тумблер,
callout «сохраните настройку», подзаголовки ZHA / Zigbee2MQTT, поле тем и
строки действий контролами набора. Заголовок больше не удваивается
(дефект 1). Лист набора вносится в теневой корень компонента; токены
--hpf-* наследуются с хоста. Прежний render без embedded не тронут — его
держит контракт #459;
- Room fill colors — три подзаголовка (Lights / Temperature / Zigbee signal,
Q6) и плитки цвета по три в ряд: название на свотче, hex, число
прозрачности; свотч — trigger прежнего hp-color-opacity (Q5);
- Light-source glow — две плитки и поле радиуса с единицей внутри рамки;
- Plan — плашка цвета стен, сегмент фона, при Static — плашка фона с Reset и
подсказкой (тема / свой цвет);
- Sun — gs.sun_missing остаётся callout'ом (К5); север: поле градусов с «°»,
ссылка Clear, «N» и компас 44 px справа — индикатор набора, но тянуть его
по-прежнему можно (smoke_sun гоняет настоящий drag; старый 120-px SVG и его
CSS в plan.styles.ts удалены); лучи — строка-тумблер с подписью; источник
лучей — сегмент (sun-settings-view.ts переписан с <select> на segmented,
логика та же);
- Data — подзаголовки с «?» и строки действий набора.
К10 (Q1): Save только при изменениях и без ошибок (радиус > 0, север 0–359),
статус в футере, вопрос при закрытии с изменениями; снимок при открытии —
editors/general-form-state.ts поверх общего editors/dialog-baseline.ts (на него
же переведён space-form-state.ts). _settingsDialog не получил новых ключей.
Удалено мёртвое: _renderColorRow (рантайм и карточка), .sunrow/.suncol,
ключи gs.wall_group, gs.bg_group, gs.bg_theme, gs.radar_show_live.help(.aria)
в четырёх локалях. Девять новых ключей — в i18n/settings.
Смоки переписаны осознанно, с пометкой #600: smoke_general_settings (инвентарь
по классам набора, подзаголовки .hpf-sub h4, пояснения под «?» и подписью,
ни одного .rhint), smoke_sun (сегмент источника лучей вместо select, поле
#gs-north, radius drag 18 px), smoke_backup_transfer (подзаголовок и строка
действий), smoke_settings_dialog_cards (callout вместо .rhint). Новый фокусный
свидетель demo/smoke_general_settings_form.mjs: семь карточек и отсутствие
старой разметки, один заголовок у Zigbee, десять плиток, компас 44, цели 44,
запись плиток/плашки/радиуса/севера/сегментов в свои ключи, Save/dirty/вопрос
при закрытии.
Мутанты: general-save-enabled-without-changes,
general-tile-opacity-writes-a-neighbour-key, general-north-clear-writes-zero,
topology-embedded-draws-its-own-heading; якоря state-callout-hidden-under-help
и dialog-card-loses-its-heading переставлены на новую разметку.
Контрактные тесты: houseplan-source.mjs приводит `host.` к `this.` и в телах
вынесенных диалогов; color-picker — три явных showOpacity=true (плитки, плашка
стены, цвет комнаты); счётчик производных .aria 35 → 34.
Бюджет: стартовый граф 290 711 — потолок ОПУЩЕН 292 500 → 292 200 (ушёл
120-px компас из eager-листа и мёртвые ключи); ленивый редакторский 237 929 —
потолок 236 300 → 238 600 с разбором в bundle-budget.mjs.
Гейты: typecheck, build, bundle:sync, npm test 2792 pass, mutation-gate
--check, семь смоков «Общих», smoke_color_picker_consumers без правок.
Issue: #600
User-Visible: no
§4 SPEC.md референса, четыре карточки: Basics (имя и масштаб в сетке 1fr/175,
подложка двумя choice-карточками, callout о пересчёте масштаба), Appearance
(строка-тумблер границ с иконкой и подписью, сегмент стиля стен с образцами
линии, плашка цвета с hex/прозрачностью/Reset, сегмент заливки с деталями
режима, компактный список «Видимые слои» с логикой «включено = видно» — К2),
Room cards (строка-тумблер названий, четыре плитки значений с отключением и
callout-ссылкой, слайдер размера с числом, концами и «Сбросить до 100%», образец
карточки внутри блока на тинте — Q2), Sun & light (сегмент фона, плашка цвета
фона, радио «свет через окна» в строку, север селектом с компасом 44 px и полем
градусов, строка-тумблер свечения).
Одна форма на два рантайма. `renderSpaceForm` в `editors/space-form.ts`
собирает тело и футер; редакторский вход (`space-settings-dialog.ts`) и
онбординг подают порт с тем, что у них различается, и сами рисуют
`<hp-dialog data-kind=…>` — контракт #489 требует литеральный kind. Копия
разметки онбординга на 300 строк удалена; локальные дубли CELL_CM_* и
strictNumber там же — тоже («одно число — один источник»).
К10 (решение владельца Q1): Save активен только при изменениях и без ошибок,
в футере «Есть несохранённые изменения» либо ссылка «Проверьте поля: N» к
первому ошибочному; «✕», Cancel и Escape с изменениями спрашивают через
`_confirmDanger`, без изменений закрывают сразу. Снимок делается при открытии
(`editors/space-form-state.ts`, WeakMap по хосту) — `SpaceDialogState` не
получил ни одного ключа. Валидация: имя, подложка без изображения, диапазон
температур, север 0–359 — с `aria-invalid` и текстом под полем.
Строки. 51 новый ключ живёт в ленивом словаре `i18n/settings` (приём
#423/#459), не в первом кадре: стартовый граф 291 260 при потолке 292 500.
Отдельный словарь, а не `support`, — иначе онбординг тянул бы весь словарь
редактора ради одной формы. 11 существующих ключей переименованы по Q6
(«Space name», «Appearance», «Room cards», «Draw it myself», «Zigbee signal
next to devices»…), 9 мёртвых удалены из четырёх локалей. `hp-color-opacity`
получил `hide-label`: подпись рисует плашка, `label` остаётся для title/aria и
`smoke_color_picker_consumers` проходит без правок (AC5).
Смоки. Переписаны осознанно (К6 снят), каждый — с пометкой #600 у правки:
smoke_hide_layers (компактные строки, инверсия К2 проверяется кликом),
smoke_ux_fixes (сегмент, строка-тумблер, поля °C, ширина 560 — Q7),
smoke_dialog_footer_width (560 для form-shell), smoke_temp_fill (два поля по
id), smoke_settings_dialog_cards (три сегмента), smoke_space_settings и
smoke_space_create_display_defaults и smoke_lazy_editor_chunk (choice-карточка
по смыслу, не по индексу — порядок §4.2 draw → file), smoke_space_scale_defaults
(поле с единицей). Новый фокусный свидетель demo/smoke_space_settings_form.mjs:
состав карточек и отсутствие старой разметки, оболочка 560 и один скроллер,
цели 44, запись каждого нового контрола в свой ключ, Save/dirty/вопрос при
закрытии, семь контрольных ширин (320…1920).
Мутанты: space-fill-segment-writes-a-neighbour-key,
space-save-enabled-without-changes, space-discard-without-asking,
space-layer-toggle-loses-inversion.
Контрактные тесты: `houseplan-source.mjs` дописывает общие модули диалогов к
логическому исходнику; коллектор i18n видит `help(...)` порта и `shelp(...)`
(заодно встал на учёт `radar.help`, проходивший мимо счётчика — 30 → 35).
Потолок ленивого редакторского графа 225 900 → 236 300 (замер 235 560) с
разбором в bundle-budget.mjs: словарь на четыре локали и подписи — большая
часть роста; промежуточно, итог в конце серий.
Гейты: typecheck, build, bundle:sync, npm test 2792 pass (красным был только
потолок — поднят с доводом), 10 смоков пространства зелёные,
smoke_color_picker_consumers без правок, mutation-gate --check.
Issue: #600
User-Visible: no
Серия 0 из пяти по #600. Ничего из четырёх диалогов ещё не переложено — эта
серия даёт им инструменты и оболочку.
Набор (`form-kit.ts` + `form-kit.styles.ts`), числа из §3.1 SPEC.md референса,
цвета через переменные темы HA:
- полная строка настройки `toggleRow` — сетка 28/1fr/44: иконка, название +
«?», подпись, тумблер справа; тумблер — нативный чекбокс, нарисованный CSS,
как у редактора панели: один вид в HA, на стенде и в golden, и всё тот же
`input[type=checkbox]` для смоков; компактный вариант для однотипных списков;
- `field`, `fieldGrid` (две колонки и 1fr/175), `subsection`, `unitInput`,
`rangeLine`, `rangeEnds`, `radioRow`, `choiceCards`;
- `valueTiles` (4 колонки), плитки цвета в CSS; `colorField` — компактная
плашка вокруг существующего `hp-color-opacity` (Q5): свотч открывает ту же
панель, рядом hex, число прозрачности и Reset; нативного пикера нет;
- `callout` (К5: сообщения о состоянии остаются на виду), `compass` 44 px,
`footerStatus`, `chip`, `tintBlock`, `textLink`, `divider`;
- `segmented` получил образцы линии, иконки и `disabled`; вид — как в
референсе: контейнер на канве, активный сегмент на тинте.
Оболочка (`hp-dialog.ts`, атрибут `form-shell` у четырёх диалогов настроек):
ширина 560 через `--hp-dialog-wide-width`, которую `:host([wide])` уже читал;
высота min(940px, 100vh − 48); единственный скроллер — `.content`, тело формы
высотой не владеет (К6, дефект 7 из issue); «✕» 44 px; бейдж имени пространства
в заголовке (`badge`). Generic `wide` и остальные диалоги не тронуты (Q7).
`hp-dialog[form-shell] .body` — без max-height и overflow, канва 16 px.
Фикстуры листа диалогов `test/fixtures/form-kit-card-dialog*.css` пересняты
намеренно; лист панели совпал с замороженным побайтово (К9, AC11).
`editor.loading` → «Загрузка…» в четырёх локалях (дефект 9); aria-текст прежний.
Потолок ленивого редакторского графа 222 900 → 225 900 с доводом в
`bundle-budget.mjs`: рост — CSS-строки набора, не код; значение промежуточное,
серии 1–4 выкидывают старые правила, итог замеряется в конце.
Гейты: typecheck, build, bundle:sync, `npm test` (2791 pass; красными были
только потолок — поднят — и неотслеженные файлы манифеста — добавлены),
`smoke_settings_dialog_cards`, `smoke_room_settings`.
Issue: #600
User-Visible: no
AC0 задачи: пока референс лежит вложением в issue, ревью сравнивает продукт с
макетом «на глаз», и ровно это дало выпущенный в 1.77.0-beta.2 ложный паритет —
карточки снаружи, старая разметка внутри.
Содержимое архива `houseplan-settings-design-2026-09-18.zip`
(SHA-256 06f07fc212947ee9da1a39aaab2d14cbd41154cc9effe39bc48e1c2e1517255b,
сверен побайтово с вложением #591) разложено по образцу #505:
- `SPEC.md`, `IMPLEMENTATION-GUIDE.md`, `OPEN-POINTS.md`, карта полей Space —
с этого коммита часть ТЗ: расхождение называется ссылкой на пункт документа;
- `reference/` — свёрстанный прототип четырёх форм;
- `screenshots/` — четыре формы целиком, 560 px, 2x;
- `README.md` — происхождение, отпечаток, границы использования;
- `ACCEPTANCE.md` — место для парных кадров «референс / продукт» по AC1.
Автор — сотрудник владельца (подтверждено 20.09 в #600), внешний грант не нужен.
Windows-лаунчеры `START.cmd` и `START.ps1` не переносились: `node serve.mjs`
делает то же самое. `room-fill.test.mjs` перенесён как документ намерения, CI
его не запускает — под `docs/**` не попадает ни один тестовый glob.
В бандл отсюда не попадает ничего; `repo-hygiene` и полный набор зелёные
(2793 pass).
Issue: #600
User-Visible: no
Точный поиск добавляет дорогие события границ комнаты только когда более близкий кандидат действительно отброшен этой границей. Корректность кеш-хинта сохраняется, обычный pan/zoom не платит за лишний fallback.
Issue: #585
User-Visible: no
Linux Docs Screenshots 35476276726 подтвердил отсутствие визуальных изменений во всех 11 сценах; обновлён только fingerprint исходников.
Issue: #585
User-Visible: no
Кешированное смещение больше не завершает поиск до точной проверки пересечений с границами комнаты и стен. Добавлен регрессионный тест для более близкого решения у диагональной границы.
Issue: #585
User-Visible: no
Перечисляются только реальные пересечения конечных границ, новые границы обрабатываются инкрементально, а дробные координаты дают только необходимые floor/ceil-соседи.
Issue: #585
User-Visible: no
Строгий docs-гейт #585 обнаружил отложенную визуальную дельту уже
смёрженного #598: карточки групп в диалогах пространства и устройства.
Все три кадра просмотрены; остальные восемь Linux-сцен совпали побайтно.
Съёмка и проверка воспроизводимости:
https://github.com/Matysh/houseplan-card/actions/runs/35465854646
Issue: #585
User-Visible: no
Съёмка — Linux CI, Validate 35454723038 на acf179e4; приёмка — Windows
владельца с HP_ALLOW_FOREIGN_CAPTURE (#455).
Объявлены ровно одиннадцать сцен AC8; необъявленных расхождений нет —
иначе accept.mjs отказал бы до записи.
Issue: #598
User-Visible: no
Release: v1.77.0-beta.1
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/35454723038
`scripts/check-docs.mjs` считает отпечаток от всего `src/**`, поэтому правка
трёх диалогов его состарила, хотя ни один из десяти сценариев `demo/docs`
диалоги настроек не открывает.
`npm run docs:accept -- --identical`: все 11 кадров совпали попиксельно,
принят только отпечаток исходников (81fa92d2). Переснятых кадров нет.
Issue: #598
User-Visible: no
Закрывает находки код-ревью r1 по #598.
M1 — неприкосновенный смок восстановлен. `demo/smoke_tap_run.mjs` вернулся
побайтово к версии `origin/dev`: он один из девяти `.srcrow`-смоков, которые
ТЗ объявило неизменяемыми, и правка в нём обесценивала собственное
доказательство AC2/AC3. Проверки карточек переехали в новый
`demo/smoke_settings_dialog_cards.mjs` — отдельного свидетеля ТЗ не запрещало.
M2 — сегментированный переключатель сделан там, где он обещан и уместен:
`space-bg-mode` и `space-zero-wall-style` переведены из `<select>` в
`segmented`. Остальные выборы в трёх диалогах остаются списками осознанно:
привязка маркера, роль света и режим свечения уже `role="radiogroup"`
(`.markerradios`), позиция значения закреплена смоком
`smoke_device_preview_parity`, списки сущностей и тем длинные. Сокращение
объявлено в ТЗ и в обоих changelog.
M3 — мутант `state-callout-hidden-under-help` патчит теперь оба состояния:
`marker.run_target_gone` и `gs.sun_missing`; гард — новый смок.
Заодно: `ensureFormKitStyles(this.host)` не вызывался ни в одном из трёх
диалогов — стили набора приезжали только из диалога комнаты, и без него
сегменты имели высоту 14 px вместо 44. Нашёл новый смок проверкой
`bgSegmentTargetIsBigEnough`.
Гейты: typecheck, test (2788 pass), build, bundle:sync, три смока,
mutation-gate --check, оба мутанта задачи.
Issue: #598
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
`node scripts/check-docs.mjs` краснел на материале ревью: диф трогает два файла
`src/**`, а отпечаток скриншотов считается от всего `src/**` и устарел. PROCESS.md
§8 называет этот гейт безусловным при любом диффе по `src/**`, и пропуск уже
стоил инцидента — #230, #234 оставили `dev` с красным job `docs` до #237.
Расхождения пиксельного нет и быть не могло: стили панели собираются побайтово
тем же текстом.
npm run docs:accept -- --identical
Все 11 кадров попиксельно совпали с закоммиченными:
принят только отпечаток исходников (f561ec99).
Issue: #597
User-Visible: no
Набор контролов был поднят из редактора боковой панели в #594, но сама панель
осталась на своей копии тех же правил. Две копии расходятся молча: правка в
наборе не доедет до панели, и наоборот.
Просто вызвать `formKitCss` из панели нельзя. Общие правила в её листе не идут
подряд: между карточкой-группой и обводкой фокуса стоит сетка `.summary-general-grid`,
между фокусом и выключенным состоянием — `button { font: inherit }`, между ним и
рядом-переключателем — `svg`. Вставить один общий кусок значило бы переставить её
объявления, а перестановка правил равной специфичности — уже не подстановка:
проверять пришлось бы каждое правило по отдельности.
Поэтому генератор разрезан на пять именованных фрагментов — карточка-группа,
обводка фокуса, выключенное состояние, ряд-переключатель, подпись ряда, — и
панель подставляет каждый на своё место. `formKitCss` остаётся их композицией,
чтобы у диалогов не появилось пяти вызовов вместо одного.
Доказательство вместо обещания. У настроек панели нет ни одного golden-кадра: в
матрице нет сцены `dialog: 'summary'`, и «панель не изменилась» показать
пикселями нечем. Замена эталону — побайтовое сравнение собранного листа с
замороженным снимком с `origin/dev`:
длина было 14585 стало 14585 — ПОБАЙТОВО СОВПАЛО
Две находки ревью ТЗ закрыты тестами, а не формулировками:
- проверка «копии не осталось» ловила один фрагмент из пяти (`min-height: 54px`
встречается только в ряду-переключателе). Теперь цикл по всем пяти текстам;
проба — фрагмент карточки-группы, оставленный в комментарии, — краснит именно
этот цикл, а прежняя проверка была бы зелёной;
- «выход `formKitCss` остаётся байт в байт» опиралось на тесты, сравнивающие
подстроки и счётчики. Добавлены две фикстуры с полным текстом обеих форм листа
диалогов — с рядом-переключателем и без. Форма с переключателем сегодня не
используется никем, и её расхождение не поймал бы никто до первого потребителя.
Три замороженные фикстуры — осознанный долг: они удаляются на том шаге эпика,
который законно меняет вид панели. Записано рядом с фикстурой.
Issue: #597
User-Visible: no
Встроенная библиотека мебели была смешанной: 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
Шаг слияния конвейера считает patch-id материала и кандидата через
`git diff --full-index`, когда `dev` сдвинулся за время ревью. Дифф задачи,
пересобирающей бандл, несёт три копии `houseplan-card.js` — у #594 это
7 103 616 байт. `sh()` не задавал `maxBuffer`, умолчание `spawnSync` — 1 МиБ,
и процесс убивался по ENOBUFS на 1 027 868 байтах.
Вторая половина дефекта прятала первую: `r.error` нигде не читался, а
`status: r.status ?? 1` выдавало `null` за «git вернул 1». `must()` брал
`r.stderr || r.stdout`, stderr был пуст — и в issue уезжало
«Слияние не выполнено: diff: <огрызок диффа>». Огрызок выглядит осмысленным
и уводит разбор в сторону.
Путь исполняется только при сдвинувшемся `dev`, поэтому дефект дремал. Но
конвейер сам пушит в `dev` документ ревью на каждом раунде, так что
`devMoved` — норма, а не исключение.
Что изменено:
- `MAX_COMMAND_OUTPUT_BYTES` (256 МиБ) и явный `maxBuffer` в `sh()`;
- `sh()` называет сбой запуска в `stderr` результата вместо молчания;
- `sh()` экспортирован, у обоих пунктов есть исполнимый свидетель;
- мутант `merge-candidate-truncates-the-candidate-diff` возвращает 1 МиБ.
Issue: #596
User-Visible: no
`test/release-gate.test.mjs` строил ожидания из живого состояния репозитория, а
`localEvidence` читает `baselines.reviewedRun` из сообщения HEAD-коммита. Пока
вершиной был обычный коммит, поле оставалось `null` и всё сходилось. Как только
вершиной становился коммит приёмки эталонов, поле становилось непустым, и
`evaluateCiProof` законно требовал объявленный прогон — которого заглушка
`loadContext` не отдавала. Тест краснел на всей ветке, ради которой #573 и
писалась, и вместе с ним краснел бы `dev` сразу после слияния такой ветки.
Воспроизведено на `issue/594-form-kit-room@68bb7558`: 2776 pass / 1 fail, из-за
чего конвейер ревью не запустился вовсе.
Продуктовый код не менялся: контракт «объявленный прогон обязан существовать,
быть завершённым, не отменённым и быть Validate» — ровно то, что задумано, и он
намеренно не требует `success`, потому что приёмка эталонов по определению
ссылается на прогон, где golden покраснел.
Что изменено:
- зелёный путь теста считается на evidence с `baselines.reviewedRun: null` и
больше не зависит от того, каким коммитом оказалась вершина;
- контракт объявленного прогона вынесен в отдельный тест и проверяется с трёх
сторон: завершённый неотменённый Validate с `conclusion: 'failure'` — зелёно;
прогона нет — отказ; прогон отменён — отказ;
- мутант `declared-baseline-review-run-never-checked` обнуляет объявление и
краснеет на новом тесте.
Доказательство на фикстуре «вершина — приёмка эталонов»: старый тест 8 pass /
1 fail, новый 10 pass / 0 fail.
Issue: #595
User-Visible: no
Внешний пользователь завёл #370 как баг: в houseplan-space-card нет теней от
стен, хотя в houseplan-card они есть. Разбор показал, что кода это не касается —
ограничение намеренное и записано в src/space-render.ts:353 («the compact card
intentionally has no live radial pools»). visibilityPolygon из
src/light-visibility.ts импортируют ровно два файла, houseplan-card.ts и
houseplan-editor-runtime.ts; space-render.ts не импортирует его вовсе. Пулов нет,
а тень существует только как форма пула — затенять нечего.
Но претензия справедлива, просто адресована не туда. Единственная запись о
намеренности жила в комментарии исходника, которого пользователь видеть не может,
а руководство обещало обратное: «маркеры используют те же состояния, значения,
тревоги и эффекты, что полный план» — про свет ни слова. В LIGHT.md про
компактную карточку тоже не было ничего. Человек полез в код именно потому, что
документация молчала, и сам корректно предположил, что это может быть намеренно.
Теперь сказано в трёх местах: оба руководства и LIGHT.md, с причиной — пулы это
самая дорогая часть отрисовки, у неё свой перф-воркфлоу и бюджеты, и компактная
карточка платит за дешевизну именно ими.
Issue: #370
User-Visible: no
Запас ушёл с 26 КБ до 8.3 КБ за сутки. По документам код-ревью видно, что это не
диффузное расползание, а один шаг плюс обычная работа:
#317 256127 · #318 256091 · #341 256046 · #354 257212 · #159 256828
#357 271143 <- +14 КБ за один заход
#20 271455 · #361 272848 · #359 272469 · #360 273697 <- текущий факт
Шаг на #357 — plan-art мебели: 44 top-view символа в eager-графе, которые платит
каждый план, включая планы без единого предмета мебели.
Потолок 282000 -> 300000. Правило #352 сохранено: 273697 x 1.10 = 301067, то
есть 300000 остаётся внутри надбавки ~10% над измеренным фактом. Запас
возвращается к 26.3 КБ — примерно к тому, что было до #357.
Запись честная и в комментарии сказана прямо: рекалибровка ничего не ускоряет и
ничего не чинит. Она фиксирует новую норму и возвращает гейту способность красить
того, кто вырастил бандл, а не того, кто пушнул последним. Настоящий рычаг —
ленивый граф, варианты 1 и 2 из #367.
Добавлено предупреждение: пока запас меньше 15000 Б, гейт печатает ::warning:: и
строку в summary. Прежняя редакция полагалась на то, что человек заметит тренд в
выводе; за сутки его не заметил никто, потому что каждая отдельная строка
выглядела нормально. Текст предупреждения называет лечение — иначе следующий
читатель поднимет потолок ещё раз и назовёт это решением.
Тест закрепляет обе стороны: надбавка не больше 10% и не меньше 5% (меньше —
возврат лотереи «красит последний коммит»), тревога срабатывает строго ниже
порога, превышение описывается как превышение. Три мутанта проверены руками,
один добавлен в реестр.
Issue: #367
User-Visible: no
Конвейер исполняет версию из ветки по умолчанию, поэтому файл обязан совпадать
в main и dev побайтово. Содержательная правка сделана в dev (#365), здесь копия.
Issue: #365
User-Visible: no