20 KiB
SPEC-REVIEW-472-r2
Этап: spec (PROCESS.md §2.4) · Трек: лёгкий (small) · Заход r2 ·
блокирующих циклов 2/2 (лимит лёгкого трека — 2, §4)
SHA на момент ревью: f52b2449eaadaa2f604634780a0da926779ad9c3 (dev)
Ревьюер ≠ автор ТЗ.
Скоуп повторного раунда
Issue #472: еженедельный полный мутационный прогон (mutation-gate.yml) может
падать/отменяться без адресата. ТЗ в теле issue (лёгкий трек), контракт из 6
пунктов, AC1–AC8, теперь три мутанта на будущий scripts/mutation-gate.mjs.
Продукт не затронут — чистая CI/process-задача, кода ещё нет (ветки
issue/472-* не существует, см. «Как проверялось»).
Раунд r1 (жёлтый, docs/reviews/SPEC-REVIEW-472-r1.md, SHA 615045cb) нашёл
M1 (AC3 не различает «сбежавший мутант» и «уже красный guard»), M2 (AC5 не
даёт job report прав на скачивание артефактов) и L1 (неверный номер строки).
Автор ответил комментарием «Оба Medium исправлены в теле ТЗ, Low тоже» и
отредактировал тело issue. Предмет этого раунда — дельта тела issue:
правки контракта п.3 (AC3, «Мутанты»), п.4 (AC5) и абзаца «Проблема» (ссылка
на строку). Полный разбор не требуется: dev не сдвинулся в затронутых файлах
между 615045cb и текущим HEAD (см. ниже), новая подсистема не появилась,
контракт поведения не сменился — только текст ТЗ.
Как проверялось
- Перечитано тело issue #472 целиком (текущая редакция) и все четыре комментария (S2, «ТЗ готово», вердикт r1, «Оба Medium исправлены»).
git log --oneline 615045cb..HEAD -- .github/workflows/mutation-gate.yml .github/workflows/validate.yml .github/workflows/announce.yml scripts/mutation-gate.mjs test/mutation-gate.test.mjs→ пусто: ни один из файлов, на которые опирается контракт, не менялся между материалом r1 и текущим HEAD (f52b2449, второй коммит после615045cb— не по этой задаче, документ ревью #475). Значит фактическое состояние репозитория, описанное в r1, актуально и сейчас; переоткрывать сверку с файлами вне делты незачем.scripts/mutation-gate.mjs:runMutant(строка 6759 —FAIL ${mutant.id}: тест остался зелёным на сломанном коде),runCleanGuards(строки 6771–6793 —FAIL чистый прогон: ${guard} красный без мутанта),main(строка 6897, вызовrunCleanGuardsдо цикла мутантов) — перечитаны заново, подтверждают, что обе формыFAILпечатаются в один и тот же лог именно так, как описывает исправленный п.3.grep -oP "id: '\K[^']+" scripts/mutation-gate.mjs | grep -vE '^[a-z0-9-]+$'→ пусто (509 id,grep -c "id: '"подтверждает то же число, что и в r1) — техническая посылка M1 «все id —[a-z0-9-]+, различить формы дёшево» не сломалась..github/workflows/mutation-gate.yml:permissions: contents: read— строка 31;concurrency.group: mutation-gate/cancel-in-progress: true— строки 34–36;checkout ref: ... || 'dev'— строка 52. L1 (ссылка была на строку 33) подтверждена закрытой: текст issue сейчас ссылается на 52..github/workflows/validate.yml: jobpreflight.permissions— строки 38–41 (contents: read,actions: read,issues: read) — прецедент, на который ссылается M2-фикс в контракте п.4, подтверждён построчно.git status --short,git branch -a | grep 472,git log --all --oneline | grep 472— рабочее дерево чистое, веткиissue/472-*нет, кода по этой задаче в истории нет: контракт всё ещё чисто в стадии ТЗ, преждевременного кода (§12) не обнаружено.- Gates (
typecheck/test/build) не запускал: для #472 ещё нет ни строки продуктового или тестового кода (проверено выше), это ревью ТЗ, а не код-ревью — гейты неприменимы, ровно как в r1.docs/SCOPE.md,docs/USER-GUIDE.ru.md, канонические документы подсистем — не применимы: задача не трогает продукт и видимое пользователю поведение (чистый CI/process, как и установил r1).
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| M1 — AC3 не отличал «сбежавший мутант» от строки «guard уже красный без мутанта»; наивный парсер получил бы фиктивный id | Контракт п.3 теперь явно вводит escaped (id обязан существовать в реестре, иначе — unparsed) и redGuards (для FAIL чистый прогон: …) как разные ведра; AC3 переписан с перечислением всех четырёх случаев; добавлен третий мутант, целящийся именно в эту путаницу |
Тело issue, «Контракт» п.3 (абзац «две формы FAIL…»), таблица AC/строка AC3, «Мутанты»/строка mutation-report-red-guard-as-mutant |
M2 — AC5 называл для job report только permissions: issues: write, job не смог бы скачать артефакты шардов (actions: read отсутствует) |
Контракт п.4 и AC5 перечисляют contents: read, actions: read, issues: write с обоснованием каждого права (checkout, download-artifact, gh issue) |
Тело issue, «Контракт» п.4 (первый абзац), таблица AC/строка AC5 |
L1 — ссылка на строку mutation-gate.yml:33 для checkout ref: dev неверна (фактически 52) |
Ссылка исправлена на mutation-gate.yml:52, подтверждено grep -n на HEAD f52b2449 |
Тело issue, «Проблема», последний абзац |
Все три закрыты по существу (строка контракта/AC, не только заявление автора в комментарии).
Унаследовано из r1
Не проверялось заново — дельта этого раунда их не задевает, а файлы, от
которых зависит их доказательство, не менялись с 615045cb:
- Контракт п.1 (раздельные concurrency-группы по
github.event_name) и AC1; - Контракт п.2 (артефакт лога на шард,
if: always()) и AC2; - Контракт п.5 (
workflow_syncрасширяется наmutation-gate.yml) и AC8; - Контракт п.6 («что не меняется» — реестр, гварды, шардирование,
--changed, праваmutants); - AC6 (повторный отказ дописывает комментарий, не создаёт issue), AC7 (отсутствие Telegram-секретов не роняет job);
- «Откат» (один коммит, без миграции данных);
- Критерии лёгкого трека §5 (все пять выполнены, отказа от трека не требуется).
Источник: docs/reviews/SPEC-REVIEW-472-r1.md, материал — SHA 615045cb1882 fa36164a55d544dc6520f2f58618 (dev). Проверено, что git log --oneline 615045cb..HEAD для .github/workflows/mutation-gate.yml,
.github/workflows/validate.yml, .github/workflows/announce.yml,
scripts/mutation-gate.mjs, test/mutation-gate.test.mjs пуст — основание
для наследования не устарело.
Находки
Medium (в скоупе задачи) — 1
M3 (новая, введена самим фиксом M1). Контракт п.3 противоречит себе насчёт
того, попадают ли redGuards/unparsed в тело письма, которое видит
владелец, — а AC4 проверяет только escaped-часть тела.
- Файл: тело issue #472, раздел «Контракт», п.3 «Репортёр — отдельный тестируемый скрипт», и таблица AC, строка AC4.
- Первый абзац п.3 (тот самый, что закрывает M1) прямо обещает: «В теле они
идут отдельными разделами: сбежавшим —
--id=<id>, красным гардам — их команда как есть» — то естьredGuardsобязаны попасть в тело отдельным разделом. - Третий абзац того же пункта, который буквально перечисляет содержимое
тела, называет только: «дата, ссылка на прогон, SHA
dev, таблица шардов, список сбежавших мутантов с ихguardиз реестра… и одна фраза «что делать»… [--id=<id>] на каждый» [сбежавшего] — ниredGuards, ниunparsedздесь не упомянуты вовсе. - AC4 — единственный критерий, доказательство которого касается содержимого
title/body, — описывает то же самое узкое перечисление: «тело — ссылку на прогон, SHA и по одной команде--id=на сбежавшего». ПроredGuards/unparsedв теле AC4 не говорит ничего. - Симметричный, более мелкий след того же недосмотра: первый bullet п.3
объявляет форму возврата
mutationGateReportкак{ title, body, escaped, redGuards, shards }— без поляunparsed, хотя тот же абзац требует отдельный списокunparsedдля строк с id вне реестра, и AC3 явно включаетunparsedв перечень того, что репортёр обязан произвести. - Как это проявляется (разобрано по тексту — реализации ещё нет, «проверено
чтением, не исполнением» неприменимо, применяю «проверено по контракту»):
реализация, которая берёт AC4 буквально и пишет unit ровно на «заголовок +
дата + ссылка + SHA + таблица шардов + список escaped с
--id=», проходит все восемь AC — и при этомredGuards/unparsedникогда не попадают в письмо, которое читает владелец. Это тот же класс дефекта, ради устранения которого заведена вся задача («у отказа нет адресата»): «guard был красным ещё до мутанта» или «встретилась нераспознаннаяFAIL-строка» — оба случая реального отказа шарда — снова тихо не долетают до issue/Telegram, просто на один уровень глубже (не сам факт падения шарда, а его причина). - Почему это находка именно r2, а не пропуск r1: до фикса M1 контракт вообще
не различал
redGuards/unparsedкак отдельные сущности — само это разделение и обещание «отдельных разделов» появилось в тексте только сейчас, вместе с ответом на M1. Дельта этого раунда его и порождает. - Что чинит: одна фраза в AC4 (или новый AC4b) — тело обязано содержать
redGuardsотдельным разделом с командой гарда «как есть», и явно сказать, что происходит сunparsedв теле письма (свой раздел — либо, если решение «в тело не выводить, только в служебное поле», написать это прямо, а не оставлять молчание, которое ревьюер обязан трактовать как недосказанность, а не как решение); плюс добавитьunparsedв объявленную форму возвратаmutationGateReport.
Low — 1 (не блокирует, на усмотрение автора)
L2. Раздел «Затронутые файлы» всё ещё говорит «scripts/mutation-gate.mjs
— два мутанта», хотя после фикса M1 в разделе «Мутанты» их три (добавлен
mutation-report-red-guard-as-mutant). Реализацию не путает — список мутантов
в разделе «Мутанты» исчерпывающий и однозначный, реализатор будет читать его,
а не считать по итоговой сводке, — но счётчик стоит поправить для
согласованности документа.
Что проверено и корректно
- M1: закрытие сверено с фактическим кодом
runMutant/runCleanGuards/mainна HEAD — обе формыFAILдействительно печатаются в один лог в точности так, как теперь описывает контракт; техническая посылка (все id —[a-z0-9-]+, различение дёшево) реверифицирована и держится. - M2: закрытие сверено с прецедентом
validate.yml(preflight.permissions, строки 38–41) — набор правcontents: read, actions: read, issues: writeи обоснование каждого совпадают с моделью GitHub Actions (job-levelpermissionsзаменяет, а не дополняет). - L1: номер строки
mutation-gate.yml:52подтверждёнgrep -nна HEAD. - AC1, AC2, AC6–AC8, «Откат», критерии лёгкого трека §5 — не тронуты дельтой,
файлы, на которых основано их доказательство, не менялись с
615045cb; наследуются без переисполнения (раздел выше). - Кода по issue #472 нет нигде в дереве и истории — задача честно остаётся в стадии ТЗ, преждевременного кода (§12) не обнаружено.
Чего не проверял
npx tsc --noEmit/npm test/npm run build— не запускал: для #472 нет ни строки кода (провереноgit status/git branch/git log --all), гейты неприменимы на этапе ревью ТЗ.node scripts/mutation-gate.mjsживьём, эмуляция--shardсо сломанным guard — не запускал:scripts/mutation-gate-report.mjsиtest/mutation-gate-report.test.mjsещё не существуют (подтвержденоls), исполнять нечего; вывод по M1/M3 получен чтением контракта и существующегоscripts/mutation-gate.mjs, а не исполнением.- golden / браузерные смоки / инварианты модели / performance-профили — не применимы: diff не существует, задача не трогает продукт, фронтенд или геометрию.
- Контракт п.1, п.2, п.5, п.6, AC1, AC2, AC6–AC8, «Откат» — не переразбирал по существу сверх сверки «файлы не менялись» (см. «Унаследовано из r1»); если у ревьюера были к ним вопросы, они должны были прозвучать в r1.
- Точность фразы «паттерн [пропуска при отсутствующих секретах] уже есть в
announce.yml» (AC7, контракт п.4, абзац про Telegram) — эта часть текста не менялась между r1 и r2 и не входит в дельту; r1 принял AC7 как доказанный, переоткрывать вне делты по правилам §2.10 не стал.
Вердикт
Одна находка Medium в скоупе задачи (M3), High нет. По §2.4/§7.2 это жёлтый вердикт: автор правит текст ТЗ (тело/AC4, при необходимости — форму возврата в п.3), фикс проходит третий заход. Бюджет циклов лёгкого трека — 2 (§4); эта находка использует второй из них, то есть лимит на этом раунде исчерпан. Дальнейшая правка возможна («заход» и «цикл» — разные величины, заходов может быть больше двух), но при новой блокирующей находке в r3 решение по §4 переходит владельцу (разделить/отклонить/арбитраж), а не автоматически продолжает цикл.
Материал раунда
- Ветка:
dev, коммитf52b2449eaadaa2f604634780a0da926779ad9c3. - Файлы, от которых зависит контракт, не менялись с материала r1
(
615045cb1882fa36164a55d544dc6520f2f58618):git log --oneline 615045cb..HEAD -- .github/workflows/mutation-gate.yml .github/workflows/validate.yml .github/workflows/announce.yml scripts/mutation-gate.mjs test/mutation-gate.test.mjs→ пусто. - Ветки задачи
issue/472-*не существует — ТЗ живёт в теле issue (лёгкий трек), материал этого раунда — текущая редакция тела issue #472 на момент разбора.
Материал раунда
- Ветка:
dev, коммит `` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Якоря снять не удалось: ветки задачи нет, материал читался по
dev.