Files
houseplan-card/docs/reviews/SPEC-REVIEW-348-r1.md
T
2026-08-28 11:58:46 +03:00

17 KiB
Raw Blame History

SPEC-REVIEW-348-r1

  • Issue: https://github.com/Matysh/houseplan-card/issues/348
  • Этап: spec (PROCESS.md §2.4)
  • Заход: r1 (первый и единственный документ ревью на этот issue)
  • Ветка / SHA материала: issue/348-german-localization @ 2e650a02 (один коммит docs: specify German localization поверх dev@918fc9e2)
  • Трек: полный (обоснование в S2-analysis корректно: несколько поверхностей, новый языковой/loading UX-контракт, влияние на perf budget и touch — критерии §5 не проходят одновременно)
  • Вердикт: зелёный

Скоуп ревью

Диф origin/dev..origin/issue/348-german-localization — только класс C:

docs/specs/348-german-localization.md | 385 +++++++++++++++++++++++++
docs/specs/README.md                  |   1 +

Продуктового кода нет, гейты code-уровня (typecheck/test/build) к предмету ревью неприменимы — это ревью текста ТЗ, а не кода. Ниже — раздел «Гейты и проверки» с тем, что фактически было прогнано для проверки технических утверждений документа.

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

Ревью состязательное: документ прочитан целиком, каждое проверяемое техническое утверждение сверено с реальным состоянием origin/dev, а не принято на слово автора.

  1. Зависимость #62. Проверено gh issue view 62 — S8-merged, то есть инфраструктура (typed registry, locale resolution, parity gates) уже в dev, а не гипотетическая. Прочитан src/i18n/registry.ts: LANGUAGE_REGISTRY, resolveLanguageCode (exact → primary → fallback), languageOptions — контракт locale resolution, который ТЗ обещает не менять, действительно существует в описанном виде.
  2. Бюджет initial View. Число «255 910 B gzip из 256 000 B» — не принято на веру: собран бандл с origin/dev (npm run build + node scripts/bundle-budget.mjs), результат: initial View: 255910 B gzip, lazy editor: 131779 B gzip — точное совпадение с ТЗ, включая экстремально узкий запас (90 B). INITIAL_VIEW_GZIP_BUDGET = 256_000 подтверждён в scripts/bundle-budget.mjs.
  3. Классификация lazy-групп в манифесте. Прочитан scripts/bundle-manifest.mjs: сейчас ровно два динамических класса (lazyOnboardingFiles по имени чанка, всё остальное — lazyEditorFiles). Требование ТЗ добавить третий класс lazyLocaleFiles — реальное изменение этого файла, и он прямо назван в §12 «Затронутые файлы» — не забыт.
  4. Прецедент lazy-loading + bounded retry. Найдены EDITOR_RETRY_ASSET_TOKEN / ONBOARDING_RETRY_ASSET_TOKEN в scripts/bundle-manifest.mjs и рабочие await import('./houseplan-editor-runtime') / import('./houseplan-onboarding-runtime') в src/houseplan-card.ts, а также браузерный тест demo/smoke_lazy_editor_chunk.mjs с перехватом маршрута (page.route). План §6 (dedupe, page-lifetime cache, bounded retry, fail-open English, fingerprint) и план тестов AC4/AC5 (задержанный импорт, provoked failure) — не фантазия, а расширение уже работающего в продукте паттерна на новый субъект (locale вместо editor/onboarding).
  5. Терминология и реальность строк. Проверены по src/i18n/en.json ключи, стоящие за глоссарием ТЗ: markup.partition → «Partition», markup.column → «Column», space.zero_wall_style → «Zero-thickness walls», decor.fill → «Fill», space.glow_enabled/marker.glow_radius_label → «Glow» и т.д. — термины глоссария не изобретены, они соответствуют реальным строкам продукта. Сверено с docs/USER-GUIDE.ru.md: «Space» → «Пространство» в действующей русской терминологии, немецкое «Bereich» не противоречит смыслу.
  6. Мёртвые legacy-ключи. editor.lang_en / editor.lang_ru в src/i18n/en.json существуют и не встречаются больше нигде в src/*.ts и test/*.mjs — заявление §7 «доказанно неиспользуемые» подтверждено поиском, а не принято на слово.
  7. Backend translations. custom_components/houseplan/translations/en.json реально содержит только config/options/issues — соответствует §3.1 («config flow, options flow и repair issue»).
  8. CONFIG-COMPATIBILITY.md не упоминает language вовсе — подтверждает заявление ТЗ «language остаётся строковым полем без изменения schema и миграции», а не пропуск темы.
  9. 390 px как канонический touch-viewport. Подтверждено по demo/smoke_*.mjs (card_controls, color_picker, gear_tabs, plan_picker и др. используют width: 390) — ссылка ТЗ на этот размер в §8 не выдумана.
  10. Существование release-артефактов. docs/USER-GUIDE.md, docs/USER-GUIDE.ru.md, docs/TESTING.md, оба CHANGELOG*.md существуют — §12/§15 указывают на реальные файлы.
  11. Обязательные разделы §7.1 PROCESS.md. Сценарий (§1), что человек увидит до/после (§2), скоуп/не-скоуп перевода (§3), контракт поведения (§4–9), модель данных и миграция (§5, §14 — «миграция не нужна»), AC1…AC10 с доказательством (§10), план автотестов (§11), риски (§13), откат (§14), release-артефакты (§15) — присутствуют по существу, не только по заголовку.

Находки

Ни одной находки уровня High или Medium. Два Low-замечания, оба решением ревьюера сняты без правки (документ остаётся годным для DoR), с записью причины ниже — как того требует §12 PROCESS.md («Оставили в тексте ревью» не считается закрытием, поэтому обе оставлены явной записью, а не формулировкой «доработать»).

L1 — de-CH унаследует «ß», хотя стандартная швейцарская орфография его не использует

docs/specs/348-german-localization.md, §4 правило 3 («Используются настоящие ä, ö, ü, ß…») применяется к единственному словарю de, на который по §5 резолвится и de-CH. Реальный швейцарский стандартный немецкий не использует «ß» вовсе (пишет «ss»), поэтому носитель de-CH увидит орфографически нетипичный для своего региона текст.

Почему не блокирует. Это решение — прямое следствие уже принятого на этапе S2-analysis и не оспоренного владельцем "safe default" — «de-DE/de-AT/de-CH используют общий de» (единый словарь для регионов был явно объявлен и принят молчанием владельца по §2.2 до написания ТЗ). Требовать от одного словаря одновременно двух орфографий — уже отдельная, более крупная задача (региональные варианты внутри одного языка), а не пробел этого ТЗ. Дефект не функциональный: интерфейс остаётся читаемым, AC2 (locale resolution) не страдает.

Решение ревьюера: снято с записью. Если в будущем швейцарские пользователи укажут на несоответствие, это отдельный issue на региональный вариант орфографии, не возврат текущего ТЗ.

L2 — явного блока «принято предположительно, поменять свободно» в конце документа нет

PROCESS.md §7.1 предписывает собирать не-продуктовые технические решения в отдельный блок в конце ТЗ, чтобы ревьюер мог их оспорить одним взглядом. В этом документе такие решения (устройство page-lifetime cache, форма dedupe, retry policy, разбиение ролей в manifest) разбросаны по §6–§7 как формулировки контракта, а не собраны отдельным блоком.

Почему не блокирует. По содержанию все технические решения либо прямо привязаны к проверяемому AC (AC4, AC5, AC6, AC7 — это не «свободно меняемые предположения», а часть контракта, который тестами и будет доказываться), либо повторяют уже работающий в проекте паттерн (editor/onboarding lazy-loading, retry-asset token). Оспаривать в них нечего — ревью выше по каждому пункту нашло реальное покрытие, а не догадку. Форматное требование не задевает проверяемость ТЗ.

Решение ревьюера: снято с записью, правка не нужна.

Что проверено и признано корректным

  • Классификация трека (полный) и перечисление нарушенных критериев §5 в S2-analysis — соответствует содержанию задачи.
  • Зависимость #62 реально S8-merged, её API (LANGUAGE_REGISTRY, resolveLanguageCode, languageOptions) совпадает с тем, что ТЗ обещает расширить, а не заменить.
  • Locale resolution matrix (§5) не меняет существующую политику exact → primary → English — подтверждено чтением resolveLanguageCode.
  • Заявленный текущий bundle budget (255 910 / 256 000 B gzip) — точное, а не приблизительное число; проверено сборкой.
  • План тестов (AC1–AC10) — для каждого назван реалистичный, уже прецедентный на проекте способ доказательства (unit registry-parity, Playwright semantic smoke, delayed-import smoke по образцу smoke_lazy_editor_chunk.mjs, bundle-manifest unit).
  • Не-скоуп (§3.2) корректно исключает контент, который карточка принципиально не переводит нигде (имена HA-сущностей, значения, README) — согласовано со SCOPE.md («мы не редактируем реестр HA», «UI, не документация»).
  • Откат (§14) не требует миграции данных — подтверждено отсутствием language в CONFIG-COMPATIBILITY.md.
  • Оба changelog, оба User Guide, TESTING.md существуют как файлы — release artifacts (§15) указывают на реальные, а не гипотетические цели правки.
  • Открытых продуктовых вопросов действительно не осталось: все пять дефолтов, которые ТЗ фиксирует как решённые (native label, единый словарь для de-*, отсутствие перевода HA-контента, English fallback, отсутствие регрессии EN/RU), были явно объявлены и приняты по правилу «молчание — согласие» ещё в S2-analysis, до написания ТЗ — это соответствует предписанному в PROCESS.md порядку (продуктовые вопросы решаются пачкой, с дефолтом, не в спеке задним числом).

Чего не проверял и почему

  • Гейты typecheck/test/build/bundle:budget на самой ветке задачи — diff ветки не содержит кода (только docs/specs/**), гейты этого рода проверяют код, а не текст ТЗ; прогон дал бы тот же результат, что и на dev, и ничего не доказал бы про качество спецификации. Вместо этого сборка dev использована лишь как источник факта (текущий bundle budget), см. пункт 2 «Как проверялось».
  • node scripts/check-docs.mjs — diff не касается src/**, условие запуска (PROCESS.md §8) не выполнено.
  • Смоки, golden, backend pytest — нет кода для прогона; они появятся только на этапе реализации и будут предметом код-ревью.
  • Точный итоговый размер lazy-loader кода (риск «Loader превышает initial budget даже без словаря», §13 ТЗ) — не проверяем и не можем проверить на этапе спецификации: кода ещё нет. Риск в документе назван явно, с мерой («только доказанная очистка мёртвых i18n keys, budget не повышается») и с проверкой через AC6 (bundle-manifest/budget unit) — этого достаточно для DoR; если реализация не уложится, это остановит код-ревью, а не проходит на этом этапе незамеченным.
  • Ручной аудит немецкого перевода на естественность/грамматику — вне полномочий этого ревью (ревьюер ТЗ не носитель и не переводчик); проверена структура контракта (glossary, правила капитализации, placeholders, no-English-fallback gate), а не текстовое качество конкретных строк, которых на этапе ТЗ ещё и не существует (кроме примеров таблицы §4).

Унаследовано из предыдущего раунда

Неприменимо — это первый и единственный заход (r1), лимит циклов ТЗ на полном треке (4) не тронут.

Итог

ТЗ полное, проверяемое, все десять AC имеют названный способ доказательства, технические утверждения документа сверены с реальным состоянием репозитория и не являются догадками, выданными за факт. Продуктовые вопросы закрыты корректно на этапе аналитики. Два Low-замечания сняты с записью причины. Задача готова к переходу в S5-ready.