16 KiB
CODE-REVIEW-703-r2
Issue: #703 · «stable promotion ложно судит уже проверенную бета-линию»
Трек: show (владелец задал критерии в теле issue)
Материал: 241f8d48f500f09c21ee91f55e34ce552ce2b5b4, ветка
issue/703-promotion-range-base поверх origin/dev
Заход: r2 · блокирующих циклов израсходовано 1 из 2
Скоуп
Инфраструктурная задача (не класс A). Три AC в теле issue не изменились с r1
(см. CODE-REVIEW-703-r1): AC1 (граница по зелёному прогону на dev/тегу),
AC2 (новое нарушение после границы по-прежнему красное), AC3 (контракт
validate.yml + PROCESS.md §10.2). Этот раунд — точечный фикс по единственной
находке High из r1, плюс правка Low. User-Visible: no в трейлере — верно,
поведения продукта дифф не меняет.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
High — releaseTaggedShas() в реальном Validate всегда пуст: ни один checkout не ставит fetch-tags: true, а actions/checkout@… (v7) при дефолте передаёт git fetch --no-tags безусловно |
Добавлен fetch-tags: true к обоим checkout, читающим диапазон |
.github/workflows/validate.yml:74 (job preflight) и :347 (job changes); test/validate-workflow.test.mjs:372-379 теперь требует точную строку with: { fetch-depth: 0, filter: 'blob:none', fetch-tags: true } для обеих job |
Low — тест AC2 «пуш в dev» вызывал rangeBase с ИДЕНТИЧНЫМИ аргументами, что и «hotfix на main»: второй ассерт был дубликатом первого |
onDev теперь получает свой вход — только прогоны dev (без main) и более старый fallback (fx.beta3 вместо fx.candidate), плюс явный assert.equal(onDev.base, fx.candidate), которого раньше не было вовсе |
test/promotion-range.test.mjs:131-134 |
Проверено исполнением, не только чтением диффа — см. «Как проверялось».
Унаследовано из r1
Без повторной проверки принято (материал не менялся в дельте r1→r2 — см.
git diff 4496c232a0a5..HEAD --stat: тронуты только validate.yml, два
тестовых файла и добавленный docs/reviews/CODE-REVIEW-703-r1.md):
- AC1, первая часть (SHA, зелёный на
dev, даёт пустой диапазон) — доказано исполнением и мутацией в r1 (CODE-REVIEW-703-r1.md, раздел «Что проверено и корректно»). КодpickRangeBase/headGreen-ветка не менялся в этой дельте. - AC2, оба сценария логики (упавший HEAD не граница; повтор упавшего SHA
судится тем же диапазоном) — доказано в r1 мутацией и прямым вызовом
pickRangeBase; код не менялся. - AC3, контракт workflow (job
changes/preflight читают обе ветки, preflight не падает наevent.beforeдляmain, PROCESS.md §10.2) — подтверждено чтением в r1; в дельте r1→r2 эти строки не тронуты (см.git diffниже — правка ограничена добавлениемfetch-tags: trueи двумя строками комментария рядом). releaseTaggedShasкорректно разбирает annotated и lightweight теги — логика функции не менялась, проверено в r1 отдельным прогономgit for-each-refна реальном репозитории.- PROCESS.md §10.2 соответствует коду — файл не менялся в этой дельте.
Материал r1: 4496c232a0a53186b5db426b584b99a431f7cfbb, вердикт документа —
красный, High 1.
Как проверялось
Прочитаны заново: тело issue #703, оба прежних комментария и вердикт r1 («Взял», «Сделано», вердикт конвейера), новый комментарий автора «Сделано (r2, по ревью r1)». PROCESS.md §2.10 (объём по дельте), §8, §10.2 повторно не перечитывались — не изменились с r1.
Дельта разобрана целиком: git diff 4496c232a0a5..HEAD --stat — 4 файла
(без учёта опубликованного docs/reviews/CODE-REVIEW-703-r1.md, который сам
и есть материал предыдущего раунда): .github/workflows/validate.yml (+10/-6
строк — комментарии и fetch-tags: true в двух with:), два тестовых файла.
scripts/classify-base.mjs и PROCESS.md в дельте НЕ участвуют — логика
выбора базы не менялась, только источник данных (checkout).
Прогнано:
node --test test/promotion-range.test.mjs→pass 6, fail 0.node --test test/validate-workflow.test.mjs→pass 28, fail 0.npm run gate:small→ зелёный (99 с; сборка+typecheck, no-new-any, no-new-private-writes, smoke-select, юниты, bundle-policy, lint:unused). Дублирует то, что Validate на этом SHA уже подтвердил зелёным (https://github.com/Matysh/houseplan-card/actions/runs/36756938908), прогнал сам как дополнительную проверку дельты, не потому что ссылка недостаточна.node scripts/smoke-select.mjs— не перезапускал отдельно,gate:smallвключает его в себя: «исполняемого frontend-диффа нет».
Мутация на закрытие High (главная проверка раунда): временно убрал
fetch-tags: true из обоих with: { … } в validate.yml
(sed на точную строку), перезапустил test/validate-workflow.test.mjs →
pass 27, fail 1 — новый тест AC3 красится ровно на этой мутации. Откатил
файл из бэкапа, git status --short после отката пуст — эксперимент следов
не оставил, тест умеет падать именно там, где закрывает находку.
Проверил также область поражения находки: grep по validate.yml показал,
что classify-base.mjs --mode=range (путь, которому нужны теги) вызывается
только в двух местах — preflight (:214) и changes (:392, :398) — и оба их
checkout теперь получили fetch-tags: true; остальные ~12 checkout в файле
(job reuse, golden, smoke и т.д.) этот путь не используют, править их не
требовалось.
Находки
Нет. High из r1 закрыт и проверен мутацией на этом же материале; Low из r1 закрыт с более сильным ассертом, чем требовалось для «не блокирует».
Замечание не на уровне находки: новый вход onDev ({ dev } без main,
fallback=fx.beta3) отличается от onMain количеством переданных файлов
прогонов, но реальный validate.yml всегда запрашивает ОБЕ ветки на обоих
путях (preflight: for branch in dev main; changes: свой + other) —
то есть тест «пуш в dev» и «пуш в main» в проде получают одинаково полные
данные, разница только в том, чей SHA судится. Тест это не воспроизводит
буквально, но он и не должен: сам сценарий «резолюция base работает и когда
данные по другой ветке недоступны» (реалистичный случай — gh api для
other вернул {} при сетевой ошибке, что и обрабатывает || echo '{}' в
workflow) — законная, пусть и более узкая, проверка. Это была находка Low
уровня «бухгалтерия» ещё в r1 и решение снять её оставлено на усмотрение
автора; новый вариант строже прежнего (явный ассерт на base, не только на
privateWrites), поэтому закрытие принимается.
Что проверено и корректно
- Обе строки
with:вvalidate.yml(:74, :347) идентичны и содержатfetch-tags: trueрядом с уже существующимfetch-depth: 0— теги и полная история качаются одним и тем же шагом, лишнего сетевого трафика (blob) не добавляется, что и заявлено в комментарии кода. test/validate-workflow.test.mjs:372-379проверяет точную строкуwith:для ОБЕИХ job (preflight,changes) — правка одной из двух молча не пройдёт мимо теста.- Мутация (см. «Как проверялось») подтверждает: тест краснеет именно на
отсутствии
fetch-tags: true, а не на чём-то соседнем. - Скоуп дельты узкий и не расширяет зону риска:
scripts/classify-base.mjsне тронут — сама логика выбора базы (то, что уже проверено мутацией и прогоном в r1) не менялась, изменился только вход (реальный источник тегов). - Трейлеры коммита
241f8d48—Issue: #703,User-Visible: no— верны, дифф ограничен CI-инфраструктурой. - «Одно число — один источник» (§8) — неприменимо, дифф не вводит величин, видимых пользователю.
Чего не проверял
- Полный
npx tsc --noEmit/npm run buildсо сверкой трёх копий бандла отдельно не гонял:gate:smallвключаетnpm run build(typecheck внутри) иbundle-policy --verify, а Validate на этом SHA зелёный (run 36756938908) — оба основания есть, дублировать полный набор незачем. - Браузерные смоки —
gate:smallуже прогналsmoke-select: «исполняемого frontend-диффа нет»; Chromium не поднимал, тело issue не называет смок. npm run golden:verify— меткиci:goldenнет,demo/golden/**не тронут.python -m pytest tests_backend—custom_components/**/*.pyне тронут.npm run invariants— дифф не касается геометрии модели плана.- Performance-профили — не названы в AC, дифф не трогает рендер.
- Мутанты по диффу — трек
show, не запрашивались (см. заголовок задачи); дифф не содержит продуктового кода. - Живой промоушен
dev → mainна реальном GitHub Actions — по-прежнему не воспроизведён (это и есть единственный способ доказать находку r1 «в бою», а не только чтениемdist/index.jsэкшена); ближайшая проверка — следующий настоящий stable-promotion. Не находка: то же ограничение уже зафиксировано в r1 и автором.
Таблица «AC · чем доказан · чем краснеет» (защитные AC)
| AC | чем доказан | чем краснеет |
|---|---|---|
| AC1 (пустой диапазон на доказанном SHA) | test/promotion-range.test.mjs, тест «#703 AC1: промоушен …» |
мутация на HEAD-проверку (доказано в r1, код не менялся) |
| AC1 (тег как граница без прогонов dev) | тот же файл, тест «#703 AC1: без прогонов dev …» + для продакшн-пути — test/validate-workflow.test.mjs assertion на fetch-tags: true |
мутация sed этого раунда: убрал fetch-tags: true → validate-workflow.test.mjs падает (pass 27, fail 1); юнит-тест на фикстуре краснеет мутацией из r1 |
| AC2 (новое нарушение красное) | тесты «#703 AC2: …» ×2, второй сценарий теперь с собственным входом | отрицательный случай внутри теста (доказано в r1, код не менялся) |
| AC3 (workflow-контракт) | test/validate-workflow.test.mjs, тест «#703 AC3» |
строковые ассерты на точный with: — точечно, но соразмерно контракту YAML |
Вердикт
Зелёный. High из r1 (release-tag граница неработоспособна в реальном Validate
из-за отсутствия fetch-tags: true) закрыт точечно — добавлен ровно в те два
checkout, что используют classify-base.mjs --mode=range, — и проверен
мутацией на этом материале: тест краснеет при откате правки. Low из r1
(дублирующий ассерт в тесте AC2) закрыт с более сильной проверкой, чем
требовалось. Дельта r1→r2 не расширяет риск — логика выбора базы
(scripts/classify-base.mjs) не менялась, менялся только вход. Все три AC
задачи выполнены и доказаны: AC1/AC2 логикой и мутациями (унаследовано из r1
там, где код не менялся, дополнительно подтверждено в r2 там, где менялся),
AC3 контрактом workflow. gate:small зелёный, Validate на этом SHA зелёный.
Материал раунда
- Ветка:
issue/703-promotion-range-base, коммит241f8d48f500— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
f0fa1b457febbf21e510dd2b88305134928cb23agit log --all --format='%H %T' | grep f0fa1b457feb - Тело issue:
d3b8c4a60a2666a458283fb684284710ad5982fe267e8a362f852f3dedaa5761 - Вердикт конвейера:
green· High 0