Commit Graph
861 Commits
Author SHA1 Message Date
Sergey Matyunin 5254fb9ef5 fix: reconcile lightweight interaction frames
Issue: #451
User-Visible: no
2026-09-04 22:04:17 +03:00
Sergey Matyunin c0d61ca3d2 fix: keep large-plan interactions off the full render path
Issue: #451
User-Visible: yes
2026-09-04 22:03:45 +03:00
Claude 28a4cb5eec fix(gates): съёмка кадров в чужой среде отказывается заранее
Вопрос владельца: зачем агенты снимают PNG на Windows, если кадры мы всё
равно не принимаем, тем более что WSL есть на обеих машинах. Ответ по
коду: этому ничто не мешало. Ни один из шести скриптов съёмки и приёмки
не знал, на какой он ОС — ни `process.platform`, ни win32, ни WSL не
упоминались нигде. Съёмка отрабатывала штатно, а стена появлялась на
приёмке, и текст стены говорил «сцен-свидетелей 0 из 10», то есть
подсказывал неверный вывод «надо объявить больше сцен» — от которого до
`--expect-change` на всю матрицу одна команда.

Причина запрета не политика, а физика: Windows растеризует текст через
DirectWrite, с другим субпиксельным сглаживанием и DPI, поэтому
байтового совпадения с принятым эталоном не даёт никогда, свидетелей
среды быть не может, и приёмка откажет всё равно. Флаги детерминизма из
#410 убирают разброс внутри среды, а не между ОС.

Что сделано:

- `golden:capture` отказывается до запуска браузера, в тексте отказа
  готовая команда для WSL;
- `golden:verify` остаётся законным в любой среде: он ничего не
  принимает, а как грубая проверка полезен;
- обе приёмки (golden и документации) отказываются в чужой среде;
- к отказу свидетелей приписывается фраза про расхождение среды — та
  самая, которой не хватало, чтобы отказ не читался как «объяви больше
  сцен»;
- платформа уезжает в манифесты рядом с версией Chromium: у кадров
  появился провенанс среды;
- осознанный обход есть и требует причину: HP_ALLOW_FOREIGN_CAPTURE.

Главный урок задачи — про цену правки файла, у которого записан хеш.
Первая редакция встроила проверку в `demo/docs/capture.mjs`, и гейт
документации сразу покраснел: его sha записан в индексе скриншотов, и
`scripts/check-docs.mjs` их сверяет. То есть проверка, которая ничего не
рисует, стоила бы пересъёмки всех картинок документации и визуальной
приёмки владельца. Поэтому для документации отказ живёт шагом раньше —
`npm run docs:capture` вызывает `scripts/assert-capture-env.mjs` — и
шагом позже, на приёмке. По той же причине гейт golden стоит в
`demo/golden/policy.mjs`, а не в `run.mjs`: последний входит в корпус
sourceFingerprint. Тест закрепляет обе границы: гейт обязан быть в
policy.mjs и в npm-скрипте и обязан отсутствовать в двух
фингерпринтуемых файлах.

Платформа в юнитах — параметр, а не `process.platform`: иначе тест был
бы зелёным на Linux и красным на машине владельца, то есть тестом про
хост, а не про правило.

AGENTS.md приведён к состоянию после #401 (принимается любая среда,
доказавшая себя байтовым совпадением непринятых кадров) и разводит
проверку и съёмку — прежний текст сливал их в «advisory» и утверждал
«accepted only on a complete Linux CI artefact».

Свидетели, все проверены отрицательным прогоном: снятый гейт съёмки,
гейт, отказывающий и на verify, обход без причины, отказ без команды,
убранная приписка про среду, отцепленные гейты обеих приёмок,
переставшая бросать обёртка, npm-скрипт без проверки и возврат гейта в
каждый из двух фингерпринтуемых файлов. Проверка подключения сначала
смотрела только на импорт модуля и молча проходила, когда отказ
заменяли на `void` — теперь она проверяет вызов бросающей обёртки.

Гейты: npm test 1918 tests, 1917 pass, 0 fail; typecheck зелёный;
check-docs зелёный (индекс скриншотов не задет); pytest без HA 378
passed, 3 skipped.

Issue: #455
User-Visible: no
2026-09-04 19:26:35 +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 Matyunin 169fed01f0 feat: replace Labs expiry with persistent alpha switch
Issue: #448
User-Visible: yes
2026-09-04 16:31:06 +03:00
Sergey Matyunin 112ec0eb66 test: accept Zigbee settings golden frames
Issue: #54
User-Visible: no
Release: v1.72.0-beta.1
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/33875831578
2026-09-04 16:11:16 +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 39ba4e2b88 test: exercise shared room action guard
Issue: #152
User-Visible: no
2026-09-04 10:26:50 +03:00
Sergey Matyunin 15cfa96553 test: fix room fit ownership witness
Issue: #152
User-Visible: no
2026-09-04 10:25:32 +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 04e3f5ee43 fix: snap furniture to wall surfaces
Issue: #445
User-Visible: yes
2026-09-04 01:27:26 +03: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
Sergey Matyuninandclaude[bot] fa41d2981c fix: keep vacuum routes pending until a floor is chosen
Issue: #441
User-Visible: yes
2026-09-03 19:22:55 +00: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 26b0b58320 test: prove the multi-floor overlay in the production bundle
User-Visible: no
Issue: #162
2026-09-03 19:21:39 +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 c958e2431f test: isolate room pointer modality witness
Issue: #440
User-Visible: no
2026-09-03 16:53:35 +03:00
Sergey Matyunin 53847e3997 fix: harden beta 2 audit paths
Issue: #440
User-Visible: yes
2026-09-03 16:50:54 +03:00
Sergey Matyuninandclaude[bot] d5ad0371aa fix: roll back rejected general settings save
Issue: #439
User-Visible: yes
2026-09-03 13:30:04 +00: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] 44a38b3378 test: stabilize kiosk gear smoke
Issue: #51
User-Visible: no
2026-09-02 21:56:04 +00:00
Sergey Matyuninandclaude[bot] 590d178e29 fix: address custom decor image review
Issue: #51
User-Visible: no
2026-09-02 21:56:04 +00: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 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 2038ab91b9 fix: harden support feedback pipeline
Issue: #423
User-Visible: yes
2026-09-02 21:25:30 +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 59ea93b4ba Make lost-space smoke prove synchronous refusal
Issue: #417
User-Visible: no
2026-09-02 16:26:50 +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 Matyunin a79d4cd9d2 fix: preserve geometry cache during Area baseline
Issue: #126
User-Visible: yes
2026-09-02 12:54:51 +03:00
Sergey Matyunin 5282967784 test: accept reviewed support polish goldens
Issue: #415
User-Visible: no
Release: v1.70.0-beta.4
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/33593529139
2026-09-02 08:21:20 +03:00
Sergey Matyuninandclaude[bot] a0720e7584 fix: align support controls
Issue: #415
User-Visible: yes
2026-09-02 05:08:51 +00:00
Sergey Matyunin 95a958cca2 test: accept reviewed help and feedback goldens
Issue: #43
User-Visible: no
Release: v1.70.0-beta.3
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/33574362360
2026-09-02 03:23:40 +03:00
Sergey Matyunin 23c6781a82 test: resize the furniture smoke host with its viewport
Issue: #414
User-Visible: no
2026-09-02 03:12:24 +03:00
Sergey Matyunin 9e47c30088 test: update mode smoke for help button (#43)
Issue: #43
User-Visible: no
2026-09-02 03:07:53 +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 9d5c0357b0 test: keep grid scale static fixture coherent
Issue: #411
User-Visible: no
2026-09-02 00:08:02 +03:00
Matysh ca0b318e5e ci: make the stability measurement a gate, not a printout
Замер сделал свою работу — теперь он остаётся как проверка. Если кадр снова
начнёт зависеть от времени, шаг упадёт, а не напечатает число в лог. Стоит
перед съёмкой набора: публиковать артефакт, снятый недетерминированной
съёмкой, смысла нет.

Issue: #410
User-Visible: no
2026-09-01 23:20:09 +03:00
Matysh ed700c8c8e fix: crop the documentation frames on whole pixels
Замер снял главное: три снимка подряд в одном состоянии страницы совпадают
побайтово у всех десяти сценариев. Значит рендер детерминирован, а плавает то,
что приходит на вход съёмке.

Обрезка считается из живого DOM через getBoundingClientRect и приходит
дробной. Дробная обрезка заставляет Chromium ресемплить кадр — и тогда сдвиг
раскладки на десятую пикселя переписывает границы всех элементов на единицы
уровней. Ровно эта подпись в #410: 76 пикселей, максимум 2 уровня, alpha не
тронута, всё на сглаженных границах полей.

Рамка расширяется наружу, а не округляется к ближайшему: обрезка обязана
содержать цель целиком.

Issue: #410
User-Visible: no
2026-09-01 23:13:59 +03:00
Matysh d945b03208 test: measure whether the frame drifts inside one page state (#410)
Флаги растеризации убрали один кадр из трёх, но device-editor и device-info
плавают по-прежнему. Дальше гадать нельзя: нужен ответ, плавает ли кадр внутри
одного состояния страницы или разница копится между подготовками сценария.

Режим --stability=N делает N снимков подряд без единой правки состояния и
сравнивает их попиксельно в самой странице — тем же приёмом, что у golden.
Печатает число различающихся пикселей, максимум по RGB, задета ли alpha и
bbox.

Ветка временная.

Issue: #410
User-Visible: no
2026-09-01 23:09:05 +03:00
Matysh 823fc316b2 fix: pin rasterisation for the documentation capture
Съёмка скриншотов документации запускалась вообще без флагов детерминизма,
тогда как golden имел их с самого начала. Отсюда и плавающие кадры: включённое
субпиксельное сглаживание даёт разный результат от прогона к прогону, а
дельта — единицы уровней в RGB на сглаженных границах при неизменной alpha —
это его подпись, а не изменение продукта.

Измерено до починки: два прогона канонического workflow на одном и том же
dev SHA 184e0098, одном Chromium 151.0.7922.34 и одном oxipng 10.2.0 дали три
разошедшихся кадра из десяти — 06-device-editor, 08-room-card, 09-device-info.

Взяты те же три флага, что у golden: --disable-lcd-text снимает субпиксельное
сглаживание, --font-render-hinting=none — зависимость от хинтинга,
--force-color-profile=srgb фиксирует профиль. Добавлен reducedMotion: 'reduce'
и два кадра ожидания перед съёмкой: animations: 'disabled' гасит анимации, но
не гарантирует, что запланированный ре-рендер успел лечь в композитор.

Байтовый контракт приёмки не ослаблен ни в одном месте — чинится источник
шума, а не проверка.

Правка меняет capture.mjs, поэтому captureScriptSha256 в манифесте протух и
check-docs красный до пересъёмки. Пересъёмка неизбежна и по существу: с
выключенным субпиксельным сглаживанием переписываются все десять кадров сразу,
то есть приёмка идёт через --no-witnesses --reason.

Issue: #410
User-Visible: no
2026-09-01 22:59:17 +03: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 8ac8f0105a fix: let the product build the marker-dialog fixture
Прогон показал, что дописать binding и bindingMode было мало: у объявленного
типа больше сорока обязательных полей, и рендер упал на следующем
недостающем — теперь на name внутри _markerDraft. Гоняться за типом руками
бессмысленно.

Смок открывает диалог штатным _openMarkerDialog() и переопределяет три поля.
Это заодно и доказательство, что дефекта поведения нет: продукт своим же
путём собирает объект, на котором рендер не падает.

Issue: #404
User-Visible: no
2026-09-01 19:19:05 +03: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