From 4cabcbe828e0ec7349414cab4626d43f78d95f88 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 3 Sep 2026 09:56:54 +0300 Subject: [PATCH] =?UTF-8?q?chore(gates):=20=D1=83=D0=B1=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20=D1=87=D0=B8=D1=81=D0=BB=D0=BE=D0=B2=D0=BE=D0=B9=20?= =?UTF-8?q?=D1=85=D1=80=D0=B0=D0=BF=D0=BE=D0=B2=D0=B8=D0=BA=20=D0=B1=D1=8E?= =?UTF-8?q?=D0=B4=D0=B6=D0=B5=D1=82=D0=B0=20=D0=B8=D0=B7=20#423?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проверка `assertSupportBundleOwnership` бросала при `initialViewGzipBytes >= 291046` — «граф не стал больше, чем на момент закрытия #423». Запас до порога: пятнадцать байт на бете 1.71.0, сто четыре сейчас. Пятнадцать байт gzip меньше одной строки локали, поэтому первый же посторонний коммит получил бы красный CI с сообщением про копирайт формы поддержки — к которому не имеет отношения. «Граф не вырос» — критерий приёмки на момент задачи, а не свойство продукта. Свойство продукта охраняют `INITIAL_VIEW_GZIP_BUDGET` (300000) и предупреждение о низком запасе: они судят размер целиком и не привязаны к чужому номеру issue. Долговечная половина того же контракта — владение графом (маркеры формы отсутствуют в initial View, присутствуют в ленивом редакторе) — осталась и по-прежнему падает в обе стороны. Новый тест закрепляет, что размер в эту функцию вернуться не может: 0, 291046, 10 МБ и `undefined` проходят одинаково, пока владение соблюдено. Гейты: typecheck зелёный, npm test 1787/1786 pass 0 fail, npm run build зелёный (dist побайтово не изменился — продуктовый код не тронут), node scripts/bundle-budget.mjs зелёный: initial View 290942 Б, бюджет 300000 Б, запас 9058 Б плюс честное предупреждение о запасе. Issue: #429 User-Visible: no --- scripts/bundle-budget.mjs | 36 ++++++++++++++++++++++++++++-------- test/bundle-assets.test.mjs | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/scripts/bundle-budget.mjs b/scripts/bundle-budget.mjs index c492a188..3fa3fae9 100644 --- a/scripts/bundle-budget.mjs +++ b/scripts/bundle-budget.mjs @@ -42,13 +42,39 @@ export const INITIAL_VIEW_GZIP_BUDGET = 300_000; * средние фичи до стены, а не после неё. */ export const LOW_HEADROOM_WARNING_BYTES = 15_000; -export const SUPPORT_LAZY_INITIAL_BASELINE_BYTES = 291_046; + +/** + * Числового храповика здесь больше нет, и это решение, а не упущение (#429). + * + * До #429 функция бросала при `initialViewGzipBytes >= 291 046` — «граф не стал + * больше, чем был на момент закрытия #423». На бете 1.71.0 запас до этого + * порога составлял пятнадцать байт, на момент правки — сто четыре. Пятнадцать + * байт gzip меньше одной строки локали: первый же посторонний коммит получил бы + * красный CI с сообщением про копирайт формы поддержки, к которому не имеет + * отношения. + * + * Гейт, обвиняющий не ту задачу, — худший вид гейта: его выключают, не + * разбираясь, и вместе с ним выключают проверку владения графом, которая как + * раз долговечна. Поэтому число снято, а проверка владения осталась. + * + * Что именно было снято по существу: «граф не вырос» — это критерий приёмки на + * момент задачи, а не свойство продукта. Свойство продукта охраняет общий + * бюджет (`INITIAL_VIEW_GZIP_BUDGET`) и предупреждение о запасе; они судят + * размер целиком и не привязаны к чужому issue. + */ export const SUPPORT_LAZY_MARKERS = [ 'Contact details (email/tg/WhatsApp), optional.', 'Контакт для связи (email/tg/WhatsApp), необязательно.', ]; -/** Keep form-only support copy in the lazy editor graph (#423). */ +/** + * Форма поддержки живёт только в ленивом графе редактора (#423). + * + * Функция судит ВЛАДЕНИЕ, а не размер: маркеры формы обязаны отсутствовать в + * `initialViewFiles` и присутствовать в `lazyEditorFiles`. Размер охраняют + * `assertBundleBudget` и `lowHeadroomWarning` — им для этого не нужен чужой + * номер issue (#429). + */ export function assertSupportBundleOwnership( manifest, root = 'dist', @@ -67,12 +93,6 @@ export function assertSupportBundleOwnership( throw new Error(`support form copy missing from lazy editor graph: ${marker}`); } } - if (manifest.initialViewGzipBytes >= SUPPORT_LAZY_INITIAL_BASELINE_BYTES) { - throw new Error( - `initial View graph ${manifest.initialViewGzipBytes} B gzip did not improve on #423 baseline ` - + `${SUPPORT_LAZY_INITIAL_BASELINE_BYTES} B`, - ); - } } /** Тревога о запасе: `null`, пока его хватает. */ diff --git a/test/bundle-assets.test.mjs b/test/bundle-assets.test.mjs index 90eaf265..ea4ff07f 100644 --- a/test/bundle-assets.test.mjs +++ b/test/bundle-assets.test.mjs @@ -9,7 +9,6 @@ import { } from '../scripts/bundle-manifest.mjs'; import { INITIAL_VIEW_GZIP_BUDGET, LOW_HEADROOM_WARNING_BYTES, - SUPPORT_LAZY_INITIAL_BASELINE_BYTES, assertBundleBudget, assertSupportBundleOwnership, lowHeadroomWarning, } from '../scripts/bundle-budget.mjs'; import { compareBundleTrees, sha256Bytes, verifyBundleTree } from '../scripts/bundle-tree.mjs'; @@ -93,7 +92,6 @@ test('#423 support form copy belongs only to the lazy editor graph', () => { const manifest = { initialViewFiles: ['initial.js'], lazyEditorFiles: ['editor.js'], - initialViewGzipBytes: SUPPORT_LAZY_INITIAL_BASELINE_BYTES - 1, }; const markers = ['lazy English marker', 'lazy Russian marker']; assert.doesNotThrow(() => assertSupportBundleOwnership(manifest, temp, markers)); @@ -105,11 +103,9 @@ test('#423 support form copy belongs only to the lazy editor graph', () => { writeFileSync(join(temp, 'initial.js'), 'header only'); assert.throws( () => assertSupportBundleOwnership( - { ...manifest, initialViewGzipBytes: SUPPORT_LAZY_INITIAL_BASELINE_BYTES }, - temp, - markers, + { initialViewFiles: ['initial.js'], lazyEditorFiles: [] }, temp, markers, ), - /did not improve/, + /missing from lazy editor graph/, ); } finally { rmSync(temp, { recursive: true, force: true }); @@ -275,3 +271,32 @@ test('запас на момент рекалибровки выше порог // Иначе рекалибровка была бы бессмысленной: гейт сразу же начал бы кричать. assert.ok(INITIAL_VIEW_GZIP_BUDGET - 273_697 > LOW_HEADROOM_WARNING_BYTES); }); + +test('#429 проверка владения не судит размер графа', () => { + // Числовой храповик #423 оставлял пятнадцать байт запаса и покрасил бы + // первый же посторонний коммит сообщением про копирайт формы поддержки. + // Гейт, обвиняющий не ту задачу, выключают не разбираясь — вместе с + // долговечной проверкой владения. Здесь закреплено, что размер вернуться в + // эту функцию не может: любое значение проходит, пока владение соблюдено. + const temp = mkdtempSync(join(tmpdir(), 'houseplan-support-size-')); + try { + writeFileSync(join(temp, 'initial.js'), 'header only'); + writeFileSync(join(temp, 'editor.js'), 'lazy English marker · lazy Russian marker'); + const markers = ['lazy English marker', 'lazy Russian marker']; + const base = { initialViewFiles: ['initial.js'], lazyEditorFiles: ['editor.js'] }; + for (const initialViewGzipBytes of [0, 291_046, 10_000_000, undefined]) { + assert.doesNotThrow( + () => assertSupportBundleOwnership({ ...base, initialViewGzipBytes }, temp, markers), + `размер ${initialViewGzipBytes} не должен влиять на проверку владения`, + ); + } + // Размер по-прежнему охраняется — но общим бюджетом, а не чужим номером. + assert.match( + lowHeadroomWarning(9058) || '', + /запас бюджета 9058 Б/, + 'предупреждение о запасе остаётся единственным честным сигналом о размере', + ); + } finally { + rmSync(temp, { recursive: true, force: true }); + } +});