17 KiB
SPEC-REVIEW-520-r2
Якоря
- 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. - sha256 текущего тела issue (
gh issue view 520 --repo Matysh/houseplan-card --json body -q .body | sha256sum):7f68e4cd99242f44e13e9929cd0a7aa9dc3cb6ae5f871878670bade18a2436ac. Отличается от хеша r1 (5477d052e0e87182f51a9b3b05d37fce0a7ada3d9dc66c10f92 7f64564ccc6e4) — тело менялось, что и ожидается после возврата на правки. - Заход r2. Бюджет циклов §4 до этого ревью: 1/4 (израсходован r1, жёлтый вердикт). Вердикт ниже — снова жёлтый, поэтому расходует цикл: после этого ревью бюджет 2/4.
- Материал кода не менялся:
git log --oneline -5на момент этого ревью — тот же диапазон, что видел r1 (ad2f9afd— публикация документа r1,27ea23c1/3d439805/37ef970d— диагностика бенчмарка,test(perf):, без единой строки вsrc/**). Изменилось только тело issue.
Скоуп ревью
Повторный заход ревью ТЗ (PROCESS.md §2.4, §2.10) после жёлтого вердикта r1.
Предмет — дельта тела issue между r1 и r2: три правки, заявленные автором
в комментарии 2026-09-10T15:23:01Z («M1 — раздел «Риски»», «M2 — AC3», «M3 —
изометрия»), плюс всё, до чего эта дельта дотягивается (AC4, симметрия
профилей). Остальной текст ТЗ (продуктовая рамка, AC1/AC2/AC5, откат,
release-артефакты, «принято предположительно») дельту не задевает —
наследуется из r1 без повторной проверки (см. раздел «Унаследовано»).
Дельта локальна: правка текста ТЗ, ни ребейза, ни смены контракта, ни новой подсистемы — полный разбор всей задачи заново не требуется.
Как проверялось
- Получено текущее тело issue #520 (
gh issue view 520 --json body) и три новых комментария после r1 (фикс-комментарий автора, меткаS4-spec-reviewвозвращена). - Прочитан
docs/reviews/SPEC-REVIEW-520-r1.mdцеликом — вердикт, три находки Medium, материал (SHA27ea23c1ec25, деревоf80bcdd58b72, тело5477d052e0e8…). - Каждая находка r1 сверена построчно с текущим текстом, а не принята
на слово автора:
- M1 (риски) — раздел
### Рискив теле issue теперь существует, 4 пункта. Проверил сам утверждение «потребителейelementPropertiesв дереве нет»:grep -rn "elementProperties" src test scripts demo --exclude-dir=srv— 0 совпадений, подтверждает пункт 1 раздела. - M2 (AC3) — строка AC3 в таблице теперь помечена
(не гейт, запись), колонка «чем краснеет» заменена на «— · критерий поглощён AC1+AC2+AC4»; AC3 больше не выдаёт себя за независимо доказуемый гейт. - M3 (изометрия) — AC4 расширен до «
modelReadyMsобоих профилей — взаимодействия и изометрии — внутри относительного лимита»; в «Контракте» добавлено «изометрия чинится тем же изменением и проверяется тем же способом (AC4)».
- M1 (риски) — раздел
- Симметрию M3 перепроверил количественно, а не по формулировке.
Пересчитал по формуле
demo/performance/evaluate.mjs:73-118(effective = min(hardMaxMs, max(base*(1+ratio), base+allowance))) не толькоmodelReadyMs(как это сделал r1), но и второе число из того же предложения «Симптома» —firstStableRenderMsпрофиля изометрии. Прочитаны бюджетыdemo/performance/budgets-large-house-isometric.json:6-7:firstStableRenderMs:maxRegressionRatio: 0.2,noiseAllowanceMs: 250,hardMaxMs: 3500. См. находку Medium-1 ниже — результат разошёлся с тем, что покрывает амендированный AC4. - Проверено, что потолок
cache.entries.cleanFloorвbudgets-large-house-isometric.jsonиbudgets-isometric-smoke.jsonуже равен 100 (не поднимался в914e8402, который трогал только два файла interaction-профиля) — значит для AC5 симметрии с изометрией не требуется, вопрос из хвоста M3 («и его budgets-файлов, если они регрессировали похожим образом по cleanFloor») закрыт отрицательным результатом: там регресса нет. git log --oneline -5иgit status— рабочая копия наad2f9afd, той же, что видел r1; продуктовый код (src/**) не менялся между раундами, гейты кода этому раунду не относятся (см. «Чего не проверял»).
Находки
Medium-1 — амендированный AC4 не покрывает второе число из того же предложения «Симптома»: firstStableRenderMs изометрии
Файл: тело issue #520, AC4 (строка 59 полученного тела) против «Симптома» (строка 3) и «Контракта» (строка 46).
«Симптом» приводит для профиля изометрии два числа одним предложением:
«Профиль изометрии — то же: modelReadyMs 2165.6 → 2626.3 при относительном
потолке 2598.72, firstStableRenderMs 2227 → 2678.2». Правка M3 (закрытие
находки r1) добавила в AC4 только первое из них — modelReadyMs «обоих
профилей». Второе число того же предложения контракт не упоминает вовсе.
Пересчитано по той же формуле, которую сам автор и r1 применили к
modelReadyMs (demo/performance/evaluate.mjs:107-118,
budgets-large-house-isometric.json:7: maxRegressionRatio: 0.2,
noiseAllowanceMs: 250, hardMaxMs: 3500):
relativeLimit = max(2227 * 1.2, 2227 + 250) = max(2672.4, 2477) = 2672.4
effective = min(hardMaxMs=3500, relativeLimit=2672.4) = 2672.4
Кандидат 2678.2 > 2672.4 — превышение на 5.8 мс (≈0.22 %). Тот же класс
«мягкого» (не абсолютного) нарушения, что и modelReadyMs изометрии
(2626.3 против 2598.72, превышение ≈1.05 %), который M3 признал достаточным
основанием для правки AC4. По одинаковому стандарту оба числа одного
предложения должны попасть в контракт одинаково — либо оба, либо ни одного с
явным объяснением почему.
Это не формальность: относительные пределы (в отличие от абсолютного
firstStableRenderMs ≤ 3000 профиля interaction) не гейтуют обычный
Validate, они видны только при ручном чтении «полного сравнения» —
единственный документ, который скажет будущему код-ревьюеру, что именно
сверять в этом логе, — это текст AC4. Формулировка «Контракта» («изометрия
… проверяется тем же способом (AC4)», строка 46) сейчас утверждает больше,
чем буквально требует таблица AC4: в контракте — «тем же способом» для
изометрии в целом, а в AC4 — только одна из двух её метрик.
Чем чинится: одной правкой AC4 — добавить firstStableRenderMs
изометрии в тот же относительный чек, либо явно исключить его одной фразой
(например, «в пределах шума 250 мс, отдельно не гейтуем») там же, где ТЗ уже
объясняет статус AC3. Не блокирует контракт — правка одна и дешёвая.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| Medium-1 — отсутствует раздел «Риски» (§7.1) | Добавлен раздел ### Риски, 4 пункта; названный ревьюером риск (elementProperties) снят с указанием способа проверки |
Тело issue, раздел ### Риски; проверено grep -rn "elementProperties" src test scripts demo --exclude-dir=srv → 0 совпадений |
| Medium-2 — AC3 не имеет дешёвого способа доказательства | AC3 явно понижен до записи: помечен (не гейт, запись), «чем краснеет» заменено на «поглощён AC1+AC2+AC4» — больше не выдаёт себя за независимый гейт |
Тело issue, строка AC3 таблицы AC |
| Medium-3 — регрессия изометрии не отражена в контракте/AC | AC4 расширен на modelReadyMs обоих профилей; в «Контракте» явно сказано, что изометрия чинится тем же изменением |
Тело issue, AC4 (строка 59) и «Контракт» (строка 46). Частично — см. новую находку Medium-1 этого раунда: второе число того же предложения «Симптома» (firstStableRenderMs изометрии) осталось непокрытым |
Унаследовано из r1
Приняты без повторной проверки — дельта их не задевает:
- Причина регрессии измерена, не догадка: три технических утверждения о
поведении
wrapped-свойств Lit (createProperty, форсированная запись вchangedPropertiesна первом апдейте, работаrequestUpdateс необъявленным именем) — r1 сверил дословно с исходником@lit/reactive-element(docs/reviews/SPEC-REVIEW-520-r1.md, раздел «Как проверялось» п.4, материал SHA27ea23c1ec25, деревоf80bcdd58b72). - AC1 (source-scan тест по образцу существующего
config-adoption-ownership.test.mjs) и AC2 (юнит на вызовonBodyReplaced) — доказуемы дёшево и детерминированно, текст этих строк между r1 и r2 не менялся. - AC5 (
cache.entries.cleanFloor≤ 100 на двух interaction-файлах бюджетов) — привязан к точным файлам, числа сверены построчно; не менялся. - Откат (revert двух строк
static properties+ потолка 180) и release-артефакты (User-Visible: no, разблокируетv1.74.0-beta.1) — просты, однозначны, не менялись. - Продуктовая рамка (администратор дома, поверхность — первый показ
большого дома) совпадает с
docs/SCOPE.mdJ1 и фикстурой 60 комнат/200 устройств — r1 сверил, текст не менялся. - Выбор полного трека — верен, не оспаривается.
- Ни одного вопроса владельцу не требуется — весь материал технический, подтверждено и в r1, и в этом раунде.
Документ-источник: docs/reviews/SPEC-REVIEW-520-r1.md, материал —
ветка issue/520-first-frame на 27ea23c1ec25, дерево f80bcdd58b72.
Что проверено и корректно (этот раунд)
- Все три пункта фикс-комментария автора (
2026-09-10T15:23:01Z) подтверждены чтением текущего тела issue, а не приняты по заявлению — M1 и M2 закрыты полностью, M3 закрыт частично (см. Medium-1 выше). - Потолки
cache.entries.cleanFloorв бюджетах изометрии (budgets-large- house-isometric.json,budgets-isometric-smoke.json) не поднимались в914e8402и уже равны 100 — симметричная правка AC5 для изометрии не требуется, это установлено чтением файлов, а не предположением. - Код между раундами не менялся (
git log,git statusнаad2f9afd) — дешёвые гейты (tsc,test,build) этому раунду не относятся по той же причине, что и в r1: предмет этапа — текст ТЗ, продуктовой правки в дереве ещё нет.
Чего не проверял
- Гейты кода (
tsc --noEmit,npm test,npm run build,check-docs.mjs) — не гонял:src/**не менялся ни в r1, ни между r1 и r2, весь диапазон коммитов — диагностика бенчмарка (test(perf):). npm run invariants— не гонял: геометрия и её ссылки не тронуты.- Реальный прогон
Full Performance— не запускал; числа Симптома (включая пересчитанныйfirstStableRenderMsизометрии) взяты из текста issue и перепроверены только арифметически по опубликованной формуле и файлам бюджетов, не переизмерены заново на живом рантайме. - Регистрацию мутанта
adoption-bodies-declared-reactiveвscripts/mutation-gate.mjs— предмет код-ревью, ещё не существует.
Вердикт
Жёлтый. High: 0, Medium: 1 (в скоупе задачи — чинится автором в тексте ТЗ,
без нового issue). Возврат в «ТЗ в работе» (S3-spec).
Материал раунда
- Ветка:
issue/520-first-frame, коммитad2f9afd2bfe— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
cc8e3bd73eeb7274db2725b8b308e12dc59a36e4git log --all --format='%H %T' | grep cc8e3bd73eeb - Тело issue:
c5e79b63b5312eb40c5243fba19bd003f9b72e11b83fed51c1b1522547cc349c - Вердикт конвейера:
yellow· High 0