Files
houseplan-card/docs/reviews/SPEC-REVIEW-627-r1.md
T
2026-09-24 01:15:34 +00:00

20 KiB
Raw Blame History

SPEC-REVIEW-627-r1

Issue: #627 — «Бюджет бандла: граф онбординга вырос 13,9 → 33,8 КБ gzip без потолка; редакторский в 531 Б от потолка; словари settings/support/topology грузятся для всех языков» Этап: ревью ТЗ (PROCESS.md §2.4). Трек: полный (метка small не выставлена; владелец назвал 3 нарушенных критерия §5 в комментарии-оценке — влияние на производительность, не одна поверхность, сложность > 3). Заход: r1 · блокирующих циклов израсходовано 0 из 4 (лимит для полного трека — 4).

Вердикт

Зелёный. High: 0, Medium: 0 (в скоупе задачи — не заведено, т.к. находок нет), Low: 2, обе сняты этим ревью с записью ниже.

Скоуп ревью

ТЗ живёт в теле issue (правило #517) — рецензируется тело issue #627, раздел ## ТЗ, плюс единственный комментарий-оценка (Matysh, 2026-09-24T01:06:10Z), который фиксирует отказ от лёгкого трека и корректировку AC2 «≤20 КБ» → измеримое значение. Комментариев-возражений или уточнений владельца после этого нет — раунд первый, второго материала не существует, поэтому разделы «Закрытие раунда r0» и «Унаследовано» не применяются (правило появляется только с r2, §2.10).

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

  1. docs/SCOPE.md, AGENTS.md, PROCESS.md §1, §2.4, §2.5, §2.9, §2.10, §4, §5/§5.1, §7.1, §7.2, §12 — прочитаны целиком для этой сессии.
  2. Тело issue #627 и его единственный комментарий получены gh issue view 627 --json body,labels,comments (репозиторий уже локально доступен, gh аутентифицирован).
  3. Фактическая сверка числовых и технических утверждений ТЗ с текущим кодом (обязательный шаг для «не додумано/не выполнимо», раз ТЗ полагается на точные измерения):
    • src/i18n/settings.ts, support.ts, topology.ts — подтверждено, что все три модуля сегодня статически импортируют en/ru/de/fr (совпадает с утверждением «словари грузятся для всех языков»).
    • src/i18n/registry.ts, language-runtime.ts — подтверждено существование LanguageRuntime (две попытки, отпечаток сборки, fallback-состояние) и составного гейта languageRenderGate (inert+aria-busy, ветки cold/warm/ready) — механизм, который ТЗ предлагает переиспользовать по экземпляру на пространство, действительно существует и умеет то, что от него требуется.
    • scripts/bundle-budget.mjs — подтверждены LAZY_EDITOR_GZIP_CEILING = 245_400, LAZY_GRAPH_CEILING_BAND = 2_000, lazyGraphCeilingViolation (полоса ±2000, «выше — отказ», «ниже полосы — опустите потолок») и что правило #593 уже гейтит editor/furniture-art тем же механизмом, который ТЗ просит распространить на onboarding. Числа ТЗ (244 433 / 245 400, запас 967 Б) совпадают с текущими константами и не расходятся с методом счёта.
    • scripts/bundle-manifest.mjs — подтверждено, что lazyOnboardingFiles / lazyOnboardingGzipBytes уже считаются в манифесте (но не гейтятся — точно то, что описывает «Проблема»), и что механизм retry-токенов сейчас держит ровно 7 замен (editor/onboarding/isometric/german/french/furnitureArt/pdf — «1/1/1/1/1/1/1»), что подтверждает посылку «тем же механизмом, что у de/fr» как техническую, а не гипотетическую.
    • docs/USER-GUIDE.ru.md:207-215 — контракт #348 (нейтральный индикатор без вспышки английского, две ограниченные попытки, тост «Не удалось загрузить языковой пакет…» при отказе) подтверждён дословно; ключ toast. locale_load_failed и notifyLanguageLoadFailures найдены в src/houseplan-card.ts:2530 и src/i18n/registry.ts — ТЗ не придумывает поведение, а корректно расширяет уже задокументированный контракт.
    • scripts/mutation-registry.mjs — существующий аналог french-locale- wrong-dictionary (guard smoke_french_locale.mjs) подтверждён; ТЗ ссылается на него как на образец для нового мутанта корректно.
    • Все 13 смоков и 4 существующих unit-теста, названных в «Плане автотестов», проверены на существование файлов (demo/smoke_*.mjs, test/*.test.mjs) — присутствуют все, кроме заявленного новым test/i18n-lazy-namespaces. test.mjs (его отсутствие и есть корректное «новый»).
    • test/zigbee-topology.test.mjs:503-511 — подтверждено, что hasTopologyTranslation('ru'|'de', …) сегодня вызывается синхронно; ТЗ верно называет необходимость перевести эти вызовы на ожидание ensure, иначе после лениизации тест проверял бы откат, а не перевод.
  4. Гейты этого этапа не запускались: ревью ТЗ проверяет текст, а не код — исполняемых гейтов на этой стадии нет, изменений в рабочем дереве не делалось (документ не кладётся в docs/reviews/, см. системную инструкцию).

Проверка обязательных разделов (§7.1)

Все обязательные разделы присутствуют и не пустые: сценарий (персона + поверхность + момент — для обеих персон отдельно, включая явное «не должна добавить ни байта… во View» для домочадцев/киоска) · что человек увидит до и после (одной фразой, без терминов реализации, с явным «английская вспышка … недопустима ни в одном кадре») · проблема (с измеренной таблицей и экспериментом, включая честное признание «исходный AC2 недостижим») · скоуп и не-скоуп (5 пунктов / 4 пункта, включая явный отказ от выноса form-kit из первого кадра как отдельного issue) · контракт поведения (загрузка / холодное открытие / смена языка на лету / отказ / гейт хоста) · UX («новых экранов нет», переиспользуются существующие) · модель данных и миграция («нет», обосновано) · i18n («ключи не меняются») · AC1–AC7, каждый с «чем доказан» и «чем краснеет» · план автотестов (unit/смоки/мутанты/прочее) · риски (5 пунктов) · откат · release-артефакты.

Два продуктовых раздела («сценарий», «что человек увидит») идут первыми и отвечают на вопрос, а не подменяются техническим описанием — соответствует требованию §7.1.

Проверка однозначности и доказуемости AC

AC1–AC7 численно непротиворечивы и проверены на согласованность между собой и с текущими константами (см. «Как проверялось» выше):

  • AC1/AC2: пороги (≤ 28 000 Б, −15 000 Б от 244 433) арифметически совместимы с полосой ±2000 и правилом #593; отдельно указано, что «−6 500 Б от 34 526» слабее, чем «≤ 28 000» (28 026 против 28 000) — избыточная, но не противоречивая формулировка (Low L1, снято, см. ниже).
  • AC3–AC7: каждый привязан к конкретному тесту/смоку и конкретному мутанту, кроме AC7, для которого «чем краснеет» — «любой лишний запрос» в существующем смоке AC4, а не отдельный именованный мутант; это оправдано, так как AC7 проверяет отсутствие запроса, а не логическую ветку — мутанта, ломающего «ничего не грузить», для такого свойства не бывает содержательного.
  • Ни одно AC не описывает поведение, которого нет ни в одном документе: контракт заимствован из #348/USER-GUIDE дословно, а не придуман.

Догадка vs решение

Раздел «Принято предположительно» отделяет ровно то, что процесс разрешает решать без владельца (§7.1: «где хранится состояние… именование… стратегия тестов»): en статически / ru-de-fr лениво, гранулярность чанков «пространство × язык», место загрузчиков в ленивых, а не в первом графе, переиспользование LanguageRuntime, составной гейт хоста, переиспользование тоста. Все шесть пунктов действительно технические (не наблюдаемы пользователем) и корректно помечены как «поменять свободно» — ревьюер не оспаривает ни один: они согласуются с уже проверенным устройством language-runtime.ts и registry.ts.

Продуктовых вопросов владельцу в ТЗ нет; проверка показывает, что их действительно не осталось — единственная точка, которая могла требовать решения владельца («AC2 недостижим»), уже решена в комментарии-оценке самим автором со ссылкой на измерение, а не угадана.

Готовность к DoR (§2.5) — проверено заранее, чтобы не возвращать по мелочи

  • ТЗ есть, ссылка issue↔ТЗ на месте (тело issue).
  • AC1…AC7 пронумерованы, у каждого способ доказательства указан.
  • Затронутые модули названы на уровне класса (A: src/i18n/**, src/houseplan-card.ts, рантаймы; B: scripts/bundle-*.mjs, тесты, смоки, реестр мутантов; D: бандл) — детализация соответствует принятой в проекте практике (не требуется построчный список файлов).
  • i18n: новых ключей нет — пункт закрыт явным «не меняются».
  • Миграция/compatibility: «нет», обосновано отсутствием изменений конфига.
  • Влияние на перф: названо количественно (это и есть предмет задачи).
  • Влияние на touch: не названо явным пунктом в ТЗ — см. Low L2 ниже.
  • Release-артефакты: названы (бандл, манифест), golden — явно «без изменений эталонов», changelog — явно «не нужен» с обоснованием.
  • Откат: есть.
  • Открытых продуктовых вопросов нет (проверено выше).

Находки

Low L1 — избыточное (не противоречивое) условие в AC2

docs/reviews (issue #627, тело, раздел AC2): условие «не менее чем на 6 500 Б ниже 34 526» (≤ 28 026) строго слабее соседнего «≤ 28 000» и никогда не станет самостоятельно связывающим — оно ничего не меняет и не создаёт двусмысленности для реализующего агента (действующий порог всё равно 28 000). Не блокирует; снимаю с записью, а не требую правки ТЗ ради стилистики.

Low L2 — влияние на touch не названо явным пунктом

DoR (§2.5) требует «влияние на touch по docs/TOUCH-SUPPORT.md (View и киоск — блокирующие)» отдельным пунктом; в ТЗ такого пункта нет буквально. Косвенно он закрыт: комментарий-оценка называет ровно три нарушенных критерия лёгкого трека из пяти (перф, одна поверхность, сложность) и явно не называет «влияние на touch» — по методу §5 (нарушенный критерий называется явно, остальные, значит, не нарушены) touch не задет, и по существу задача не трогает ни одного интерактивного элемента, только сеть/тайминг загрузки словарей за уже существующим индикатором. Не блокирую: добавление буквальной строки «Touch: нет влияния — не тронута ни одна интерактивная поверхность» было бы чисто редакционным дополнением, ценность которого меньше стоимости очередного цикла ревью ради одной строки (тот самый принцип, ради которого написан §2.10). Снимаю с записью; при возврате в S3-spec по другой причине эту строку стоит добавить заодно.

Что проверено и корректно

  • Классификация трека (полный, не small) обоснована и совпадает с фактическим объёмом изменения (3 поверхности, влияние на перф, сложность ленивого гейта).
  • Числа проблемы и AC воспроизведены и не противоречат текущему коду и константам гейта бюджета.
  • Технический механизм, на который опирается ТЗ (LanguageRuntime, гейт хоста, retry-токены), существует и умеет ровно то, что от него требуется — задача не полагается на несуществующий код.
  • Контракт поведения — не изобретение, а документированное расширение #348.
  • Все AC проверяемы и у каждого назван падающий мутант или иной способ «умеет падать», кроме AC7, где это оправдано природой утверждения (отсутствие запроса).
  • Не-скоуп разумно ограничивает задачу и не откладывает решение продуктового вопроса под видом технического.

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

  • Не запускал npm run typecheck/npm test/npm run build — на этапе ревью ТЗ кода ещё нет, гонять гейты не по чему.
  • Не проверял вручную числа эксперимента (не коммитился) из «Проблемы» — доверяю им как контексту принятия решения по AC2, они не являются частью AC и не требуют независимого воспроизведения на этом этапе; AC2 их не использует напрямую (там свои измеримые пороги).
  • Не читал полностью историю связанных issues #423/#459/#474/#593/#600/#608/#614 — использовал их только как источник конкретных фактов (константы, наличие функций), которые перепроверил в коде напрямую, а не как источник доверия.

Материал раунда: тело issue #627 (раздел ## ТЗ) на момент чтения 2026-09-24T01:06Z, репозиторий на 0f97e3e639c4eb06b7866b8c1c757d98e2c0a5f7 (dev, только для сверки технических утверждений ТЗ с текущим кодом — ТЗ не привязано к коду и ревьюется как текст issue).


Материал раунда

  • Ветка: dev, коммит 0f97e3e639c4 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: f2603b2998c17fd63d3bc1248b5e2be36e4b598f
    git log --all --format='%H %T' | grep f2603b2998c1
    
  • Тело issue: 11ffae080ad26691a4b8dbd32f7736895c0c25a3f0cabe1b09da0e21666e6562
  • Вердикт конвейера: green · High 0