From e20560afa867d33fdb1d287a6a92eb3d78d474e6 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2026 07:08:05 +0000 Subject: [PATCH] docs: review document for #517 Issue: #517 User-Visible: no --- docs/reviews/SPEC-REVIEW-517-r2.md | 212 +++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 docs/reviews/SPEC-REVIEW-517-r2.md diff --git a/docs/reviews/SPEC-REVIEW-517-r2.md b/docs/reviews/SPEC-REVIEW-517-r2.md new file mode 100644 index 00000000..882b1573 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-517-r2.md @@ -0,0 +1,212 @@ +# 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