20 KiB
CODE-REVIEW-673-r1
Материал: 855bede537e7b7dd9bdd11aab1d1cbb4b6f6623d (issue/673-stage6-golden, git diff origin/dev...HEAD)
Заход: r1 · блокирующих циклов 0/4
Скоуп
Issue #673: пять сцен Stage 6, утверждённых по эскизу при приёмке #649 (AC12),
жили только в STAGE6_ACCEPTANCE_SCENARIOS — экспорте, который использовал
исключительно ручной demo/capture_stage6_acceptance_649.mjs, а
docs/design/649-25d-stage6/ACCEPTANCE.md прямо называл их «диагностическими,
не golden». Регрессионной защиты у визуального языка Stage 6 (четыре сочетания
темы/пола плюс hover) не было. Задача обслуживает J1 (docs/SCOPE.md) косвенно
— через регрессионную защиту продуктового рендера, которым J1 уже закрыт;
класс изменений B (test/**, scripts/**, demo/**) плюс класс D
(demo/golden/baselines/**) — ни одного файла класса A. Инфраструктурный по
характеру, но без пропуска код-ревью, как и требует AGENTS.md.
User-Visible: no на всех трёх коммитах корректен: пользователь ничего не
видит по-другому, меняется только состав регрессионного корпуса.
Диапазон изменений (git diff origin/dev...HEAD)
Три коммита, у каждого на месте Issue: #673 / User-Visible: no:
15207dba(класс B) —STAGE6_ACCEPTANCE_SCENARIOSперенесён передGOLDEN_SCENARIOSи включён туда через...STAGE6_ACCEPTANCE_SCENARIOSвместо отдельного, никуда не подключённого экспорта;GOLDEN_MATRIX_VERSION65 → 66; правки комментария вmatrix.mjsи вACCEPTANCE.md(сцены больше не описаны как будущая работа); новый тестtest/golden-matrix.test.mjs(#673 Stage 6 designer acceptance scenes are canonical golden entries); новый мутантstage6-acceptance-left-diagnostic-onlyвscripts/mutation-registry.mjs; правка комментария вdemo/capture_stage6_acceptance_649.mjs(текст, не логика).fc237f93(класс D,Release: v1.78.0-beta.5+Baseline-Reviewed-Local) — пять PNG-эталонов вdemo/golden/baselines/, обновлёнbaselines-index.json(версия, fingerprint,witnesses.count121→126, пять новых записей вscenarios).855bede5(класс C) —docs/images/screenshots.json:sourceFingerprintи всеsourceSha256подняты на новое значение;imageSha256не менялись (см. «Находки», это же поле — предмет находки Medium).
Как проверялось
Validate на материале зелёный
(https://github.com/Matysh/houseplan-card/actions/runs/36304416965) —
typecheck, npm test, npm run build + bundle-policy --verify
доверяю этому прогону и не перегонял отдельно ради них самих, но по факту
перегнал часть с целью проверить конкретные заявления автора (см. ниже).
Прогнано мной в этом раунде:
| Гейт | Команда | Результат |
|---|---|---|
| Typecheck | npx tsc --noEmit |
чисто |
| Полный unit-набор | npm test |
3156 pass, 1 skipped (не по теме диффа), 0 fail |
| Build | npm run build |
собрался |
| Точечный тест изменения | node --test --test-name-pattern="#673" test/golden-matrix.test.mjs |
ok |
| Тест умеет падать (мутация вручную) | заменил ...STAGE6_ACCEPTANCE_SCENARIOS,\n // #663 AC3/AC10/AC12: на текст мутанта stage6-acceptance-left-diagnostic-only из scripts/mutation-registry.mjs, перезапустил тот же тест, откатил через git checkout |
тест красный (strictEqual падает на первой же сцене), после отката — зелёный, git status чист |
| Golden — видимое изменение корпуса (пять новых сцен) | npm run bundle:sync && npm run golden:verify |
184/184 passed, включая все пять isometric-stage6-* побайтово против принятых эталонов |
| Смок-выбор по диффу | node scripts/smoke-select.mjs --base origin/dev --head HEAD |
«Исполняемого frontend-диффа нет» — выбирать нечего, диф не трогает src/** |
Доки-гейт (не обязателен по правилу «только если диф трогает src/**», но диф явно трогает demo/golden/matrix.mjs, входящий в корпус отпечатка скриншотов — прогнал целенаправленно) |
node scripts/check-docs.mjs --external --screenshots=strict |
ERROR: screenshot source fingerprint is stale — см. находку Medium ниже |
Не прогонял и почему: pytest tests_backend — диф не трогает
custom_components/**/*.py; инварианты модели
(npm run invariants) — диф не трогает геометрию и не ссылается на неё;
performance — не назван в AC. npm run inventory не запускал, числа тестов
в этом документе не привожу вручную нигде, кроме прямого вывода npm test
выше (не буду хранить его как отдельно поддерживаемое число).
AC · чем доказан · чем краснеет
Из «Ожидаемое» в теле issue:
| AC | Чем доказан | Чем краснеет |
|---|---|---|
Пять сцен в GOLDEN_SCENARIOS, эталоны сняты и приняты по канону (Linux CI/WSL, golden:accept -- --reviewed) |
git diff показывает ...STAGE6_ACCEPTANCE_SCENARIOS в GOLDEN_SCENARIOS; baselines-index.json несёt Baseline-Reviewed-Local: sha256:adb377c4…, тот же хеш — в трейлере коммита fc237f93; golden:verify (прогнал сам) — 184/184, все пять isometric-stage6-* побайтово совпали |
Новый тест #673 Stage 6 designer acceptance scenes are canonical golden entries + мутант stage6-acceptance-left-diagnostic-only; мутацию применил вручную и подтвердил красный (см. таблицу гейтов выше) — «умеет падать» проверено исполнением, не со слов автора |
ACCEPTANCE.md и комментарий в matrix.mjs больше не описывают план как будущую работу |
Прочитано: диф заменяет «These are diagnostic frames, not golden baselines… PNGs are reduced…» на «The five product scenes are canonical entries of GOLDEN_SCENARIOS (#673)…»; комментарий в matrix.mjs синхронно переписан («These exact objects are part of GOLDEN_SCENARIOS below») |
Не защитный AC (текст документации, не код) — доказательство чтением, без исполнения, это допустимо для не-защитных AC |
| Если сцена недетерминирована (солнце, hover) — зафиксировать причину в issue | Комментарий автора в issue: два независимых повторных захвата дали одинаковые SHA-256 для всех пяти сцен — недетерминизм не обнаружен, записано явно | Условный AC; отрицательный результат («не обнаружено») сам по себе не даёт мутанта — принимаю запись как факт по issue, не проверяя повторный захват самостоятельно (см. «Чего не проверял») |
Что проверено и корректно
- Порядок объявлений в
matrix.mjs:STAGE6_ACCEPTANCE_SCENARIOS(строка 230) объявлен после константstage3DenseMarkers/stage3DenseLayout/stage3RequiredOverlays(строки 66–108), на которые ссылается — модуль выполняется без ошибки, порядок безопасен. - Новый unit-тест сверяет объекты по ссылке
(
GOLDEN_SCENARIOS.find(...) === scenario), а не по глубокому равенству — ловит именно «сцена осталась копией, а не тем самым проверенным объектом», что и было дефектом до фикса. demo/capture_stage6_acceptance_649.mjsтеперь описан верно: остаётся диагностическим для side-by-side с лабораторным кадром, но baseline и приёмка идут через общую golden-матрицу — соответствует новому текстуACCEPTANCE.md.- Трейлеры на месте на всех трёх коммитах; класс D коммит (
fc237f93) несёт ровно один из двухBaseline-Reviewed*(-Local), и егоsha256совпадает сlocalAttestation.sha256вbaselines-index.json— то самое «одно число, один источник» для этого конкретного значения. - Изменение классов файлов по коммитам совпадает с
AGENTS.md:15207dbaтрогает только B,fc237f93— только D (baselines + индекс),855bede5— только C (docs/images/screenshots.json). Ни один коммит не смешивает классы способом, который усложнил бы откат. docs/images/screenshots.json: все 11imageSha256не изменились междуorigin/devи материалом — подтверждает заявление коммита «PNG не менялись» (проверил программно, файл по файлу).smoke-select.mjsподтверждает, что диф не задевает исполняемый frontend — отсутствие браузерных смоков в этом раунде оправдано, а не пропущено.
Находки
Medium (в скоупе — жёлтый вердикт, чинится в этой же ветке)
docs/images/screenshots.json: закоммиченный sourceFingerprint не совпадает
с тем, что реально считает visualFingerprint() на этом же дереве.
Коммит 855bede5 целиком посвящён обновлению этого отпечатка («Все 11 кадров
попиксельно совпали с закоммиченными… обновлён только отпечаток исходников»),
и это единственная причина его существования в диффе. Однако:
$ node scripts/check-docs.mjs --external --screenshots=strict
ERROR screenshot source fingerprint is stale; run npm run docs:capture and accept before the beta candidate (#479)
$ node -e "import('./scripts/source-fingerprint.mjs').then(m=>console.log(m.visualFingerprint(process.cwd())))"
4c475f0121499124efd0ae3233a06b05f0ab4a117adf17d942e792bcc7462238
$ node -e "console.log(require('./docs/images/screenshots.json').sourceFingerprint)"
843b7c3049ed5864ba567165c6ec6479581fbb366d5b929748423ccebcc4ae14
Дерево на момент проверки чистое (git status --short пуст, git clean -ndx -- src demo/golden demo/fixtures пуст) — расхождение не побочный эффект
незакоммиченных файлов у меня в рабочей копии.
Почему это находка задачи, а не постороннее наблюдение: диф трогает
demo/golden/matrix.mjs, который входит в корпус отпечатка
(scripts/source-fingerprint.mjs: demo/golden/**/*.mjs, кроме
accept.mjs/policy.mjs), поэтому пересчёт отпечатка после правки —
неизбежное следствие этого же диффа, и коммит 855bede5 — прямая попытка его
сделать. Попытка не удалась: записанное значение не то, что даст пересчёт на
принятом дереве.
Почему Validate на этом SHA всё равно зелёный: docs-джоб выбирает режим
через node scripts/classify-changes.mjs --screenshots-mode, а на этом SHA
он возвращает warn (нет коммита с трейлером Release: на вершине) —
несовпадение уходит в предупреждение, не в ошибку:
$ node scripts/classify-changes.mjs --screenshots-mode
warn
$ node scripts/check-docs.mjs --external --screenshots=warn
WARN screenshot source fingerprint is stale; run npm run docs:capture and accept before the beta candidate (#479)
Documentation checks passed (7 files, 12 external links).
Это ровно тот контракт, который AGENTS.md называет реальным блокиратором
(«docs is a real blocker… exactly what went red after the #113 merge»): он
молчит сейчас только потому, что вершина ветки не несёт Release:, и красным
станет на первом же настоящем кандидате беты/релиза — не обязательно на
v1.78.0-beta.5, а на любом, который придётся собирать поверх этого дерева,
что подставит совершенно постороннюю задачу.
Отдельно проверил: то же расхождение (другими числами) уже существует и на
origin/dev (2f857a0f, до этой ветки) — visualFingerprint там даёт
719db849…, а манифест несёт 22e73a30…. То есть корень проблемы вероятно
шире одной этой ветки (инструмент захвата и его вызов расходятся давно), но
это не снимает находку: 855bede5 заявляет, что чинит именно это конкретное
число для этого дерева, записывает новое значение — и оно тоже неверное.
Чинить в этой ветке можно и нужно: перезапустить
npm run docs:capture && npm run docs:accept -- --identical (или
разобраться, почему --identical пишет несвежий candidate.sourceFingerprint)
и добиться, чтобы check-docs.mjs --screenshots=strict проходил на финальном
дереве перед пере-подачей на ревью.
Low (снимаю сам, с записью)
Комментарий автора в issue #673 называет SHA реализации
557d4e38e26b647437f2c931454737faadf6d440 — валидный по формату (40 hex),
но отсутствующий в этом репозитории (git cat-file -t не находит объект) и не
совпадающий ни с одним из трёх коммитов материала, ни с source.commit в
baselines-index.json (c9a5df602becb…), ни с вершиной 855bede5. Судя по
идентичным committer-датам всех трёх коммитов материала (07:51:54Z), между
записью комментария и финальным пушем прошёл как минимум один перезапис
истории. Материал ревью пришпилен к 855bede5 независимо от текста
комментария (пайплайн берёт SHA по метке, не по тексту), и описанная в
комментарии работа фактически соответствует материалу — поэтому это не
находка «материал не был запушен до метки» (#499), а всего лишь неточная
ссылка, которая собьёт с толку при будущей археологии issue. Не блокирует;
записываю и снимаю.
Чего не проверял
pytest tests_backend, инварианты модели (npm run invariants), performance-профили — не запускал: диф не трогаетcustom_components/**/*.py, геометрию/ссылки на неё, и ни один из них не назван в AC issue.- Реальный повторный WSL-захват для проверки заявления «недетерминизм не
обнаружен» — принял запись автора об идентичных SHA-256 двух захватов как
факт по issue, сам не переснимал (
golden:captureна не-Linux отказывает, а пересъёмка на этом же Linux-сендбоксе доказала бы лишь то же самое, что уже доказалgolden:verify, — байтовое совпадение с принятым, не недетерминизм рендера солнца/hover в принципе). - Скриншоты
docs/**глазами — не смотрел PNG-байты, только сверялimageSha256программно (не изменились) и нашёл несовпадениеsourceFingerprint, см. находку Medium. - Полный
npm run gate:smallцеликом — заменил точечными командами из таблицы гейтов, все нужные для этого диффа part покрыты либо ими, либо зелёным Validate по ссылке.
Вердикт
Жёлтый. Один Medium в скоупе задачи (устаревший sourceFingerprint
документационных скриншотов, который сам коммит утверждает, что исправил, но
проверка check-docs.mjs --screenshots=strict красная на материале). High: 0.
Основной механизм задачи — перенос пяти сцен Stage 6 в канонический
GOLDEN_SCENARIOS с мутационно доказанным защитным тестом — реализован верно
и проверен исполнением (мутация вручную подтверждена красной), возврат нужен
только ради этого одного пункта.
Материал раунда
- Ветка:
issue/673-stage6-golden, коммит855bede537e7— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
501871281b5d8907e5a15faa4ea779c12e3fda1cgit log --all --format='%H %T' | grep 501871281b5d - Тело issue:
2d5e8509154786e88d54451c677aae851abbbb856cf4fef150eeb92d232af154 - Вердикт конвейера:
yellow· High 0