Files
houseplan-card/docs/reviews/SPEC-REVIEW-520-r3.md
T
2026-09-10 15:38:40 +00:00

19 KiB

SPEC-REVIEW-520-r3

Якоря

  • Issue: #520 «Первый устойчивый кадр большого дома вырос до 3.36 с при потолке 3.0 с — регрессия после #509/#500», метка S4-spec-review, состояние OPEN (подтверждено gh issue view 520 --json labels,state, 2026-09-10).
  • ТЗ: тело issue #520, раздел ## ТЗ, продолжает жить в теле issue (#517: архив docs/specs/ закрыт для новых ТЗ) — то же место, что в r1/r2.
  • sha256 текущего тела issue (gh issue view 520 --repo Matysh/houseplan-card --json body -q .body | sha256sum): 5eca6e230c290483bba634505217d17072f4b4a121c4aa2111b08a0fc6d007c3. Отличается от хеша r2 (7f68e4cd99242f44e13e9929cd0a7aa9dc3cb6ae5f871878670 bade18a2436ac) — тело менялось, как и ожидается после возврата на правки.
  • Заход r3. Бюджет циклов §4 до этого ревью: 2/4 (израсходован r1 и r2, оба жёлтые). Вердикт ниже — зелёный, цикла не образует, бюджет остаётся 2/4.
  • Материал кода не менялся между r2 и r3: git log --oneline ad2f9afd.. b49721a0 даёт ровно один коммит — публикацию документа r2 (docs/reviews/SPEC-REVIEW-520-r2.md, 203 добавленных строки, git diff --stat ad2f9afd..b49721a0 -- src/ demo/ scripts/ test/ пуст). Рабочая копия сейчас на b49721a0 (detached HEAD от origin/issue/520-first-frame, подтверждено git status, git rev-parse HEAD). Изменилось только тело issue на GitHub — SHA репозитория тут не причём, доказательство ТЗ находится вне git.

Скоуп ревью

Повторный заход ревью ТЗ (PROCESS.md §2.4, §2.9/§2.10) после жёлтого вердикта r2. Предмет — дельта тела issue между r2 и r3: одна правка, заявленная автором в комментарии 2026-09-10T15:33:29Z («Исправление по ревью ТЗ r2 → заход r3»): AC4 расширен на firstStableRenderMs изометрии, плюс отрицательный результат про cache.entries.cleanFloor изометрии перенесён из документа r2 в текст «Причины». Автор сам называет объём дельты: «одна строка AC4 и одна фраза в «Причине»» — проверено построчно, а не принято на слово (см. «Как проверялось»).

Дельта локальна: правка текста ТЗ, ни ребейза, ни смены контракта, ни новой подсистемы — код продукта между r2 и r3 не менялся вовсе (см. «Якоря»). Полный разбор всей задачи заново не требуется.

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

  1. Получено текущее тело issue #520 (gh issue view 520 --json body) и комментарий автора после r2 (единственный новый комментарий, метка S4-spec-review возвращена).
  2. Прочитан docs/reviews/SPEC-REVIEW-520-r2.md целиком — вердикт, одна находка Medium-1, материал (SHA ad2f9afd2bfe, дерево cc8e3bd73eeb, тело c5e79b63b531…) — и docs/reviews/SPEC-REVIEW-520-r1.md для раздела «Унаследовано».
  3. Находка Medium-1 r2 сверена построчно с текущим текстом, а не принята на слово автора. r2 требовал: строка AC4 должна покрывать firstStableRenderMs изометрии тем же относительным пределом, что и modelReadyMs, — либо явно исключить её одной фразой. Текущая строка AC4 (тело issue, строка 59):

    «timing.firstStableRenderMs.median профиля взаимодействия ≤ 3000 (абсолютный потолок) и обе метрики старта — modelReadyMs и firstStableRenderMs — внутри относительного лимита к базе a44fbd37 на обоих профилях, взаимодействия и изометрии», «чем краснеет»: «превышение любого из четырёх относительных пределов или абсолютного потолка — job красный». Это буквально включает firstStableRenderMs изометрии — предмет находки Medium-1. Закрытие полное, не частичное.

  4. Пересчитаны все четыре относительных предела, которые теперь называет AC4, по формуле demo/performance/evaluate.mjs:73-118 (relativeLimit = max(base*(1+ratio), base+allowance), effective = min(hardMaxMs, relativeLimit)), чтобы убедиться, что счёт «четырёх» верен и не сбит на единицу:
    • modelReadyMs interaction: база 1382, budgets-large-house- interaction.json (maxRegressionRatio: 0.3, noiseAllowanceMs: 200, hardMaxMs: 2500) → max(1796.6, 1582) = 1796.6; кандидат 1930 > 1796.6 — нарушен.
    • firstStableRenderMs interaction: база 2792.7, тот же файл (ratio 0.3, allowance 250, hardMax 3000) → max(3630.5, 3042.7) = 3630.5, effective = min(3000, 3630.5) = 3000 — здесь относительный предел поглощён абсолютным потолком, что и заявляет отдельная первая половина AC4; двойного счёта нет.
    • modelReadyMs isometric: база 2165.6, budgets-large-house- isometric.json (ratio 0.2, allowance 200, hardMax 3000) → max(2598.72, 2365.6) = 2598.72; кандидат 2626.3 > 2598.72 — нарушен (установлено ещё в r1).
    • firstStableRenderMs isometric: база 2227, тот же файл (ratio 0.2, allowance 250, hardMax 3500) → max(2672.4, 2477) = 2672.4; кандидат 2678.2 > 2672.4 — нарушен (предмет находки Medium-1 r2, теперь покрыт текстом AC4). Итого ровно четыре относительных предела, все они действительно сейчас нарушены кандидатом — формулировка AC4 точна, не расширяет и не сужает набор относительно того, что показывает «Симптом».
  5. Проверено согласование «Контракта» (строка 46: «изометрия … чинится тем же изменением и проверяется теми же метриками старта (AC4)») с новым текстом AC4 — формулировка «тем же способом» из r2 заменена на «теми же метриками старта», что точнее отражает: AC4 теперь буквально покрывает обе метрики обоих профилей, а не «тот же способ» в общем виде. Расхождений между «Контрактом» и таблицей AC больше нет.
  6. Проверена вторая часть дельты — фраза в «Причине» (строка 24): «В бюджетах изометрии этот потолок не поднимался и уже равен 100 — там правка не нужна». Перепроверено чтением файлов (не принято на слово): grep -n cleanFloor demo/performance/budgets-{isometric-smoke,large- house-isometric,interaction-smoke,large-house-interaction}.json — оба файла изометрии дают 100, оба файла взаимодействия — 180. Совпадает с отрицательным результатом, который сам r2 получил самостоятельно (не то же самое, что просто скопировать вывод r2, — числа сверены заново на этом SHA, хоть код и не менялся).
  7. git log --oneline ad2f9afd..b49721a0 и git diff --stat — единственный новый коммит это публикация документа r2; продуктовый код (src/**, demo/**, scripts/**, test/**) не менялся между r2 и r3 — гейты кода этому раунду не относятся (см. «Чего не проверял»), как и в r1/r2.
  8. Проверены state/labels issue (OPEN, bug, P1, S4-spec-review) — соответствуют ожиданиям заголовка задания (заход r3, 2/4 циклов).

Находки

Нет находок, переживших проверку. Единственная находка r2 (Medium-1) закрыта полностью — см. «Закрытие раунда r2» и п.3–5 «Как проверялось». Новых находок делом дельты не выявлено: правка локальна (одна строка AC4, одна фраза «Причины»), обе проверены построчно и не вводят новых противоречий с остальным текстом ТЗ.

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

Находка r2 Чем закрыта Где это видно
Medium-1 — амендированный AC4 не покрывал firstStableRenderMs изометрии (второе число того же предложения «Симптома», что и уже покрытый modelReadyMs) AC4 переписан: «обе метрики старта — modelReadyMs и firstStableRenderMs — внутри относительного лимита… на обоих профилях»; «чем краснеет» — «любого из четырёх относительных пределов»; «Контракт» синхронизирован формулировкой «теми же метриками старта (AC4)» Тело issue, AC4 (строка 59) и «Контракт» (строка 46). Численно перепроверено в п.4 «Как проверялось» — все четыре предела названы верно, счёт не сбит

Унаследовано из r2 (и, транзитивно, из r1)

Приняты без повторной проверки — дельта r2→r3 их не задевает, код между раундами не менялся:

  • Причина регрессии измерена, не догадка: три технических утверждения о поведении wrapped-свойств Lit — r1 сверил дословно с исходником @lit/reactive-element (docs/reviews/SPEC-REVIEW-520-r1.md, «Как проверялось» п.4, материал SHA 27ea23c1ec25, дерево f80bcdd58b72).
  • M1 (раздел «Риски») и M2 (AC3) из r1 — закрыты в r2 и сверены там построчно (docs/reviews/SPEC-REVIEW-520-r2.md, «Как проверялось» п.3, «Закрытие раунда r1»), текст этих разделов между r2 и r3 не менялся.
  • AC1 (source-scan тест по образцу config-adoption-ownership. test.mjs) и AC2 (юнит на onBodyReplaced) — доказуемы дёшево и детерминированно, текст этих строк не менялся с r1 (сверено построчно с текущим телом в этом раунде — строки идентичны цитатам r1/r2).
  • AC5 (cache.entries.cleanFloor ≤ 100 на двух interaction-файлах бюджетов) — привязан к точным файлам, не менялся; числа перепроверены заново в п.6 «Как проверялось» этого раунда (не просто унаследованы).
  • Откат (revert двух строк static properties + потолка 180) и release-артефакты (User-Visible: no, разблокирует v1.74.0-beta.1) — просты, однозначны, текст не менялся с r1.
  • Продуктовая рамка (администратор дома, поверхность — первый показ большого дома) совпадает с docs/SCOPE.md J1 и фикстурой 60 комнат/200 устройств — r1 сверил, текст не менялся.
  • Выбор полного трека — верен, не оспаривается ни разу за три раунда.
  • Ни одного вопроса владельцу не требуется — весь материал технический (см. обоснование r1: «продуктовых развилок в задаче нет»), подтверждено в r1 и не оспорено в r2; делта r3 тоже чисто техническая (числа, счёт относительных пределов) — вывод не меняется.

Документы-источники: docs/reviews/SPEC-REVIEW-520-r1.md (материал — ветка issue/520-first-frame на 27ea23c1ec25, дерево f80bcdd58b72), docs/reviews/SPEC-REVIEW-520-r2.md (материал — та же ветка на ad2f9afd2bfe, дерево cc8e3bd73eeb).

Что проверено и корректно (этот раунд)

  • Единственная правка фикс-комментария автора (2026-09-10T15:33:29Z) подтверждена чтением текущего тела issue, а не принята по заявлению — Medium-1 r2 закрыт полностью, не частично.
  • Счёт «четырёх относительных пределов» в AC4 арифметически точен: пересчитаны все четыре по опубликованной формуле и файлам бюджетов, все четыре сейчас действительно нарушены кандидатом — AC4 не занижает и не завышает то, что реально показывает «Симптом».
  • «Контракт» (строка 46) и таблица AC4 больше не расходятся в формулировке охвата изометрии — предыдущая асимметрия («тем же способом» против буквально одной метрики) устранена.
  • Фраза «Причины» про потолок cache.entries.cleanFloor изометрии (100, правка не нужна) подтверждена чтением всех четырёх файлов бюджетов заново на этом SHA, не просто перенесена из документа r2 на слово.
  • Код между r2 и r3 не менялся (git log, git diff --stat на диапазоне ad2f9afd..b49721a0) — дешёвые гейты кода (tsc, test, build) этому раунду не относятся по той же причине, что и в r1/r2: предмет этапа — текст ТЗ, продуктовой правки в дереве ещё нет.
  • Остальной текст ТЗ (унаследованное) не тронут дельтой — проверка инвариантов и код-ревью инфраструктуры (мутант, тесты) остаются предметом код-ревью после S5-ready.

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

  • Гейты кода (tsc --noEmit, npm test, npm run build, check-docs.mjs) — не гонял: src/** не менялся ни разу за три раунда, весь диапазон коммитов ветки — диагностика бенчмарка (test(perf):). Предмет этапа spec — текст ТЗ.
  • npm run invariants — не гонял: геометрия и её ссылки не тронуты ни кодом, ни правкой ТЗ.
  • Реальный прогон Full Performance — не запускал; числа «Симптома» и пересчитанные относительные пределы взяты из текста issue и файлов бюджетов в дереве, перепроверены арифметически по опубликованной формуле, не переизмерены заново на живом рантайме.
  • Регистрацию мутанта adoption-bodies-declared-reactive в scripts/mutation-gate.mjs — предмет код-ревью, ещё не существует (продуктовой правки AC1/AC2 в дереве ещё нет).
  • Полную сверку разделов §7.1, не тронутых дельтой (Риски, откат, release-артефакты, продуктовая рамка) — заново не перечитывал построчно, только сверил, что их текст байт-в-байт совпадает с тем, что видели r1 и r2 (см. «Унаследовано»).

Вердикт

Зелёный. High: 0, Medium: 0. ТЗ готово к разработке (S5-ready). Единственная находка r2 закрыта полностью и точно; новых находок дельта не вносит. Бюджет циклов §4 не расходуется (зелёный вердикт цикла не образует) и остаётся 2/4.



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

  • Ветка: issue/520-first-frame, коммит b49721a0bd98 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 50d8affd923b0a5e133ee1e453bb9388a5f44c78
    git log --all --format='%H %T' | grep 50d8affd923b
    
  • Тело issue: 60492a3aec5ff7fed54f7b32c040b7a118526c209609d736ee68e116eb8f0aa6
  • Вердикт конвейера: green · High 0