Files
houseplan-card/docs/reviews/SPEC-REVIEW-512-r2.md
2026-09-09 18:52:44 +03:00

16 KiB
Raw Permalink Blame History

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 на HEAD 8fba3987 (ветка на этом коммите же и стоит); дельта — 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 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 66d626bb0e5fbf4c669c8c0e796af0dbf0dffeba
    git log --all --format='%H %T' | grep 66d626bb0e5f
    
  • ТЗ docs/specs/512-golden-version-seam-and-docs-identical-accept.md, блоб 0ff211f5598812b6d66f31cb136727b161b9c7ed
    git log --all --find-object=0ff211f5598812b6d66f31cb136727b161b9c7ed -- docs/specs/512-golden-version-seam-and-docs-identical-accept.md
    
  • Вердикт конвейера: green · High 0