# SPEC-REVIEW-517-r2 ## Скоуп Issue #517 (заход r2) — то же самое решение: перенос хранения ТЗ полного трека из `docs/specs/-*.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:` в разных документах — противоречия нет. - Метка `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 видит правку тела | сравнение хеша в reusableGreenVerdict снято | test/review-doc-guard.test.mjs`; раздел «Мутанты» → `reuse-ignores-changed-issue-body`. Технически совместимо с текущей сигнатурой `reusableGreenVerdict(docs, differs)` (`scripts/review-doc-guard.mjs:480`, сверено выше). | | **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/-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` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `a49e0aead06501023d32560d3d5f7cf91574fb30` ``` git log --all --format='%H %T' | grep a49e0aead065 ``` - Вердикт конвейера: `green` · High 0