mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 05:08:53 +00:00
Проверка (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
Issue: #551 User-Visible: no
189 lines
15 KiB
Markdown
189 lines
15 KiB
Markdown
# 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
|