mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Запас ушёл с 26 КБ до 8.3 КБ за сутки. По документам код-ревью видно, что это не диффузное расползание, а один шаг плюс обычная работа: #317 256127 · #318 256091 · #341 256046 · #354 257212 · #159 256828 #357 271143 <- +14 КБ за один заход #20 271455 · #361 272848 · #359 272469 · #360 273697 <- текущий факт Шаг на #357 — plan-art мебели: 44 top-view символа в eager-графе, которые платит каждый план, включая планы без единого предмета мебели. Потолок 282000 -> 300000. Правило #352 сохранено: 273697 x 1.10 = 301067, то есть 300000 остаётся внутри надбавки ~10% над измеренным фактом. Запас возвращается к 26.3 КБ — примерно к тому, что было до #357. Запись честная и в комментарии сказана прямо: рекалибровка ничего не ускоряет и ничего не чинит. Она фиксирует новую норму и возвращает гейту способность красить того, кто вырастил бандл, а не того, кто пушнул последним. Настоящий рычаг — ленивый граф, варианты 1 и 2 из #367. Добавлено предупреждение: пока запас меньше 15000 Б, гейт печатает ::warning:: и строку в summary. Прежняя редакция полагалась на то, что человек заметит тренд в выводе; за сутки его не заметил никто, потому что каждая отдельная строка выглядела нормально. Текст предупреждения называет лечение — иначе следующий читатель поднимет потолок ещё раз и назовёт это решением. Тест закрепляет обе стороны: надбавка не больше 10% и не меньше 5% (меньше — возврат лотереи «красит последний коммит»), тревога срабатывает строго ниже порога, превышение описывается как превышение. Три мутанта проверены руками, один добавлен в реестр. Issue: #367 User-Visible: no
120 lines
6.4 KiB
JavaScript
120 lines
6.4 KiB
JavaScript
#!/usr/bin/env node
|
||
import { readFileSync } from 'node:fs';
|
||
import { resolve } from 'node:path';
|
||
import { pathToFileURL } from 'node:url';
|
||
|
||
// #352: the budget guards the CLASS of regression — tens of kilobytes from
|
||
// an accidentally imported dependency or an eagerly bundled dictionary —
|
||
// not every byte. v1.69.0-beta.1 shipped at 255 993 B gzip against a
|
||
// 256 000 B ceiling: seven bytes of headroom turned the gate into a
|
||
// lottery where the unlucky LAST commit goes red, not the one that grew
|
||
// the bundle (5740324b was exactly that). The ceiling therefore keeps a
|
||
// deliberate ~10% allowance over the measured fact; the fact and headroom
|
||
// are printed on every run so the trend is visible long before the wall.
|
||
//
|
||
// #367, рекалибровка 29.08. Запас съеден с 26 КБ до 8.3 КБ, и это не диффузное
|
||
// расползание, а один шаг плюс обычная работа. Факт по документам код-ревью:
|
||
//
|
||
// #317 256 127 · #318 256 091 · #341 256 046 · #354 257 212 · #159 256 828
|
||
// #357 271 143 ← +14 КБ за один заход
|
||
// #20 271 455 · #361 272 848 · #359 272 469 · #360 273 697 ← текущий факт
|
||
//
|
||
// Шаг на #357 — plan-art мебели: 44 top-view символа в eager-графе, которые
|
||
// платит КАЖДЫЙ план, включая планы без единого предмета мебели. Остальные
|
||
// приросты по килобайту и относятся к обычным фичам.
|
||
//
|
||
// Поэтому запись честная: рекалибровка НИЧЕГО не ускоряет и ничего не чинит.
|
||
// Она фиксирует, что 273 697 Б — новая норма, и возвращает рабочий запас, чтобы
|
||
// гейт снова красил того, кто вырастил бандл, а не того, кто пушнул последним.
|
||
// Настоящий рычаг остаётся прежним и описан в #367: вынести plan-art мебели в
|
||
// ленивый чанк (−13.6 КБ, вариант 1) либо русский словарь (вариант 2).
|
||
//
|
||
// Measured fact at recalibration (dev @ 360, 29.08.2026): 273 697 B gzip.
|
||
// 273 697 × 1.10 = 301 067 — потолок 300 000 держится внутри правила ~10%.
|
||
export const INITIAL_VIEW_GZIP_BUDGET = 300_000;
|
||
|
||
/**
|
||
* Порог, ниже которого запас перестаёт быть запасом (#367).
|
||
*
|
||
* Прежняя редакция полагалась на то, что человек заметит тренд в выводе. За
|
||
* сутки запас ушёл с 26 КБ до 8.3 КБ, и не заметил никто — потому что каждая
|
||
* отдельная строка выглядела нормально. Предупреждение срабатывает за две
|
||
* средние фичи до стены, а не после неё.
|
||
*/
|
||
export const LOW_HEADROOM_WARNING_BYTES = 15_000;
|
||
|
||
/** Тревога о запасе: `null`, пока его хватает. */
|
||
export function lowHeadroomWarning(headroom, threshold = LOW_HEADROOM_WARNING_BYTES) {
|
||
if (!Number.isFinite(headroom) || headroom >= threshold) return null;
|
||
if (headroom < 0) {
|
||
return `бюджет превышен на ${-headroom} Б — гейт уже красный`;
|
||
}
|
||
return `запас бюджета ${headroom} Б, меньше порога ${threshold} Б:`
|
||
+ ' следующая средняя фича упрётся в стену. Рекалибровка это не лечит —'
|
||
+ ' смотрите ленивые графы (#367)';
|
||
}
|
||
|
||
export function assertBundleBudget(manifest, budget = INITIAL_VIEW_GZIP_BUDGET) {
|
||
if (manifest?.schema !== 1 || !Array.isArray(manifest.files)) {
|
||
throw new Error('invalid houseplan-assets.json');
|
||
}
|
||
if (!manifest.lazyEditorFiles?.length) {
|
||
throw new Error('bundle has no lazy editor graph');
|
||
}
|
||
if (!manifest.lazyLocaleFiles?.length) {
|
||
throw new Error('bundle has no lazy locale graph');
|
||
}
|
||
if (manifest.initialViewFiles.some((path) => manifest.lazyEditorFiles.includes(path))) {
|
||
throw new Error('initial View graph overlaps lazy editor graph');
|
||
}
|
||
if (manifest.initialViewFiles.some((path) => manifest.lazyLocaleFiles.includes(path))) {
|
||
throw new Error('initial View graph overlaps lazy locale graph');
|
||
}
|
||
if (manifest.lazyLocaleFiles.some((path) => manifest.lazyEditorFiles.includes(path)
|
||
|| manifest.lazyOnboardingFiles?.includes(path))) {
|
||
throw new Error('lazy locale graph overlaps an editor graph');
|
||
}
|
||
if (manifest.initialViewGzipBytes > budget) {
|
||
throw new Error(
|
||
`initial View graph ${manifest.initialViewGzipBytes} B gzip exceeds ${budget} B budget`,
|
||
);
|
||
}
|
||
return {
|
||
initialViewGzipBytes: manifest.initialViewGzipBytes,
|
||
lazyEditorGzipBytes: manifest.lazyEditorGzipBytes,
|
||
lazyLocaleGzipBytes: manifest.lazyLocaleGzipBytes,
|
||
};
|
||
}
|
||
|
||
if (import.meta.url === pathToFileURL(process.argv[1] || '').href) {
|
||
try {
|
||
const manifest = JSON.parse(readFileSync(resolve('dist/houseplan-assets.json'), 'utf8'));
|
||
const result = assertBundleBudget(manifest);
|
||
const headroom = INITIAL_VIEW_GZIP_BUDGET - result.initialViewGzipBytes;
|
||
const lines = [
|
||
`initial View: ${result.initialViewGzipBytes} B gzip`
|
||
+ ` (budget ${INITIAL_VIEW_GZIP_BUDGET} B, headroom ${headroom} B)`,
|
||
`lazy editor: ${result.lazyEditorGzipBytes} B gzip`,
|
||
`lazy locale: ${result.lazyLocaleGzipBytes} B gzip`,
|
||
];
|
||
for (const line of lines) console.log(line);
|
||
const warning = lowHeadroomWarning(headroom);
|
||
if (warning) console.log(`::warning::${warning}`);
|
||
// #352: the trend belongs where humans look — the run summary.
|
||
if (process.env.GITHUB_STEP_SUMMARY) {
|
||
const { appendFileSync } = await import('node:fs');
|
||
appendFileSync(process.env.GITHUB_STEP_SUMMARY, [
|
||
'### Бюджет бандла',
|
||
`| граф | gzip | бюджет | запас |`,
|
||
`|---|---|---|---|`,
|
||
`| initial View | ${result.initialViewGzipBytes} B | ${INITIAL_VIEW_GZIP_BUDGET} B | ${headroom} B |`,
|
||
...(warning ? ['', `> ${warning}`] : []),
|
||
'',
|
||
].join('\n'));
|
||
}
|
||
} catch (error) {
|
||
console.error(error instanceof Error ? error.message : String(error));
|
||
process.exit(1);
|
||
}
|
||
}
|