Files
houseplan-card/docs/reviews/SPEC-REVIEW-265-r2.md
T
2026-08-25 11:09:33 +00:00

187 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW-265-r2
- Issue: [#265](https://github.com/Matysh/houseplan-card/issues/265) — «Рефакторинг 2/5: один контракт шва импорта — ремап ссылок за собой и идемпотентная уборка»
- Этап: `S4-spec-review` (ревью ТЗ, PROCESS.md §2.4)
- Заход: r2 · блокирующих циклов израсходовано 1/4 до этого вердикта
- ТЗ: `docs/specs/265-import-reference-seam.md`, ветка `issue/265-import-seam-contract`, SHA на момент ревью — HEAD (`67bcce8e`, коммит «docs: address import seam spec review»)
- Ревьюер: Claude (свежая сессия, без переписки с автором)
## Скоуп ревью
Второй заход, разбор по дельте (PROCESS.md §2.10, issue #214). Продукт кода в
этой задаче ещё нет — единственный изменённый файл класса C —
`docs/specs/265-import-reference-seam.md`; диапазон дельты
`git diff 2cf47fc1..HEAD -- docs/specs/265-import-reference-seam.md`
(2cf47fc1 — SHA, на котором получен вердикт r1, назван явно в шапке
`SPEC-REVIEW-265-r1.md`, повторно искать его не пришлось).
Дельта локальна: автор правил ровно те четыре раздела, на которые указали
находки r1 (сценарий/«до-после», i18n-обязательства AC10, способ доказательства
по каждому AC, раздел рисков), плюс сквозную перенумерацию заголовков,
вызванную вставкой двух новых секций. Контракт поведения (lineage-алгоритм,
матрица ссылок, immutable candidate, порядок remap, edge cases) не менялся ни
по одной строке — рёбейза на ушедший вперёд `dev` не было, новая подсистема не
затронута, объём правки заметно меньше исходной задачи. Условия «разбор
остаётся полным» (§2.10) не выполнены — сокращение объёма законно.
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| **M1** — «что человек увидит» слито со сценарием, написано языком реализации; поверхность/touch не названы | Раздел разделён на «§1 Сценарий и персона» и «§2 Что человек увидит до и после»; «До/После» переписаны бытовым языком без «lineage»/«canonical root»/«candidate»; отдельно назван desktop-only статус и touch/kiosk-поведение | `docs/specs/265-import-reference-seam.md:19-50` |
| **M2** — AC10 хеджирует «если отчёт станет виден», хотя §9(старое)/§10(новое) уже безусловно описывает новый текст; i18n-ключи не перечислены | AC10 переписан без условности; конкретные RU/EN ключи перечислены поимённо | AC10: `265-import-reference-seam.md:435-439`; список ключей: `265-import-reference-seam.md:292-305` |
| **M3** — AC1–AC9 не называют способ доказательства | Добавлена таблица «§15.1 Матрица доказательств» — по одной строке на каждый AC1–AC10 | `265-import-reference-seam.md:441-454` |
| **M4** — нет отдельного обязательного раздела «Риски» | Добавлен «§13. Риски» — таблица из 8 строк (риск / последствие / снижение) | `265-import-reference-seam.md:347-358` |
Все четыре находки r1 закрыты предметно, не декларативно: в каждом случае в
дельте есть конкретный новый текст, а не переформулировка старого абзаца под
тем же смыслом.
## Унаследовано из r1
Без повторной проверки принято (документ `docs/reviews/SPEC-REVIEW-265-r1.md`,
SHA `2cf47fc1`, полный разбор первого захода):
- §3 «Подтверждённое состояние кода» (было §2) — построчная сверка с
`import_export.py` (`_fresh`, `_repair_target_space_refs`,
preview/apply/revalidate) и с пробелами `scripts/model-invariants.mjs`;
текст не менялся, изменился только номер раздела;
- §4 «Унаследованные продуктовые решения» (было §3) — сверка со статусом и
текстом закрытых #244/#248/#252/#258/#262; содержимое не менялось;
- §5 «Цели и границы» (было §4), §6 «Канонический lineage id» (было §5,
включая проверку edge case с ложным совпадением root), §7 «Неизменяемый
кандидат preview/apply» (было §6), §9 «Порядок remap и конфликты» (было §8),
§11 «Инварианты и отказоустойчивость» (было §10), §12 «Совместимость и
миграция» (было §11), §14 «Edge cases» (было §12), §16–§19 (план реализации,
проверки, rollback, допущения) — содержимое не менялось, изменилась только
нумерация;
- терминология («Оптимизировать планы», «carrier») — сверена с
`docs/USER-GUIDE.ru.md`/`docs/WALL-THICKNESS.md` в r1, дельта эти фразы не
трогает;
- три «унаследованных решения владельца» и отсутствие продуктовых вопросов
владельцу — не пересматривались, дельта их не касается.
## Как проверялось
1. Найден вердикт r1 и SHA: `docs/reviews/SPEC-REVIEW-265-r1.md` называет SHA
`2cf47fc1` в шапке документа явно — искать по логу не пришлось.
2. Дельта: `git diff 2cf47fc1..HEAD --stat` — изменены только
`docs/specs/265-import-reference-seam.md` (+168/-63 строк) и сам файл
`docs/reviews/SPEC-REVIEW-265-r1.md` (добавлен публикующим шагом, не
автором). Продуктового кода в дельте нет.
3. По каждой из находок M1–M4 найдена конкретная замена в тексте (таблица
выше), а не общее заявление «поправлено» — сверено построчно через
`git diff 2cf47fc1..HEAD -- docs/specs/265-import-reference-seam.md`.
4. Проверены новые фактические утверждения, добавленные именно в этой дельте
(§2.10 требует разбирать AC/утверждения, которых касается дельта, а
новый текст i18n-ключей и таблицы доказательств — целиком продукт этой
дельты):
- `backup.repaired_target_refs` и `backup.dropped_marker_links` —
существуют в `src/i18n/en.json:995-996` и `src/i18n/ru.json:995-996`,
утверждение «существующие … сохраняются» подтверждено, а не
угадано;
- `docs/USER-GUIDE.md`/`docs/USER-GUIDE.ru.md` существуют, термины
«Оптимизировать планы»/«Общие настройки» в них есть;
- `check-docs` (упомянут в матрице доказательств AC10) —
`scripts/check-docs.mjs` существует;
- `check-i18n` (упомянут в той же строке AC10) — **не существует**: нет
такого имени ни в `package.json` (`python3 -c "..."` дамп секции
`scripts`), ни файла `scripts/check-i18n.mjs`, ни упоминания в
`.github/workflows/*.yml`. Единственная реальная механическая проверка
en/ru паритета ключей — тест `test/i18n.test.mjs` («i18n: en and ru
dictionaries carry the same key set»), часть обычного `npm test`, а не
отдельная команда с таким именем. См. находку M5 ниже.
5. Проверена внутренняя согласованность перенумерации: `grep -n '§[0-9]'` по
всему файлу — все перекрёстные ссылки (`§8` в 6.2, AC3, 15.1; `§10` в AC10)
указывают на корректный новый номер соответствующего раздела; провисших
ссылок на старые номера не найдено.
6. Гейты этапа: `npx tsc --noEmit`/`npm test`/`npm run build` не запускались —
диапазон дельты не содержит кода (только `docs/specs/**`), на этапе
`S4-spec-review` продуктового кода ещё нет, гейты по своему условию
неприменимы — то же основание, что в r1. `node scripts/check-docs.mjs` не
запускался: дельта не касается `src/**`.
## Находки
### M5 — AC10 называет несуществующий гейт `check-i18n` как обязательное доказательство (Medium, в скоупе)
**Файл:** `docs/specs/265-import-reference-seam.md`, §15.1 «Матрица
доказательств», строка AC10 (443-454, конкретно строка со списком
`` `check-i18n`, `check-docs`, RU/EN guide/changelog diff, targeted browser
smoke и reviewed canonical golden import preview ``).
Это прямое следствие правки M3 (появление самой таблицы) — то есть новая
находка внутри дельты этого раунда, а не наследие r1.
`check-i18n` не существует нигде в репозитории: ни как npm-скрипт в
`package.json` (полный список ключей `scripts` не содержит такого имени;
единственный i18n-related артефакт — `test/i18n.test.mjs`), ни как файл
`scripts/check-i18n.mjs`, ни как шаг в `.github/workflows/*.yml`. Реальная
механическая проверка паритета en/ru-ключей — тест
`test/i18n: en and ru dictionaries carry the same key set` внутри обычного
`npm test`; `check-docs.mjs` в этой же строке — существующий скрипт
(`scripts/check-docs.mjs`), это единственная корректная часть строки.
Это ровно тот класс дефекта, о котором предупреждает регламент ревью:
утверждение об инструменте, которого нет ни в одном документе и ни в одном
скрипте, вписано в раздел, чья единственная функция — назвать проверяемый
способ доказательства AC (DoR §2.5: «у каждого указано, чем он
доказывается»). Разработчик на S7 либо потратит время на поиск
несуществующей команды, либо спишет её как опечатку без исправления текста
ТЗ — оба исхода хуже правильной строки с самого начала.
**Почему Medium, а не High:** не меняет дизайн, не блокирует понимание
контракта — соседние 9 строк таблицы верны и однозначны, ошибка локальна
к одному имени в одной ячейке.
**Как чинится:** заменить `check-i18n` на существующий механизм — например,
«`npm test` (`test/i18n.test.mjs`, паритет ключей en/ru) плюс ручная сверка
переводов ревьюером» — без изменения остального содержания строки AC10 и
без изменения самого AC10.
## Что проверено и корректно
- Все четыре находки r1 (M1–M4) закрыты предметно: в каждом случае есть новый
фрагмент текста, отвечающий именно на то, что просила находка, а не
косметическая переформулировка (детали — таблица «Закрытие раунда r1»).
- Новые фактические утверждения, добавленные этой дельтой (существующие
i18n-ключи, `check-docs.mjs`, наличие `USER-GUIDE.md`/`USER-GUIDE.ru.md`),
проверены по факту, а не приняты на слово — кроме `check-i18n` (см. M5),
все подтвердились.
- Раздел рисков (§13) содержит по одному пункту на каждый архитектурно
значимый риск, который upstream-документ и r1-ревью уже обсуждали
(ложное совпадение lineage, расхождение Python/TS, память preview,
неполнота матрицы, duplicate-policy vs light-link, fail-closed на legacy,
revalidate race, повреждение геометрии) — пусто не осталось.
- Перенумерация разделов (1→19) внутренне согласована, ни одной "битой"
перекрёстной ссылки на старый номер не найдено.
- Раздел «Что человек увидит» (§2) теперь отвечает на вопрос PROCESS.md §7.1
без терминов реализации, а §1 явно называет desktop/View/kiosk/touch
поверхности — ровно то, чего не хватало в r1.
- `docs/specs/README.md` не тронут в этой дельте (правка не требовалась,
строка таблицы уже добавлена в r1 и остаётся верной).
## Чего не проверял
- Не запускал `typecheck`/`test`/`build`/смоки/инварианты — продуктового кода
в дельте нет, гейты неприменимы на этапе `S4-spec-review` (то же основание,
что в r1).
- Не проверял `node scripts/check-docs.mjs` — дельта не касается `src/**`.
- Не пересматривал технические решения §3–§9, §11–§12, §14, §16–§19 —
дельта их не касается, унаследованы из r1 (раздел выше).
- Не проверял конкретные будущие формулировки переводов en/ru строк отчёта
(`backup.preserved_unresolved_hint` и др.) на стилистическую корректность —
тексты появятся в реализации, это предмет код-ревью.
- Не оценивал эффективность/производительность заново — алгоритмическая
часть (§6, §8, §9, §11) дельтой не изменена, оценка r1 остаётся в силе.
## Вердикт
Все четыре находки r1 закрыты предметно и проверяемо. Дельта этого раунда,
однако, сама вносит одну новую Medium-находку в скоупе — несуществующий
гейт `check-i18n`, названный обязательным доказательством AC10. High-находок
нет, находка вне скоупа не заводится: правка ограничена той же ячейкой той же
таблицы, которую и так предстоит один раз перечитать.
**Вердикт: жёлтый · заход r2 · блокирующих циклов 2/4 · High: 0 · Medium: 1 → в задаче**