perf: принять шаг изометрии на жесте в бюджетах двух профилей (#585)
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (isometric) (push) Failing after 4m40s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (interaction) (push) Failing after 5m4s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (isometric-stage3) (push) Failing after 4m33s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (blend) (push) Failing after 5m6s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (space-default) (push) Failing after 3m55s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (overlay) (push) Failing after 4m2s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (plan-snap) (push) Failing after 4m1s
Проверка (CI) / Классификация изменённых файлов (push) Successful in 34s
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Skipped
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Successful in 45s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Failing after 1m9s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Проверка (CI) / Геометрия: TS/Python parity исполнена (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Failing after 1m47s
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Failing after 2m21s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (large-house) (push) Failing after 12m40s
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Successful in 2m51s
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Failing after 1m33s
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Successful in 4m31s
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Successful in 3m28s
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 26s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (space-glow) (push) Failing after 29m2s

Полные бенчмарки кандидата v1.76.0 (прогон 35097102695, SHA c3d64789) против
v1.75.0 покраснели на двух скрытых изометрических профилях:

| метрика | v1.75.0 | v1.76.0 | предел |
|---|---|---|---|
| large-house-isometric resizePreview | 603 | 981.4 | 753 |
| large-house-isometric panZoom | 90.8 | 205.2 | 150.8 |
| stage3-dense stateUpdate | 77.5 | 169.1 | 152.5 |
| stage3-dense resizePreview | 582.5 | 765.6 | 732.5 |
| stage3-dense panZoom | 90.6 | 171.7 | 150.6 |

Шаг настоящий и объяснённый: #583 добавил скрытому 2.5D-виду геометрии, а
поиск свободного места для подписей даже после ускорения решёткой стоит вдвое
дороже, чем до #583. Абсолютные потолки не тронуты и держатся с запасом
(panZoom 205 при 600, resize 981 при 2200); все семь пользовательских профилей
зелёные — регрессия целиком внутри вида за `hp_alpha`.

Решение владельца 2026-09-16: принять. Рычаг — допуск в миллисекундах, а не
коэффициент: он покрывает разовый сдвиг уровня и продолжает ловить рост от
нового уровня, тогда как поднятый коэффициент разрешил бы удвоение навсегда.
Значения одинаковы у обоих профилей — контракт #160 требует, чтобы плотный
двойник Stage 3 делил с историческим профилем каждый общий потолок, и тест
`performance-workflow` это стережёт.

Допуски временные. #585 переписывает поиск на перебор границ препятствий
вместо скана диска 48 px; когда он приедет, значения возвращаются к 150/60/75.
Тест `performance-budget` фиксирует и числа, и то, что наблюдённый шаг проходит,
а следующий такой же — уже нет.

Честно о границе метода: сравнение идёт с ПРЕДЫДУЩЕЙ вершиной main, а она уже
несёт эту же линейку, поэтому следующий прогон был бы зелёным и без правки
бюджетов. Правка сделана не ради зелёного прогона, а чтобы принятый уровень был
записан явно и проверялся тестом. Отдельно завожу, что базой стабильного
кандидата должен быть предыдущий стабильный тег, а не вершина main.

npm test 2721/2720/0 fail.

Issue: #585
User-Visible: no
Release: v1.76.0
This commit is contained in:
Codex
2026-09-16 17:36:47 +03:00
parent c3d6478976
commit 9683a59016
4 changed files with 66 additions and 6 deletions
+15
View File
@@ -231,6 +231,21 @@ runner jitter, sat at 16 → 20 against a 19.2 limit on the v1.73.0 stable
comparison. The allowance widens the count check alone to 16 → 21 for that
baseline; it is not a licence for more work per task.
The same two isometric profiles carry a widened **gesture** allowance:
`resizePreviewMs` 450 ms, `panZoomMs` 150 ms and `stateUpdateMs` 120 ms, held
identical on both profiles because the #160 contract requires the Stage 3 dense
twin to share every common ceiling with `large-house-isometric-v1`. Owner decision 2026-09-16, #585: the Full Performance run
of the v1.76.0 stable candidate against v1.75.0 (run 35097102695) measured
resizePreview 603 → 981 ms and panZoom 91 → 205 ms in the hidden 2.5D view.
The step is real and understood — #583 gives that view more geometry, and the
overlay-collision search still costs about twice what it did before #583, even
after the coarse-lattice speed-up. Absolute ceilings did not move (panZoom 205
against 600, resize 981 against 2200) and every user-visible profile stayed
green. The lever is milliseconds, not the ratio: it absorbs one level shift and
still gates growth from the new level. #585 rewrites the search to enumerate
obstacle boundaries instead of scanning the 48 px disc; when it lands, these
allowances go back to 150/60/75.
The `cleanFloor` entry ceiling is 100: the reviewed fixture warms exactly 100
deterministic room/physical-body entries. An extra 20 means that one complete
floor was invalidated and rebuilt, so fixture extensions must recalibrate this
@@ -9,9 +9,9 @@
"openingUpdateMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 75, "hardMaxMs": 1000 },
"overlayInteractionMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 75, "hardMaxMs": 1000 },
"spaceSwitchMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 100, "hardMaxMs": 1800 },
"stateUpdateMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 75, "hardMaxMs": 1000 },
"resizePreviewMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 150, "hardMaxMs": 2200 },
"panZoomMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 60, "hardMaxMs": 600 },
"stateUpdateMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 120, "hardMaxMs": 1000 },
"resizePreviewMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 450, "hardMaxMs": 2200 },
"panZoomMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 150, "hardMaxMs": 600 },
"settingsDialogMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 100, "hardMaxMs": 1200 },
"switchCycleMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 250, "hardMaxMs": 8000 }
},
@@ -7,9 +7,9 @@
"firstStableRenderMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 250, "hardMaxMs": 3500 },
"viewToggleMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 100, "hardMaxMs": 1500 },
"spaceSwitchMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 100, "hardMaxMs": 1800 },
"stateUpdateMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 75, "hardMaxMs": 1000 },
"resizePreviewMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 150, "hardMaxMs": 2200 },
"panZoomMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 60, "hardMaxMs": 600 },
"stateUpdateMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 120, "hardMaxMs": 1000 },
"resizePreviewMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 450, "hardMaxMs": 2200 },
"panZoomMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 150, "hardMaxMs": 600 },
"settingsDialogMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 100, "hardMaxMs": 1200 },
"switchCycleMs": { "stat": "median", "maxRegressionRatio": 0.2, "noiseAllowanceMs": 250, "hardMaxMs": 8000 }
},
+45
View File
@@ -257,6 +257,51 @@ test('interaction aggregate keeps hosted-runner headroom without weakening compo
assert.equal(smoke.timings.editorSeriesMs.hardMaxMs, 750);
});
test('изометрический допуск жеста покрывает ровно принятый шаг #583, не больше (#585)', () => {
const isometric = readBudget('budgets-large-house-isometric.json');
const dense = readBudget('budgets-isometric-stage3-dense.json');
// Полные бенчмарки кандидата v1.76.0 (прогон 35097102695) против v1.75.0:
// скрытый 2.5D стал дороже на жесте — #583 добавил виду геометрии, а поиск
// свободного места для подписей всё ещё вдвое дороже, чем до него. Решение
// владельца 2026-09-16: принять шаг по трём метрикам двух изометрических
// профилей. Рычаг — допуск в миллисекундах, а НЕ коэффициент: он покрывает
// разовый сдвиг уровня и продолжает ловить рост от нового уровня. Чинит шаг
// #585 (перебор границ препятствий вместо скана диска), после него допуски
// возвращаются к 150/60/75.
assert.equal(isometric.timings.resizePreviewMs.noiseAllowanceMs, 450);
assert.equal(isometric.timings.panZoomMs.noiseAllowanceMs, 150);
assert.equal(isometric.timings.stateUpdateMs.noiseAllowanceMs, 120, 'профили-близнецы обязаны совпадать по общим метрикам (#160)');
assert.equal(dense.timings.stateUpdateMs.noiseAllowanceMs, 120);
assert.equal(dense.timings.resizePreviewMs.noiseAllowanceMs, 450);
assert.equal(dense.timings.panZoomMs.noiseAllowanceMs, 150);
for (const budget of [isometric, dense]) {
for (const metric of ['resizePreviewMs', 'panZoomMs', 'stateUpdateMs']) {
assert.equal(budget.timings[metric].maxRegressionRatio, 0.2, 'коэффициент не рычаг');
}
assert.equal(budget.timings.resizePreviewMs.hardMaxMs, 2200, 'абсолютный потолок не двигался');
assert.equal(budget.timings.panZoomMs.hardMaxMs, 600);
assert.equal(budget.timings.modelReadyMs.noiseAllowanceMs, 200, 'загрузка допуска не получала');
assert.equal(budget.timings.spaceSwitchMs.noiseAllowanceMs, 100);
}
for (const file of ['budgets.json', 'budgets-large-house-plan-snap.json', 'budgets-large-house-interaction.json']) {
const other = readBudget(file);
assert.equal(other.timings.panZoomMs.noiseAllowanceMs, 60, `${file} допуска жеста не получал`);
assert.equal(other.timings.resizePreviewMs.noiseAllowanceMs, 150, `${file} допуска ресайза не получал`);
}
// Наблюдённые числа прогона проходят, следующий такой же шаг — нет.
const limit = (budget, metric, baseline) => Math.max(
baseline * (1 + budget.timings[metric].maxRegressionRatio),
baseline + budget.timings[metric].noiseAllowanceMs,
);
assert.ok(limit(isometric, 'resizePreviewMs', 603) >= 981.4, 'принятый шаг проходит');
assert.ok(limit(isometric, 'resizePreviewMs', 603) < 1100, 'следующий шаг того же размера — нет');
assert.ok(limit(isometric, 'panZoomMs', 90.8) >= 205.2);
assert.ok(limit(isometric, 'panZoomMs', 90.8) < 260);
assert.ok(limit(dense, 'stateUpdateMs', 77.5) >= 169.1);
assert.ok(limit(dense, 'resizePreviewMs', 582.5) >= 765.6);
assert.ok(limit(dense, 'panZoomMs', 90.6) >= 171.7);
});
test('isometric long-task count allowance covers the lazy iso-chunk split, nothing else (#507)', () => {
const isometric = readBudget('budgets-large-house-isometric.json');
assert.equal(isometric.longTasks.countNoiseAllowance, 5, 'owner-accepted +2 tasks of the lazy iso-scene-render split plus jitter');