16 KiB
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:
- К2 — было (цит. по r1, находка M1): «Тост показывается ... Карточка
подписывается при подключении... показывает тост» (без указания, какая из
четырёх поверхностей рендер-гейта). Стало: «Тост показывается ТОЛЬКО в
houseplan-card(View — единственная поверхность с тост-инфраструктурой_showToast)... Остальные три поверхности рантайма (space-card, GUI-редакторы обеих карточек) остаются приconsole.warn, как сейчас, — тост-инфраструктура там не заводится.» - Ключ тоста — было
i18n.load_failed(находка L1). Сталоtoast.locale_load_failed, в К2 и в AC4. - Остальной текст (К1, К3, AC1–AC3, «Откат», трейлер
User-Visible: yes) не изменился — сверено посимвольно с цитатами вSPEC-REVIEW-354-r1.md(разделы «Что проверено и корректно», «Скоуп»).
Дельта локальна (правка формулировки одного контракта плюс переименование одного ключа), не задевает новую подсистему, не меняет контракт поведения сверх уже согласованного в r1, по объёму несопоставима с исходной задачей — условие «разбор остаётся полным» (PROCESS.md §2.10) не наступает. Разбор этого раунда ограничен дельтой: заново проверялись только К2 (N7) и ключ тоста — единственное, что дельта задевает.
Как проверялось (r2)
- Прочитано текущее тело issue #354 целиком, сверено с цитатами r1 — найдены ровно две правки (см. «Дельта» выше), больше отличий нет.
- Прочитан комментарий владельца «Ревизия 2 ТЗ по r1» — подтверждает, что обе правки целенаправленно закрывают M1 и L1, а не что-то другое.
- Перечитан текст К2 revision 2 на однозначность: названа ровно одна
поверхность (
houseplan-card/View), явно перечислены три поверхности, остающиеся приconsole.warn, явно сказано «тост-инфраструктура там не заводится» — двух прочтений эта формулировка больше не допускает. grep -n '"toast\.' src/i18n/en.json— подтверждён неймспейсtoast.*(40+ существующих ключей);grep -rn "locale_load_failed\|i18n.load_failed" src/ test/ demo/— пусто: новый ключ не конфликтует с существующим кодом и ещё не реализован (ожидаемо для стадии ТЗ).grep -n "6.3\|houseplan-space-card" docs/specs/348-german-localization.md— подтверждён раздел «6.3. Render gate» и список из четырёх поверхностей, на который опирается формулировка К2 (та же ссылка, что использовал r1).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, раздел «Скоуп» (SHA77302eaf). - К1 (замена литерала на
new LanguageRuntime(...)) реализуема без побочных поломок — все четыре вызывающих места используют только контрактstate/dictionary/ensure— принято поSPEC-REVIEW-354-r1.md, раздел «Что проверено и корректно», пп. 1–2 (SHA77302eaf, строки кода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, раздел «Что проверено и корректно» (SHA77302eaf); текст АС1–АС3 в ревизии 2 не менялся (сверено построчно, см. «Дельта» выше). - Паритет-тесты (
test/i18n.test.mjs) действительно стерегут новый ключ независимо от его имени — принято поSPEC-REVIEW-354-r1.md, пп. 7 и L1 (SHA77302eaf); вывод не зависит от конкретного имени ключа, поэтому переименование вtoast.locale_load_failedего не меняет. - Существующий смок
demo/smoke_german_locale.mjsуже содержит сценарий «оба ретрая упали → English», в который AC4 добавляет проверку тоста без новой инфраструктуры смоков — принято поSPEC-REVIEW-354-r1.md, п. 8 (SHA77302eaf). - Подписка/отписка (К2) соответствует принятой конвенции
subscribeXxx(...)— принято поSPEC-REVIEW-354-r1.md, п. 9 (SHA77302eaf). - Открытый нефинирующий пункт для код-ревью: не проверялось, как
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) — их ещё нет; сверка с паритет-тестами и glossarydocs/specs/348-german-localization.md— задача код-ревью.
Итог
Вердикт зелёный: обе находки r1 (M1 Medium, L1 Low) закрыты точечной
правкой текста, дельта не вносит новых находок и не задевает то, что r1 уже
принял. Issue переходит в S5-ready.