Commit Graph
234 Commits
Author SHA1 Message Date
Sergey Matyunin 793d06c75e feat: show Zigbee routes to coordinator
Issue: #457
User-Visible: yes
2026-09-05 10:50:19 +03:00
Sergey Matyunin b286d7ba43 feat: copy spaces without room bindings
Issue: #456
User-Visible: yes
2026-09-05 09:25:08 +03:00
Claude de30066be7 test(gates): мутанты на восемь модулей горячего пути из #451
#451 принёс 1 651 строку в восьми новых модулях, а единственный мутант
того релиза патчил houseplan-card.ts и houseplan-editor-runtime.ts —
места, ОТКУДА код ушёл. Вынос в модули был правильным решением, но
защита осталась смотреть на старые файлы.

Восемь мутантов, семь модулей, все прогнаны штатным раннером (чистый
прогон зелёный, мутант красный):

- live-editor-view-mode-routes-live — режим View обязан оставаться
  реактивным: живой путь редактора там означает план, который перестал
  отвечать на Home Assistant у двух персон из трёх;
- live-editor-first-gesture-frame-goes-live — первый кадр жеста меняет
  выделение и хром и обязан остаться реактивным;
- pointer-move-queue-keeps-first-move — очередь last-wins: сохранение
  ПЕРВОГО коллбэка кадра рисует план там, где палец уже не находится;
- live-hass-tick-never-deferred — тик состояния посреди жеста
  откладывается намеренно, и флаг отложенности гарантирует его
  воспроизведение после;
- live-viewport-identity-projection-not-recognized — см. ниже;
- render-invalidation-unknown-key-ignored — классификатор обязан
  ошибаться в сторону перерисовки на незнакомых ключах hass;
- resize-live-preflight-keeps-every-room — живой resize проверяет только
  задетые комнаты, иначе каждый кадр становится полной проверкой плана;
- render-lifecycle-diagnostics-cache-never-invalidated — кэш диагностики
  обязан сбрасываться, иначе красная точка нового устройства не загорится.

Мутант на live-viewport пришлось заменить, и это стоит записать. Issue
предлагал снять `setLayerProjection(layer, null)` из
`commitHouseplanViewport` — прогон показал, что тест остаётся ЗЕЛЁНЫМ:
следом идёт `paintLiveViewport(root, painted, painted)`, который на
равных аргументах даёт identity и обнуляет проекцию сам. Тот цикл —
подстраховка, а не контракт. Настоящий контракт — распознавание identity
(`isIdentityLiveLayerProjection`), потому что даже единичный transform
переключает путь композитинга и сдвигает установившийся растр на
несколько уровней цвета. Мутируется теперь он.

`src/live-hover.ts` остался без мутанта сознательно: его контракты либо
чисто производительные (мемо по наведённой комнате), либо доменные
(подсветка комнаты, застрявшая после ухода курсора). Первое мутантом не
ловится в принципе, второе — только браузерным смоком, которого в
песочнице нет. Записано в тесте и в issue.

Чтобы требование не жило в памяти, добавлен гейт: у каждого модуля
горячего пути обязан быть свой мутант, и он не имеет права патчить
houseplan-card.ts или houseplan-editor-runtime.ts — то есть исходный
дефект #458 больше не воспроизводим молча. Проверено отрицательным
прогоном: перевод патча любого из модулей на старое ядро краснит гейт.

Гейты: npm test 1960 tests, 1959 pass, 0 fail; typecheck зелёный;
mutation-gate --check зелёный на всех якорях; каждый из восьми мутантов
прогнан отдельно.

Issue: #458
User-Visible: no
2026-09-05 09:08:09 +03:00
Sergey Matyunin cb68492c99 fix: address render performance review
Issue: #451
User-Visible: no
2026-09-04 23:45:57 +03:00
Codex d5e114524c fix: комментарий засчитывается вердиктом только по документу своей задачи
User-Visible: no
Issue: #454
2026-09-04 20:18:59 +03:00
Codexandclaude[bot] 8d8263202f ci: считать раунды ревью по опубликованным документам
User-Visible: no
Issue: #454
2026-09-04 16:58:31 +00:00
Sergey Matyunin aa51ed2bf5 fix: complete double background fit delivery
Issue: #449
User-Visible: no
2026-09-04 19:06:29 +03:00
Sergey Matyunin 05ef318133 feat: fit all on double background tap
Issue: #449
User-Visible: yes
2026-09-04 18:33:03 +03:00
Sergey Matyuninandclaude[bot] 8935a736cb fix: accept Zigbee2MQTT raw network maps
Issue: #450
User-Visible: yes
2026-09-04 14:58:04 +00:00
Sergey Matyunin 4203a01b04 fix: reject malformed Zigbee topology replies
Issue: #54
User-Visible: no
2026-09-04 15:26:06 +03:00
Sergey Matyunin 85702afc0c feat: add contextual Zigbee topology hover
Issue: #54
User-Visible: yes
2026-09-04 14:40:09 +03:00
Sergey Matyunin 3a1f2b2d86 fix: address room fit code review
Issue: #152
User-Visible: no
2026-09-04 11:20:41 +03:00
Sergey Matyunin 4aad9facdf feat: fit rooms from view
Issue: #152
User-Visible: yes
2026-09-04 10:24:25 +03:00
Sergey Matyunin 6b07b70d7c fix: preserve exterior furniture side and nudge decor
Issue: #447
User-Visible: yes
2026-09-04 08:58:48 +03:00
Sergey Matyunin a0f0a52410 fix: preserve rollback across config canonicalization
Issue: #442
User-Visible: yes
2026-09-04 02:31:02 +03:00
Sergey Matyunin 04e3f5ee43 fix: snap furniture to wall surfaces
Issue: #445
User-Visible: yes
2026-09-04 01:27:26 +03:00
Sergey Matyuninandclaude[bot] 582066d017 test: harden marker rollback review gates
Issue: #442
User-Visible: no
2026-09-03 21:42:57 +00:00
Sergey Matyuninandclaude[bot] 3a88f3ad6c fix: roll back rejected marker writes atomically
Issue: #442
User-Visible: yes
2026-09-03 21:42:57 +00:00
Sergey Matyunin cebd20a9b5 fix: polish vacuum map routes
Treat explicit empty route lists as authoritative, preserve them through single-space export, group deleted-space routes, and render vacuums from the immutable vacuum-only snapshot subset.

Issue: #443
User-Visible: yes
2026-09-03 23:24:08 +03:00
Codex 49f69f60db fix: refine a high-residual calibration on the map's own floor
User-Visible: no
Issue: #162
2026-09-03 19:55:52 +03:00
Codex 8e2892b95f fix: type the new vacuum routing code and add map_routes to the config schema
User-Visible: no
Issue: #162
2026-09-03 19:41:55 +03:00
Codex 4032b810b7 fix: calibrate a robot map against the space it is assigned to
User-Visible: no
Issue: #162
2026-09-03 19:30:42 +03:00
Codex 26b0b58320 test: prove the multi-floor overlay in the production bundle
User-Visible: no
Issue: #162
2026-09-03 19:21:39 +03:00
Codex 9809ec7a04 feat: validate vacuum map routes and clean them up with their space
User-Visible: no
Issue: #162
2026-09-03 19:18:13 +03:00
Codex 567b456890 feat: warn on the dock when a moving robot has no floor to draw on
User-Visible: yes
Issue: #162
2026-09-03 19:18:13 +03:00
Codex ca6ebc3035 feat: assign each robot map to a floor in the device editor
User-Visible: yes
Issue: #162
2026-09-03 19:18:13 +03:00
Codex fec99b907b feat: record vacuum trails under the route that produced them
User-Visible: no
Issue: #162
2026-09-03 19:18:13 +03:00
Codex c38501ef50 feat: route live vacuum overlays to the active map space
User-Visible: yes
Issue: #162
2026-09-03 19:18:13 +03:00
Codex 9126e5b430 feat: pure map-to-space routing contracts for multi-floor vacuums
User-Visible: no
Issue: #162
2026-09-03 19:18:13 +03:00
Sergey Matyunin 5409f0b2e3 refactor: isolate room pointer preflight
Issue: #440
User-Visible: no
2026-09-03 16:59:31 +03:00
Sergey Matyunin 53847e3997 fix: harden beta 2 audit paths
Issue: #440
User-Visible: yes
2026-09-03 16:50:54 +03:00
Claude 5f90eaf4ef fix(gates): потолок initial-графа с полосой вместо разового храповика
#429 снял `SUPPORT_LAZY_INITIAL_BASELINE_BYTES = 291046` — порог,
привязанный к критерию приёмки #423, с пятнадцатью байтами запаса и
сообщением про копирайт формы поддержки. Снять было правильно. Но снят
он оказался ровно на том релизе, где сработал бы:

  beta.1 291 031 · снятый порог 291 046 · beta.2 291 069

Рост случился внутри той же беты, уже после снятия, и заметить его стало
нечем: единственным сигналом остался `lowHeadroomWarning`, который горит
постоянно — третий аудит подряд, и третий раз без реакции.

Механизм по образцу ядер (#425): потолок 292 000 Б с полосой 2 000 Б,
двусторонний. Рост выше потолка — отказ с числом и указанием, что делать
(поднять потолок в том же коммите с объяснением либо вынести код в
ленивый граф, #367). Падение ниже полосы — тоже отказ: незафиксированный
выигрыш отыгрывается молча, именно так запас бюджета ушёл с 26 КБ до
8.3 КБ за сутки.

Полоса, а не точное число, — по измерению, а не из осторожности. На
beta.2 initial-чанк стал МЕНЬШЕ на 344 сырых байта и при этом на 40 байт
больше в сжатом виде: gzip не монотонен по исходнику. Точный храповик по
gzip краснел бы на коммитах, которые код сокращают, — та же лотерея, о
которой предупреждает комментарий к бюджету 300 000. Потолок поставлен
так, чтобы факт лежал ближе к середине полосы: 931 Б до отказа сверху,
1 069 Б снизу.

Предупреждение о запасе стало погашаемым: `LOW_HEADROOM_ACKNOWLEDGED_CEILING`
привязан к ЗНАЧЕНИЮ потолка, поэтому признание долга перестаёт покрывать
ровно тогда, когда потолок поднимут. Сейчас `null` — не погашено, и текст
говорит, чем гасится. Превышение самого бюджета признанием не гасится:
там отказ, а не тревога.

Свидетели. Девять мутаций, каждая краснит свой тест: снятие верхней
стороны, снятие нижней, потолок выше бюджета, полоса 50 Б, признание
поверх превышения бюджета, молчащее устаревшее признание, и две — про
подключение: вызов потолка убран из main и отказ понижен до console.log.
Последние две прошли молча на первой редакции тестов — статическая
проверка «в main есть вызов» была бы циклическим доказательством, за
которое #430 снял циклический тест гарда benchmark, поэтому добавлен
прогон настоящего CLI в подставном дереве.

Захардкоженный `lowHeadroomWarning(9058)` из #429 заменён на число из
поставляемого манифеста: константа в тесте выглядела измерением, не
будучи им.

Мутант `initial-view-ceiling-unplugged` в реестре.

Гейты: npm test 1819 tests, 1818 pass, 0 fail; node scripts/bundle-budget.mjs
зелёный — 291 069 внутри полосы, запас до бюджета 8 931 Б.

Issue: #438
User-Visible: no
2026-09-03 16:18:14 +03:00
Codex d4dd027b0a build: prepare v1.71.0-beta.2 candidate
Issue: #426
Issue: #427
Issue: #428
Issue: #431
Issue: #432
Issue: #434
User-Visible: no
2026-09-03 15:23:40 +03:00
Sergey Matyuninandclaude[bot] dc95563d96 feat: add reusable custom decor images
Issue: #51
User-Visible: yes
2026-09-02 21:56:04 +00:00
Codex d4ecace64c test: cap the two frontend cores with a ratchet (#425)
User-Visible: no
Issue: #425
2026-09-03 00:26:47 +03:00
Codex 0f5161164f test: prove the drift gate itself can fail (#422)
User-Visible: no
Issue: #422
2026-09-02 22:38:51 +03:00
Codex 653b9a7632 docs: refresh the capture fingerprint after the clip extraction (#422)
User-Visible: no
Issue: #422
2026-09-02 22:14:41 +03:00
Codex d59ceee905 feat(gates): measure capture drift between runs and anchor reachability (#422)
User-Visible: no
Issue: #422
2026-09-02 22:10:16 +03:00
Codex 09c410000a fix: pin the compositor so a frame depends only on the commit (#424)
User-Visible: no
Issue: #424
2026-09-02 21:59:43 +03:00
Sergey Matyunin f4b425f3c0 test: prove three defensive contracts fail red
Issue: #421
User-Visible: no
2026-09-02 20:13:58 +03:00
Sergey Matyunin 489e289f79 fix: guard Area snapshot cleanup
Issue: #419
User-Visible: yes
2026-09-02 19:18:28 +03:00
Sergey Matyunin 2b978d63ef Fix support preview and retry races
Issue: #418
User-Visible: yes
2026-09-02 17:10:05 +03:00
Sergey Matyunin e46220f3eb Fix confirmation guards for unrenderable branches
Issue: #417
User-Visible: yes
2026-09-02 16:25:24 +03:00
Sergey Matyuninandclaude[bot] 1a953718a7 test: cover help and feedback browser flows (#43)
Issue: #43
User-Visible: no
2026-09-02 00:05:37 +00:00
Sergey Matyunin b4809a12ff fix: await draft deletion through the card facade (#405)
Issue: #405
User-Visible: no
2026-09-01 22:23:57 +03:00
Sergey Matyuninandclaude[bot] 372d838ec2 fix: close beta 2 polish gaps
Issue: #406
User-Visible: yes
2026-09-01 16:32:25 +00:00
Matysh 2903374b72 fix: the exception guard reads its counter after delivery, not before
Гард «uncaught exception внутри карточки» жил в demo/serve.mjs с 2026-07-27 и
не срабатывал ни разу в самом частом случае. Счётчик читался синхронно, а
Playwright доставляет pageerror асинхронно по CDP: если исключение возникло
после последнего обращения смока к странице, счётчик к моменту проверки
нулевой, а browser.close() уносит недоставленное событие. В логе это видно
дословно — EXC печатается после результата и до OK.

finish() теперь делает round-trip по открытым страницам перед чтением
счётчика. Страницы регистрируются там, где создаются: ссылок на них у
finish(browser, out) нет, а менять сигнатуру нельзя — так её зовут 205
смоков.

Medium-1 жёлтого ревью ТЗ закрыт расширением, а не оговоркой. Страницы,
созданные смоком после launch(), регистрация в launchInternal не покрывает:
smoke_zoom_flash печатал своё EXC2 мимо счётчика, три страницы
smoke_svg_sandbox не имели слушателя вовсе. Документировать слепую зону в
задаче, которая существует ради устранения слепой зоны, значит закрыть issue,
оставив дефект. Наружу отдана одна функция watchPage(page): подписка и
регистрация неразделимы, иначе появится страница, чьи исключения считаются, а
доставки не ждёт никто.

Разрыв оказался шире, чем в ревью: проверка по всему набору нашла ещё два
файла со своей подпиской — smoke_cold_view_toggle и smoke_cold_view_vacuum.
Они не слепая зона, их страница приходит из launchColdView и уже
зарегистрирована, а свой счётчик они превращают в отдельное утверждение.
Поэтому инвариант сформулирован как «ни одна страница не создаётся мимо
гарда» и закреплён по всему набору, а не по двум названным файлам.

reportPageErrors() из #407 стал асинхронным: второй читатель счётчика обязан
ждать доставку так же, как finish(). Пять смоков получили await.

Фикстура smoke_danger_confirmation приведена к объявленному типу: без binding
и bindingMode _bindingHasHaPage падал на undefined.split(':') — два
исключения, которых гард не видел. Дефекта поведения нет, все 15 мест в src/,
создающих диалог, binding пишут; врала фикстура.

Два отступления от ТЗ, каждое по измеренной причине. Пробы лежат в
demo/guard/, а не demo/fixtures/: последний входит в корпус sourceFingerprint,
и каждый файл там объявил бы устаревшими бандл, скриншот-индекс и
golden-индекс — пробы же не касаются ни одного пикселя. Поведение
доказывается в job со браузером, а не в npm test: job «Фронтенд» браузеры не
ставит, и тест молча скипался бы — тот самый тихий успех, против которого вся
задача.

Issue: #404
User-Visible: no
2026-09-01 19:08:07 +03:00
Sergey Matyuninandclaude[bot] 355b0516c7 fix: preserve area relocation state
Issue: #403
User-Visible: yes
2026-09-01 15:08:22 +00:00
Codex 00b6f41233 fix: the danger confirmation lives outside render()'s branches (#402)
hp-confirm sat at the end of a chain of early returns, so in onboarding
(«no spaces yet»), in the fixed-floor states and without a space it did
not exist at all: the trash button next to a saved plan was dead and the
promise hung forever, because the decision event had no source in the
DOM. An already open dialog vanished the moment the card slipped into
one of those branches, leaving the caller waiting for a resolution that
could never come. Before #32 a browser confirm() worked there.

render() is now a wrapper: it takes the body — the old chain, unchanged,
as _renderBody — and renders the confirmation beside it. That fixes the
class rather than the instance: a branch added later cannot lose the
dialog again. noChange and nothing are passed through untouched, since
neither may be wrapped in a template; in those states _confirmDanger
refuses the request outright instead of leaving it pending, which is the
honest answer while the card is not on screen and the user has pressed
nothing.

_tapConfirm and _vacCalConfirm deliberately stay where they are. They
share the same final branch, but they have no promise (a synchronous
exec, a dialog closed by hp-close), so the defect cannot occur there,
and their entry points require a drawn plan.

Proven by a separate smoke rather than an addition to
smoke_danger_confirmation: that file keeps deliberately incomplete
dialog fixtures open, and the extra re-renders this change needs make
them throw. The new smoke runs under touch emulation, because
TOUCH-SUPPORT § Safety floor forbids bypassing a destructive
confirmation and the broken branch pierced that floor on finger as
surely as on mouse. Reverting the wrapper reddens it.

User-Visible: yes
Issue: #402
2026-08-31 16:47:05 +03:00
Codex d153b20a1d fix: make the handle paint order a named decision and mutate it (#400)
CODE-REVIEW-400-r1 Medium: the registered mutant edited a comment, not
the order — it could not reproduce the regression AC1 exists to catch.
That is the same defect class this issue is fixing elsewhere, in my own
guard.

The order is now HANDLE_PAINT_ORDER, a named constant, because it IS the
hit priority rather than an accident of where the blocks sit in the
template. The mutant flips that constant, so it reproduces exactly the
behaviour the audit found.

Also: smoke_furniture picked the SE corner as handles[3], an index that
silently depended on the old paint order — CI shard 3 went red on four
checks. It now selects by role (corner handles, third of four), which is
what the test actually means.

User-Visible: no
Issue: #400
2026-08-31 08:14:55 +03:00