Files
houseplan-card/docs/reviews/CODE-REVIEW-551-r4.md
T
claude[bot] c83f19ae52
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 40s
Проверка (CI) / Классификация изменённых файлов (push) Successful in 32s
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Skipped
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Failing after 46s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Проверка (CI) / Геометрия: TS/Python parity исполнена (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 25s
docs: review document for #551
Issue: #551
User-Visible: no
2026-09-13 11:52:39 +00:00

189 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CODE-REVIEW-551-r4
## Скоуп
Issue #551 (инфраструктурная, метка `infra`) — разнесение конвейера ревью на
три job с независимыми бюджетами. Раунды r1–r3 уже признали конструкцию
выполненной (зелёный, без находок); в задачу этот вердикт до сих пор не
применён — оба последних захода (r2, r3) закончились зелёной моделью, но
**стадия публикации/интеграции падала** на живых прогонах уже после
слияния/повторной активации `process.yml`, и автор возвращал задачу в
`S6-in-progress` для фикса именно интеграции, а не по замечанию ревью.
Материал этого раунда — ровно `62e0eff21999eebfc9aecbfe99edb14d9ccf716f`.
Дельта отсчитывается от материала r2/r3 (`23f48829c26deec968af6645301900880899139e`,
дельта r2 от r3 — нулевая, см. ниже): один коммит `62e0eff2` поверх
`23f48829`, 3 файла, +26/−5 строк (`git diff --shortstat 23f48829..62e0eff2`):
- `.github/workflows/process.yml` — шаг «Проверить полноту и происхождение
результата» в job `integrate`: запись `structured_output` в
`$GITHUB_OUTPUT` заменена с `{ echo <<EOF_RESULT; cat verdict.json; echo
EOF_RESULT; }` на явный `printf` с гарантированным `\n` перед закрывающим
delimiter'ом;
- `scripts/mutation-gate.mjs` — новый мутант
`review-integration-delimiter-follows-json-without-newline`;
- `test/review-doc-guard.test.mjs` — два новых `assert` в существующем тесте
`#551: gates, модель и интеграция…`, закрепляющие фикс.
Класса A нет (тот же трек, что r1–r3), ТЗ/ревью ТЗ не требуются.
## Что чинит эта дельта (по логам, не со слов автора)
Живой дефект: run [34754400084](https://github.com/Matysh/houseplan-card/actions/runs/34754400084)
упал в `integrate` с `Matching delimiter not found` при чтении
`structured_output`. Причина в бывшем коде: `cat "$dir/verdict.json"` пишет
JSON как есть (если файл без завершающего LF — а `jq -e`/сериализаторы это не
гарантируют), а следующая по счёту команда `echo 'EOF_RESULT'` в блоке `{ ...
} >> file` — это отдельный `write()`, который ничего не подставляет между
предыдущим выводом и своим: без LF в конце JSON delimiter приклеивается к
последней строке значения, и GitHub Actions не находит начало delimiter'а на
отдельной строке.
Фикс (`process.yml:1212-1216` после дельты):
```
printf 'structured_output<<EOF_RESULT\n' >> "$GITHUB_OUTPUT"
cat "$dir/verdict.json" >> "$GITHUB_OUTPUT"
printf '\nEOF_RESULT\n' >> "$GITHUB_OUTPUT"
```
Явный `\n` перед `EOF_RESULT` гарантирует делимитер на отдельной строке
независимо от того, кончается ли `verdict.json` переводом строки. Если файл
уже кончается LF — на выходе будет один лишний пустой перенос перед
delimiter'ом; это не влияет на `jq`-парсинг ниже по конвейеру (`OUT: ...
structured_output`, `printf '%s' "$OUT" | jq -r`) — JSON с хвостовым
whitespace для `jq` эквивалентен исходному. Прочитано и проверено вручную.
Это прямое попадание в AC3 issue («timeout/cancel/**failure** каждой стадии
оставляет понятное состояние; лимиты review cycles не расходуются на
невыполненный review») — двумя живыми прогонами (r2→r3 и r3→r4) подтверждено,
что счётчик циклов действительно не тратился на несостоявшуюся интеграцию
(«блокирующих циклов израсходовано 0 из 4» в постановке этого захода), т.е.
защитное свойство AC3 сработало даже при поломке самой публикации.
## Проверено
- `git diff 23f48829..62e0eff2` — три файла, ровно эти три хунка, ничего
лишнего; job `guard`, `prepare`, `model_review`, структура контракта,
проверки sha256/полей — не тронуты ни строкой.
- Прочитано построчно: новый код в `process.yml:1212-1216` синтаксически и
семантически корректен (см. разбор выше), остальной шаг («Проверить
полноту и происхождение результата») не менялся вне этих трёх строк.
- Независимо (не полагаясь на заявление автора) прогнал:
- `node --test --test-name-pattern="#551" test/review-doc-guard.test.mjs`
→ 1/1 pass;
- `node scripts/mutation-gate.mjs --id=review-integration-delimiter-follows-json-without-newline`
→ «чистый прогон» + «заявленный тест покраснел на мутанте» → **поймано 1
из 1**. Мутант возвращает ровно старый баг (`printf 'EOF_RESULT\n'` без
ведущего `\n`), и новый `assert.match(resultOutput, /cat ...>>
"\$GITHUB_OUTPUT"[\s\S]*printf '\\nEOF_RESULT\\n' >>.../)` действительно
красится на этом мутанте — дисциплина «тест умеет падать» соблюдена для
прогнанного мной гейта.
- `node scripts/smoke-select.mjs --base 23f48829 --head 62e0eff2` →
«Исполняемого frontend-диффа нет… Browser-smoke этим диффом не
выбираются» — выбирать нечего, `src/**` не тронут.
- ID нового мутанта уникален в реестре (`grep -n
'review-integration-delimiter-follows-json-without-newline'
scripts/mutation-gate.mjs` — одно определение), guard-команда та же, что и
у соседних #551-мутантов, конфликтов нет.
- Трейлеры коммита `62e0eff2`: `Issue: #551`, `User-Visible: no` — верно,
правка меняет только служебный CI-механизм передачи данных между stages,
видимое поведение продукта не меняется; changelog не требуется.
Не перегонял `npx tsc --noEmit` / `npm test` / `npm run build` + сверку трёх
копий бандла — Validate зелёный на этом точном SHA:
https://github.com/Matysh/houseplan-card/actions/runs/34754874119 (упомянут
автором и подтверждён как ссылка на прогон именно этого коммита). Не
перегонял `check-docs.mjs`, `golden:verify`, backend pytest, инварианты
модели, performance-профили и полный набор browser-смоков — diff не касается
`src/**` (кроме тестового файла `test/review-doc-guard.test.mjs`, не
влияющего на отпечаток документации), Python, геометрии/`layout`/
`marker.space`/`open_spans`, визуального рендера или производительности;
`smoke-select.mjs` подтверждает пустой отбор.
## Закрытие раунда r3
r3 не содержал находок («Находок нет», зелёный, High 0 / Medium 0) — этому
раунду нечего закрывать по замечаниям. Причина существования r4 —
операционный сбой стадии публикации/интеграции ПОСЛЕ зелёного вердикта r3
(run [34754400084](https://github.com/Matysh/houseplan-card/actions/runs/34754400084)),
диагностированный автором как отдельный дефект границы стадий (delimiter
приклеен к JSON). Дельта этого раунда — прямой фикс именно этого сбоя (см.
разбор выше); других изменений кода в диапазоне r3→r4 нет.
## Унаследовано из r3 (без повторной проверки)
Документ r3 не был закоммичен в `docs/reviews/` (интеграция того захода
упала на дефекте, который правит именно материал этого раунда) — источник
вывода r3: комментарий ревью в issue #551 от 2026-09-13T11:29:02Z, материал
`23f48829c26deec968af6645301900880899139e` (дельта от r2/r3 к r4 — только
три файла выше, всё остальное дерево идентично). Наследуется:
- разнесение конвейера на три job (`prepare`/`model_review`/`integrate`) с
независимыми бюджетами 55/45/55 минут и их назначение по коду —
подтверждено в r1 (`docs/reviews/CODE-REVIEW-551-r1.md`, материал `31ef70ce`)
и повторно не тронуто ни в r2, ни в r3, ни в этой дельте;
- контракт передачи между стадиями (`prepared.json`/`manifest.sha256`,
полная сверка полей через `jq -e`, `git rev-parse HEAD`/`HEAD^{tree}`
против заявленного материала) — из r1, не задет;
- поведение при timeout/cancel/failure каждой стадии и правило «ранний отказ
не расходует цикл» — из r1, не задет; двумя живыми провалами интеграции
(r2→r3, r3→r4) это правило дополнительно эмпирически подтверждено вне
ревью;
- отказ на неполном/чужом evidence (точный набор имён файлов artifact'а,
sha256, отказ от failed/cancelled/skipped результата модели) — из r1, не
задет;
- фикс r2 «structured verdict сохраняется как объект, а не boolean предиката
`jq -e`» (`process.yml`, шаг «Запечатать результат модели», строки до
`- name: Передать результат интеграции`) и его мутационный свидетель
`review-model-seals-verdict-as-boolean` — заявлен в r2, независимо
перепроверен в r3 (`node --test --test-name-pattern="#551"` + прогон
мутанта, «поймано 1 из 1»); дельта r3→r4 этот шаг не трогает
(`git diff 23f48829..62e0eff2` не включает диапазон `seal`), поэтому
принимается без повторного прогона в этом раунде;
- три исходных мутационных свидетеля r1 (`review-model-checks-out-moving-dev`,
`review-integration-skips-evidence-checksum`,
`review-integration-trusts-failed-model`) — из r1, не задеты ни одной
дельтой r2/r3/r4.
## Найдено
Находок нет — ни High, ни Medium, ни Low.
## Не проверялось (и почему)
- Полный набор `tsc`/`test`/`build`+сверка бандла, `check-docs.mjs`,
`golden:verify`, backend pytest, инварианты модели, performance-профили,
полный набор browser-смоков — не требуются по соразмерности (PROCESS.md
§8) и по факту зелёного Validate на этом точном SHA (см. выше) плюс
пустому отбору `smoke-select.mjs`.
- Реальное поведение исправленного шага в проде за пределами уже
показанных run — не переисполнялось повторно мной (GitHub Actions
недоступен для интерактивного запуска из этого ревью); проверка —
чтением кода плюс воспроизведением локального юнит-теста и мутанта,
которые моделируют тот же самый путь (`process.yml`-текст плюс regex по
нему), а не реальным запуском workflow.
## Вердикт
Зелёный. Дельта r4 — точечный фикс одной строки протокола передачи данных
между стадиями (`GITHUB_OUTPUT` delimiter), выявленный вторым живым прогоном
после r3. Фикс корректен (разобран построчно), закреплён тестом и
мутационным свидетелем, который я прогнал независимо и подтвердил, что он
красит именно старое поведение. Всё содержательное из AC issue #551
(разнесение бюджетов, контракт, отказ на неполном evidence, поведение при
провале стадии) наследуется из r1 без изменений; правка r2 (boolean-подмена)
перепроверена в r3 и не задета этой дельтой. Находок нет, скоуп не расширен.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/551-review-stage-budgets`, коммит `62e0eff21999` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `8ea4718a882bb877b6288a234823406709f5659c`
```
git log --all --format='%H %T' | grep 8ea4718a882b
```
- Тело issue: `824f489aab52c7d76ade6634cbdb819c9c36ff9261ae2982484d5199a4bc23d0`
- Вердикт конвейера: `green` · High 0