19 KiB
CODE-REVIEW #695 r1
Материал: origin/dev..HEAD, ровно один коммит
57ce10721fc0e48cb48b20798d7fb861b4c75ba8 поверх dev@7d4d75bd.
Диапазон: PROCESS.md, AGENTS.md, docs/process/AUTHOR.md,
docs/process/REVIEWER.md, .github/workflows/_process.yml,
scripts/mutation-registry.mjs, scripts/task-packet.mjs,
test/process-digests.test.mjs, test/task-packet.test.mjs.
Скоуп
Инфраструктурная задача (§1: ни одного файла класса A) — без S1–S6, вход сразу
на S7-code-review, что подтверждено веткой issue/695-track-labels и меткой
process на issue. Заявленный автором скоуп (комментарий «Взял»): канон
треков PROCESS §5/§5.1/§2/§4/§7/§9/§11, конспекты AUTHOR/REVIEWER, AGENTS,
task-packet.mjs, лимит циклов по track:* в _process.yml. Явно вне
скоупа — поведение конвейера по треку (мутанты, слияние ship без ревью
модели, ребейз) — это #696, отдельная задача; проверял только то, что заявлено
сделанным в #695.
Проверял по строке кода/текста, а не по заявлению автора: каждый пункт хендоффа сверен с диффом и/или исполнением ниже.
Как проверялось
| Гейт | Команда | Результат |
|---|---|---|
| Validate на материале | CI run на 57ce1072 |
зелёный (ссылка в задаче ревью), дешёвые гейты (tsc, npm test, build+bundle-policy) не перегонял — приняты по этой ссылке (#343) |
| Целевой юнит-набор | node --test test/process-digests.test.mjs test/task-packet.test.mjs test/entry-cost.test.mjs |
28 pass, 0 fail (прогнано мной точечно поверх зелёного Validate, т.к. это ядро правок) |
process-gate.mjs (офлайн) |
node scripts/process-gate.mjs --range origin/dev..HEAD |
«гейт пройден, предупреждений 0» |
mutation-gate --check |
node scripts/mutation-gate.mjs --check |
browser guards: 200/200; 3 предупреждения — все про #650 (geometry-corpus/nightly-reuse), не связаны с этим диффом, baseline |
Мутант process-digest-dead-anchor |
вручную заменил якорь AUTHOR.md #51-метки-тяжёлых-проверок-и-прежние-метки → #51-метки-тяжёлых-проверок, прогнал node --test --test-name-pattern="#634 конспект: каждая ссылка" test/process-digests.test.mjs, вернул файл |
краснеет: AssertionError — тест поймал мёртвый якорь |
Мутант process-digest-bullet-without-canon-link |
вручную снял ([§5.1](...)) у пункта про track:show/ci:*, прогнал --test-name-pattern="#634 конспект: каждый пункт", вернул файл |
краснеет: пункт без ссылки на канон пойман |
| Якоря §5/§5.1 в новых ссылках | headings()/markdownLinks() из scripts/md-anchors.mjs (настоящий алгоритм GitHub slug, не схлопывающий дефисы) — покрыто тестом выше |
зелёный тест = якоря 5-треки-ship-show-ask--метка-владельца и 51-метки-тяжёлых-проверок-и-прежние-метки существуют и совпадают с реальными заголовками |
| Ветка/трейлеры | git log, git show -s --format=full HEAD |
один коммит, Issue: #695, User-Visible: no, ветка issue/695-track-labels — соответствует §3 п.10 |
Не прогонял (и почему): golden/скриншоты, браузерные смоки, pytest tests_backend,
инварианты модели, check-docs.mjs, performance-профили — диапазон не
затрагивает src/**, custom_components/**/*.py ни один файл класса A;
smoke-select.mjs не запускал по той же причине (нет продуктового диффа,
которому смок мог бы соответствовать). no-new-any.mjs не применим — TS не
менялся.
Находки
Medium (в скоупе) — «инфраструктура без метки трека» не читается как track:show нигде в коде
Файл: PROCESS.md:607 (§5.1) и отсутствие реализации в
.github/workflows/_process.yml, scripts/task-packet.mjs.
Новый канон §5.1 утверждает:
«...продуктовая задача без трековой метки — как
track:ask; инфраструктурная задача (§1) без трековой метки — какtrack:show. Новые задачи получают толькоtrack:*.»
Это прямое, машинно-проверяемое правило (наравне с «trivial/small читаются как
show», которое реализовано). Скоуп задачи явно включает «лимит циклов по
track:* в _process.yml» и task-packet.mjs. Ни один из них правило не
реализует:
.github/workflows/_process.yml:87-93(guardjob) считаетSMALL=trueтолько по меткамsmall,trivial,track:show,track:ship. Нет ветки, которая бы распознавала инфраструктурную задачу без трековой метки — сам bash-скрипт вообще не читает признак «инфраструктура» (толькоgh issue view --json labels, без диффа). Инфраструктурная задача без метки трека (обычный случай: конспект автора нигде не просит поставитьtrack:*инфраструктурной задаче — ни вdocs/process/AUTHOR.md, ни в «Вход в процесс» этого файла) получаетlimit=4, а не2, как обещает §5.1.scripts/task-packet.mjs:180-183(buildPacket) для инфраструктурной ветки никогда не вызываетtrackFromLabels()— track всегда равен'инфраструктурный'/'инфраструктурный (предварительно...)', даже когда меток трека нет вовсе. ДокстрокаtrackFromLabels(строки 128-134) прямо говорит «задача без трековой метки — какask» без исключения для инфраструктуры, то есть и здесь правило §5.1 не реализовано ни в основной, ни в обходной ветке.
Воспроизведение (не гипотеза — существующий зелёный тест доказывает
обратное поведение): test/task-packet.test.mjs:171-183
('#632: statusless or returned infra issue without spec keeps the class A ban') собирает пакет с labels: ['infra', 'S7-code-review'] (нет ни одной
track:*, small, trivial) и утверждает packet.track === 'инфраструктурный'
— то есть текущий код по конструкции не даёт 'show' для этого случая, и тест
это фиксирует как ожидаемое поведение уже сейчас, при полностью зелёном прогоне.
Почему это не редакционная мелочь. Практический эффект — не
косметический: лимит циклов управляет тем, когда конвейер обязан
эскалировать задачу владельцу (review-4, §4). Инфраструктурная задача без
метки трека (а таких, по конспекту автора, будет большинство — постановка
метки трека нигде не предписана для инфраструктурного входа) при 2 подряд
жёлтых/красных вердиктах должна была бы уже упереться в лимит и уйти на
решение владельца (разделить/отклонить/арбитраж), а по факту реализованного
кода получает бюджет 4 — вдвое больше заявленного в собственном канона этой
же задачи. Это ровно тот класс расхождения «канон говорит одно, автоматизация
делает другое», который сам документ требует не игнорировать, а заводить
находкой (шапка PROCESS.md: «Расхождение не игнорируется, а заводится issue с
меткой process»); поскольку расхождение целиком внутри диффа и заявленного
скоупа этой задачи — чинится здесь, не отдельным issue.
Чем закрыть: либо реализовать правило (например, _process.yml мог бы
трактовать S7-code-review без каких-либо track:*/small/trivial меток
как SMALL=true, когда issue не несёт продуктовых S1-S6-признаков — но тогда
нужен признак, отличающий это от «продуктовая задача без метки = ask», которого
у bash-скрипта сейчас нет вовсе), либо явно понизить формулировку §5.1
до «рекомендации», сняв слово «читается как» и обязанность машинного
соответствия, как это сделано для temporarily-not-implemented частей ship
(«пока #696 не влит...», PROCESS.md:562-564) — то есть либо код, либо честная
оговорка о недоделке, но не утверждение без покрытия.
Что проверено и корректно
- Таблица треков §5 и её колонки (для чего, маршрут, ТЗ, ревью ТЗ,
локальный гейт, код-ревью, лимит циклов) — согласована между PROCESS.md,
AGENTS.md,
docs/process/AUTHOR.md,docs/process/REVIEWER.md; сверено построчно диффом каждого файла, расхождений в формулировках не нашёл. trackFromLabels()для явно помеченных задач (track:ship/track:show/track:ask/ легасиtrivial/small/ без меток → продуктовая задача) — корректна и покрыта новым тестомtest/task-packet.test.mjs(#695: трек по меткам), прогнан, зелёный. Проверено исполнением, не только чтением. Приоритетtrack:ship→track:show→track:ask→ legacy → default реализован в объявленном порядке (§5: «метка владельца главнее критериев», явныйtrack:askпереопределяет legacy-метки) — сверил построчно с.github/workflows/_process.yml:87-93:if has track:ask; then SMALL=false; TRIVIAL=false; fiидёт после чтения legacy-меток, порядок совпадает.- Лимит циклов для явно помеченных
track:show/track:ship/track:ask—_process.ymlстроки 87-99 корректно матчатся:track:show/track:ship/ legacysmall/trivial→limit=2, явныйtrack:askвозвращаетlimit=4даже поверх legacy-меток. Дешёвая, но реальная проверка: прогнал bash-логику построчно на всех комбинациях меток изhas(); для случаев, где метка трека явно стоит, поведение соответствует таблице §5 и §4. - Якоря конспектов (AC2 #634) — оба новых заголовка (
§5,§5.1) и все ссылки на них изAUTHOR.md/REVIEWER.mdрезолвятся настоящим алгоритмом GitHub-слага (scripts/md-anchors.mjs, без схлопывания дефисов) — проверено исполнением зелёногоtest/process-digests.test.mjs(5/5) и двумя ручными мутациями, которые «умеют падать» (см. таблицу гейтов). Оба перенацеленных мутанта вscripts/mutation-registry.mjs(process-digest-dead-anchor,process-digest-bullet-without-canon-link) действительно ловят регресс — проверено воспроизведением, не по названию. - Ключевые формулировки конспекта («Метка владельца главнее критериев»,
«ожидаемое поведение уже зафиксировано») дословно совпадают в PROCESS.md §5 и
в
AUTHOR.md, со ссылкой на верный раздел — проверено тестомtest/process-digests.test.mjs(правило KEY_RULES), зелёный. - Трейлеры и провенанс коммита — один коммит,
Issue: #695,User-Visible: no, веткаissue/695-track-labelsотdev@7d4d75bd;User-Visible: noкорректен — изменение не задевает ни одного пользовательского поведения (правок в CHANGELOG нет и не требуется).process-gate.mjs --range origin/dev..HEAD— 0 предупреждений. - Отсутствие продуктового кода — диффом не задет ни один файл класса A
(
src/**,custom_components/houseplan/**/*.py, i18n); инфраструктурный маршрут применён верно. - Обратная совместимость легаси-меток (
small/trivial→show, продукт без метки →ask) реализована и в_process.yml, и вtask-packet.mjs, и задокументирована в §5.1 — согласованно во всех трёх местах. - Мусора после диффа не осталось:
grepпо репозиторию не находит мёртвых ссылок на старые заголовки§5 лёгкий трек/§5.1 короткий трекни в одном скрипте, тесте или документе внеdocs/reviews/.
Чего не проверял
- Поведение конвейера, отличное от лимита циклов (мутанты по диффу на
ship, слияниеshipбез ревью модели, ребейз-семантика треков) — заявлено автором как вне скоупа (#696), не проверял вовсе. - Живой прогон
_process.ymlguard job на реальном issue сtrack:show/track:ship— GitHub Actions с реальнымgh issue viewне воспроизводил; проверка bash-логики — чтением и ручной подстановкой значенийhas(), не исполнением job. Записываю явно: проверено чтением, не исполнением для всей ветки guard job, кроме той части, что покрытаprocess-gate.mjs(офлайн-подмножество). Тесты, исполняющие именно этот bash-скрипт (_process.yml), в репозитории не существуют ни для старой, ни для новой логики — это не новый пробел этой задачи, а существующее свойство инфраструктуры тестирования workflow. docs/reviews/INDEX.md, golden, скриншоты, смоки, perf, pytest, инварианты модели — не прогонял: диапазон не касаетсяsrc/**,custom_components/**/*.py, визуала или геометрии.- Полный
npm test(3223 теста) не перегонял целиком — принят по зелёному Validate на этом SHA (#343); перегонял точечно только изменённые файлы (process-digests,task-packet,entry-cost) и мутации по ним. - Не проверял, действительно ли
process-labels/labels.tsv(вне репозитория, в «папке владельца» по словам автора) создаётtrack:ship,track:ask,ci:full,ci:golden,ci:mutantsс корректными цветами/описаниями — это файл вне git-дерева репозитория, недоступен ревью.
Вердикт
Единственная находка — Medium, в скоупе задачи (реализация лимита циклов по
track:* в _process.yml и track в task-packet.mjs — часть заявленного
скоупа), не High: явных меток трека это не касается, дефект сужен до случая
«инфраструктурная задача без единой метки трека», расхождение делает конвейер
мягче объявленного (даёт больше циклов, а не меньше) — не блокирует
использование, но противоречит только что написанному собственному канону.
Без High это жёлтый вердикт, возврат автору на исправление в этом же issue.
Вердикт: жёлтый · заход r1 · блокирующих циклов 1/4 · High: 0 · Medium: 1 → в задаче
Материал раунда
- Ветка:
issue/695-track-labels, коммит57ce10721fc0— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
2faf4770f1ba6a32a599552db9c611bfb5cc5a18git log --all --format='%H %T' | grep 2faf4770f1ba - Тело issue:
c74209278f8dd164514eda40d8841d6eefdeffd3b490a90f47b20e3ff2b667e7 - Вердикт конвейера:
yellow· High 0