21 KiB
CODE-REVIEW #698 r1
Материал: origin/dev..HEAD, ровно один коммит ea2c000af5e4bd47140c2c255fca16ad2a136f6c
поверх dev@9e4bfb43 (рабочая копия уже на нём). Диапазон: .gitattributes,
PROCESS.md, scripts/merge-candidate.mjs, scripts/mutation-registry.mjs,
scripts/rebase-generated.mjs, test/rebase-generated.test.mjs. Файлов класса
A нет — инфраструктурный трек (§1), вход сразу на S7-code-review, метка
track:show на issue.
Скоуп
Issue #698 называет три источника из 14/48 возвратов #600–#691 по общим
файлам, где правки двух задач не противоречат друг другу: (1) записи
## Unreleased в обоих CHANGELOG, (2) docs/images/screenshots.json, (3)
scripts/bundle-budget.mjs / scripts/monolith-baseline.json. Формального ТЗ
нет (инфраструктура, PROCESS.md §1) — проверял по тексту раздела «Предложение»
issue и по факту автоматизации, а не по чужому изложению.
Реализовано:
.gitattributes:merge=unionдляdocs/CHANGELOG.mdиdocs/CHANGELOG.ru.md— строки## Unreleasedобъединяются на ребейзе, слиянии иgit merge-treeбез остановки.scripts/rebase-generated.mjs:UPSTREAM_WINS = ['scripts/monolith-baseline.json']— конфликт именно на этом пути решается в пользуdev(checkout --ours), без abort; любой другой путь по-прежнему аборт с полным списком.scripts/merge-candidate.mjs:PATCH_ID_EXCLUDESрасширяет прежнее исключениеdocs/reviewsна оба CHANGELOG иmonolith-baseline.json— эти файлы больше не входят в patch-id кандидата, соседняя строка не отправляет зелёную задачу обратно на ревью.docs/images/screenshots.jsonсознательно не тронут: автор в заявочном комментарии ссылается на #697 («после #697 ветки задач его не коммитят»). На момент этого ревью #697 в статусеS1-new(не начат, не триажирован) — конфликты на этом файле продолжат случаться до его реализации. Автор это прямо раскрыл, не спрятал; тема не входит в делаемый диапазон #698 (см. «Что проверено»).scripts/bundle-budget.mjsне включён ни вUPSTREAM_WINS, ни вPATCH_ID_EXCLUDES, хотя исходная «Проблема» issue называет его рядом сmonolith-baseline.json. Прочитал файл: он вперемешку хранит числовые потолки и содержательные словари (GRAPH_LABELS,NAMESPACE_ENGLISH_CONSUMERS,SUPPORT_LAZY_MARKERS), большинство числовых потолков уже имеют полосу (INITIAL_VIEW_CEILING_BAND,LAZY_GRAPH_CEILING_BAND) — это не тот же тип конфликта, что чистый производный JSONmonolith-baseline.json, и слепое разрешение «взять dev» рисковало бы стереть чужой словарь. Решение не включать файл в это ревью не оспариваю.
Как проверялось
| Гейт | Команда | Результат |
|---|---|---|
| Validate на материале | CI run на ea2c000a (ссылка в задаче ревью) |
зелёный; дешёвые гейты (tsc, npm test, build+bundle-policy --verify) не перегонял — приняты по этой ссылке (#343) |
| Целевой юнит-набор | node --test test/rebase-generated.test.mjs |
15/15 pass |
| Целевой юнит-набор | node --test test/merge-candidate.test.mjs |
19/19 pass |
| Целевой юнит-набор | node --test test/rebase-on-dev.test.mjs |
6/6 pass |
| Согласованность конспекта | node --test test/process-digests.test.mjs |
5/5 pass |
process-gate.mjs (офлайн) |
node scripts/process-gate.mjs --issues |
«гейт пройден, предупреждений 1» — предупреждение информационное: инфраструктурный диапазон, статусная метка не обязательна до S7-code-review |
| Реестр мутантов, дешёвая половина | node --test --test-name-pattern="every mutant patch anchors exactly once|every guard command points" test/mutation-gate.test.mjs |
2/2 — якоря трёх новых мутантов не отстали от кода |
Мутант changelog-union-driver-dropped |
вручную убрал строку docs/CHANGELOG.md merge=union из .gitattributes, прогнал node --test --test-name-pattern="#698: записи ченджлога" test/rebase-generated.test.mjs, восстановил файл |
краснеет (AssertionError, false !== true) |
Мутант monolith-baseline-conflict-is-manual-again |
вручную заменил UPSTREAM_WINS на Object.freeze([]) в scripts/rebase-generated.mjs, прогнал --test-name-pattern="#698: конфликт в базе", восстановил файл |
краснеет (ok:false, reason:'manual' вместо ожидаемого авторазрешения) |
Мутант merge-patch-id-sees-changelog |
вручную вернул PATCH_ID_EXCLUDES к списку без обоих CHANGELOG в scripts/merge-candidate.mjs, прогнал --test-name-pattern="#698: patch-id", восстановил файл |
краснеет (AssertionError на docs/CHANGELOG.md) |
| Чтение живого гейта монолита | scripts/monolith-metrics.mjs: compareWithBaseline |
band = name === 'bundleBytes' ? bundleBand : 0 — для 5 из 6 метрик допуск нулевой сегодня (см. находку Medium) |
| Провенанс коммита | git show -s --format=full ea2c000a |
Issue: #698, User-Visible: no — корректно, ни одно пользовательское поведение продукта не задето |
| Восстановление рабочей копии | git status --porcelain после каждой ручной мутации |
пусто — дерево вернулось к исходному состоянию перед следующим шагом |
Не прогонял (и почему): npx tsc --noEmit, полный npm test, npm run build
- сверка трёх копий бандла,
node scripts/check-docs.mjs,mutation-gate --checkцеликом (с пересборкой бандла на мутанта) — приняты по зелёному Validate на этом SHA (#343); диапазон не трогаетsrc/**,check-docs.mjsне требуется. Golden/скриншоты, браузерные смоки,pytest tests_backend, инварианты модели, performance-профили — не применимо: диапазон не трогает визуал, Python или геометрию плана.
Что проверено и корректно
--ours/--theirsво время rebase не перепутаны. Комментарий в коде («На ребейзе--ours— сторона, на которую ребейзят, то есть dev») верен git-семантике (во времяgit rebaseHEAD — это цель,--theirs— переигрываемый коммит); тест «конфликт в базе метрик монолита решается в пользу dev» подтверждает это исполнением реального git:hostRefsпосле разрешения равен значению из коммитаneighbour(dev), а неtask.planStop/rebaseRegeneratingне путаютUPSTREAM_WINSсextra/index. Иmanual, и возвращаемыйextraкорректно исключают оба новых пути;plan.upstreamобрабатывается отдельной веткой (checkout --ours+add) доplan.index. Тест «база метрик вместе с конфликтом в коде» подтверждает, что при добавлении обычного (не покрытого) конфликтующего пути срабатывает прежний полный abort со списком['a.mjs', 'scripts/monolith-baseline.json'], а не частичное разрешение.- Оба вызывающих получают правило бесплатно.
UPSTREAM_WINSзашита вscripts/rebase-generated.mjs, а не передаётся черезopts.extra— и авторскийrebase-on-dev.mjs, и конвейерныйmerge-candidate.mjs::rebaseOntoиспользуют общийrebaseRegeneratingбез дублирования логики. - Все три новых мутационных гвардиана реально ловят регресс — проверено
ручным воспроизведением (патч → красный тест → откат), а не по имени
okв реестре мутантов. - Safety net не теряется при неверном бейзлайне. После авторазрешения в
пользу dev реальное расхождение вычисленных метрик всё равно ловит Validate
самого кандидата:
merge-candidate.mjsдиспатчитvalidate.ymlна смерженный SHA и ждёт результат, красный возвращает задачу вS6-in-progress— молча неверный бейзлайн вdevне проходит. - Патч-id корректно исключает то, что ребейз сливает сам — соседняя правка нейтрального файла не отправляет зелёную задачу обратно на ревью; подтверждено мутационным тестом.
- Трейлеры и класс изменений корректны:
Issue: #698,User-Visible: no, ни одного файла класса A;PROCESS.mdдокументирует изменение тем же коммитом. scripts/bundle-budget.mjsобоснованно вне скоупа этой правки (см. «Скоуп») — не переоткрываю как находку.
Находки
Medium (в скоупе — блокирует зелёный вердикт)
PROCESS.md утверждает существование «теста полосы на Validate» для монолитного бейзлайна, которого сегодня нет.
Новый абзац этого же коммита (PROCESS.md, рядом с #643): «...а на конфликте
в scripts/monolith-baseline.json ребейз берёт сторону dev — число для
объединённого дерева судит тест полосы на Validate (#699)». Формулировка в
настоящем времени читается как факт о текущей автоматизации.
По факту: scripts/monolith-metrics.mjs:compareWithBaseline сегодня считает
band = name === 'bundleBytes' ? bundleBand : 0 — для пяти из шести чисел
(delegates, portMembers, hostRefs, portPrivates, harnessPrivates)
допуск нулевой, точное совпадение, снижение тоже FAIL. Это буквально проблема,
которую описывает сам #699 («Двусторонние храповики сейчас с нулевым или
минимальным запасом» / «5 чисел монолита... точное совпадение»), а #699
находится в статусе S1-new — не триажирован, тем более не смёржен. «Тест
полосы» реально существует только для одной метрики из шести (bundleBytes,
BUNDLE_BYTES_BAND) — и этот механизм существовал до #698/#699, не является
их результатом.
Канон сам формулирует правило для себя (шапка PROCESS.md): «При расхождении
этого документа с... scripts/* побеждает фактическая автоматизация.
Расхождение при этом не игнорируется, а заводится issue с меткой process».
Данный абзац — ровно такое расхождение, внесённое тем же коммитом, который
должен был его избежать: автор или ревьюер, читающий канон после мержа #698,
но до принятия #699, вправе решить, что небольшой дрейф hostRefs/delegates
после авторазрешения конфликта в пользу dev будет прощён «полосой» — а на деле
Validate уронит кандидата точным несовпадением, и время уйдёт на выяснение,
почему написанное в каноне не совпадает с поведением автоматизации.
Не High: реальное расхождение по-прежнему красит Validate кандидата (см. «Что
проверено» — safety net не теряется), неверный бейзлайн не проходит в dev
молча. Но это Medium и в скоупе задачи — формулировку добавила именно она,
чинится точечной правкой в этом же issue, например: «текущий Validate
сравнивает число с dev точно (нулевой допуск для всех метрик, кроме
bundleBytes) — станет полосой после #699; до этого несовпадение красит
Validate кандидата, а не проходит тихо».
Low (сняты ревьюером, без правки)
scripts/rebase-on-dev.mjs: предиктивный--dry-run-лог (splitConflicts/predicted.manual) не знает ни проmerge=unionв.gitattributes, ни проUPSTREAM_WINS— при параллельной правкеdocs/CHANGELOG.md/docs/CHANGELOG.ru.mdилиscripts/monolith-baseline.jsonон печатает «менялись с обеих сторон... возможен ручной конфликт», хотя фактический ребейз (несколько строк ниже, черезrebaseRegenerating) разрешает его автоматически. Чисто предсказательный текст для человека, сам исход ребейза не меняет и уже проверен тестами выше. Снимаю: cosmetic, не мешает ни одному AC этой задачи; дешевле поправить при следующей правке файла, отдельный issue для строки лога избыточен.- Риск «
merge=unionсклеит содержательный конфликт двух правок одной старой строки ченджлога в два дубля» — раскрыт самим автором в хендофф-комментарии как принятый компромисс для append-only лога (дубль увидит релиз-менеджер при сборке нот беты). Согласен с оценкой, не переоткрываю.
Чего не проверял
- Живой прогон
merge-candidate.mjs::rebaseOnto/_process.ymlна реальном GitHub Actions с настоящим конфликтом между двумя параллельными PR — не воспроизводил. Логика проверена реальным git (временные репозитории в тестах, не мок) и точечными ручными мутациями — это не то же самое, что живой запуск конвейера на паре реальных веток. Записываю явно: для факта исполнения на реальном GitHub Actions — проверено чтением и контролируемым тестом на настоящем git, не живым запуском workflow. - Полный
npm test(весь набор) не перегонял целиком — принят по зелёному Validate на этом SHA (#343); точечно перегонял только файлы из дельты (rebase-generated,merge-candidate,rebase-on-dev,process-digests) плюс дешёвую половинуmutation-gate.test.mjs. npx tsc --noEmit,npm run build+ сверка трёх копий бандла, полныйmutation-gate --check(с пересборкой на мутанта) — не перегонял, приняты по Validate; диапазон не трогаетsrc/**.- golden/скриншоты, браузерные смоки,
pytest tests_backend, инварианты модели, performance — не применимо: диапазон не трогает визуал, Python или геометрию плана. - Долгосрочная устойчивость find/replace-якорей трёх новых мутантов к
дальнейшим ребейзам
dev— проверена только на текущем материале. - Существо #697 и #699 — вне скоупа этой задачи, не проверял по существу,
только прочитал их текущий статус (
S1-newу обоих) для находки Medium выше.
Вердикт
Единственная блокирующая находка — Medium, в скоупе: формулировка PROCESS.md,
добавленная этим же коммитом, описывает как факт «тест полосы на Validate»
для монолитного бейзлайна, которого сегодня нет (нулевой допуск для пяти из
шести чисел, полоса существует только для bundleBytes и не как результат
этой задачи). Сам механизм разрешения конфликтов — .gitattributes union,
UPSTREAM_WINS, PATCH_ID_EXCLUDES — реализован корректно, проверен
исполнением настоящего git и ручными мутациями по всем трём защищаемым
линиям, и не создаёт риска молчаливого слияния неверного числа (реальное
расхождение по-прежнему красит Validate кандидата). Без High это жёлтый
вердикт: формулировка канона должна отражать факт автоматизации, а не
опережать ещё не принятую задачу (#699).
Вердикт: жёлтый · заход r1 · блокирующих циклов 1/2 · High: 0 · Medium: 1 → в задаче
Материал раунда
- Ветка:
issue/698-rebase-auto-resolve, коммитea2c000af5e4— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
cb640cf165f861e9b1f8827a5dc40df3c5435e77git log --all --format='%H %T' | grep cb640cf165f8 - Тело issue:
e17d18976553c2da4042b649a2073c581aa5108f58a57cf7c2902b6d639859c0 - Вердикт конвейера:
yellow· High 0