From 9a7020ce428e506804e213d447bf4d8f64c353d7 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Sun, 27 Sep 2026 11:52:45 +0000 Subject: [PATCH] docs: review document for #676 Issue: #676 User-Visible: no --- docs/reviews/INDEX.md | 3 +- docs/reviews/SPEC-REVIEW-676-r3.md | 200 +++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 docs/reviews/SPEC-REVIEW-676-r3.md diff --git a/docs/reviews/INDEX.md b/docs/reviews/INDEX.md index 6c33c068..85d1206c 100644 --- a/docs/reviews/INDEX.md +++ b/docs/reviews/INDEX.md @@ -1,11 +1,12 @@ # Индекс ревью -Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1107, issue: 396. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. +Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1108, issue: 396. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. | Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы | |---|---|---|---|---:|---:|---|---| | #676 | [SPEC-REVIEW-676-r1.md](SPEC-REVIEW-676-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | AC8 не называет проверку двух из трёх ветвей подавления подсказки; риск «рамка перехватывает события под другим инструментом» не привязан ни к одному AC; не задан tie-break для равного протяжки по обеим осям (снимаю сам) | `src/stairs-editor-model.ts` `stairs-view.ts` `demo/smoke_stairs.mjs` `src/furniture.ts` | | #676 | [SPEC-REVIEW-676-r2.md](SPEC-REVIEW-676-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | AC8/К8 перечисляют четыре ветви stairTargetState, а не пять; состояние self выпадает из… | `src/stairs-editor-model.ts` `docs/STAIRS.md` `src/stairs.ts` `stairs-view.ts` | +| #676 | [SPEC-REVIEW-676-r3.md](SPEC-REVIEW-676-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | — | | #675 | [CODE-REVIEW-675-r1.md](CODE-REVIEW-675-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | | #673 | [CODE-REVIEW-673-r1.md](CODE-REVIEW-673-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | docs/images/screenshots.json: закоммиченный sourceFingerprint не совпадает с тем, что р…; Комментарий автора в issue #673 называет SHA реализации 557d4e38e26b647437f2c931454737f… | `docs/images/screenshots.json` `demo/golden/matrix.mjs` `scripts/source-fingerprint.mjs` `accept.mjs` `policy.mjs` `AGENTS.md` `baselines-index.json` | | #673 | [CODE-REVIEW-673-r2.md](CODE-REVIEW-673-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | — | diff --git a/docs/reviews/SPEC-REVIEW-676-r3.md b/docs/reviews/SPEC-REVIEW-676-r3.md new file mode 100644 index 00000000..2104f971 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-676-r3.md @@ -0,0 +1,200 @@ +# SPEC-REVIEW-676-r3 + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/676 +- **Этап:** `S4-spec-review` (ревью ТЗ, PROCESS.md §2.4) +- **Трек:** полный (метки `bug`, `P1`, `S4-spec-review`; лёгкий трек — нет, + подтверждено аналитиком в r1: несколько поверхностей, новый UX-контракт + размещения протяжкой и подсказки, сложность > 3). +- **Материал:** тело issue #676, раздел `## ТЗ` (редакция 3, 27.09, + `sha256 72bccdff…2183`) + 7 комментариев (добавился ровно один — «редакция + 3» — и вердикт r2). +- **Заход:** r3 · блокирующих циклов израсходовано 2 из 4 (лимит для + полного трека — 4; зелёный вердикт этого раунда цикл не тратит) +- **Роль:** ревьюер ТЗ (не автор) + +## Скоуп ревью (по дельте, PROCESS.md §2.10) + +Предмет r3 — ровно одна точечная правка редакции 3, объявленная автором в +комментарии `IC_kwDOTOcLQM8AAAABXQSkCA`: «К8 сформулирован инверсией — +подсказка только при `stairTargetState === 'active'`, то же условие, что +делает лестницу ссылкой; остальные четыре состояния перечислены поимённо, +включая `self`. AC8 получил пятый шаг смока: `target_space_id: 'f1'` на +этаже `f1` → подсказки нет.» + +Это единственная находка Medium раунда r2 («AC8/К8 перечисляют четыре ветви +`stairTargetState`, а не пять; состояние `self` выпадает из контракта +тултипа»). Код задачи по-прежнему не существует: ветки `issue/676-*` нет +(`git branch -a | grep -i 676` — пусто), `git diff 9fe7f88c..HEAD --stat` +между материалом r2 и текущим `HEAD` (`19edda83`) показывает только +публикацию документа `SPEC-REVIEW-676-r2.md` и обновление +`docs/reviews/INDEX.md` — ни одной строки продуктового кода. Дифф кода для +этой задачи не существует, гейты `typecheck`/`npm test`/`npm run build`/ +`check-docs.mjs`/смоки/`golden:verify`/инварианты — неприменимы на этом +этапе, как и в r1/r2 (см. «Чего не проверял»). + +Остальные разделы ТЗ (Сценарий, Скоуп/Не-скоуп, К1–К7, UX, модель данных, +i18n, план автотестов кроме строки AC8, риски, откат, release-артефакты, +«принято предположительно») дельтой r2→r3 не задеты — построчно сверены с +содержанием, процитированным/подтверждённым в r1 и r2, расхождений нет; они +наследуются без повторной проверки (см. «Унаследовано из r2»). + +**SCOPE-проверка (`docs/SCOPE.md`):** не меняется относительно r1/r2 — +сценарий закрывает J6 (редактирование объектов плана без регресса в +сохранённых данных) и J1/J2 через корректную навигацию/подсказку лестницы в +Просмотре. + +## Как проверялось + +1. Подтверждено, что кода задачи всё ещё нет: `git branch -a | grep -i 676` + не находит рабочую ветку; `git diff 9fe7f88c..HEAD --stat` (материал + r2 → текущий `HEAD`) — только `docs/reviews/INDEX.md` (+3/-1) и добавление + `docs/reviews/SPEC-REVIEW-676-r2.md` (211 строк, публикация отчёта r2). + Никаких файлов `src/**`/`test/**`/`demo/**` в диффе нет. +2. Прочитано тело issue #676 целиком (`gh issue view 676 --json body`, + sha256 `72bccdff…2183`) и все 7 комментариев (`gh issue view 676 --json + comments`), включая новый (редакция 3, `IC_…XQSkCA`) и вердикт r2 + (`IC_…XQQAzQ`). +3. Сверена ровно одна правка редакции 3 с текстом находки r2 построчно: + - **Medium (self, r2):** требовал либо переформулировать К8/AC8 как + инверсию с пятым смоук-шагом на `self`, либо явно обосновать пропуск. + Новый К8: «Подсказка показывается **только** когда + `stairTargetState(...) === 'active'` — то же условие, что делает + лестницу ссылкой… все остальные **четыре** состояния функции — + `missing`… `self`… `deleted`… `fixed`… — подсказки не дают» — это ровно + формулировка-инверсия из варианта (а), рекомендованного r2, с явным + перечислением всех пяти исходов по имени. Новый AC8: «все **пять** + состояний `stairTargetState`: `active`… `missing`… **`self` + (`target_space_id: 'f1'` на этаже `f1`) — `null`**… `deleted`… + `fixed`…» — пятый шаг добавлен буквально так, как просил r2. +4. Перепроверена не только текстуально, а по исходной функции: заново + прочитан `src/stairs-editor-model.ts:97-104` (`stairTargetState`, + актуальный код на `HEAD` `19edda83`) — сигнатура и порядок веток не + изменились с r2: возвращает `active | missing | self | deleted | fixed`; + ветка `self` — `stair.target_space_id === currentSpaceId` (строка 101), + проверяется до `spaceIds.has(...)`. Формулировка К8/AC8 редакции 3 + («`self` — цель, совпадающая с текущим пространством», пример + `target_space_id: 'f1'` на этаже `f1`) буквально соответствует этому + коду — не является догадкой. +5. Перепроверено, что не-скоуповый код Просмотра (`src/stairs-view.ts:64`, + актуальная строка на `HEAD`: `const active = interactive && targetState + === 'active';`) уже применяет ровно ту же инверсию к навигации, что К8 + редакции 3 теперь явно требует для тултипа — новая формулировка + контракта согласована с уже работающим соседним поведением, а не + противоречит ему (одно условие `=== 'active'`, один источник истины для + «когда лестница активна»). +6. Сверено, что `docs/STAIRS.md:21-23` («A missing, self or deleted target + leaves the stair visible and editable but shows a repair warning…») не + противоречит новой формулировке К8 — канон уже группирует `self` с + `missing`/`deleted` как непроходной для активации исход, задача лишь + явно называет его вместо того, чтобы полагаться на «остальное». +7. Разделы, не тронутые дельтой r2→r3 (Сценарий, Скоуп/Не-скоуп, К1–К7 кроме + упомянутого в п. 3, UX, модель данных, i18n, план автотестов кроме + строки AC8, риски, откат, release-артефакты, «принято предположительно»), + не перечитывались заново построчно против кода — они уже проверены в + r1/r2 и текстуально идентичны в текущем теле issue (сверено чтением + полного текущего тела против содержания, процитированного в + SPEC-REVIEW-676-r1.md и SPEC-REVIEW-676-r2.md); см. «Унаследовано из r2». + +## Находки + +Нет. Единственная находка r2 (Medium, ветка `self` в К8/AC8) закрыта именно +так, как r2 рекомендовал технически (вариант «а» — инверсия условия плюс +пятый смоук-шаг), формулировка проверена по актуальному коду +(`stairs-editor-model.ts`, `stairs-view.ts`) и не расходится с ним. Новых +несоответствий, внесённых самой правкой r3, не найдено. + +## Закрытие раунда r2 + +| Находка r2 | Чем закрыта | Где это видно | +|---|---|---| +| Medium: АС8/К8 перечисляют четыре ветви `stairTargetState`, а не пять; `self` выпадает из контракта тултипа | К8 переформулирован как инверсия («подсказка только при `active`», остальные четыре ветви — включая `self` — перечислены поимённо); AC8 получил пятый смоук-шаг для `self` | Тело issue #676, редакция 3, разделы «Контракт поведения → К8» и таблица «Критерии приёмки → AC8» (комментарий `IC_kwDOTOcLQM8AAAABXQSkCA`); подтверждено чтением `src/stairs-editor-model.ts:97-104` на `HEAD` `19edda83` — сигнатура и ветка `self` совпадают с текстом К8/AC8 | + +## Унаследовано из r2 (и транзитивно из r1) + +Принято без повторной построчной проверки против кода в этом раунде — +проверено ранее и не задето дельтой r2→r3: + +- Обязательные разделы §7.1 (Сценарий, Что человек увидит, Проблема, + Скоуп/Не-скоуп, Контракт К1–К7, UX, Модель данных и миграция, i18n, План + автотестов кроме строки AC8, Риски, Откат, Release-артефакты, Принято + предположительно) — присутствуют и однозначны (SPEC-REVIEW-676-r1.md, + раздел «Как проверялось» пп. 4–8; SPEC-REVIEW-676-r2.md, раздел «Что + проверено и корректно»). +- Диагностика «Проблема» и фактический фундамент контракта (константы + `STAIR_MIN_CM`/`STAIR_MAX_CM`/`FURN_WALL_CELLS`, существующие функции + `resizeFurnitureTransform`, `fieldToCm`/`clampWallCm`, порядок отрисовки + `wallbodies` vs `_renderTextFrame`, единый CSS-класс курсора, i18n-паритет, + 4 golden-сцены лестниц) — сверены с кодом на материале r1 + (`f1a87fba`/`4c3048e8`) и не менялись с тех пор ни в issue, ни в + соответствующем коде (точечно перепроверено для `stairs-editor-model.ts` + и `stairs-view.ts` в этом раунде, см. «Как проверялось» пп. 4–5; остальные + файлы не тронуты дельтой и не перечитывались). +- AC12 (инертность рамки узлов под другими инструментами) и его связь с К2 — + закрыты в r2, текст не менялся в r3 (SPEC-REVIEW-676-r2.md, «Что проверено + и корректно»). +- Tie-break диагонали 45° в К1 — закрыт в r2 (низкая находка r1), текст не + менялся в r3. +- Все 9 исходных дефектов из «Что происходит» сведены к контрактам К1–К8 и + покрыты AC1–AC11 (плюс AC12 из r2) без пропусков — установлено в r1, не + затронуто дельтой r2→r3 (SPEC-REVIEW-676-r1.md, «Что проверено и + корректно»). +- Откат и модель данных не меняются, `docs/CONFIG-COMPATIBILITY.md` не + задевается — установлено в r1, текст раздела не менялся. + +## Что проверено и корректно (этот раунд) + +- Правка редакции 3 текстуально и по коду закрывает ровно ту находку, ради + которой был начат раунд r3 — не больше и не меньше (не расширяет и не + сужает скоуп, не меняет ничего в других разделах ТЗ). +- Новая формулировка К8 согласована с уже существующим, не-скоуповым кодом + Просмотра (`stairs-view.ts:64`, инверсия `=== 'active'`) — один источник + истины для «когда лестница активна», а не два разных условия для + тултипа и навигации. +- AC8 остаётся однозначным и называет способ доказательства (`smoke`, пять + отдельных шагов с отдельными флагами) для каждого из пяти состояний + функции — буквальная реализация по тексту не может закрыть AC8, пропустив + `self`, как это было возможно с редакцией 2. +- Материал раунда корректно идентифицирован: код задачи по-прежнему + отсутствует (только публикация документа r2 в диффе `9fe7f88c..HEAD`), + так что вся оценка — снова чисто по тексту ТЗ и по уже существующему коду + соседних (не-скоуповых) модулей, которые контракт называет. + +## Чего не проверял + +- Не запускал `npm run typecheck`/`npm test`/`npm run build`, + `node scripts/check-docs.mjs`, смоки, `golden:verify`, инварианты модели — + кода задачи не существует ни на материале r1/r2, ни на текущем `HEAD` + (`19edda83`): `git branch -a` не находит ветку `issue/676-*`, `git diff + 9fe7f88c..HEAD` содержит только документ ревью r2 и индекс. Дифф для этих + гейтов не существует, они относятся к этапу код-ревью, который наступит + после реализации. +- Не перечитывал заново построчно против кода разделы, не тронутые дельтой + r2→r3 (К1–К7 кроме ветки `self`, УХ, модель данных, i18n, план автотестов + кроме AC8, риски, откат, release-артефакты) — они уже дважды проверены + (r1 по коду, r2 текстуально против r1) и текстуально идентичны в текущем + теле issue; см. «Унаследовано из r2». +- Не проверял заново достижимость `self` через реальный UI (перенос/слияние + помещений) — для снятия находки r2 было достаточно того, что модель + (`stairs.ts:84`) не отвергает такие данные и канон (`STAIRS.md:21-23`) + документирует состояние как поддерживаемое; глубже не уходил, как и в r2. +- Не проверял #663/#669 по существу (смежные задачи, не в скоупе). + +## Вердикт + +Вердикт: зелёный · заход r3 · блокирующих циклов 2/4 · High: 0 · Medium: 0 + +--- + +--- + + + +## Материал раунда + +- Ветка: `dev`, коммит `19edda83e35e` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `4963bc95c5187a923844c4e6ab5ae387a0af5094` + ``` + git log --all --format='%H %T' | grep 4963bc95c518 + ``` +- Тело issue: `72bccdffa1d7cdfdd71c8c8ee5d1cd66f64b6aba7db2003b718c0af2f2292183` +- Вердикт конвейера: `green` · High 0