Files
houseplan-card/docs/reviews/CODE-REVIEW-551-r1.md
T
claude[bot] d07aac3581
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 53s
Проверка (CI) / Классификация изменённых файлов (push) Successful in 28s
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 1m22s
Проверка (CI) / HACS: валидация репозитория (push) Failing after 21s
Проверка (CI) / Hassfest: манифест интеграции (push) Failing after 20s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Failing after 16m12s
Проверка (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) Successful in 4m7s
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Failing after 3m13s
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 17s
docs: review document for #551
Issue: #551
User-Visible: no
2026-09-13 10:23:06 +00:00

16 KiB
Raw Blame History

CODE-REVIEW-551-r1

Скоуп

Issue #551 (инфраструктурная, метка infra, ни одного файла класса A) — разнести единственный монолитный job review в .github/workflows/process.yml на три независимых job с собственными бюджетами: prepare (deterministic gates, 55 мин), model_review (работа модели, 45 мин), integrate (публикация и слияние, 55 мин). Материал ревью: коммит 31ef70cee617812b5bf32964269425f2aae78faa (единственный коммит на ветке issue/551-review-stage-budgets над origin/dev).

Изменённые файлы: .github/workflows/process.yml (класс B), scripts/mutation-gate.mjs (класс B), scripts/wait-verdict.mjs (класс B), test/review-doc-guard.test.mjs (класс B), AGENTS.md и PROCESS.md (класс C, документирование того же коммита). Класса A нет — трек инфраструктурный, ТЗ и ревью ТЗ не требуются (§1 PROCESS.md), обязательно только код-ревью.

Приёмка (из тела issue) — разбор по каждому пункту

  1. «Долгий gate не съедает выделенный model-time и не запускает модель на непригодном материале» — выполнено. Ожидание validate-gate.mjs (до 45 мин) осталось в prepare (шаг gate, .github/workflows/process.yml:561-580); model_review не содержит ни одного вызова validate-gate.mjs (проверено awk по границам job — совпадений 0) и получает материал строго после зелёного/пропущенного гейта (if: needs.prepare.outputs.proceed == 'true' && needs.prepare.outputs.reuse != 'true', process.yml:735).
  2. «Смена SHA/tree/spec/review round между стадиями обнаруживается; старый green не разрешает новый merge» — выполнено. prepare собирает prepared.json (schema, run_id, run_attempt, issue, stage, cycle, branch, material_sha/tree/specs/issue_body, validate_result/url, rebase_note, validated_note, spec_body_*) и публикует artifact с manifest.sha256 (process.yml:676-720). model_review проверяет sha256sum -c и побитовое совпадение всех полей через jq -e плюс отдельно git rev-parse HEAD / HEAD^{tree} против контракта (process.yml:774-799). integrate повторяет ту же проверку над результатом модели (process.yml:1173-1218). Оба места проверены мутационными свидетелями (см. таблицу ниже).
  3. «Timeout/cancel/failure каждой стадии оставляет понятное состояние; лимиты review cycles не расходуются на невыполненный review» — выполнено. Ранние отказы (конфликт с dev, красный Validate) переводят задачу в S6-in-progress уже внутри prepare, без вызова модели — цикл не расходуется (это поведение не менялось, только физически осталось в отдельном job). Провал model_review или prepare ловится в integrate шагом «Проверить исходы предыдущих стадий» (process.yml:1131-1152) и не трогает метку; финальный шаг «Позвать владельца, если стадия упала» называет конкретную стадию (process.yml:1541-1563).
  4. «Неполная или подменённая передача evidence между jobs отвергается» — выполнено. integrate сверяет точный набор имён файлов artifact'а (manifest.sha256 prepared.json review-document.md verdict.json, не более и не менее), контрольные суммы и полное совпадение контракта, а вердикт отдельно проверяется на структуру (verdict ∈ {green,yellow,red}, high/medium — числа, summary — строка).
  5. «Есть измерение durations по стадиям до/после на сопоставимом сценарии» — частично, и это ожидаемо, а не находка. У каждой из трёх стадий есть шаг duration (if: always()), значения сведены в таблицу ## Бюджеты стадий (#551) в GITHUB_STEP_SUMMARY. «До» — сопоставимый прогон #550 (run 34750166665: 590 с суммарно, 170 с ожидание Validate, 385 с модель, 3 с слияние), названный автором в хендоффе. «После» физически не может быть измерено в рамках этого ревью: process.yml вступает в силу только из ветки по умолчанию (§10.4 п.2), а эта ветка ещё не влилась — то есть сам факт объективного измерения «после» откладывается до первого реального прогона новой версии, что и заявлено автором явно («Активация... потребует отдельной прямой команды владельца»). Это не пробел реализации, а неизбежное следствие того, что задача меняет сам конвейер ревью.

Ограничение скоупа выдержано: модель, число циклов ревью (4), правила owner arbitration не менялись — job guard (где считается cycle) не тронут ни одной строкой (diff начинается ровно на переименовании review: → prepare:, до этого — идентичный текст). Общий periodic reconciler очереди не затронут.

Как проверялось

Диапазон материала: git log --oneline origin/dev..HEAD — один коммит (31ef70ce); git diff origin/dev...HEAD — 6 файлов,見 выше.

Гейт Команда Результат
Дешёвые гейты (typecheck/test/build+сверка бандла) — не гонял повторно: Validate зелёный на этом точном SHA, https://github.com/Matysh/houseplan-card/actions/runs/34751210053 (см. врезку задачи, подтверждено ссылкой)
check-docs.mjs — не требуется: diff не трогает src/** (git diff --stat подтверждает)
Browser-смоки node scripts/smoke-select.mjs --base origin/dev --head HEAD «Исполняемого frontend-диффа нет… Browser-smoke этим диффом не выбираются» — прогон смоков не требуется
golden:verify — не требуется: diff не меняет визуальный результат (не тот класс файлов)
Инварианты модели / geometry — не требуется: геометрия/layout/marker.space не затронуты
pytest tests_backend — не требуется: Python не тронут
Prettier (синтаксис изменённого YAML) npx prettier@3.9.6 --check .github/workflows/process.yml exit 0, «All matched files use Prettier code style!»
YAML парсится python3 -c "import yaml; yaml.safe_load(open('.github/workflows/process.yml'))" OK, 4 job: guard/prepare/model_review/integrate
Целевой юнит-тест целиком node --test test/review-doc-guard.test.mjs 57/57 pass
Реестр мутантов (дешёвая часть) node --test test/mutation-gate.test.mjs 51/51 pass
Применимость патчей трёх новых мутантов node scripts/mutation-gate.mjs --check --id=<id> (×3) ok на все три
Прогон трёх новых мутантов целиком (патч → тест) node scripts/mutation-gate.mjs --id=<id> (×3) «поймано 1 из 1» на каждом — см. таблицу ниже

Не прогонял: npx tsc --noEmit, npm test, npm run build + сверка трёх копий бандла — они покрыты зелёным Validate на этом же SHA (ссылка выше, правило §8/#343). no-new-any.mjs не проверял отдельно: diff не трогает src/** / .ts, гейт применяется к TS-исходникам, не к YAML/JS-скриптам, которые здесь менялись, и никакого any new-line эти файлы не вводят по построению языка (bash/YAML).

Защитные AC — таблица «чем краснеет»

AC Чем доказан Чем краснеет
Модель получает ровно материал prepare, а не подвижный dev test/review-doc-guard.test.mjs, тест #551: gates, модель и интеграция…, node --test --test-name-pattern="#551" Мутант review-model-checks-out-moving-dev (ref: dev вместо needs.prepare.outputs.material_sha) — прогнан, тест красит: поймано 1 из 1
Интеграция не принимает результат модели без сверки контрольной суммы тот же тест Мутант review-integration-skips-evidence-checksum (убирает sha256sum -c) — прогнан, поймано 1 из 1
Интеграция не доверяет failed/cancelled/skipped результату модели при reuse≠true тот же тест Мутант review-integration-trusts-failed-model (if false; then вместо проверки MODEL_RESULT) — прогнан, поймано 1 из 1
Три job сериализованы по issue (общая concurrency-группа) test/review-doc-guard.test.mjs, тест «конвейер: посторонняя метка не входит в concurrency» (обновлён под 3 job) не мутировал отдельно — чистая структурная проверка регэкспом по каждому из трёх job, без внешнего протекающего эффекта; отнесена к формату, не к защите (см. правило PROCESS.md §2.7 про AC без заявленной защиты)

Всем трём мутантам — правки самого .github/workflows/process.yml, guard — node --test --test-name-pattern="#551" test/review-doc-guard.test.mjs; для YAML-мутаций собирать бандл не требуется, поэтому мутация дешёвая и прогнана целиком, а не только на применимость.

Прочитано кодом (без мутации, «проверено чтением»)

  • Пути отказа prepare (конфликт с dev, красный Validate) переставляют метку внутри самого prepare, до model_review/integrate — прочитано, логика не изменилась относительно старого job, физически перенесена без правок условий (steps.branch, steps.material, steps.gate остались локальными outputs того же job).
  • integrate с if: always() && needs.guard.outputs.stage != '' корректно выполняется и при провале/пропуске prepare/model_review: шаг «Проверить исходы предыдущих стадий» сам классифицирует три исхода (провал prepare, ранний возврат без ревью, провал model_review) и не создаёт вердикт в необорванных случаях — прочитано построчно, логика веток проверена вручную для всех четырёх комбинаций (PREPARE_RESULT×PROCEED×REUSE×MODEL_RESULT).
  • Имена artifact'ов (review-prepared-…, review-result-…) детерминированы issue+run_id+run_attempt, совпадают между upload- и download-шагами дословно — download-artifact@v4 не читает чужие run, что исключает подмену между параллельными прогонами разных issue/раундов.
  • reuse=true ветка (#499) не регрессировала: model_review целиком пропускается по job-level if, integrate корректно трактует REUSE=true как разрешение не дожидаться model_review.result == success (короткое замыкание в бинарном &&), путь «зелёный без вызова модели» сохранён дословно в шаге decide.
  • Трейлеры коммита корректны: Issue: #551, User-Visible: no (правки не меняют пользовательское поведение продукта — только служебный CI); класса A нет, changelog не требуется.

Найдено

Находок нет — ни High, ни Medium, ни Low.

Не проверялось (и почему)

  • Реальные post-факто длительности новых трёх job в проде — физически недоступны до активации process.yml в ветке по умолчанию (см. AC5 выше); это ожидание конвейера, а не пропуск проверки.
  • actionlint — инструмент недоступен в окружении; синтаксис и структура job проверены Prettier + python3 yaml.safe_load + ручным разбором ссылок needs.*/steps.* по каждому job построчно.
  • Полный ночной набор (golden, backend, performance, все смоки) — не запускался: diff не задевает ни один из этих классов файлов, smoke-select.mjs подтверждает пустой отбор. Соразмерность по PROCESS.md §8.

Вердикт

Зелёный. Все пять пунктов приёмки выполнены (AC5 — с честной оговоркой о природе задачи, не находка). Три протестированных мутационных свидетеля ловят именно те регрессии, которые процесс требует доказать: подмену материала, пропуск контрольной суммы, доверие недоверенному результату модели. Скоуп не расширен, модель/лимит циклов/owner arbitration не тронуты.


Материал раунда

  • Ветка: issue/551-review-stage-budgets, коммит 31ef70cee617 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 56c26f1d8695228c45c53e439447fc4cfd7a955b
    git log --all --format='%H %T' | grep 56c26f1d8695
    
  • Тело issue: 824f489aab52c7d76ade6634cbdb819c9c36ff9261ae2982484d5199a4bc23d0
  • Вердикт конвейера: green · High 0