Files
houseplan-card/docs/reviews/CODE-REVIEW-619-r1.md
T
2026-09-23 03:20:15 +00:00

12 KiB
Raw Blame History

CODE-REVIEW — issue #619, заход r1

Материал: origin/dev..HEAD, вершина 0d047450ce2dca2062c3fdd038092abc221173af (рабочая копия соответствует, git status чист). Трек — инфраструктурный (§1): ни один изменённый файл не относится к классу A (src/**, custom_components/houseplan/**/*.py, манифесты, i18n не тронуты) — .github/workflows/validate.yml, scripts/ci-proof.mjs, scripts/release-gate.mjs, docs/DEVELOPMENT.md, три тестовых файла — это классы B/C. Ускоренный вход S7-code-review без ТЗ применён правомерно.

Скоуп задачи

Дефект из аудита: на main после fast-forward с dev диапазон классификации merge-base(origin/dev, HEAD) == HEAD даёт пустой диф → тяжёлые job (smoke, golden, performance_smoke) пропускаются → ci-proof.mjs требует полный proof и получает skipped → release-gate падает на самом свежем (и единственно судимом) прогоне, хотя более старый прогон того же коммита был зелёным. Владелец принял два из трёх предложенных исправлений:

  1. main классифицируется как dev (полный набор, без фильтра путей);
  2. release-gate.mjs/ci-proof.mjs перебирают все прогоны точного SHA и принимают любой зелёный, а не только самый новый.

E2E retry/window (третий вариант) сознательно не тронут — это заявлено в хендоффе и не входит в обязательные AC.

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

  • Прочитан весь diff (git diff origin/dev...HEAD), построчно разобраны scripts/ci-proof.mjs (selectCiProofVerdict) и scripts/release-gate.mjs (classifyValidateProofs), а также правки validate.yml (шаги base и classify в job changes, шаг «Новый код не добавляет any» в job frontend).
  • Дешёвые гейты не перегонялись целиком: Validate на 0d047450 зелёный (https://github.com/Matysh/houseplan-card/actions/runs/35813271933), этого прогона достаточно для tsc/npm test/npm run build/сверки бандла.
  • Точечно прогнан изменённый тестовый материал: node --test test/release-gate.test.mjs test/ci-proof.test.mjs test/validate-workflow.test.mjs → 50/50 зелёных, включая три новых теста под #619.
  • Дисциплина «тест умеет падать»: временно откатил scripts/ci-proof.mjs, scripts/release-gate.mjs и .github/workflows/validate.yml к версиям origin/dev и перегнал те же три файла — упали ровно 4 теста (три новых под #619 плюс проверка текста docs/DEVELOPMENT.md), 46 остались зелёными. Рабочая копия восстановлена в исходное состояние сразу после проверки (git status --porcelain пуст).
  • node scripts/smoke-select.mjs --base origin/dev --head HEAD → «Исполняемого frontend-диффа нет … Browser-smoke этим диффом не выбираются». Смоки не запускал — инструмент подтверждает, что выбирать нечего (diff не касается src/**).
  • node scripts/check-docs.mjs не запускал: diff не трогает src/**, условие «любая правка фронтенда» не выполняется.
  • Инварианты модели (npm run invariants) не запускал: diff не касается геометрии, толщины стен, layout, marker.space, open_spans.
  • golden:verify, pytest tests_backend — не запускал: рендер и backend не тронуты.
  • Трейлеры коммита: Issue: #619, User-Visible: no — присутствуют и верны (изменение процесса CI, видимого поведения продукта нет); changelog-файлы в diff отсутствуют, что и требуется при User-Visible: no.

Разбор по AC

AC1 (release-gate.test.mjs: новейший прогон failed из-за пропущенного smoke, старший — green → вердикт green). Доказано тестом #619: any complete green proof on the exact SHA survives a newer failed duplicate (release-gate.test.mjs:114) и вспомогательным selectCiProofVerdict([red, newerFull]).status === 'green' (ci-proof.test.mjs:136). Прочитан механизм: classifyValidateProofs теперь идёт по прогонам от новых к старым и останавливается (break) только когда находит status === 'green' (было: останавливался на первом не-cancelled/не-stale, то есть на самом решающем и необязательно зелёном). selectCiProofVerdict фильтрует cancelled/stale, среди оставшихся ищет green; если находит — отдаёт его вне зависимости от позиции в списке; если нет — откатывается к самому новому решающему (то же fail-closed поведение, что раньше). Корректность подтверждена и падением тестов на отклоненном коде (см. выше).

AC2 (на main тяжёлые job не пропускаются из-за пустого диффа). Доказано тестом #619: Validate на main раскрывает полный набор так же, как на dev (validate-workflow.test.mjs:181): проверяет, что паттерн [ "$REF" = "refs/heads/dev" ] || [ "$REF" = "refs/heads/main" ] встречается дважды в job changes (шаги base и classify) и что в job frontend шаг «Новый код не добавляет any» использует ту же пару условий для выбора PROVEN_BASE. Прочитан код: шаг classify при REF = dev или main вызывает classify-changes.mjs --all (все флаги true, включая триггер для smoke/golden/performance_smoke через needs: [changes, frontend, reuse]) — пропуска по пустому диффу для main больше нет. Шаг base считает range_base тем же путём для main, что и раньше только для dev, и это тот самый доказанный предок, который берёт «новый код не добавляет any», а не HEAD..HEAD.

AC3 (первый стабильный релиз после слияния проходит с первой попытки — зафиксировать в issue). Не может быть доказан до реального релиза после merge; автор явно пометил его как «проверяется постфактум» и не выдал результат за готовый. Это корректно для этого раунда: AC1/AC2 — код и тесты, AC3 — наблюдение, которое существует только после слияния и следующего релиза. Не блокирует ревью, но зафиксировано как открытый пункт, подлежащий проверке позже (в issue или в S8 после факта).

Что проверено и корректно

  • Классы файлов — только B/C, ускоренный infra-трек применён по правилам.
  • Комментарии в коде и docs/DEVELOPMENT.md синхронно объясняют новое поведение (#619 проставлен и там и там), формулировка «Any complete green full proof … is sufficient» дословно совпадает с regex-проверкой в release-gate.test.mjs:132.
  • Безопасность нового допущения «принять любой зелёный на SHA»: sha/tree candidate сверяются внутри evaluateCiProof для каждого прогона отдельно, так что «зелёный, но для другого дерева» не может проскочить — Git SHA однозначно определяет дерево, а evaluateCiProof их сверяет явно (candidate: { sha, tree }). Риск «стащить чужой зелёный proof» не подтвердился.
  • Изменение эффекта на review/merge-потребителей: docs/DEVELOPMENT.md прямо говорит, что они используют ту же missing/pending/… state machine — новая семантика «любой зелёный на SHA побеждает» действует одинаково для всех трёх потребителей, это осознанное и документированное расширение, не побочный эффект.
  • Стоимость: полный Validate на main дороже прежнего пустого прогона, но main пушится только при релизном fast-forward — редко, риск принят и назван в хендоффе.

Чего не проверял и почему

  • Полный npx tsc --noEmit / npm test / npm run build — зелёный прогон Validate на этом самом SHA уже это доказал.
  • check-docs.mjs, инварианты модели, golden:verify, backend pytest, performance-профили, браузерные смоки — diff их не касается (src/**, геометрия, backend, рендер не тронуты); выбор подтверждён smoke-select.mjs.
  • AC3 — по определению не проверяется в этом раунде (см. выше).

Итог

Оба обязательных AC (AC1, AC2) доказаны тестами, которые умеют падать (это проверено вручную откатом кода). Изменение сфокусировано на дефекте из аудита, не расширяет скоуп, задокументировано, трейлеры верны. Блокирующих находок нет.

Вердикт: зелёный


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

  • Ветка: issue/619-release-gate-main-proof, коммит 0d047450ce2d — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: d4073f7337335628d277195b3e561393ca63d582
    git log --all --format='%H %T' | grep d4073f733733
    
  • Тело issue: b87774de2903ecd45752cc104e9f482c95e51fd3920252ae985c38166ea8d74a
  • Вердикт конвейера: green · High 0