mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-01 04:09:17 +00:00
@@ -0,0 +1,212 @@
|
||||
# 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:` в
|
||||
разных документах — противоречия нет.
|
||||
- Метка `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/<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 закрыты
|
||||
по существу и подтверждены построчной сверкой текста, а не заявлением
|
||||
автора.
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `dev`, коммит `6b31e94501cd` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `a49e0aead06501023d32560d3d5f7cf91574fb30`
|
||||
```
|
||||
git log --all --format='%H %T' | grep a49e0aead065
|
||||
```
|
||||
- Вердикт конвейера: `green` · High 0
|
||||
Reference in New Issue
Block a user