From e5a62f7ffd05b3f22f376d5e9179a2e1159fa110 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2026 14:46:52 +0000 Subject: [PATCH] docs: review document for #512 Issue: #512 User-Visible: no --- docs/reviews/SPEC-REVIEW-512-r2.md | 81 ++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 docs/reviews/SPEC-REVIEW-512-r2.md diff --git a/docs/reviews/SPEC-REVIEW-512-r2.md b/docs/reviews/SPEC-REVIEW-512-r2.md new file mode 100644 index 00000000..dc834cc0 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-512-r2.md @@ -0,0 +1,81 @@ +# 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