21 KiB
SPEC-REVIEW-517-r2
Скоуп
Issue #517 (заход r2) — то же самое решение: перенос хранения ТЗ полного
трека из docs/specs/<NN>-*.md в тело issue и перенос доказуемости «вердикт
ревью вынесен на этом тексте» на sha256 нормализованного тела issue,
записываемый в блок якорей документов ревью. Пять поверхностей неизменны:
process.yml (шаг снятия хеша тела и сравнение с последним зелёным
SPEC-REVIEW), scripts/review-doc-guard.mjs (anchorIssueBodyFrom/
сравнение хешей/расширение reusableGreenVerdict), scripts/process-gate.mjs
(замена проверки 3), scripts/task-packet.mjs (смена приоритета источника
AC), PROCESS.md/AGENTS.md/docs/specs/README.md. User-Visible: no,
продукт и словари не затронуты.
Между r1 и r2 был ровно один зафиксированный правкой тела issue момент
(GraphQL userContentEdits, единственная запись, editedAt
2026-09-10T06:58:09Z, за секунду до комментария автора «ТЗ r2 — обе
находки закрыты») плюс снятие метки small тем же событием (timeline:
unlabeled: small в 06:58:09/06:58:12). Веток issue/517-* по-прежнему
нет — кода ещё не существует, ревью снова чисто по тексту тела issue.
Предмет этого раунда — дельта, а не задача целиком (§2.10): что именно
изменилось в теле issue и в метках со времени материала r1 (dev
a824acc1b18a, там же зафиксирован блок «Материал раунда» документа r1),
и закрывает ли изменение ровно то, что указал r1.
Как проверялось
- Найден вердикт r1 и материал, на котором он получен: комментарий
claudeот2026-09-10T06:54:41Z(Вердикт: жёлтый · заход r1 · блокирующих циклов 1/2 · High: 0 · Medium: 2) и документdocs/reviews/SPEC-REVIEW-517-r1.md, блок «Материал раунда»: веткаdev, коммитa824acc1b18a. Для этапа ТЗ SHA веток не при чём (материал — текст тела issue), но он фиксирует момент, ДО которого действовали находки r1. - Дельта тела issue: штатного git-диффа у тела issue нет (это и есть дыра,
которую сама задача #517 закрывает), поэтому дельта восстановлена по
трём независимым источникам вместо одного:
gh api graphql→repository.issue(number:517).userContentEdits— ровно одна запись правки,editedAt: 2026-09-10T06:58:09Z. Полеdiffв GraphQL API для правок такого объёма возвращает не построчный дифф, а полный текст после правки (сверено побайтово:diff-вывод идентичен текущему телу,md5sumсовпадает) — само по себе это не инструмент дельты, но подтверждает: правка ровно одна, и она датирована между вердиктом r1 (06:54:41) и комментарием «ТЗ r2» (06:58:10).- Дословные цитаты r1 из «старого» текста (У3 «reuse... не затрагиваются», старая формулировка У1 «на spec шага material нет») сверены построчным поиском в текущем теле: обеих строк в тексте больше нет — на их месте новые У5 и исправленный У1 (см. ниже).
- Комментарий автора «ТЗ r2 — обе находки закрыты» (
06:58:10) как декларация того, что именно должно было измениться; каждый пункт декларации сверен построчно с телом issue, а не принят на слово. gh api repos/.../issues/517/timeline— подтверждён факт и точное время снятия меткиsmall(unlabeledв06:58:09и повторно06:58:12, разница — служебные события конвейера).
- Технические claim'ы, которые дельта вносит заново, сверены с кодом, а не
приняты на слово автора:
- Исправленный У1 («шаг
materialуже выполняется и на этапеspec») перепроверен по.github/workflows/process.yml:403-404:if: steps.rebase.outputs.conflict != 'true'— условия поstageнет,rebase(единственный шаг сstage=='code') наspecпросто не выполняется, его output пуст,'' != 'true'истинно. Формулировка У1 после правки точна. - Новый АС6/У5 (
reusableGreenVerdictполучает третий аргумент — хеш тела) сверен с текущей сигнатуройreusableGreenVerdict(docs, differs)(scripts/review-doc-guard.mjs:480) и с уже существующимиanchorTreeFrom/anchorVerdictFrom(той же схемы: регэксп по блоку якорей) — добавление третьего параметра и симметричногоanchorIssueBodyFrom-сравнения технически укладывается в существующую форму функции, это не создаёт нового паттерна. - Проверено размежевание двух разных хеш-сравнений, которые вносит
дельта, — они не конфликтуют, а отвечают на разные вопросы: (а) АС2 —
«менялось ли тело со времени последнего зелёного SPEC-REVIEW»; (б)
АС6 — «менялось ли тело со времени документа, который
reuseрассматривает для повторного применения» (это может бытьCODE-REVIEW-документ прошлого заходаcode-этапа). План читает оба как независимые проверки над одним и тем же полемТело issue:в разных документах — противоречия нет.
- Исправленный У1 («шаг
- Метка
smallподтверждена снятой:gh issue view 517 --json labels→P2, infra, S4-spec-review, process, tech-debt—smallнет; лимит цикла ревью ТЗ этого захода — 4 (§4), что совпадает с рамкой задачи. - Заново проверены только те AC/разделы, которых касается дельта: АС2 (согласованность с новым У5), новый АС6, исправленный У1, последствия снятия метки. АС1, АС3, АС4, АС5 и обязательные разделы §7.1 дельта не трогает — унаследованы из r1 (раздел ниже), с построчной сверкой, что их текст не изменился.
- Гейты (
typecheck/test/build) не прогонялись и не нужны на этом этапе: кода по-прежнему нет, веткиissue/517-*не существует, диффа дерева нет — предмет ревью текстовый, как и в r1.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где видно |
|---|---|---|
Medium-1. АС2 обещает, что правка тела после зелёного ревью ТЗ всегда даёт находку «ТЗ менялось», но У3 утверждал, что reuse/reusableGreenVerdict (#499) «не затрагиваются» — а при reuse=true модель не вызывается вовсе, находка печататься неоткуда (сценарий класса #437 r4, многораундовый путь). |
Добавлен У5: reusableGreenVerdict получает третий аргумент — текущий хеш тела; повторное применение зелёного вердикта (reuse=true) разрешено только при совпадении этого хеша с записью Тело issue: в последнем документе; документ без такой записи (весь бэклог до перехода) — как раньше, по дереву. Добавлен новый АС6 с явным условием и тремя тестовыми случаями. |
Тело issue, раздел «Уточнения S2» → У5 (дословно: «reusableGreenVerdict получает третий аргумент — текущий хеш тела: вердикт применяется повторно, только если документ несёт строку Тело issue: и хеш совпадает»); раздел «AC» → новый пункт АС6; таблица «Чем краснеет» → строка `reuse видит правку тела |
Medium-2. Метка small не соответствовала собственному описанию задачи как полнотрековой (три поверхности); заявленное оправдание метки («иначе process-gate --issues потребует файл ТЗ на класс A») не работало — класса A у задачи нет вовсе. |
Метка small снята. |
gh issue view 517 --json labels → [P2, infra, S4-spec-review, process, tech-debt], small отсутствует; timeline → событие unlabeled: small в 06:58:09; лимит цикла этого захода — 4 (§4), не 2. |
Low-1 (не блокировал, снят записью). У1 неточно утверждал: «на spec шага material нет … понадобится снимать хеш в отдельном шаге» — код показывает обратное. |
У1 переписан на точную формулировку с явной ссылкой на находку r1. | Тело issue, «Уточнения S2» → У1 (дословно: «Хеш снимает существующий шаг material: он выполняется на обоих этапах (rebase пропускается на spec, и его пустой conflict условию шага не мешает — ревью ТЗ r1, Low-1)»). Перепроверено чтением process.yml:403-404 — соответствует. |
Все три находки r1 закрыты правкой текста/меток, видимой построчно, а не заявлением автора без опоры на текст.
Унаследовано из r1
Без повторной проверки принято (документ docs/reviews/SPEC-REVIEW-517-r1.md,
материал: dev a824acc1b18a, дерево 91c557112388…) — построчно сверено,
что соответствующий текст между r1 и r2 не изменился:
- АС1, АС3, АС4, АС5 — однозначны, доказательство названо для каждого (unit / тест на фикстурах / тест на текст); формулировки не менялись между раундами (сверено посимвольно с текстом, процитированным в r1).
- Обязательные разделы §7.1 присутствуют; неприменимые (UX, модель данных/миграция, i18n, производительность) явно помечены неприменимыми с обоснованием.
- Блок принятых предположений (У4 — нормализация тела перед хешем)
оформлен по правилу «принято предположительно, менять свободно»;
продуктовых вопросов владельцу нет и не требуется — персона задачи
внутренняя (автор ТЗ/ревьюер),
docs/SCOPE.mdеё не описывает и не обязан. - Обратная совместимость формата якорей (новая строка
Тело issue:независима отspecs; старые документы без неё читаются как раньше черезanchorIssueBodyFrom → null). - Откат описан конкретно (перечислены все файлы); риск лимита тела GitHub (65 536 знаков) сопоставлен с реальным максимумом ТЗ проекта (21 КБ).
- SCOPE.md намеренно не применяется как рамка приёмки — задача не
продуктовая (
User-Visible: no), это соответствие, а не пробел.
Находки
Low-2 (не блокирует, для протокола). Полнотрековый статус этой задачи формально расходится с ещё не изменённым §2.3
Файл: тело issue #517 в целом (метаданные трека) против PROCESS.md
§2.3 в его текущей, ещё не переписанной редакции.
В чём дело. После закрытия Medium-2 задача официально полнотрековая
(метка small снята, три поверхности, автор сам называет трек «полный»).
Действующий сегодня (до слияния этой же задачи) текст §2.3 гласит: «Лёгкий
трек: ТЗ пишется в теле issue, файл не создаётся (§5)» — то есть письмо ТЗ
в тело issue без файла сегодня описано как свойство ИМЕННО лёгкого трека;
для полного трека артефакт по тому же §2.3 — файл docs/specs/<NN>-slug.md.
Тело этой issue не создаёт такого файла и, будучи полнотрековым по факту
метки, формально не соответствует ещё действующей редакции §2.3.
Почему это не блокирует. Это ровно тот самозамкнутый случай, который
задача и вводит: issue буквально озаглавлен переносом полнотрекового ТЗ в
тело, открывается блоком «Решение владельца 2026-09-10», и checkSpecs
(§10.2 п.3, сверено в r1) не срабатывает на этой задаче ни при каком трек-
статусе, потому что у неё нет коммитов класса A. Никакого гейта это не
ломает, и явное владельческое решение вверху issue закрывает вопрос по
существу — до статьи PROCESS.md дело дойдёт в рамках самого п.2 плана
(«Полный трек — ТЗ тоже в теле issue»), который эта же задача и вносит.
Рекомендация (не обязательна). Одна поясняющая фраза в тексте — например, в разделе «Сценарий и что человек увидит» — что ТЗ этой задачи уже написано по будущему правилу до его формального принятия, сняла бы даже видимость несоответствия для читателя со стороны, не знакомого с хронологией. Автор уже сам называет это «иронично» в комментарии S2 — предложение сделать эту иронию явной строкой в самом ТЗ, а не только в комментарии. Снимается записью в этом документе, отдельного действия не требует.
Что проверено и корректно
- Все три находки r1 закрыты по существу, а не косметически: Medium-1
закрыт структурным изменением контракта (
reusableGreenVerdict+ АС6 + мутант), а не ослаблением формулировки риска; Medium-2 закрыт снятием метки, реальным действием, а не только словами в комментарии. - Новый АС6 не создаёт логического противоречия с АС2: оба сравнения хеша
тела (АС2 — против последнего зелёного
SPEC-REVIEW; АС6 — против документа, которыйreuseрассматривает для повторного применения) адресуют разные вопросы и не пересекаются деструктивно — еслиreuseиз-за АС6 откажет (тело изменилось), путь с вызовом модели восстановится штатно, и именно там сработает проверка АС2. - Раздел «Риски» («правка после этого попадёт в следующий заход») остаётся
верным при новом АС6 — раньше (до фикса Medium-1) эта фраза была неточной
ровно для многораундового пути
reuse, теперь фикс её оправдывает. - Исправленный У1 сверен с кодом заново (не принят на слово, что r1 «наверное прав») — точен.
Чего не проверял
- Не прогонялись
typecheck/test/build/process-gate— кода нет, веткиissue/517-*не существует, диффа дерева нет; это гейты этапаcode, неspec. - Не проверялась синтаксическая корректность будущих правок YAML/JS —
реализации ещё не существует, оценивался только план и его согласованность
с УЖЕ существующим кодом (
process.yml,review-doc-guard.mjs). - Не переоценивались АС1, АС3, АС4, АС5 и обязательные разделы §7.1 по существу — только сверено, что их текст не изменился между r1 и r2 (см. «Унаследовано из r1»); их содержательная оценка — из r1.
- Не оценивалось качество будущих мутационных гардов сверх того, что они названы и привязаны к конкретным функциям — реализация появится в коде.
Вердикт
Зелёный. High: 0, Medium: 0. Low: 1 (Low-2, снимается записью в этом документе, отдельного действия не требует). Обе Medium-находки r1 закрыты по существу и подтверждены построчной сверкой текста, а не заявлением автора.
Материал раунда
- Ветка:
dev, коммит6b31e94501cd— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
a49e0aead06501023d32560d3d5f7cf91574fb30git log --all --format='%H %T' | grep a49e0aead065 - Вердикт конвейера:
green· High 0