Files
houseplan-card/docs/reviews/SPEC-REVIEW-354-r1.md
T
2026-08-28 14:45:28 +00:00

16 KiB
Raw Blame History

SPEC-REVIEW-354-r2

  • Issue: https://github.com/Matysh/houseplan-card/issues/354
  • Этап: ревью ТЗ (PROCESS.md §2.4), лёгкий трек (small)
  • Заход: r2 · блокирующих циклов израсходовано 1 из 2 (лимит ТЗ на лёгком треке — 2; r1 был жёлтым и потратил 1 цикл — §4/#227)
  • ТЗ: тело issue #354, «Ревизия 2» (файл в docs/specs/ отсутствует — корректно для small)
  • SHA на момент ревью: 149b2af7 (dev, рабочее дерево чистое; с момента r1 (77302eaf) в код не внесено ни одного продуктового коммита — единственный новый коммит 149b2af7 кладёт документ ревью r1, продуктового/тестового кода не касается)
  • Вердикт: зелёный

Примечание о нумерации захода

Стартовые данные этой сессии указывали «Заход: r1 · блокирующих циклов израсходовано 0 из 2». Это не соответствует фактическому состоянию: в docs/reviews/SPEC-REVIEW-354-r1.md (закоммичен 149b2af7) и в комментариях issue уже есть завершённый раунд r1 — жёлтый вердикт от claude (комментарий IC_kwDOTOcLQM8AAAABRRPAyw, 2026-08-28T14:40:01Z, SHA 77302eaf), за которым последовала правка автора («Ревизия 2 ТЗ по r1», комментарий IC_kwDOTOcLQM8AAAABRRPa0g) и возврат метки S4-spec-review. Раунд, который разбирает эта сессия, — второй по факту (r2), а не первый; бюджет цикла к этому моменту уже потратил 1 из 2 (жёлтый вердикт r1 расходует бюджет по §4). Документ поэтому назван и пронумерован как r2, вопреки стартовым данным — иначе он затёр бы уже существующий SPEC-REVIEW-354-r1.md или конфликтовал бы с ним по номеру, ровно тот сценарий, от которого предостерегает сам номер захода. Расхождение стартовых данных со стоянием репозитория — само по себе не блокирует эту задачу, но стоит показать тому, кто обслуживает конвейер.

Скоуп

Без изменений относительно r1: собрать продакшн-LANGUAGE_RUNTIME в src/i18n/registry.ts как new LanguageRuntime(...) вместо рукописного литерала (germanDictionary/germanPending/germanFailed/settleGerman), чтобы существующий test/i18n-runtime.test.mjs действительно проверял код продакшна, плюс попутный (N7) тост при отказе загрузки словаря локали. Ревизия 2 не меняет предмет задачи и не расширяет её — правит формулировку одного контракта (К2) и переименовывает один i18n-ключ. Конфликта со docs/SCOPE.md по-прежнему нет (см. SPEC-REVIEW-354-r1.md, раздел «Скоуп» — наследуется, ниже).

Дельта r1 → r2

git diff <SHA r1>..HEAD не показателен: SHA r1 (77302eaf) и текущий (149b2af7) отличаются только doc-коммитом ревью r1, продуктового кода нет вообще (стадия ТЗ). Предмет дельты — правка тела issue #354, зафиксированная автором как «Ревизия 2 ТЗ по r1» (комментарий IC_kwDOTOcLQM8AAAABRRPa0g). Сверено построчно с текстом, процитированным в SPEC-REVIEW-354-r1.md:

  1. К2 — было (цит. по r1, находка M1): «Тост показывается ... Карточка подписывается при подключении... показывает тост» (без указания, какая из четырёх поверхностей рендер-гейта). Стало: «Тост показывается ТОЛЬКО в houseplan-card (View — единственная поверхность с тост-инфраструктурой _showToast)... Остальные три поверхности рантайма (space-card, GUI-редакторы обеих карточек) остаются при console.warn, как сейчас, — тост-инфраструктура там не заводится.»
  2. Ключ тоста — было i18n.load_failed (находка L1). Стало toast.locale_load_failed, в К2 и в AC4.
  3. Остальной текст (К1, К3, AC1–AC3, «Откат», трейлер User-Visible: yes) не изменился — сверено посимвольно с цитатами в SPEC-REVIEW-354-r1.md (разделы «Что проверено и корректно», «Скоуп»).

Дельта локальна (правка формулировки одного контракта плюс переименование одного ключа), не задевает новую подсистему, не меняет контракт поведения сверх уже согласованного в r1, по объёму несопоставима с исходной задачей — условие «разбор остаётся полным» (PROCESS.md §2.10) не наступает. Разбор этого раунда ограничен дельтой: заново проверялись только К2 (N7) и ключ тоста — единственное, что дельта задевает.

Как проверялось (r2)

  1. Прочитано текущее тело issue #354 целиком, сверено с цитатами r1 — найдены ровно две правки (см. «Дельта» выше), больше отличий нет.
  2. Прочитан комментарий владельца «Ревизия 2 ТЗ по r1» — подтверждает, что обе правки целенаправленно закрывают M1 и L1, а не что-то другое.
  3. Перечитан текст К2 revision 2 на однозначность: названа ровно одна поверхность (houseplan-card/View), явно перечислены три поверхности, остающиеся при console.warn, явно сказано «тост-инфраструктура там не заводится» — двух прочтений эта формулировка больше не допускает.
  4. grep -n '"toast\.' src/i18n/en.json — подтверждён неймспейс toast.* (40+ существующих ключей); grep -rn "locale_load_failed\|i18n.load_failed" src/ test/ demo/ — пусто: новый ключ не конфликтует с существующим кодом и ещё не реализован (ожидаемо для стадии ТЗ).
  5. grep -n "6.3\|houseplan-space-card" docs/specs/348-german-localization.md — подтверждён раздел «6.3. Render gate» и список из четырёх поверхностей, на который опирается формулировка К2 (та же ссылка, что использовал r1).
  6. git log 77302eaf..HEAD --oneline — один коммит (149b2af7, doc r1), без изменений в src/**/test/**: код и его состояние идентичны зафиксированным в r1, повторная проверка К1/К3/AC1–AC3 по коду не нужна.

Гейты (tsc/test/build) не гонялись — стадия ревью ТЗ, кода по задаче ещё нет; как и в r1, это не пропуск, а неприменимость этапа.

Находки

Нет. High: 0, Medium: 0, Low: 0.

Обе находки r1 закрыты правкой текста, новых находок дельта не вносит.

Закрытие раунда r1

Находка r1 Чем закрыта Где это видно
M1 (Medium, в скоупе) — К2 не называл, на какой из четырёх поверхностей рендер-гейта показывается тост N7; формулировка «Карточка» в единственном числе была совместима с двумя разными объёмами работы. К2 переписан: «Тост показывается ТОЛЬКО в houseplan-card (View...)... Остальные три поверхности рантайма (space-card, GUI-редакторы обеих карточек) остаются при console.warn, как сейчас, — тост-инфраструктура там не заводится.» Формулировка совпадает с предложенным в r1 вариантом по умолчанию почти дословно. Тело issue #354, раздел «Контракт», К2, первый и последний абзацы.
L1 (Low, зафиксирована) — ключ i18n.load_failed был единственным вне пространства toast.*. Ключ переименован в toast.locale_load_failed — и в К2, и в AC4. Тело issue #354, К2 («новым ключом toast.locale_load_failed») и AC4 («текстом toast.locale_load_failed»).

Унаследовано из r1

Без повторной проверки в этом раунде — код и тесты с r1 не менялись (единственный новый коммит — doc-коммит ревью, класс C), проверка велась только над текстом ТЗ:

  • Скоуп задачи и соответствие docs/SCOPE.md — принято по SPEC-REVIEW-354-r1.md, раздел «Скоуп» (SHA 77302eaf).
  • К1 (замена литерала на new LanguageRuntime(...)) реализуема без побочных поломок — все четыре вызывающих места используют только контракт state/dictionary/ensure — принято по SPEC-REVIEW-354-r1.md, раздел «Что проверено и корректно», пп. 1–2 (SHA 77302eaf, строки кода houseplan-card.ts:10713, editor.ts:118, space-card.ts:766, space-editor.ts:60).
  • АС1–АС3 однозначны и механически проверяемы (grep, instanceof-юнит, мутационный тест на К3) — принято по SPEC-REVIEW-354-r1.md, раздел «Что проверено и корректно» (SHA 77302eaf); текст АС1–АС3 в ревизии 2 не менялся (сверено построчно, см. «Дельта» выше).
  • Паритет-тесты (test/i18n.test.mjs) действительно стерегут новый ключ независимо от его имени — принято по SPEC-REVIEW-354-r1.md, пп. 7 и L1 (SHA 77302eaf); вывод не зависит от конкретного имени ключа, поэтому переименование в toast.locale_load_failed его не меняет.
  • Существующий смок demo/smoke_german_locale.mjs уже содержит сценарий «оба ретрая упали → English», в который AC4 добавляет проверку тоста без новой инфраструктуры смоков — принято по SPEC-REVIEW-354-r1.md, п. 8 (SHA 77302eaf).
  • Подписка/отписка (К2) соответствует принятой конвенции subscribeXxx(...) — принято по SPEC-REVIEW-354-r1.md, п. 9 (SHA 77302eaf).
  • Открытый нефинирующий пункт для код-ревью: не проверялось, как subscribeLanguageLoadFailures-колбэк получит код локали de из warn(message, error) (сигнатура класса передаёт только текст). Не находка (решаемо замыканием при единственном сегодня ленивом языке), но стоит явно проговорить на код-ревью, если добавится второй ленивый язык — перенесено из SPEC-REVIEW-354-r1.md, раздел «Чего не проверял».

Что проверено и корректно (r2, по дельте)

  • К2 revision 2 однозначен: ровно одна поверхность для тоста (houseplan-card), явно перечислены три поверхности без тоста, явно сказано, что новая тост-инфраструктура на них не заводится — двух прочтений формулировка не допускает, находка M1 закрыта по существу, а не косметически.
  • Новый ключ toast.locale_load_failed следует существующей конвенции именования тостов (проверено grep по src/i18n/en.json, 40+ ключей в том же пространстве) и не конфликтует с уже существующим кодом (grep пуст).
  • Правка не расширяет скоуп и не вводит новый UX-контракт: критерии лёгкого трека (§5 PROCESS.md) по-прежнему выполнены — ревизия 2 их не колеблет (одна поверхность тоста явно зафиксирована как единственная, а не расширена на четыре).
  • АС4 (после ревизии) по-прежнему проверяем механически: юнит на subscribeLanguageLoadFailures, смок smoke_german_locale с проверкой card._toast, мутационный тест на «тост выброшен».

Чего не проверял

  • Код по задаче ещё не написан (стадия ТЗ) — гейты tsc/test/build не запускались, как и в r1; на этой стадии они неприменимы.
  • Не проверял заново K1/K3/AC1–AC3 по коду — код с момента r1 не менялся (см. «Дельта» и «Унаследовано из r1» выше), текст этих пунктов ТЗ тоже не менялся.
  • Не проверял реальные тексты переводов для toast.locale_load_failed (en/ru/de) — их ещё нет; сверка с паритет-тестами и glossary docs/specs/348-german-localization.md — задача код-ревью.

Итог

Вердикт зелёный: обе находки r1 (M1 Medium, L1 Low) закрыты точечной правкой текста, дельта не вносит новых находок и не задевает то, что r1 уже принял. Issue переходит в S5-ready.