16 KiB
SPEC-REVIEW-512-r2
- Issue: #512 — «Golden: текст версии через seam вне кадров;
docs:accept --identicalпо попиксельной идентичности» - Этап: ревью ТЗ (PROCESS.md §2.4)
- Материал:
docs/specs/512-golden-version-seam-and-docs-identical-accept.mdна HEAD8fba3987(ветка на этом коммите же и стоит); дельта —git diff e606b808..HEAD -- docs/specs/512-golden-version-seam-and-docs-identical-accept.md. - Заход: r2 · блокирующих циклов израсходовано 1 из 4 (зелёный вердикт бюджет не тратит, §4/#227).
- Предыдущий раунд: SPEC-REVIEW-512-r1, вердикт красный, High 1 (H1), получен на SHA
e606b808.
Скоуп ревью
Раунд не первый и делта локальна: правка задевает только §6 (docs:accept --identical, п.1 и п.3), формулировку AC3 и список §11 «Затронутые файлы». Остальные разделы (§1–5, §7–10, §12) байт-в-байт совпадают с версией, проверенной в r1. Ребейза нет, контракт поведения продукта не менялся (User-Visible: no по-прежнему), новая подсистема не затронута — оснований для полного повторного разбора нет. Разбираю дельту и всё, до чего она дотягивается: закрытие H1, консистентность нового текста §6 с реальным кодом demo/docs/capture.mjs/scripts/docs-accept.mjs, и не подрывает ли новая формулировка соседние AC (AC4, AC5), которых дельта не называет впрямую.
Как проверялось
- Прочитан вердикт r1 из комментариев issue: находка H1 (правка
demo/docs/capture.mjsчерез новый флаг--outломает отдельный отsourceFingerprintинвариантcaptureScriptSha256, из-за чего первый же кандидат беты краситdocsвcheck-docs --screenshots=strictнезависимо от визуальных отличий; AC3 не называл, обновляется ли это поле). git diff e606b808..HEAD -- docs/specs/512-golden-version-seam-and-docs-identical-accept.md— единственный содержательный диф раунда (второй файл вgit diff --stat— самdocs/reviews/SPEC-REVIEW-512-r1.md, публикация предыдущего раунда, не предмет ревью).- Подтверждено, что
src/**/scripts/**в дереве всё ещё не тронуты:git diff origin/main...HEAD --statпоказывает только три файла вdocs/**. Гейты кода по-прежнему неприменимы (см. «Чего не проверял»). - Новый текст §6 п.1 сверен построчно с реальным
demo/docs/capture.mjs:OUTPUT = resolve(ROOT, 'docs/images')(capture.mjs:20) — скрипт действительно пишет PNG иscreenshots.jsonпрямо вdocs/images, а не в параметризуемый каталог; отказ ТЗ от флага--outв пользу «бэкап → штатный прогон на месте → восстановление» соответствует реальной сигнатуре инструмента, а не выдуман.- Манифест, который пишет
capture.mjsв конце прогона (capture.mjs:342-355), уже содержитcaptureScriptSha256: sha256(readFileSync(SCRIPT))— то есть кандидат, полученный локальным прогоном без правкиcapture.mjs, естественно несёт актуальный хеш текущего файла. Формулировка §6 п.3 «captureScriptSha256берётся из кандидата» технически исполнима без дополнительного вычисления — это то же поле, что кандидат и так посчитал. scripts/docs-accept.mjs(verifyDocsCandidate, строки 67-69) уже сегодня сравниваетmanifest.captureScriptSha256с фактическим sha диска для существующего флоу--from/--reviewed— семантика поля («хешcapture.mjsна момент съёмки кандидата») в проекте уже установлена, новый режим её не изобретает, а переиспользует.- Текущий закоммиченный
docs/images/screenshots.jsonсодержит ровно поля, которые §6 п.3 перечисляет как «из закоммиченного» (imageSha256для каждого кадра,chromium,oxipng) и «из кандидата» (sourceFingerprint,captureScriptSha256) — набор полей в ТЗ и в реальном манифесте совпадает 1:1. - Число кадров:
grep -c '"file":' docs/images/screenshots.json= 11, совпадает с «11 кадров» в новом предложении §6 п.3 про одноразовую переприёмку.
- Проверено отсутствие остаточных упоминаний отменённого флага
--outгде-либо в файле ТЗ (grep -n -- '--out\b'— пусто) — старая формулировка не оставила следов в §8/§11. - Обновлённое AC3 и обновлённая строка теста
test/docs-accept.test.mjsв §8 сверены на согласованность: AC3 требует «идентичные кадры → только fingerprint иcaptureScriptSha256в манифесте», тест описывает то же плюс явно называет, чтоimageSha256/chromiumостаются прежними и что кадры на диске не меняются (через инъекцию, без реального Chromium в unit-тесте) — согласовано с §6 п.1 «в любом исходе возвращает байты кадров на место». - Перечитаны §7 (симметричный кейс для golden) и §12 (принятые предположения) — дельта их не касается и не должна: правка H1 закрывает вопрос иначе (авто-обновление поля при любом будущем прогоне инструмента), а не через добавление ещё одного разового шага вида §7.
Закрытие раунда r1
| Находка (r1) | Чем закрыта | Где видно |
|---|---|---|
H1 — ТЗ правит demo/docs/capture.mjs (--out), из-за чего меняется его sha256, captureScriptSha256 расходится с диском, и check-docs --screenshots=strict красит docs на первом кандидате беты; AC3 не называл, чинит ли это --identical |
ТЗ отказалось от правки capture.mjs: новый механизм §6 п.1 запускает штатный, неизменённый capture.mjs (пишет прямо в docs/images, как и сегодня), инструмент сам бэкапит закоммиченные кадры+манифест во временную папку и восстанавливает байты кадров при любом исходе; манифест-приёмка (§6 п.3) теперь берёт captureScriptSha256 из кандидата наравне с sourceFingerprint — это закрывает класс дефекта навсегда (любая будущая правка capture.mjs без визуальных изменений чинится тем же локальным прогоном), а не только для этой задачи |
docs/specs/512-…md §6 п.1 («не меняется», «в любом исходе возвращает байты кадров на место»), §6 п.3 («captureScriptSha256 берётся из кандидата»), AC3, §11 (demo/docs/capture.mjs убран из списка правок, docs/images/screenshots.json добавлен как «fingerprint-only, через --identical»), §8 (обновлённая строка теста) |
Находка закрыта полностью: и убрана причина (правка файла-сторожа), и добавлена общая защита (поле обновляется из кандидата), а не только точечный костыль под эту задачу.
Унаследовано из r1
Без повторной проверки приняты (документ docs/reviews/SPEC-REVIEW-512-r1.md, SHA e606b808, дельта их не касается):
- Обоснование полного трека и провал критерия §5 «одна поверхность» (семь мест продукта + отдельный инструмент приёмки).
- §4 «Seam версии» — построчное совпадение перечня точек чтения
CARD_VERSIONвhouseplan-card.ts/houseplan-editor-runtime.tsс реальным кодом, включая исключения (hp_retry,console.info) и то, чтоrelease-contract.mjsпродолжает читать константы, а не seam. - §5 «Golden-харнес» — единственность использования
cardVersionизpackage.jsonвharness.mjs:1923, сохранениеintegrationVersion: '0.0.0-golden-backend'вmatrix.mjs, нормализация версии бандла вbaselines-index.jsonчерезforeignFingerprintNormalizer(#481). - §8 мутанты (
version-seam-ignores-override,docs-identical-accepts-any-frame) — конкретны и ловятся названными тестами по образцу существующегоtest/houseplan-source.mjs. - Отсутствие продуктового вопроса владельцу: задача
User-Visible: no, единственное DOM-видимое местоgs.about_version— внутриsupport-dialog, ключ и текст не меняются (AC6). - Низкоприоритетное наблюдение r1 (не находка):
--identicalдаёт 0 отличий только в среде, воспроизводящей растеризацию шрифтов закоммиченных кадров (тот же канон Linux/WSL, что и уdocs:capture) — сценарий §1.1 обещает «минуту локально» без этой оговорки; не блокирует, стоит упомянуть в будущей правкеdocs/DEVELOPMENT.md(уже в списке затронутых файлов).
Находки
Нет.
Что проверено и корректно
- Новый текст §6 технически исполним и не содержит непомеченных догадок: каждое фактическое утверждение (что
capture.mjsпишет прямо вdocs/images, что манифест уже содержитcaptureScriptSha256, что существующийverifyDocsCandidateуже понимает семантику этого поля) сверено с реальным кодом, а не принято на слово автора. - AC3 однозначен и доказуем тем же способом, что был заявлен в r1 (unit на компаратор + интеграционный прогон на реальных кадрах текущего дерева), формулировка соответствует новому механизму §6.
- §11 «Затронутые файлы» согласован с новым §6:
demo/docs/capture.mjsкорректно исключён (не правится),docs/images/screenshots.jsonкорректно добавлен (одноразовая fingerprint-only переприёмка в этом же PR, т.к. seam трогаетsrc/**, входящий вsourceFingerprint). - Новая формулировка §6 п.3 не противоречит §7 (переприёмка golden) и не нуждается в симметричном шаге по образцу §7 — выбранное решение («поле обновляется из кандидата всегда») по факту сильнее того, что просил r1 (не разовый костыль, а постоянное поведение инструмента).
- Дельта не расширяет и не сужает скоуп (§2/§3 не менялись), не вводит нового продуктового поведения —
User-Visible: noостаётся верным.
Чего не проверял
- Гейты кода (
npx tsc --noEmit,npm test,npm run build,node scripts/check-docs.mjs) не прогонял: на этапе ревью ТЗ они неприменимы и в r1, и сейчас — изменений вsrc/**/scripts/**по-прежнему нет,git diff origin/main...HEAD --statпоказывает только три файла вdocs/**. Менять нечего, прогонять не на чем. - Не проверял поведение нового механизма §6 при аварийном прерывании процесса (
SIGKILL) между записью кандидата и восстановлением бэкапа — это деталь реализации (try/finally на уровне кода), не тестируемая на уровне ТЗ/unit; не поднимаю как находку: не новый класс риска для проекта (общий для любого инструмента, временно перезаписывающего рабочее дерево с последующим восстановлением), ни один AC от этого не ломается, ничего не коммитится автоматически (docs-accept.mjsпрямо документирует «здесь не делается коммит»). docs/USER-GUIDE.ru.md— не проверял повторно, задача не меняет видимое поведение (наследовано из r1, дельта тем более не меняет).- Демо/смоки, golden:verify, инварианты модели, performance-профили — не запускал: нет ни одного изменения в
src/**(спек-стадия), поэтому эти гейты неприменимы, а не пропущены.
Материал раунда
- Ветка:
issue/512-golden-version-seam-docs-identical, коммит8fba3987df92— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
66d626bb0e5fbf4c669c8c0e796af0dbf0dffebagit log --all --format='%H %T' | grep 66d626bb0e5f - ТЗ
docs/specs/512-golden-version-seam-and-docs-identical-accept.md, блоб0ff211f5598812b6d66f31cb136727b161b9c7edgit log --all --find-object=0ff211f5598812b6d66f31cb136727b161b9c7ed -- docs/specs/512-golden-version-seam-and-docs-identical-accept.md - Вердикт конвейера:
green· High 0