Commit Graph
9 Commits
Author SHA1 Message Date
Matysh cd0ecf4db2 fix: the witness floor comes from the matrix, not from surviving baselines
Порог свидетелей считался от числа сцен со статусом не missing-baseline, то
есть от эталонов, уцелевших на диске. Обход в одну команду: git rm
demo/golden/baselines/*.png — все сцены становятся missing-baseline, порог
обращается в ноль, свидетелей никто не требует, и чужая съёмка всей матрицы
принимается без единого следа причины в манифесте. Отказ
goldenAcceptanceRefusal этого не ловит: он требует объявить каждую новую сцену
в --expect-new, а объявить их все ничто не мешает.

Прежняя редакция объясняла ноль тем, что первичная съёмка свидетелей иметь не
может. Верно по факту и неверно по выводу: невозможность доказать среду не
отменяет требования, она требует сказать это вслух. Теперь и первичная съёмка
идёт через --no-witnesses --reason, а причина уезжает в манифест эталонов.

Размер матрицы стал обязательным параметром, а не выводится из отчёта: у
частичного прогона (run.mjs --only=…) results короче матрицы, и порог просел
бы молча — тот же дефект в другой одежде. Отсутствие параметра — отказ.

Формула не менялась: она общая с docsWitnessFloor и обязана такой остаться.
Менялся источник счётчика. На обычной приёмке ничего не меняется: при 143
эталонах порог был и остался 10.

Issue: #408
User-Visible: no
2026-09-01 17:25:54 +03:00
Codex 7c285d25b7 test: golden acceptance keeps environment witnesses (#355)
Declaring the whole matrix in --expect-change could accept a completely
foreign capture (different font stack, different machine): no undeclared
passed scenes would remain, and undeclared passed scenes are exactly what
proves the capture environment equals the accepted baseline's. The
realistic failure is fatigue, not malice — a mass framing change where the
author lists "everything that went red", accidentally sweeping in scenes
that diverged because of the environment.

Acceptance now requires a witness floor: after subtracting
--expect-change/--expect-new, at least min(10, 10% of baseline scenes)
undeclared scenes must match their accepted baselines BYTE-FOR-BYTE (a
sub-threshold 'passed' proves nothing about the environment — #351). A
truly total repaint passes only with an explicit
--no-witnesses --reason="…", and the reason is written into the baseline
manifest — a trace in the artifact and its git history, not just in the
shell history. A first-ever capture with no baselines requires no
witnesses: every frame there is declared in --expect-new anyway.

Issue: #355
User-Visible: no
2026-08-28 18:18:24 +03:00
Claude 0c9cf9503e test: приёмка переписывает только объявленные эталоны
`passed` означает «в пределах порога», а не «байт в байт»: comparePng считает
diffRatio, и статус ставится по нему. А приёмка копировала кандидата поверх
КАЖДОГО эталона матрицы, поэтому подпороговый дрейф уезжал в контракт молча — и
накапливался: каждая приёмка подтягивала эталон к последней среде, порог не
пересекался никогда, а эталон уходил. Так 1e341c60 заменил 22 картинки, объявив
четыре.

Проект уже сталкивался с этим: ad3f9981 восстанавливал девять уехавших эталонов
руками. Такую работу обязан делать инструмент.

Теперь копируются только сцены из --expect-change и --expect-new; остальные
сохраняют и файл, и свой хеш из прежнего индекса. Индекс по-прежнему
перезаписывается на полный набор — сирота или пропавшая запись делают манифест
недействительным целиком.

Решение вынесено в чистую функцию goldenAcceptancePlan: оно одно, и ошибка в нём
дорога. Отсутствие прежнего хеша у необъявленной сцены — ошибка, а не повод
взять кандидата: без эталона бывает только новая сцена, а она обязана быть
названа в --expect-new.

Логика вернулась в demo/golden/accept.mjs, где ей и место: после #344 эти файлы
исключены из корпуса отпечатка, так что правка больше не требует пересборки и
пересъёмки. scripts/golden-accept.mjs остался проходным вызовом ради
документированной команды.

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

Issue: #351
User-Visible: no
2026-08-28 11:52:36 +03:00
Claude 3c6b473c94 test: новая golden-сцена объявляется отдельно, флагом --expect-new
Правило из #334 требовало объявлять только сцены со статусом different, а
missing-baseline пропускало без вопросов. При закрытии #346 из-за этого три
эталона каталога устройств стали контрактом без единого взгляда.

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

Поэтому флагов два и они утверждают разное: --expect-change — «я знаю, почему
старый кадр изменился», --expect-new — «я посмотрел на новый кадр». Имя в чужом
флаге тоже останавливает приёмку: путаница означает, что ревьюер думал об одной
сцене, а утверждал про другую.

Новые эталоны печатаются отдельной строкой «СТАНУТ КОНТРАКТОМ ВПЕРВЫЕ», а не
растворяются в общем списке — раньше они там и растворились.

Прежний тест «новая сцена объявления не требует» заменён: он кодировал снятое
правило. Два мутанта проверены руками — возврат молчаливого пропуска и
разрешённая путаница флагов, — каждый убит.

Issue: #350
User-Visible: no
2026-08-28 11:34:29 +03:00
Claude 4800c44e51 test: локальная съёмка golden допустима по доказательству, а не по доверию
Прежде эталон принимался только из артефакта CI: растеризация шрифтов на другой
машине может отличаться, а доказать обратное было нечем. Цена — два полных
прогона на каждый визуальный фикс, при версии матрицы 48 она платится часто.

Доказательство теперь эмпирическое: среда равна раннеру, если каждая сцена,
которую менять не собирались, совпала со своим эталоном. Расхождение
растеризации спрятать нельзя — оно задевает все сцены с текстом. Ревьюер
объявляет намерение через --expect-change, всё разошедшееся помимо списка
приёмку запрещает. Поэтому неверно угаданный тег образа не может испортить
эталоны: он может только не сработать.

То же правило независимо от среды запрещает «принять всё, чтобы CI позеленел» —
именно так эталон перестаёт быть эталоном, молча и одной командой.

scripts/golden-container.mjs снимает кандидатов в образе Playwright той же
версии, что залочена в package-lock. Хозяйский node_modules прячется анонимным
томом: он собран под Windows, и npm ci внутри контейнера сломал бы дерево.

Обёртка, а не правка demo/golden/accept.mjs, — намеренно. sourceFingerprint
включает ВСЕ .mjs из demo/golden, включая accept.mjs и policy.mjs, которые
исполняются после съёмки и ни одного пикселя изменить не могут. Их правка
объявляет устаревшими бандл и оба манифеста, то есть требует ровно того двойного
цикла, который эта задача убирает. Сужение корпуса отпечатка — отдельная задача:
сам source-fingerprint.mjs в корпусе, и одна пересборка бандла неизбежна.

Issue: #334
User-Visible: no
2026-08-28 01:59:45 +03:00
Matysh 554d2e6544 Release v1.62.0-beta.2 candidate 2026-08-11 22:12:08 +03:00
Matysh 9419842333 fix(hacs): keep exactly one *manifest.json in the tree
Validate / hassfest (push) Failing after 14s
Validate / frontend (push) Successful in 8m18s
Validate / backend (push) Failing after 10m34s
Validate / smoke (push) Failing after 24m45s
Validate / performance_smoke (push) Failing after 9m42s
Full Performance / performance (push) Failing after 54m31s
Validate / golden (push) Failing after 6m50s
Validate / hacs (push) Failing after 11s
The HACS submission check does not read hacs.json to find the integration: it
globs `*manifest.json` over the whole clone of the default branch and exits 1
unless there is exactly one (hacs/default, scripts/helpers/integration_path.py).
Three files matched — the two stand-only integrations added on 2026-07-31 and
the golden baseline index added on 2026-08-11 — so the Hassfest job of PR #9004
went red five weeks into the review queue, with a log that named no file.

The stand manifests ship as manifest.template.json and demo/stand/install.sh
renames them at install time; the golden index becomes baselines-index.json
(the exported constant keeps its name, so no consumer changes).
test/repo-hygiene.test.mjs fails if a second manifest ever appears, and the
existing golden-policy assertion — which compared against 'manifest.json' and
happily passed on 'baseline-manifest.json' — now checks the suffix.
2026-08-11 14:03:38 +03:00
Matysh b1deb0beab Avoid hassfest manifest collision
Validate / hacs (push) Failing after 8s
Validate / hassfest (push) Failing after 8s
Validate / frontend (push) Successful in 3m52s
Validate / backend (push) Failing after 8m33s
Validate / golden (push) Failing after 6m9s
Validate / smoke (push) Failing after 22m51s
2026-08-08 22:03:09 +03:00
Matysh e177c14603 v1.60.3-beta.1: harden rendering and QA tooling 2026-08-08 21:33:09 +03:00