Commit Graph
602 Commits
Author SHA1 Message Date
Sergey Matyunin 075a2d3fb1 fix(backend): flush deferred state on HA shutdown (#655)
Issue: #655
User-Visible: yes
2026-09-27 15:17:44 +03:00
Claude bdac4b4fdc ci: закрепить образ раннера, таймауты job и некруглые cron (#658)
`ubuntu-latest` с 19.10.2026 переезжает на Ubuntu 26, а golden, скриншоты
документации и перф-бюджеты сняты на текущем образе: все 43 job на раннере
теперь явно на `ubuntu-24.04`, один образ на все workflow. 26 job получили
`timeout-minutes` по наблюдённой длительности с запасом; гейт релиза — 180,
больше суммы собственных ожиданий (60 + 60 + 45). Расписания ушли с круглых
минут (ночь 02:17, мутанты 00:43, метрики 05:23, полный перф 04:11), ночь
пишет в summary сдвиг старта и предупреждает, если он больше часа.

test/workflow-hygiene.test.mjs держит все три правила по тексту workflow
(разбор `parseJobSettings` в scripts/workflow-jobs.mjs) и исполняет шаг
сдвига старта настоящим bash; порядок осознанного подъёма образа —
docs/DEVELOPMENT.md.

Issue: #658
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-27 13:57:21 +03:00
Sergey Matyuninandclaude[bot] 85cba3d8b4 docs: синхронизировать английское руководство (#668)
Английская версия догнана до структуры и содержания русского руководства. Структурный гейт защищает H2-H4 и маркер актуальной версии от нового рассинхрона.

Issue: #668
User-Visible: no
2026-09-27 09:18:50 +00:00
Sergey Matyuninandclaude[bot] 15207dbacd test: добавить Stage 6 в golden-матрицу (#673)
Issue: #673
User-Visible: no
2026-09-27 07:51:54 +00:00
Sergey Matyunin 9bc23f9871 fix: индексировать ревью стабильного релиза (#670)
Issue: #670
User-Visible: no
2026-09-27 09:43:51 +03:00
Sergey Matyuninandclaude[bot] 71c8a87e67 test: не считать README входами харнесса (#672)
Issue: #672
User-Visible: no
2026-09-27 06:32:49 +00:00
Sergey Matyunin 05dfcd1c8a ci: guard asset inputs in Validate manifest
Issue: #671
User-Visible: no
2026-09-27 08:45:07 +03:00
Sergey Matyunin d64338224d fix: учесть направление ступеней и защитить совместимость (#663)
Issue: #663
User-Visible: yes
2026-09-27 01:14:46 +03:00
Matysh ea1f19c352 perf: разбить вычитание лестниц по кадрам (#663)
Issue: #663
User-Visible: no
2026-09-26 23:48:01 +03:00
Matysh ce4f820a03 test(mutation): сделать свидетели лестниц неэквивалентными (#663)
Issue: #663
User-Visible: no
2026-09-26 22:08:28 +03:00
Sergey Matyunin d03a68b88a feat: добавить лестницы между этажами (#663)
Прямые и винтовые лестницы получили отдельную модель, инструменты редактора, безопасную межэтажную навигацию, вычитание из чистой площади и плоское отображение в 2.5D.

Issue: #663
User-Visible: yes
2026-09-26 21:00:43 +03:00
Claude c93b3a90ef fix(2.5d): room metrics keep their distance from the name at every zoom (#665)
The raised room label in 2.5D View sized its own box to the 44 px touch
minimum (min-height + justify-content: center). The name therefore sat
centred in 44 px while the metrics row, placed absolutely from the label
bottom, hung (44 - name height) / 2 below it: 18.3 px at a 9 px name and
6.4 px at a 39 px one on the demo stand, against a constant 0.1 name
height on the flat plan.

The 44 x 44 px floor moves into an invisible ::before (the door-lock
pattern), z-index -1 inside the label stacking context; the label box is
sized by its text again.

demo/smoke_iso_room_label_metrics.mjs measures gap / name height at two
cameras (font x4) in Flat and 2.5D, and the 44 px floor and area-link hit
targets on labels no raised device covers. Mutant
iso-room-label-44-box-centres-name.

Screenshots: 11 frames pixel-identical, fingerprint only.

Issue: #665
User-Visible: yes
2026-09-26 15:16:14 +03:00
Claude 3bdd3c5163 fix(header): the active editor tab highlight covers its close X (#666)
The accent fill of the active Plan/Devices/Decor tab is now a pseudo-element
stretched over the 2 px group gap and the fixed 24 px X slot of #660, so
the X sits inside the highlighted zone without any box moving: the tab,
the slot, the X and the header keep their geometry. The X takes the tab
text colour, and the keyboard focus ring moves to the same pseudo-element
so it outlines the whole zone instead of cutting it at the tab edge.
.modes becomes its own stacking context so the z-index:-1 fill paints above
the group background. On a phone the mode tabs, and the fill, stay hidden.

smoke_toolbar_stable_width: painted-pixel probes for the gap, the slot edge
and top, and the first pixel after the slot, in every editor at every width
above 480 px; the X colour equals the tab colour. The existing geometry
and hit checks are unchanged and green. Mutant
toolbar-active-highlight-stops-at-tab.

Screenshots: 11 frames pixel-identical, fingerprint only.

Issue: #666
User-Visible: yes
2026-09-26 14:36:14 +03:00
Claude 40b73cc599 fix(space-card): nothing in the static schematic is hit-testable (#664)
The stage is pointer-events:none, but that is not an inherited ban: the
shared marker styles opt the 44 px floor (.dev::before) and the painted
capsule (.device-shell-frame) back in for the interactive plan (#564).
The static card imports them whole, so since v1.76.0-beta.2 it showed a
hand cursor over every marker and swallowed clicks that do nothing.

The card now closes every descendant opt-in (.hp-static-stage *, ::before,
::after). smoke_space_card asserts the browser hit test itself
(elementFromPoint over the marker and a 9x9 grid across the stage) and
the cursor, not the stage computed style that stayed green through the
regression. Mutant static-card-descendants-hit-testable.

Screenshots: 11 frames pixel-identical, fingerprint only
(npm run docs:accept -- --identical).

Issue: #664
User-Visible: yes
2026-09-26 13:02:51 +03:00
Sergey Matyunin 85119fd9a2 test: восстановить защиту перехода редактора (#660)
Предрелизный гейт снова держит лимит монолитного файла, а защитный мутант #648 привязан к новой формуле общего бюджета сцены и панели. Мутант предметно краснит smoke_sections_resize.

Issue: #660
User-Visible: no
2026-09-26 12:42:03 +03:00
Sergey Matyuninandclaude[bot] d3c88f06f9 feat: отполировать главную панель редакторов (#660)
Issue: #660
User-Visible: yes
2026-09-26 08:51:15 +00:00
Claudeandclaude[bot] b856dd33c8 infra(process): fast-forward merge rebuilds the review index too (#657 r1 H1)
The task branch no longer carries docs/reviews/INDEX.md (1b), and
merge-candidate rebuilt it only inside rebaseOnto. When dev did not move
the fast-forward pushed the stale index and reviews_index would turn dev
red. freshIndex(tip) commits the rebuilt index on top of the material
before the push; the merge stays a fast-forward.

Real-git test: fast-forward, then reviews-index --check on the dev head
is green. Mutant merge-ff-skips-review-index.

Issue: #657
User-Visible: no
2026-09-26 07:28:24 +00:00
Claudeandclaude[bot] 88c4e4e9ae infra(process): bundle and review index change only on the way to dev (#657)
Решения владельца: 1б — индекс ревью не пересобирается в ветке задачи,
только коммитами, идущими в dev; 2б — бандл меняет только кандидат
беты/релиза, стенд dev берёт его из артефакта Validate.

- scripts/bundle-policy.mjs: коммит, трогающий dist/** или
  custom_components/houseplan/frontend/**, обязан нести Release:
  (хук commit-msg и история в CI через validate-commit-provenance;
  коммиты с датой автора до 2026-09-27 не судятся); --verify судит
  целостность свежей сборки всегда, побайтовую сверку с закоммиченной
  копией — только на коммите, меняющем бандл, или кандидате; --clean.
- release-prerelease: публикация отказывает, если отпечаток исходников
  в закоммиченном манифесте не равен отпечатку дерева (хотфикс поверх
  кандидата без пересборки).
- bundle-sync: по умолчанию только demo/srv/assets; --release
  (npm run bundle:release) — ещё и custom_components.
- rebase-on-dev: конфликт в бандле берёт копию dev, без пересборки
  и amend.
- validate.yml: job dev_build публикует card-bundle головы dev в
  сиротскую ветку dev-build (scripts/dev-build.mjs); стенд накладывает
  её demo/stand/update-dev-bundle.sh.
- _process.yml: индекс ревью больше не пересобирается при приведении
  к dev и при публикации документа в ветку задачи.
- golden-wsl-artifact/golden-container: сборка перед съёмкой не
  считается правкой источника, после — bundle:clean.
- test/bundle-tree-committed: судит закоммиченный снимок, не диск.
- 11 мутантов в реестре; PROCESS/AGENTS/DEVELOPMENT/AUTHOR/REVIEWER.

Issue: #657
User-Visible: no
2026-09-26 07:28:23 +00:00
Sergey Matyunin 43fab645b0 fix(ci): fail closed on newest full release proof (#656)
Issue: #656
User-Visible: no
2026-09-26 10:02:40 +03:00
Sergey Matyunin f5c6d70b7c fix: stabilize cold 2.5D first frame
Issue: #654
User-Visible: yes
2026-09-26 09:05:20 +03:00
Sergey Matyunin e73e56dd94 test(2.5D): cover rigid group boundaries and fallback
Issue: #651
User-Visible: no
2026-09-26 04:34:49 +03:00
Sergey Matyunin c40266431c test(2.5D): retire superseded zoom mutant
Issue: #651
User-Visible: no
2026-09-26 03:54:57 +03:00
Sergey Matyunin 4ab7ecb4a2 fix(2.5D): keep device overlays stable while zooming
Issue: #651
User-Visible: yes
2026-09-26 03:41:32 +03:00
Claude 106fdc096d fix(2.5D): prove the sun beam is cut by bodies; review r1 lows (#649)
CODE-REVIEW-649-r1 M1: the 2.5D beam's cut by physical bodies (AC7) had no
witness. Unit: a column in the beam leaves the floor between window and
column lit and shades it behind the column; smoke: a Solid partition across
the south beam removes floor from the rendered wash. Mutants: occluders
extruded toward the sun, occluders ignored, card drops occluders.

Lows: L1 stale alpha wording (DEVELOPMENT, ARCHITECTURE, card comment,
benchmark); L3 dark-theme state edges computed per theme; L4 Alert beats a
plain Hover and a plain Selected; L5 a virtual marker has no dashed ring;
L6 tiles and shadows stay without walls (show_borders off).

Issue: #649
User-Visible: no
2026-09-25 18:55:28 +03:00
Claude 2ed050311b test(mutants): stage3-w12 guard follows the renamed #649 contract test
The guard pattern "Labs iso is presentation-only" matched no test after #649
renamed it, so node --test ran the file with zero subtests and the mutant
survived (Validate 36151140394, shard 3/6). Point it at the test that still
asserts params.getAll('hp_alpha').

Issue: #649
User-Visible: no
2026-09-25 18:55:15 +03:00
Claude e5ab4659f6 feat: 2.5D stage 6 — public setting, raised tiles, soft sun, theme-free walls (#649)
- 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
2026-09-25 18:55:14 +03:00
Claude 43459bcbe0 ci(mutants): an unmatched --test-name-pattern is setup-failure, not survived (#650)
A name-filtered `node --test` that matches no test exits 0 and TAP reports
only the file itself. The clean run took that for a healthy witness and the
mutant run for `survived`, although no assertion ran either time — as in
Validate 36151140394 on #649, where a renamed test left a guard pattern empty.

- mutation-guard-outcome: nodeTestSelection/executedTestNames/
  emptyTestSelection; a green oracle with a name filter and zero executed
  named tests is `setup-failure` (clean run: «FAIL чистая подготовка»,
  diff mode: attributed like any setup failure, #568).
- mutation-gate --check: staticTestSelectionProblems — every pattern must
  match a literal test(/it(/describe( name in the guard's files; files with
  ${…} names only warn. POSIX quoting keeps \( inside "…".
- Registry today: 349 unique name-filtered commands run on dev, all execute
  ≥ 1 test; --check: 0 errors, 3 warnings (dynamic names).
- Tests: 6 new in test/mutation-guard-outcome.test.mjs (incl. the installed
  node's real TAP and a registry-wide static check); 3 mutants.
- Docs: TESTING.md pointer, testing-notes/infrastructure.md section.

Issue: #650
User-Visible: no
2026-09-25 18:31:17 +03:00
Sergey Matyuninandclaude[bot] 2452c51d00 feat: вертикальный resize карточки в Sections (#648)
Issue: #648
User-Visible: yes
2026-09-25 13:11:44 +00:00
Claude 255330b9bc Шапка на телефоне: одна строка — вкладки, масштаб и шестерёнка с меню остального (#616)
На окне не шире 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
2026-09-25 14:22:36 +03:00
Sergey Matyuninandclaude[bot] e6987459a5 fix: respect Home Assistant read-only ACL
Issue: #626
User-Visible: yes
2026-09-25 10:47:27 +00:00
Claude 0ba81a994b Процесс: независимое ревью линии перед стабильным релизом, не блокирующее выпуск (#638)
PROCESS.md §11.5: перед стабильным релизом — одно ревью поверхностей всей
линии бет «с нуля», без ТЗ и документов раундов, по SCOPE и USER-GUIDE.

- scripts/release-review.mjs: вход линии — прошлый стабильный тег, issue по
  трейлерам в схеме RELEASE-MEMBERSHIP.json, продуктовые файлы; бриф промпта.
- .github/workflows/release-review.yml (workflow_dispatch, исполняется с dev):
  prepare → model_review (модель без прав на запись, github_token #556) →
  publish (docs/reviews/RELEASE-REVIEW-vX.Y.Z.md в dev токеном процесса,
  индекс тем же коммитом, review-doc-guard). Повтор на тот же тег не тратит
  модель, если документ уже в dev.
- release.yml: job independent-review ставит ревью в очередь сразу после
  candidate, continue-on-error; ни один job выпуска от него не зависит
  (решение владельца 2026-09-25).
- REVIEWER.md, AGENTS.md, DEVELOPMENT.md; тесты и четыре мутанта.

Issue: #638
User-Visible: no
2026-09-25 12:53:14 +03:00
Claudeandclaude[bot] f0ba7c5da9 Основная панель: без счётчика устройств, × редактора в своём слоте — ширина шапки не меняется (#647)
Счётчик «N устр.» и ключ count.devices удалены вместе с правилом
`.head .count` (скрывало счётчик на ≤1100 px). Крестик закрытия редактора
вынесен из активной кнопки режима в отдельный слот `.editor-close-slot`
сразу после группы кнопок режимов: 24 × 24 px (--hp-editor-close-size),
одинаковый в редакторе и вне его; вне редактора слот пуст, aria-hidden,
без фокуса и pointer-events. Кликабельная зона × — весь слот (≥24×24, #195),
глиф 13 px; × виден на любой ширине, где видны кнопки режимов.

Смок smoke_toolbar_stable_width (1400/1000/768/390 px, en/ru): ширина
шапки и отступы кнопок стабильны во всех переходах, × виден, попадание
в край зоны закрывает редактор, нет нового overflow. smoke_editor_tabs,
smoke_edge_cases, smoke_hidden_flag переведены на слот/список устройств.
6 мутантов. docs: UX-MODES.md, STYLING-HOOKS.md, data-hp-contract.json.

Issue: #647
User-Visible: yes
2026-09-25 08:35:57 +00:00
Sergey Matyunin f293c6e9dc fix(editor): сохранять позицию кнопки при отмене Resize (#645)
Транзитная геометрия Resize теперь только вычисляет центр для текущего кадра и не удаляет сессионную позицию. Окончательная геометрия по-прежнему очищает точку, если она действительно вышла за комнату.

Issue: #645
User-Visible: yes
2026-09-25 10:15:34 +03:00
Sergey Matyunin a8437e69e5 fix(ci): атомарно печатать отчёт private-writes (#645)
Linux CI при параллельном npm test мог потерять последнюю отдельную запись
stdout после длинной таблицы --count. Формируем отчёт целиком и пишем его
одним вызовом, чтобы итоговая строка всегда доходила до unit-теста.

Issue: #645
User-Visible: no
2026-09-25 08:58:05 +03:00
Sergey Matyunin 62d0f50593 feat(editor): перетаскивание кнопки настроек комнаты (#645)
Кнопка сохраняет положение в пределах комнаты только на время сессии редактора,
корректно отменяется при pinch/cancel и учитывается подписями Resize.

Issue: #645
User-Visible: yes
2026-09-25 08:33:07 +03:00
Claudeandclaude[bot] fa319e37b3 test(harness): тестовый фасад window.__hpTest и гейт no-new-private-writes (#629)
- scripts/no-new-private-writes.mjs: смоки и demo/helpers/** не добавляют
  записей в приватное состояние карточки (присваивание, ++/--, delete по
  цепочке с сегментом _x; от this — нет) и вызовов _setMode/_openRoomEdit/
  _openMarkerDialog/_openSpaceDialog. Зачёт правки по полю, перенос блока —
  movedLinesByFile из no-new-any; исключение // private-ok: <причина>.
  --count — остаток на HEAD. Подключён в gate:small и в шаг frontend рядом с
  no-new-any, с той же базой.
- demo/helpers/hp-test.mjs: 10 операций через контрактные хуки и события
  фикстуры (setMode, setTool, switchSpace, openRoomEdit, openMarkerDialog,
  openSpaceDialog, setServerConfig, setLayout, input, close); ставится
  launch*() из demo/serve.mjs. В бандле фасада нет.
- demo/srv/demo.html: доставка houseplan_config_updated/_layout_updated,
  __pushServerConfig/__pushServerLayout; после доставки запись со старым
  expected_rev — conflict, как у настоящего сервера.
- HP_SMOKE_CHECKS=1 печатает имена проверок в finish().
- smoke_area_relocation, smoke_glow, smoke_grid_snap переведены на фасад без
  потери утверждений; новый smoke_test_facade доказывает каждую операцию.
- 7 мутантов, docs/TESTING.md (раздел + правило №6), PROCESS.md §2.7, AGENTS.md.

Issue: #629
User-Visible: no
2026-09-25 01:25:20 +00:00
Claude fa91f3a731 test: temp dirs are removed even when a test fails; lint guards it (#646)
golden-capture-provenance accept() left a ~20 MB baselines sandbox in
TMPDIR on every call (433 copies, 8.5 GB, ENOSPC in the shared sandbox).
It now reads what it needs and removes the sandbox in finally; fixtures
are removed via t.after. Same fix for the three other leaking sites
(bundle-assets hp-tree-, docs-accept hp-identical-, rebase-generated
hp-runner- outside finally).

test/temp-dir-hygiene.test.mjs parses test/**/*.mjs with the TypeScript
AST: every mkdtemp/mkdtempSync must be bound to a name removed by
rm*/rmSync in a finally block or an after/afterEach/t.after hook of the
same function, or returned by a named helper whose every call site does
so; exception `// tmp-ok: <reason>`. Two mutants witness the lint.

Issue: #646
User-Visible: no
2026-09-25 03:50:40 +03:00
Claude 2bf929e34e fix(settings): colour tiles show alpha as a checkerboard (#615)
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
2026-09-25 02:13:32 +03:00
Claude 93bacc9696 build(#618): ребейз на dev после #642, #627, #617 — пересборка бандла, потолок редактора, база монолита
Код-ревью r1 зелёное; слияние отказало на конфликте с dev. Ветка
переиграна на origin/dev 2b3959e5 (32 коммита).

- Конфликты: бандл — версия dev + пересборка; оба changelog — запись
  #618 под записью #617 в том же разделе; bundle-budget.mjs и
  monolith-baseline.json — версия dev, числа #618 измерены заново;
  docs/reviews/INDEX.md — node scripts/reviews-index.mjs.
- Ключи device_inbox.* остаются в основных словарях: #627 вынес в
  ленивые чанки только settings/support/topology, каталог устройств
  туда не входит.
- bundle-budget: LAZY_EDITOR_GZIP_CEILING 229 600 → 231 200 (замер
  230 208, база dev 228 997, +1 211 Б gzip); initial View 289 558 →
  289 884 (+326 Б, en-строки), в пределах потолка 290 400.
- monolith-baseline: hostRefs 4872 → 4883 (+11, как в r1),
  bundleBytes 2509909 → 2515825.
- src/houseplan-card.ts: снято type-зеркало `selected?` в
  DeviceInboxDialogState карточки (2 строки). После #627 запаса под
  потолком ядра (12889) не осталось; карточка поле не читает, пишет и
  читает его только рантайм через свой тип — так же, как черновики
  фильтров #44. Типы стираются, поведение не меняется.

Issue: #618
User-Visible: no
2026-09-24 23:12:30 +03:00
Claude d17bcc05fb feat(devices): пакетное «Скрыть/Показать выбранные» в каталоге устройств (#618)
На вкладках «На плане» и «Скрытые» — флажки строк, «Выбрать все (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
2026-09-24 22:50:30 +03:00
Claude 43b52826e4 build(#617): ребейз на dev после #642 и #627 — пересборка бандла, база монолита
- Ветка переиграна поверх dev с #642 (вынос диалога «Оптимизировать
  планы») и #627 (ленивые словари settings/support/topology). Текстовые
  конфликты — только в сгенерированном (бандл, база монолита) и в
  docs/reviews/INDEX.md (пересобран reviews-index.mjs); src/**, i18n,
  реестр мутантов, smoke-links слились без конфликтов.
- Бандл пересобран, три копии совпадают, bundle-tree: 30 ассетов.
- monolith-baseline: hostRefs 4 869 → 4 872 (+3 — те же три обращения к
  хосту, что и до ребейза: 4 948 → 4 951, приняты на ревью r1),
  bundleBytes 2 508 211 → 2 509 909 (+1 698, в полосе ±2 000); прочие
  числа равны базе dev.
- bundle-budget: потолки не менялись, все замеры в полосе.

Issue: #617
User-Visible: no
2026-09-24 19:59:51 +03:00
Claude 857369b18b fix(plans): загрузка плана по HTTP, предел 8 МБ проверяется до отправки (#617)
План уходил 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
2026-09-24 19:57:41 +03:00
Claude b921ba7184 build(#627): ребейз на dev после #642 — пересборка бандла, потолок редактора, база монолита
- Бандл пересобран поверх dev с #642 (вынос диалога «Оптимизировать
  планы»), три копии совпадают.
- bundle-budget: потолок ленивого графа редактора 246 000 (#642) → 229 600,
  замер 228 621 (до ребейза 228 900 при 227 869; +752 — модуль диалога #642).
  Центр полосы: 979 Б сверху, 1 021 Б до нижней границы. Потолок онбординга
  28 500 без изменений (замер 27 468).
- monolith-baseline bundleBytes 2 499 182 → 2 508 211 (+9 029: десять новых
  чанков словарей и записи манифеста, та же причина, что и до ребейза);
  прочие числа равны базе dev.
- smoke_optimize_geometry_preflight: ожидание языкового гейта #627 перед
  открытием диалога через новый порт #642 (`_editorRuntime.optimizePlans.open()`),
  разрешено в коммите задачи.

Issue: #627
User-Visible: no
2026-09-24 18:54:29 +03:00
Claude 82a48ee0ce perf(i18n): словари settings/support/topology — ru/de/fr ленивыми чанками; потолок онбординга (#627)
- ru/de/fr трёх словарей пространств — девять ленивых чанков (по одному на
  пару «пространство × язык»), английский статически как слой отката;
  рантайм — тот же LanguageRuntime (две попытки, отпечаток сборки, fallback,
  общий тост отказа), src/i18n/namespace-language.ts.
- Загрузчики рантаймов онбординга и редактора отдают поверхность в рендер
  только после того, как словарь текущего языка осел; гейт хоста учитывает
  словари загруженной на нём поверхности (смена языка на лету — ветка warm).
  Оверлей Zigbee ждёт topology до первой отрисовки.
- bundle-manifest: граф lazyNamespaceLocaleFiles, девять retry-токенов со
  строгим счётом «ровно один». bundle-budget: потолок онбординга 28 500
  (замер 27 470), редактор 245 400 → 228 900 (замер 227 869), проверки
  ленивости и владения по содержимому.
- Смок smoke_lazy_admin_locale (AC4–AC7); регресс-смоки, переключавшие язык
  синхронно, ждут гейт вместо ослабления проверок; пять мутантов.
- monolith-baseline bundleBytes 2 500 387 → 2 508 676: +10 чанков (rollup intro
  и export на каждый) и записи манифеста; gzip каждой поверхности при этом
  уменьшился.

Issue: #627
User-Visible: no
2026-09-24 14:51:18 +03:00
Claudeandclaude[bot] 66ae952c2a refactor(editor): диалог «Оптимизировать планы» — свой модуль с узким портом (#642)
Первый вынос из монолита по образцу live-*/RadarSetupController.
src/optimize-plans-dialog.ts: OptimizePlansDialogState, чистые
preflightDiagnostics/preflightVersionsDiffer и класс OptimizePlansDialog
(open/preview/toggleLivePositions/run/copyDiagnostics/
reportPreflightFailure/render, clipboardFallback) за портом из 18 членов.
Модуль не знает HouseplanEditorHostPort; импортирует его только рантайм,
карточка берёт тип.

Инлайн-фолбэк буфера обмена — WeakMap по объекту диалога
(CODE-REVIEW-295-r1 M2), дедуп dev-лога — поле класса: из карточки и порта
ушли _preflightClipboardFallback и _reportedPreflightFingerprint, пять
делегатов и две стрелки-заглушки. Харнесс (13 смоков, wall-draw-click,
golden) зовёт card._editorRuntime.optimizePlans.* — меняется только адрес.

11 текстовых утверждений i18n.test.mjs о разметке и тосте диалога и гварды
8 мутантов переехали в юнит test/optimize-plans-dialog.test.mjs
(test-build); новый мутант optimize-dialog-imports-host-port.

monolith-baseline: delegates 159→154, portMembers 350→348,
hostRefs 4948→4869, portPrivates 96→94, harnessPrivates 107→101,
bundleBytes 2499647→2499182. Потолки ядер опущены на выигрыш;
потолок lazy editor gzip пересчитан (+555 Б gzip при −465 Б сырых).

Issue: #642
User-Visible: no
2026-09-24 08:00:58 +00:00
Claude baf283c50f ci: thin default-branch callers invoke reusable bodies at @dev (#623)
Six workflows run from the default branch (issues, schedule, workflow_run):
process, process-resume, process-reconcile, mutation-gate, nightly,
process-metrics. Their bodies move to _<name>.yml (on: workflow_call); the
original files keep only triggers, run-name, permissions, concurrency and one
job `uses: Matysh/houseplan-card/.github/workflows/_<name>.yml@dev` with
`secrets: inherit`. A pipeline change becomes one commit to dev.

- caller job permissions = union of body job permissions (#556 minimum kept
  per job inside the body); caller `if` repeats the body guard for process and
  process-resume so unrelated events stay skipped;
- dispatch inputs forwarded via workflow_call inputs of the same names;
- _mutation-gate.yml keys evidence/marker on job.workflow_sha (the body SHA):
  in a called workflow github.workflow_sha belongs to the caller in main;
- action-pins: narrow exception for this repo's _*.yml at @dev with a reason;
- preflight workflow_sync compares all six thin callers (was 3 of 6);
  performance.yml excluded: its schedule judges main with main's own body;
- tests read bodies from _*.yml; new test/default-branch-workflows.test.mjs;
  six mutants; PROCESS.md §10.4, AGENTS.md, REVIEWER.md updated.

Issue: #623
User-Visible: no
2026-09-24 10:23:28 +03:00
Claude 92b83d9525 fix(process): rebase resolves a conflict only in docs/reviews/INDEX.md by rebuilding the index
A pipeline doc commit carries the review document and the rebuilt
INDEX.md; while the task waits, dev receives other tasks' documents with
their own INDEX.md, and the rebase of the branch conflicts in the index
every time. 24.09 this bounced green #617, #618, #629, #642 to S6.

scripts/rebase-generated.mjs: shared rebase helper. At every stop, if ALL
conflicting paths are docs/reviews/INDEX.md (or paths the caller
resolves itself), the index is rebuilt from the directory in the stop
tree, staged, and the rebase continues; any other path aborts and
returns the full list. CLI exit 3 = refusal with paths on stdout.

Wired into process.yml «Привести ветку к dev» (helper taken from dev via
git archive; conflict/conflicts outputs, lease, ref wait and
--commit-if-stale kept), merge-candidate rebaseOnto (claude[bot]
identity, --commit-if-stale kept) and rebase-on-dev.mjs (index next to
GENERATED_ROOTS; bundle still dev copy + rebuild).

Issue: #643
User-Visible: no
2026-09-24 09:35:58 +03:00
Claudeandclaude[bot] 44ee23ee33 ci(mutants): skip the nightly registry on a proven tree; install only the shard's environment
#620. Mutants were ~70 % of CI machine time.

1. mutation-gate.yml: a green full run (aggregator verified all six shards)
   leaves a cache marker keyed by material tree + workflow SHA. A scheduled
   night with the same tree and workflow, marker not older than 7 days, skips
   the shards and writes "reused from run N" to the run summary. Red runs
   leave no marker, so the next night runs again and still files the issue
   (#472). Manual dispatch always runs the full registry. Decision is a pure
   function in scripts/mutation-nightly-reuse.mjs.
2. changed_mutants: the shard plan (already computed before setup, #518) now
   names the environment of its guards (plan-browser=/plan-python=, from
   scripts/mutation-environment.mjs). Python + backend deps only for shards
   with pytest guards, Chromium only for shards whose guards reach
   Playwright; pip wheels cached. Every shard still runs and reports, so the
   six mutant jobs of the review proof are unchanged.

Item 3 of the issue (smoke guards -> node --test) is out of scope here.

Issue: #620
User-Visible: no
2026-09-24 05:48:42 +00:00
Claudeandclaude[bot] 040ad3dc5d test(editors): unit contracts for dialog baselines and form problems (#631)
dialog-baseline.ts and the four *-form-state.ts modules were proven only by
browser smokes. Unit tests on test-build now pin stableKey (top-level order,
transient filter, nested values as-is), no-baseline => dirty, warm transfer,
the transient set of each dialog, and every *Problems code with its order.

room-form-state.ts joins tsconfig.test.json. Three form-state mutants move
from smoke guards to these units; six new mutants cover the new contracts.

Issue: #631
User-Visible: no
2026-09-24 05:09:23 +00:00
Claudeandclaude[bot] 1fbf7704ca task-packet: trivial short track counts as product flow (#632 r1)
The trivial track (PROCESS §5.1) goes S2 -> S5 without a spec or a spec
review, so in S6/S7 its label is the only product-flow evidence. Without
it a trivial bug whose branch has no class A yet got the same false
"class A forbidden" as #607. productFlowEvidence now takes labels;
new test on the #612 body shape and mutant task-packet-trivial-is-product-flow.

Issue: #632
User-Visible: no
2026-09-24 02:52:09 +00:00