18 KiB
CODE-REVIEW-514-r2
- Issue: #514 — E2E на реальном Home Assistant как гейт стабильного релиза
- Этап: code (PROCESS.md §2.7)
- Заход: r2 · блокирующих циклов израсходовано 0 из 4
- Материал: ровно
539756cc000cd39c6e9cf8e1fda9ffe0824d46fd(рабочая копия на нём);git log --oneline origin/dev..HEAD— 6 коммитов;git diff origin/dev...HEAD— 13 файлов, +713/−3.
Почему разбор полный, а не по дельте
r1 код-ревью (комментарий issue, 2026-09-09T19:31:10Z) вынес зелёный вердикт на материале 395b0e38 без единой находки. Но пока ревью шло, dev продвинулся на коммит, ветка была ребейзнута на dev@72764cff, и владелец/страж прямо зафиксировал в issue (19:31:21–19:31:24):
Материал ревью
395b0e38и кандидат539756ccдают разные patch-id: соседние правки вdevизменили содержимое патча. … новый заход ревью читает актуальный код.
Это ровно случай §7.2 «ребейз на ушедший вперёд dev — после ребейза это другой код», прямо исключённый из сокращённого делта-разбора инструкцией к этому раунду. Объект 395b0e38 локально недоступен (ветка была пересобрана), сравнить byte-in-byte с r1 нечем — поэтому я прочитал и перепроверил весь diff origin/dev...HEAD заново, не полагаясь на выводы r1.
Скоуп изменений
scripts/e2e-gate.mjs(новый) — dispatche2e.ymlвhouseplan-e2eна теге, опознание своего прогона, ожидание, CLI..github/workflows/release.yml, jobgate— шаг «Require green E2E…» после Full Performance, только для!prerelease.scripts/mutation-gate.mjs— три новых мутанта.test/e2e-gate.test.mjs(12 тестов),test/release-workflow.test.mjs(2 теста).- Документация:
PROCESS.md,AGENTS.md,docs/DEVELOPMENT.md,docs/TESTING.md,docs/specs/README.md. docs/specs/514-e2e-stable-release-gate.md,docs/reviews/{SPEC,CODE}-REVIEW-514-r1.md— артефакты процесса, не код.
Изменений в src/**, геометрии, Python-бэкенде нет.
Как проверялось
Дешёвые гейты. Validate на точном материале 539756cc зелёный (подтверждено инструкцией; перепроверено напрямую через gh api repos/Matysh/houseplan-card/actions/runs/34395626974 — conclusion: success, head_sha: 539756cc000cd39c6e9cf8e1fda9ffe0824d46fd, событие workflow_dispatch). tsc/test/build не перегонялись целиком по правилу «дешёвые гейты на этом SHA уже подтверждены».
Прогнано лично на этом SHA (код изменился относительно r1 из-за ребейза, поэтому целевые прогоны — не целиком, а именно то, что относится к диффу):
node --test test/e2e-gate.test.mjs test/release-workflow.test.mjs→# pass 14 # fail 0.node scripts/mutation-gate.mjs --id=release-ships-on-red-e2e→поймано 1 из 1.node scripts/mutation-gate.mjs --id=release-upgrades-stable-onto-itself→поймано 1 из 1.node scripts/mutation-gate.mjs --id=release-trusts-foreign-e2e-run→поймано 1 из 1.node scripts/process-gate.mjs→ «гейт пройден, предупреждений 0» (трейлерыIssue:/User-Visible:на всех 6 коммитах — проверены и вручную, см. ниже).node scripts/smoke-select.mjs --base origin/dev --head HEAD→ «Исполняемого frontend-диффа нет… Browser-smoke этим диффом не выбираются — выбирать нечего».src/**/*.tsне тронут, поэтому не прогонял ни одинdemo/smoke_*.mjs.
Не прогонялось и почему: golden:verify (diff не меняет рендер), check-docs.mjs (diff не трогает src/**), model-invariants.mjs (геометрия/layout/толщины не затронуты), pytest tests_backend (Python не тронут), performance-профили (не названы в AC, перф-путь не тронут). Все — по правилу «объём гейтов соразмерен задаче».
Живые данные, не принятые на слово. Хендофф и ТЗ ссылаются на два реальных прогона houseplan-e2e и на состояние компаньон-репозитория — сверил напрямую через gh api, а не по цитате:
gh api repos/Matysh/houseplan-e2e/actions/runs/34393136097+/jobs—success, job'ыjourneys · HP v1.73.0 · HA stable,first-run · HP v1.73.0 · HA stable,upgrade · HP v1.72.0 · HA stable— совпадает с заявленным «зелёный, upgrade от предыдущего stable».gh api repos/Matysh/houseplan-e2e/actions/runs/34392391382+/jobs—failure, jobupgrade · HP stable · HA stableкрасный — совпадает с заявленным дефектом «upgrade_from=stable апгрейдит тег сам на себя», который и породил коммитa5c9a30e.gh api repos/Matysh/houseplan-e2e/contents/.github/workflows/e2e.yml— jobplanсобирает матрицу,journeys-devвключается только приschedule === true(см.if (schedule) include.push(...)); jobe2eназывается"${{ matrix.suite }} · HP ${{ matrix.ref }} · HA ${{ matrix.ha }}"— контракт имени совпадает сTAG_SUITES/isOurRunвscripts/e2e-gate.mjs.gh api repos/Matysh/houseplan-e2e/contents/README.md— раздел «CI» описывает роль гейта, контракт имени job,journeys-devтолько по расписанию.gh release list --repo Matysh/houseplan-card— новые релизы действительно первыми в выдаче; на этом порядке построенpreviousStable().
Проверка AC (docs/specs/514-e2e-stable-release-gate.md §10)
- AC1 (гейт держит ассеты до
success, красный/missing/cancelled/error — с причиной и ссылкой): доказаноtest/e2e-gate.test.mjs(5 тестов: green/red/missing/timeout/cancelled/token-error) + чтениемe2eGate— логика однозначно возвращаетgreenтолько приrun.conclusion === 'success'. Мутантrelease-ships-on-red-e2e(читатьcompletedкакgreen) ловится. - AC2 (опознание своего прогона по
HP <tag>, чужой dispatch игнорируется): доказано тестамиisOurRun/classifyRun+ живыми прогонами (v1.72.0 не подхватил jobupgrade · HP v1.72.0из прогона v1.73.0 — тест это воспроизводит буквально). Мутантrelease-trusts-foreign-e2e-runловится. - AC3 (пре-релизы шаг не выполняют):
test/release-workflow.test.mjsпроверяетif: ${{ !github.event.release.prerelease }}текстом workflow; прочитано вrelease.yml:59— условие идентично соседнему шагу Full Performance. - AC4 (
journeys-devне бежит на dispatch): проверено чтениемe2e.ymlвhouseplan-e2e(jobplan,if (schedule) include.push(...)— наworkflow_dispatchscheduleложно) и живым прогоном34393136097— ровно 3 job, безjourneys-dev. - AC5 (три мутанта пойманы): лично прогнал все три —
поймано 1 из 1каждый. - AC6 (документы §8,
User-Visible: no, UX/i18n/модель/перф не затронуты): все 6 коммитов несутIssue: #514иUser-Visible: no(провереноgit show -s --format=%Bна каждом); CHANGELOG не тронут — корректно дляUser-Visible: no;PROCESS.md/AGENTS.md/docs/DEVELOPMENT.md/docs/TESTING.mdобновлены точными формулировками из §8 ТЗ (сверено построчно с diff).
Все 6 AC доказаны тестом/мутантом/чтением с явной ссылкой на команду и результат — не «verified» без опоры.
Находки
Low, приняты без правки:
- Плановая job упадёт раньше первой именованной суб-job → гейт вернёт
missing, а не более точную причину. Если вhouseplan-e2ejobplanсама упадёт до появления job· HP … ·,classifyRunвернётunknownнавсегда для этого прогона, и послеappearMsгейт сообщитmissing, хотя реальная причина — падение планирования. Инвариант «не публиковать на красном» не нарушается (missingтоже блокирует ассет), только текст диагностики менее точный. Тот же вывод был у r1 на прежнем материале — дельта (ребейз) этот код не затронула, перепроверено чтением заново на539756cc. - README
houseplan-e2eописываетupgrade_from=stableкак параметр, с которым гейт всегда дёргает workflow, хотя послеa5c9a30eгейт вычисляетpreviousStable()и передаёт тег предыдущего stable (буквальныйstable— только фолбэк для самого первого релиза). Файл лежит вне материала этого ревью (другой репозиторий, процесс там не ведётся, PROCESS.md §7 к нему не применяется), исправить в этой ветке технически нельзя. Не блокирует: несоответствие только в описании CI-контракта для будущего читателя README, само поведение гейта верно и покрыто тестом#514: the upgrade suite starts from the previous stable, never from the tag under test. Снимается с записью, не заводится отдельным issue — находка не о продуктовом дефекте houseplan-card, а о строке документации соседнего репозитория, который в этот процесс не входит. - Окно
gh run list --limit 10— если в течение 45-минутного ожидания вhouseplan-e2eпроизойдёт ≥10 другихworkflow_dispatchпрогоновe2e.yml, уже отслеживаемыйtracked-прогон может выпасть из выдачи, и гейт начнёт заново искать кандидата. Тот же паттерн, что вvalidate-gate.mjs(образец, #510), реалистичность события — только ручной параллельный dispatch owner'ом того же workflow много раз подряд. Не блокирует.
High/Medium в скоупе или вне скоупа — не найдено.
Что проверено и корректно
e2eGate— чистая функция над инъектируемымиops, логика green/red/missing/error/cancelled прочитана целиком и совпадает с тестами.previousStable— фильтрует draft/prerelease/сам тег, берёт первый оставшийся (новые релизы первыми вgh release list, подтверждено прямым вызовом); фолбэк'stable'для первого релиза.isOurRun/classifyRun— корректно отличаютjourneys/first-run(несут искомый тег) отupgrade(несёт тег предыдущего stable) и корректно не путаютv1.74.0сv1.74.0-beta.1(тест на этот кейс проходит).release.yml— шаг вставлен строго после Full Performance, строго для!prerelease, токен с фолбэком, использует существующий секретHP_PROCESS_TOKEN(уже используется вprocess.yml, не новая сущность).scripts/mutation-gate.mjs— три мутанта, гардnode --test test/e2e-gate.test.mjs, все три поймали ровно ту регрессию, которую называют.- Документация (
PROCESS.md,AGENTS.md,docs/DEVELOPMENT.md,docs/TESTING.md,docs/specs/README.md) обновлена консистентно с реализацией и §8 ТЗ. houseplan-e2e/.github/workflows/e2e.ymlи README — состояние на текущий момент подтверждено напрямую черезgh api, а не по цитате из хендоффа.
Чего не проверял и почему
tsc --noEmit,npm test(полный набор),npm run buildс попиксельной сверкой бандла — не перегонял: зелёный Validate на точном539756ccуже это покрывает (ссылка выше, сверена поhead_sha).check-docs.mjs,model-invariants.mjs,golden:verify, браузерные смоки,pytest tests_backend— не запускал: diff не трогаетsrc/**, геометрию/толщины/layout, Python или рендер;smoke-select.mjsподтвердил «выбирать нечего».- Реальный dispatch
release.ymlпо событиюrelease: publishedи фактическую работуHP_PROCESS_TOKEN/E2E_DISPATCH_TOKENв проде — недоступно ревьюеру (нет прав на секреты владельца, событиеreleaseнельзя сэмулировать без публикации настоящего релиза). ТЗ честно называет это «первая живая проверка — следующий stable»; путь отказа (403 → красный шаг с понятным текстом) покрыт тестом. - Права
HP_PROCESS_TOKENнаactions: writeвhouseplan-e2e— не проверялись (нет доступа к секретам), это открытый риск, названный в самом ТЗ (§10.1) и в S2 владельцем как решаемый по факту первого stable.
Закрытие раунда r1
r1 не содержал находок — вердикт был зелёным с 0 High/Medium и тремя Low, снятыми без правки. Раунд был признан недействительным не из-за находки, а из-за смены материала: ребейз на dev@72764cff изменил patch-id (395b0e38 → 539756cc), и страж вернул задачу в S7-code-review автоматически (комментарии issue, 19:31:21–19:31:24). Таблица «находка → чем закрыта» не применима — закрывать нечего, все три Low r1 воспроизведены и подтверждены заново на актуальном коде (см. «Находки» выше, где Low №1 — тот же вывод, что и в r1, перепроверенный чтением на новом SHA).
Унаследовано из r1
Ничего. Ребейз на ушедший вперёд dev — явно перечисленный в инструкции случай, где сокращённый делта-разбор не допускается («это другой код, §7.2»). Весь diff origin/dev...HEAD на 539756cc прочитан и перепроверен заново в этом раунде: тесты и мутанты прогнаны лично, оба живых прогона houseplan-e2e и текущее состояние e2e.yml/README.md в этом репозитории сверены напрямую через gh api, а не приняты из документа r1.
Материал раунда
- Ветка:
issue/514-e2e-stable-release-gate, коммит539756cc000c— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
6b794799badda82c9266cac01b0495b220c969d6git log --all --format='%H %T' | grep 6b794799badd - ТЗ
docs/specs/514-e2e-stable-release-gate.md, блоб30795778fee11c8df610440dd86b745112e120c4git log --all --find-object=30795778fee11c8df610440dd86b745112e120c4 -- docs/specs/514-e2e-stable-release-gate.md - Вердикт конвейера:
green· High 0