@vernikr/size-report 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,8 +10,11 @@
10
10
 
11
11
  ## Статус
12
12
 
13
- **Выпуск 1.1.1 (2026-09-15).** Инструмент живёт отдельным пакетом: имя в
14
- реестре — `@vernikr/size-report` (выпуск переименования, числа не изменились).
13
+ **Выпуск 1.3.0 (2026-09-15).** Инструмент живёт отдельным пакетом: имя в
14
+ реестре — `@vernikr/size-report` (публикуется тегом из CI, без секрета). Настроек
15
+ проект может не заводить вовсе: без файла инструмент выводит их из самого проекта и
16
+ говорит об этом строкой, а `--init` закрепляет выведенное файлом (чем этот шаг
17
+ отличается от прежнего — `CHANGELOG.md` 1.3.0).
15
18
  Версия — в манифесте, а у выпуска есть `CHANGELOG.md` с разделом «Что изменится
16
19
  в числах»:
17
20
  таблица чисел в нём не пересказ, а замер на фикстуре, который сверяется с живым
@@ -79,12 +82,17 @@
79
82
  дальше он не упоминается без имени проекта.
80
83
  **Пункт R-1.2 волны 1** (`REFACTOR.md`): в пакете есть линтер — правила те же, что у
81
84
  проекта-потребителя, плюс запрет склейки операторов в одну строку; всё настоящее
82
- дерево (37 файлов) даёт ноль замечаний, `fixtures/` не линтуются — там данные.
85
+ дерево (102 файла) даёт ноль замечаний, `fixtures/` не линтуются — там данные.
83
86
 
84
87
  **Пункты R-1.1 и R-2.1 волн 1–2** (`REFACTOR.md`): вычислительная часть отчёта одна
85
88
  (`src/derived.js`) — страница исполняет тот же код, что считает статическую таблицу,
86
- и разметку для неё строит обычный исходник `src/page/app.js`, а не строка внутри
89
+ и разметку для неё строят обычные исходники (`src/page/*.js`), а не строки внутри
87
90
  движка; артефакт и разметка страницы при этом совпали со старым выводом побайтово.
91
+ Главы программы страницы разделены по предметам — состояние выбора, узлы, панель,
92
+ таблица, сборка (`WORKLOG.md` §62): вклейка склеивает их подряд, поэтому собранная
93
+ страница осталась той же побайтово, а у глав одна область видимости — это записано
94
+ в `eslint.config.js`, потому что `import` между зовущими друг друга главами завёл
95
+ бы кольцо связей.
88
96
 
89
97
  **Отчёт собирается и в проекте с модулями в `.js`** (`REFACTOR.md` R-4.6): гард
90
98
  стриппера понимает обе формы — скрипт и модуль, — поэтому подключение не требует
@@ -100,7 +108,7 @@
100
108
  есть в дереве, таблица файлов сходится с ним в обе стороны),
101
109
  `test/docs-commands.test.js` (команды и ключи есть в справке, причины отказа
102
110
  совпадают с реестром движка, ссылки на разделы ведут в существующие),
103
- `test/docs-numbers.test.js` (числа проверок и целей — факт) и
111
+ `test/docs-numbers.test.js` (числа проверок — факт) и
104
112
  `test/docs-pin.test.js` (пример установки ведёт на ревизию, чья справка знает
105
113
  названные команды), а с выпуском добавился пятый — `test/changelog.test.js`
106
114
  (версия выпуска — версия манифеста, а таблица «что изменится в числах» — не
@@ -176,8 +184,7 @@
176
184
  86 мс на каждую клетку, плюс разбор опирается на экспериментальный API (без него
177
185
  гард отступает к прежнему `node --check` — медленнее, но не мягче).
178
186
  Прогон подешевел втрое: `pnpm test` 15,7 → **5,6 с** (тогда в наборе было 39
179
- проверок), `pnpm run parity:live` 23,8 → **8,3 с**. Бюджет времени переснят с
180
- запасом на следующую проверку, а не под сегодняшнее число: набор стоит
187
+ проверок), `pnpm run parity:live` 23,8 → **8,3 с**. Набор стоит
181
188
  **23,4–29,9 с** при 124 проверках — в зависимости от загрузки машины: окна с
182
189
  загрузкой 18–70 несравнимы (в спокойном — 23,4–24,2 с, в занятых — 26,6–29,9 с;
183
190
  в среде без настроек git — 27,4 с, с `CI=1` — 29,9 с), и это свойство окна, а не
@@ -193,41 +200,38 @@
193
200
  прибавило** — 23,2 с и до него, и после: обе новые проверки измерены отдельным
194
201
  прогоном, а не выведены из разброса. Из общего времени **+8,5 с** — десять проверок хука
195
202
  (`test/hook.test.js`: сам он идёт 17,9–18,5 с и становится самым долгим файлом
196
- набора, а та же ревизия без него — 15,8–16,6 с при 107 проверках). Цель не
197
- двигалась, и это решение, а не пропуск: цель тогда была одна — полный набор
198
- **≤ 28 с** (запас 2,3 с) — измеренное в неё укладывается, а поднимают цель по делу и с измерением,
199
- а не под занятую машину: интеграционные
203
+ набора, а та же ревизия без него — 15,8–16,6 с при 107 проверках). Интеграционные
200
204
  прогоны (клон, коммиты, слияние, отказы) дешевле не сделать, не ослабив проверку.
201
- `pnpm run parity:live` **≤ 15 с** (9,3 с в обеих средах). Замеры, машина и разброс —
205
+ `pnpm run parity:live` 9,3 с в обеих средах. Замеры, машина и разброс —
202
206
  `REFACTOR.md` §5.
203
207
 
204
- **Прогонов два, и у каждого своя цель** (`REFACTOR.md` R-5.5). Цена проверки в этом
205
- наборе — не объём файла, а сколько раз файл запускает инструмент и git: запуск — это
206
- процесс Node, а клон фикстуры и сборка артефакта — сотни миллисекунд. Поэтому
207
- быстрый прогон собирает то, что доказывает по прочитанному (исходники, дерево,
208
- справка, эталонные числа на общей фикстуре), а полный добавляет то, что гоняет
209
- инструмент по многу раз на своих клонах, коммитит и ставит хуки; причина для каждого
210
- дорогого файла названа построчно в `tools/suites.js`, там же снимок стоимостей
211
- (перемерить — `pnpm run suites:measure`).
212
-
213
- | Прогон | Команда | Проверок | Цель |
214
- |---|---|---|---|
215
- | Быстрый — каждая правка | `pnpm test` | **57 из 131** | **≤ 10 с** |
216
- | Полный — выкладка и CI | `pnpm test:all` | **131** | **≤ 32 с** |
217
-
218
- Ни одна проверка не потеряна и не ослаблена: полный прогон запускает все 132 теми же
208
+ **Прогонов два, и выбор между ними по цене файла, а не по алфавиту**
209
+ (`REFACTOR.md` R-5.5). Цена проверки в этом наборе — не объём файла, а сколько раз
210
+ файл запускает инструмент и git: запуск — это процесс Node, а клон фикстуры и сборка
211
+ артефакта — сотни миллисекунд. Поэтому быстрый прогон собирает то, что доказывает по
212
+ прочитанному (исходники, дерево, справка, эталонные числа на общей фикстуре), а
213
+ полный добавляет то, что гоняет инструмент по многу раз на своих клонах, коммитит и
214
+ ставит хуки; причина для каждого дорогого файла названа построчно в
215
+ `tools/suites.js`.
216
+
217
+ | Прогон | Команда | Проверок |
218
+ |---|---|---|
219
+ | Быстрый — каждая правка | `pnpm test` | **65 из 167** |
220
+ | Полный — выкладка и CI | `pnpm test:all` | **167** |
221
+
222
+ Ни одна проверка не потеряна и не ослаблена: полный прогон запускает все 167 теми же
219
223
  файлами, а быстрый берёт их часть. Умолчание — полный: файл становится быстрым только
220
224
  явно и с причиной, поэтому новое дорогое не может тихо уехать в быстрый. Стерегут это
221
- двое, и по-разному: `test/suites.test.js` — объявление (полнота классификации,
222
- причины, потолок стоимости быстрого файла), а сам прогон (`tools/run-tests.js`)
223
- замер: он печатает стоимость каждого файла своим запуском, складывает числа проверок
224
- и валится, если вышел за цель вдвое. Снимок стоимости проверяется только замером,
225
- и это его законное место: подделанная стоимость у быстрого файла объявление проходит,
226
- а прогон её ловит. Цель при этом не порог: занятое окно растягивает прогон, и
227
- прогон, не уложившийся в цель, говорит это словами («ЦЕЛЬ НЕ ДОСТИГНУТА»), а не
228
- показывает зелёную галку при 29 с. CI зовёт полный прогон дваждыобычной средой и
229
- без настроек машины. Числа и цели в этой таблице сверяются сторожем документации
230
- (`test/docs-numbers.test.js`), а не живут второй копией без присмотра.
225
+ объявление `test/suites.test.js` (полнота классификации и причина у каждого файла) и
226
+ сторож документации `test/docs-numbers.test.js` (числа в таблице выше).
227
+
228
+ **Целей по времени у прогонов нет, и это решение, а не пропуск.** Секунды зависят от
229
+ окна машина бывает под очень разной нагрузкой, поэтому ни набор, ни CI за время
230
+ не валятся, и документ секунд не обещает: `pnpm run suites:measure` печатает
231
+ длительность каждого файла отдельным прогоном сам прогон печатает её рядом с
232
+ галочкой), но это измерение, а не порог. Разделение держится признаком файлачем он
233
+ занят, а не сколько идёт. CI зовёт полный прогон дважды: обычной средой и без настроек
234
+ машины (`GIT_CONFIG_GLOBAL=/dev/null`).
231
235
 
232
236
  **Обещанное пакетом сведено к факту.** Список поставки называл четыре пути,
233
237
  которых в репозитории нет (`dist/`, `templates/`, `CHANGELOG.md`, `LICENSE`):
@@ -242,14 +246,45 @@ fixture`) и больше не зависит ни от того, держит
242
246
  пакета `npx size-table --write`).
243
247
 
244
248
  **Проверки идут сами (шаг 6 плана, `.github/workflows/ci.yml`).** На каждый пуш и
245
- на каждый запрос правки один job проходит семь шагов теми же командами, что и у
246
- себя локально: строгий линтер, набор проверок, тот же набор в среде, где настроек
247
- машины нет вовсе (`GIT_CONFIG_GLOBAL=/dev/null`), работу из собранного тарболла,
248
- сверку с историей проекта-потребителя и воспроизводимость обоих эталонов.
249
+ на каждый запрос правки один job `verify` зовёт **одну команду** `pnpm run verify`;
250
+ список шагов живёт в одном месте (`tools/gates/run.js`) и совпадает с локальным,
251
+ поэтому проверки, которой нет в профиле, в CI быть не может (это стережёт
252
+ `test/gates-verify.test.js`). В профиле: строгий линтер, датчики раздувания,
253
+ набор проверок, тот же набор в среде, где настроек машины нет вовсе
254
+ (`GIT_CONFIG_GLOBAL=/dev/null`), работу из собранного тарболла, сверку с историей
255
+ проекта-потребителя и воспроизводимость обоих эталонов. Покрытие под c8 дороже
256
+ (полный набор под ним) и живёт в slow-профиле — `pnpm run verify:slow`,
257
+ `.github/workflows/verify-slow.yml` по расписанию.
249
258
  Секретов job не требует: история потребителя лежит в репозитории бандлом на той
250
259
  же ревизии, что записана в эталоне (`fixtures/live/`), а пересъём идёт во временный
251
260
  каталог и сверяется с закоммиченным — рабочее дерево остаётся чистым. Матрицы по
252
- версиям Node и публикаций нет намеренно: этот проход про контроль.
261
+ версиям Node нет намеренно: этот проход про контроль.
262
+
263
+ **Выпуск — это тег (`.github/workflows/release.yml`).** `git push origin v1.2.3`
264
+ прогоняет тот же полный набор, сверяет версию манифеста с тегом, проверяет работу
265
+ из собранного пакета и отправляет его в реестр — без секрета и без кода из
266
+ аутентификатора: публикация идёт по удостоверению GitHub Actions (trusted
267
+ publishing), которое npm принимает вместо токена. Издатель заведён один раз и живёт
268
+ на стороне npmjs.com, а не в репозитории: `npm trust github @vernikr/size-report
269
+ --file release.yml --repo vernikr/size-report --allow-publish` (то же самое — кнопка
270
+ Trusted Publisher в настройках пакета), права **publish** и stage publish; проверить,
271
+ что связь есть, — `npm trust list @vernikr/size-report`. Выпуск `1.2.0` прошёл именно
272
+ так: `v1.2.0` → 44 с, `+ @vernikr/size-report@1.2.0`, удостоверение подписано и
273
+ записано в журнал прозрачности.
274
+
275
+ Одна ловушка раннера стоила отдельной правки, и она не про этот пакет, а про
276
+ `setup-node`: с `registry-url` действие пишет в `.npmrc` строку
277
+ `_authToken=${NODE_AUTH_TOKEN}`, npm считает учётные данные заданными и за
278
+ удостоверением OIDC **не идёт** — публикация падает 404 при верно заведённом
279
+ издателе. Поэтому `registry-url` здесь не указан (реестр и так по умолчанию тот же, а
280
+ явный адрес живёт в `publishConfig`), и это стережёт `test/release.test.js`. Черновой
281
+ прогон из Actions («Run workflow»: по умолчанию он ничего не публикует) проходит весь
282
+ список до самого пути публикации: гоняет полный набор, проверяет работу из тарболла и
283
+ собирает пакет на черновой версии (`1.2.0` → `1.2.1-draft.0`, чтобы реестр не отказал
284
+ в уже выпущенном номере). Настроен ли издатель, черновой прогон не показывает:
285
+ `--dry-run` не обменивается удостоверением и проходит вообще без учётных данных
286
+ (проверено в пустом каталоге: код 0 без токена) — правду об этом даёт только настоящий
287
+ тег, и он её дал.
253
288
 
254
289
  Первым же прогоном CI окупился: шаг живого паритета упал не на расхождении чисел,
255
290
  а на самой проверке — вывод процессов собирался как строка, и многобайтовый символ,
@@ -347,8 +382,8 @@ deльт задан один раз и по артефакту: рост зел
347
382
  `src/metrics.js`), поэтому разойтись не могут. Минификатора нет (установка без необязательных
348
383
  зависимостей, платформа без него) — метрика отступает к упрощению, способ говорит
349
384
  об этом словами, а прогон отдаёт **код 4**, а не молчание: числа при этом те же, что
350
- у прежнего способа, — побайтово со эталоном. Черновик `--init` ведёт новые проекты
351
- сразу на сжатие; цена названа прямо в его подсказке. Файл, который минификатор не
385
+ у прежнего способа, — побайтово со эталоном. Выведенный профиль ведёт новые проекты
386
+ сразу на сжатие (и `--init` закрепляет то же самое); цена названа прямо в его подсказке. Файл, который минификатор не
352
387
  разобрал (разметка в `.js`, чужой синтаксис), — отказ кодом 2 с причиной от него
353
388
  самого и двумя готовыми выходами.
354
389
 
@@ -382,13 +417,13 @@ deльт задан один раз и по артефакту: рост зел
382
417
  живой истории (95 строк × 27 колонок, 1,23 МБ текста) тот же отчёт идёт
383
418
  **1,55 → 6,35 с** — умножается именно сбор истории, а не таблица: токенов в
384
419
  «сейчас» — **303 705**, то есть 4,05 Б на токен. Отсюда и цена набора проверок:
385
- **7,3–7,9 → 10,4 с** при 66 → 73 проверках (запас и новый бюджет — ниже). Черновик
386
- `--init` ведёт новые проекты сразу на токены.
420
+ **7,3–7,9 → 10,4 с** при 66 → 73 проверках (запас и новый бюджет — ниже). Выведенный
421
+ профиль ведёт новые проекты сразу на токены.
387
422
 
388
423
  **Волна 0 чистки пройдена** (`REFACTOR.md`): у отказов командной строки появились
389
424
  коды выхода и справка вместо стека, `--help` отвечает, `--page` и `--write`
390
425
  создают недостающий каталог, подсказка в отказе ведёт к работающей команде, а
391
- черновик `--init` больше не предлагает колонкой саму таблицу — иначе первая же
426
+ вывод настроек больше не предлагает колонкой саму таблицу — иначе первая же
392
427
  проверка настроек его отвергала.
393
428
 
394
429
  **Появились две команды: полнота и объяснение** (шаг 5 плана). `size check`
@@ -437,10 +472,25 @@ deльт задан один раз и по артефакту: рост зел
437
472
  | `CHANGELOG.md` | История выпусков и, у каждого выпуска, раздел «Что изменится в числах»: у кого числа поедут и почему |
438
473
  | `tools/parity-freeze.js` | Снимает эталон паритета (`pnpm run parity`): замороженной копией, на ревизии проекта из манифеста — `--json`, конфиг, хеш артефакта, хеш инструмента |
439
474
  | `tools/make-fixture.js` | Собирает синтетическую фикстуру (`pnpm run fixture`): детерминированную историю с ловушками плюс эталонные числа |
475
+ | `tools/synthetic/` | Сюжеты той сборки по предметам: `repo.js` — как говорим с git (закреплённые время, автор, настройки), `content.js` — что лежит в файлах, `history.js` — какие коммиты из этого получаются, `note.js` — записка к фикстуре со списком ловушек |
440
476
  | `tools/parity-live.js` | Сверяет движок с живым проектом на клоне: числа и артефакт (`pnpm run parity:live`) |
441
477
  | `tools/pack-check.js` | Собирает тарболл и проверяет, что из него всё работает: все исходники доехали, числа, артефакт и страница — как из репозитория (`pnpm run pack:check`) |
442
478
  | `tools/check-standards.js` | Проверяет, что оба эталона воспроизводятся: пересъём идёт в никуда и сверяется с закоммиченным (наши файлы — побайтово, бандл — по содержимому) и что бандл живой истории несёт `HEAD` (`pnpm run check:standards`) |
443
- | `.github/workflows/ci.yml` | CI: семь шагов на каждый пуш и запрос правки — те же команды, что локально, без секретов и матриц |
479
+ | `.github/workflows/ci.yml` | CI: работа `verify` на каждый пуш и запрос правки зовёт `pnpm run verify` тот же профиль, что локально; действия закреплены по SHA коммита |
480
+ | `.github/workflows/verify-slow.yml` | Slow-профиль по расписанию: то же плюс покрытие под c8 — дорогое не в каждом прогоне |
481
+ | `tools/gates/run.js` | Профили проверок — единственный список шагов: `fast` (каждая правка), `full` (перед отправкой и в CI), `slow` (+ покрытие); `--list` печатает команды |
482
+ | `tools/gates/metrics.js` | Датчик раздувания: правила размера и сложности, вес проверок, пометки долга — с храповиком подавлений ESLint (`.eslint-suppressions.json`) |
483
+ | `tools/gates/dup.js` | Датчик дублей: отпечатки клонов по содержимому (`dup-baseline.json`), взгляд против файла базы и против дерева `origin/main` |
484
+ | `tools/gates/deps.js` | Датчик связей: циклы, сироты, направление слоёв и неразрешимые импорты (`dependency-cruiser`) |
485
+ | `tools/gates/coverage.js` | Датчик покрытия: храповик по файлам против `coverage-baseline.json`, а не процент по репозиторию |
486
+ | `tools/gates/gatefiles.js` | Защита гейт-файлов: правка порогов, баз и обвязки без трейлера `Gate-Change:` — красный (хук `commit-msg` и CI по диапазону) |
487
+ | `tools/gates/common.js`, `tools/gate-probe.js` | Общее у датчиков (корень, разбор ключей, отчёты) и обвязка их проб: датчик зовётся командой, а не импортом |
488
+ | `.githooks/commit-msg`, `.githooks/pre-commit`, `.githooks/pre-push` | Хуки: защита гейт-файлов, быстрый профиль на правку и перед отправкой; ставятся `pnpm run hooks:install` (свой менеджер хуков не заводится) |
489
+ | `eslint.metrics.config.js`, `.eslint-suppressions.json` | Правила датчика раздувания и его база: пороги из замеров, всё, что выше, — в базе и разбирается постепенно |
490
+ | `.jscpd.json`, `dup-baseline.json` | Настройки и база датчика дублей: отпечаток считается по содержимому клона, поэтому база переносима |
491
+ | `.dependency-cruiser.cjs`, `.c8rc.json`, `coverage-baseline.json` | Правила графа связей, настройки снятия покрытия и его база по файлам |
492
+ | `AGENTS.md` | Короткая инструкция агенту репозитория: что запускать, что делать при красном, что нельзя менять |
493
+ | `.github/workflows/release.yml` | Выпуск по тегу: тот же полный набор, сверка версии манифеста с тегом и публикация в реестр по удостоверению GitHub Actions — без секрета и без кода из аутентификатора |
444
494
  | `templates/` | То, что проект берёт как есть: `size-report.config.json` (черновик настроек), `ci.yml` (описание проверки) и `README.md` (куда что кладётся и что в них менять); едут в поставке и стерегутся `pack:check` и `test/templates.test.js` |
445
495
  | `fixtures/parity/` | Эталон с `safe-resets` на коммите `bd6ef9d`: 95 строк × 27 колонок. Копия реализации, которой он снят, в дереве не лежит — её байты живут в истории и берутся оттуда по требованию (`REFACTOR.md` R-1.5) |
446
496
  | `fixtures/synthetic/` | Бандл фикстуры на 16 коммитов, её конфиг, эталонные числа и хеш артефакта |
@@ -454,10 +504,17 @@ deльт задан один раз и по артефакту: рост зел
454
504
  | `src/table.css` | Общая часть таблицы: геометрия клеток, липкие шапка и колонка, цвет дельт — одна на артефакт и страницу |
455
505
  | `src/artifact.css` | Оформление статического артефакта сверх общей части |
456
506
  | `src/page/app.css` | Оформление страницы сверх общей части: панель с деревом файлов, состояния пустоты, узкое окно |
457
- | `src/page/app.js` | Программа страницы: дерево файлов, разметка, состояние галочек, память выбора и ссылка; вклеивается в собранную страницу |
507
+ | `src/page/state.js` | Состояние страницы: данные отчёта, вид галочек, паспорт записи, память браузера и обмен ссылкой глава программы страницы |
508
+ | `src/page/dom.js` | Узлы страницы: мелкие помощники разметки (`appEl`, `appBox`) — одни на панель и таблицу |
509
+ | `src/page/panel.js` | Панель выбора: галочки метрик и файлов, категории, дерево путей, легенда; перерисовку просит у главы сборки |
510
+ | `src/page/table.js` | Таблица страницы: клетка, подпись коммита, шапка и состояния пустоты — разметка поверх общего расчёта |
511
+ | `src/page/app.js` | Сборка и запуск страницы: таблица целиком, перерисовка по выбору читателя, первая отрисовка и смена якоря; вклеивается в собранную страницу
458
512
  | `src/page/build.js` | Сборка страницы: данные, оформление и программа в одном файле без внешних ссылок |
459
513
  | `src/git.js` | Единственная граница вызова git: закрепления настроек, блобы пачкой, история, сверка с диском |
460
- | `src/strip.js` | Снятие балласта: стрипперы комментариев и отступов и правила, какая форма к какому файлу, гард компиляции |
514
+ | `src/strip.js` | Снятие балласта: какая форма к какому файлу (расширение, стратегия) и что считать точным числом вход разбора форм |
515
+ | `src/strip/js.js` | Снятие комментариев и отступов в JS: проход по случаям (комментарий, регексп, строка, символ) — строки и шаблоны насквозь |
516
+ | `src/strip/forms.js` | Формы текста со своим снятием балласта: разметка, стили, строки файла и JSON |
517
+ | `src/strip/guard.js` | Гард стриппера: снятое обязано компилироваться — скриптом в процессе или модулем в рабочем потоке |
461
518
  | `src/parse.js` | Разбор модуля: рабочий поток на прогон и отступление к `node --check`, способ разбора последнего модуля |
462
519
  | `src/parse-worker.js` | Сам разбор внутри потока: разбирает текст без исполнения, сообщает, что модулей vm в Node нет |
463
520
  | `src/metrics.js` | Реестр метрик: что измеряется, нужен ли текст и насколько честна цифра; описание метрики для читателя — в одном месте |
@@ -474,14 +531,20 @@ deльт задан один раз и по артефакту: рост зел
474
531
  | `src/data.js` | Категории файлов и контракт со страницей (`--data`) |
475
532
  | `src/render.js` | Статический артефакт: клетки, таблица, примечание (стили — в `src/css.js`) |
476
533
  | `src/config.js` | Настройки проекта-потребителя: умолчания, чтение, проверка |
534
+ | `src/project.js` | Настройки, выведенные из самого проекта (дерево и история): колонки, журнал, исключения. Без файла настроек он и есть настройки; `--init` закрепляет его файлом |
477
535
  | `src/locales.js`, `src/refusal.js`, `src/tool.js` | Тексты отчёта; коды выхода и справка; имя и версия пакета |
478
- | `src/cli.js` | Режимы командной строки и разбор ключей; главный файл пакета |
536
+ | `src/cli.js` | Вход инструмента: разбор строки, чтение проекта и доставка запроса режиму; главный файл пакета |
537
+ | `src/args.js` | Грамматика командной строки: режимы, ключи и команды плюс проверки их сочетаний — отказ называет виновника и готовую команду |
538
+ | `src/modes.js` | Режимы: собрать таблицу, сверить её с историей, отдать данные или страницу, полноту покрытия и диагностику |
539
+ | `src/init.js` | Закрепление настроек файлом (`--init`): то, что проект вывел о себе сам, ложится файлом — и проходит ту же проверку, что первый запуск |
479
540
  | `test/api.test.js` | Публичный API пакета: список имён заморожен, разбиение не имеет права его менять |
480
541
  | `eslint.config.js` | Правила оформления: те же, что у проекта-потребителя, плюс запрет склейки операторов в строке (`pnpm run lint`, `pnpm run lint:strict`) |
481
542
  | `tools/harness.js` | Обвязка проверок: пути, клоны фикстуры (в том числе общий на набор и с CRLF), запуск инструмента, разбор отказов, хеши |
482
- | `tools/suites.js` | Разделение набора: какие файлы идут в быстрый прогон причиной и снимком стоимости), почему каждый дорогой в полном, и цели обоих прогонов |
483
- | `tools/run-tests.js` | Прогон набора (`pnpm test`, `pnpm test:all`, `pnpm run suites:measure`): стоимость каждого файла своим замером, сверка числа проверок, граница бюджета |
543
+ | `tools/page-harness.js` | Обвязка проверок контракта и страницы: данные контракта, собранная страница, чтение её в настоящем DOM, переключатели панелиодна на четыре набора |
544
+ | `tools/suites.js` | Разделение набора: какие файлы идут в быстрый прогон причиной для каждого), почему каждый дорогой в полном |
545
+ | `tools/run-tests.js` | Прогон набора (`pnpm test`, `pnpm test:all`, `pnpm run suites:measure`): длительность каждого файла своим замером и сверка числа проверок |
484
546
  | `tools/docs-facts.js` | Чтение фактов из документации — один слой на четыре проверки сторожа: что документ называет (пути, зовы, адреса разделов) против того, что есть в репозитории |
547
+ | `tools/yaml.js` | Разбор подмножества YAML — один разборщик на два сторожа описаний (`templates/ci.yml` и `.github/workflows/release.yml`): вне подмножества — ошибка, а не молча пропущенная строка, включая двоеточие с пробелом в незакавыченном значении (именно оно делало описание выпуска неразбираемым, пока проверка искала подстроки) |
485
548
  | `tools/refusals.js` | Каталог отказов: по строке на каждый — причина, код выхода, обязательные фразы вывода, **что отказ советует** (`advice`: `run` — команда, `template` — форма с подстановкой, `manual` — действие человека с причиной, `coveredBy` — отдан другой проверке), а для непроверяемого — почему; карты мест отказа (`SITES`, `PRINTED`) держат числа, чтобы новый отказ не появился молча, а маркеры совета — чтобы не появился молча новый совет |
486
549
  | `test/parity.test.js` | Паритет движка с эталоном: числа, артефакт, локаль |
487
550
  | `test/frozen.test.js` | Замороженная копия: та ли это ревизия, с которой снят эталон, и воспроизводит ли она его |
@@ -491,20 +554,26 @@ deльт задан один раз и по артефакту: рост зел
491
554
  | `test/cli.test.js`, `test/cli-paths.test.js` | Отказы командной строки: справка, настройки, коды выхода — и куда инструмент пишет |
492
555
  | `test/refusals.test.js` | Отказы исполняются: каждый вызван прогоном, сверены код выхода и обещанные фразы (свои клоны — для чужого хука, обрезанной истории и ветки мимо отчёта), и **совет выполняется** — команда даёт обещанный код, не падает стеком, а где объявлено «отказ ушёл», тот же зов после неё отвечает другим |
493
556
  | `test/refusals-catalog.test.js` | Сторож каталога отказов: у каждого места отказа в исходниках есть пункт, у каждого пункта — объявленный совет, а отказы, отданные другой проверке, ею в самом деле утверждаются (названные файл и строка проверяются) |
494
- | `test/contract.test.js` | Контракт данных и страница: числа против эталона, производные против чисел артефакта, дерево файлов против путей, память выбора и ссылка против перезахода, чужого отчёта и чужого адреса, пометки приближения против подписи метрики |
557
+ | `test/contract-data.test.js` | Контракт данных: числа против эталона, состав полей против производных, пометки приближения против подписи метрики |
558
+ | `test/contract-derived.test.js` | Производные против чисел артефакта: итоги строки, дельты клетки и дельта итога — на коде, который лежит в дереве |
559
+ | `test/page-view.test.js` | Собранная страница: вклейка без копий расчёта, самодостаточность, дерево файлов, состояния пустоты, оформление и переключатели |
560
+ | `test/page-choice.test.js` | Память выбора и обмен ссылкой: перезаход, чужой отчёт, чужая и битая запись, смена адреса на открытой странице |
495
561
  | `test/module.test.js` | Модуль в расширении `.js`: измеряется без правок настроек; гард стриппера жив (доказано мутацией) и не обвиняет невиновного |
496
562
  | `test/guard.test.js` | Разбор модуля: идёт потоком, оба пути дают один вердикт, отступление работает без файла потока, сотни разборов дешевле запуска |
497
563
  | `test/runner.test.js` | Чтение вывода процесса: куски склеиваются буферами, а не приклеиваются к строке — многобайтовый символ на границе кусков не превращается в два символа-заменителя |
498
564
  | `test/git-pins.test.js` | Сторож границы git: прямых вызовов git без общего списка закреплений нет, и незакреплённое чтение показывается свидетелем (путь кавычками) |
499
- | `test/docs-paths.test.js`, `test/docs-commands.test.js`, `test/docs-numbers.test.js`, `test/docs-pin.test.js` | Сторож документации, по файлу на обещание: пути и таблица файлов; зовы, причины отказа и адреса разделов; числа проверок и цели по времени; пин в примере установки |
565
+ | `test/docs-paths.test.js`, `test/docs-commands.test.js`, `test/docs-numbers.test.js`, `test/docs-pin.test.js` | Сторож документации, по файлу на обещание: пути и таблица файлов; зовы, причины отказа и адреса разделов; числа проверок; пин в примере установки |
500
566
  | `test/changelog.test.js` | Сторож выпуска: версия в `CHANGELOG.md` — версия манифеста, а таблица «что изменится в числах» — это замер на фикстуре, сверенный с живым прогоном |
501
- | `test/suites.test.js` | Сторож разделения набора: полнота классификации (быстрый явно, полный с причиной), потолок стоимости быстрого файла, что быстрый прогон остаётся частью набора |
567
+ | `test/release.test.js` | Сторож выпуска из CI: он начинается тегом, версия берётся из манифеста, секрета и одноразового кода не требует, prerelease не уезжает в `latest`, перед публикацией идёт полный набор — и подсказка на npmjs.com называет этот же файл |
568
+ | `test/suites.test.js` | Сторож разделения набора: полнота классификации (быстрый — явно, полный — с причиной), причина у каждого файла, что быстрый прогон остаётся частью набора |
569
+ | `test/gates-metrics.test.js`, `test/gates-dup.test.js`, `test/gates-deps.test.js`, `test/gates-coverage.test.js`, `test/gates-files.test.js` | Пробы датчиков: искусственное нарушение → датчик красный, снятие → снова зелёный; прогон зовёт датчик командой, а не импортом, поэтому доказывает и код возврата |
570
+ | `test/gates-verify.test.js` | Сторож единственного списка: команды профиля против рабочих процессов, хуков и `templates/ci.yml` — проверки, которой нет в профиле, в CI быть не может |
502
571
  | `test/check.test.js` | Полнота и объяснение на настоящих коммитах фикстуры: непокрытый путь, «только отчёт», «число не сдвинулось», «мимо колонок», слияние — и что починка настроек не двигает числа |
503
572
  | `test/doctor.test.js` | Диагностика на пяти состояниях проекта: без настроек (2), полное покрытие (0), неполное (1), обрезанная история (3), нет датчика (4) — и блок покрытия равен ответу `size check`, а не считается вторым разом |
504
573
  | `test/hook.test.js` | Хуки на свежем клоне: ставятся только командой, дают отдельный коммит отчёта (в том числе после слияния), повторный запуск молчит, чужая работа и индекс не тронуты, в CI и при отказе инструмента ничего не делают, снятие возвращает проект к прежнему |
505
574
  | `test/templates.test.js` | Шаблоны: черновик настроек проходит проверку инструмента и собирает настоящий отчёт; описание проверки разбирается и зовёт только существующие команды и ключи |
506
575
  | `test/minify.test.js`, `test/tokens.test.js` | Настоящее сжатие и токены: числа против упрощения, кодировка как часть числа, честность подписи, работа без необязательной зависимости (код 4) и шов `SIZE_REPORT_NO_OPTIONAL` |
507
- | `package.json` | Манифест пакета: имя `@vernikr/size-report`, версия `1.1.1`, список поставки — только существующее |
576
+ | `package.json` | Манифест пакета: имя `@vernikr/size-report`, версия `1.2.0`, список поставки — только существующее |
508
577
 
509
578
  Оба каталога эталонов снимаются заново теми же инструментами: `pnpm run parity` и
510
579
  `pnpm run fixture` дают те же файлы. Побайтово сверяется наше — конфиг, эталонные
@@ -536,7 +605,7 @@ JSX и TSX выход зависит от настройки jsx сам
536
605
  уже собранных значениях (`data`, `render`, `page`), а настройки, тексты и отказ —
537
606
  по краям, потому что их знает любой и они не знают никого. Оба отчёта считаются на
538
607
  сборке: страница получает исходники общего расчёта и своей программы вклеенными
539
- (`src/derived.js`, `src/page/app.js`), потому что открывается она с диска, без
608
+ (`src/derived.js`, `src/page/*.js`), потому что открывается она с диска, без
540
609
  сервера и без сети. Остальное — по шагам 2–6 (`PLAN.md` §5).
541
610
 
542
611
  ## Как подключить к своему проекту
@@ -555,16 +624,23 @@ JSX и TSX выход зависит от настройки jsx сам
555
624
  ### 1. Установка
556
625
 
557
626
  ```bash
558
- pnpm add -D github:vernikr/size-report#v1.1.1
627
+ pnpm add -D @vernikr/size-report
559
628
  ```
560
629
 
561
- `npm i -D` и `yarn add -D` принимают ту же ссылку. **В npm пакет не опубликован**,
562
- поэтому `pnpm add -D @vernikr/size-report` не сработает. Репозиторий публичный, и
563
- учётных данных установка не требует: `github:` pnpm разрешает в архив
564
- `codeload.github.com` и тянет его по HTTPS.
630
+ Пакет **опубликован в реестре**, и публично: `npm view @vernikr/size-report
631
+ version` отвечает `1.3.0`, `npm access get status @vernikr/size-report` `public`,
632
+ а анонимный запрос тарболла код 200. `npm i -D` и `yarn add -D` принимают то же
633
+ имя; ни ключа, ни ссылки на репозиторий не нужно.
634
+
635
+ Тот же выпуск можно взять ссылкой на репозиторий — так установка не зависит от
636
+ реестра, но остаётся привязанной к ревизии:
637
+
638
+ ```bash
639
+ pnpm add -D github:vernikr/size-report#v1.3.0
640
+ ```
565
641
 
566
642
  Без сети (или если тянуть из codeload нечем) — тарболл: `pnpm pack` в клоне
567
- пакета, затем `pnpm add -D ./vernikr-size-report-1.1.1.tgz`.
643
+ пакета, затем `pnpm add -D ./vernikr-size-report-1.3.0.tgz`.
568
644
 
569
645
  **Почему тег, а не sha.** Короткий sha pnpm разрешает только через видимые рефы, а
570
646
  `git ls-remote` отдаёт одни верхушки веток: пока ревизия — верхушка, короткий sha
@@ -572,7 +648,7 @@ pnpm add -D github:vernikr/size-report#v1.1.1
572
648
  <sha> to a commit`. Это не рассуждение, а проба: короткий пин `6530237` ставился,
573
649
  пока `main` стоял на нём, и перестал — на следующем же коммите, а тот же sha
574
650
  целиком поставился. Имя ветки (`#main`) или тег принимаются оба, но ветка —
575
- движущаяся цель, а тег постоянен: этот выпуск стоит на теге `v1.1.1`, он же и в
651
+ движущаяся цель, а тег постоянен: этот выпуск стоит на теге `v1.3.0`, он же и в
576
652
  примере (сорок знаков тоже годятся, но их придётся брать глазами из истории).
577
653
 
578
654
  Ревизия в примере — не украшение, а часть утверждения: она закреплена за тем, что
@@ -591,27 +667,45 @@ pnpm add -D github:vernikr/size-report#v1.1.1
591
667
  GIT_SSH_COMMAND=false`): установка 3,4 с, дальше `size --write` и `size` работают
592
668
  (`WORKLOG.md` §44). Прежнее требование было ценой приватности: локально — ключ, а
593
669
  в CI — read-only deploy key перед `pnpm install` (то самое первое подключение,
594
- `WORKLOG.md` §18); шаг с ключом из шаблона ушёл вместе с приватностью. Что
595
- остаётся на потом — публикация в npm (`PLAN.md` §8.4), и там есть цена: имя
596
- `size-report` в npm занято чужим пакетом (2017 год, три версии), поэтому
597
- публикация — это имя в области владельца (`@vernikr/size-report`), а не выкладка
598
- под прежним именем; что затронуло переименование — `PLAN.md` §10.
670
+ `WORKLOG.md` §18); шаг с ключом из шаблона ушёл вместе с приватностью. Публикация
671
+ в npm сделана 2026-09-15, и у неё была цена: имя `size-report` в реестре занято чужим
672
+ пакетом (2017 год, три версии), поэтому выкладка — это ещё и смена имени на имя в
673
+ области владельца (`@vernikr/size-report`), а не только отправка архива; что
674
+ затронуло переименование — `PLAN.md` §10, чем доказана выкладка — `WORKLOG.md` §53.
599
675
 
600
- ### 2. Черновик настроек
676
+ ### 2. Настройки: их можно не заводить
601
677
 
602
678
  ```bash
603
- pnpm exec size --init # создаёт size-table.config.json
679
+ pnpm exec size --write # таблица; настроек нет — их выведет сам инструмент
680
+ pnpm exec size --init # закрепить выведенное в size-table.config.json
604
681
  ```
605
682
 
606
- Черновик собирается по самому проекту: колонки крупнейшие файлы каждого
607
- расширения, журнал по знакомым именам (`WORKLOG.md`, `CHANGELOG.md`, …), вывод
608
- в `docs/`, если он есть, `fixCommand` под ваш менеджер пакетов. Он же печатает
609
- следующие три шага. Черновик ведёт метрику `min` на настоящее сжатие
610
- (`"minify": {"engine": "esbuild"}`) и сразу включает метрику `tok`
611
- (`"tokens": {"family": "openai", "encoding": "o200k_base"}`): и минификатор, и
612
- словарь едут необязательными зависимостями и ставятся обычной установкой, а без
613
- них инструмент работает и сам скажет об этом (код 4) — правки настроек не
614
- требуются.
683
+ Начинать с настроек не нужно: без файла инструмент выводит их из проекта — колонками
684
+ берёт крупнейшие файлы, по одному от каждого расширения (иначе отчёт состоял бы из
685
+ одних крупных `.md`, и ни один `.js` не попал бы под настоящее сжатие), журналом
686
+ первый знакомый (`WORKLOG.md`, `CHANGELOG.md`, …), файлом отчёта `docs/`, если
687
+ каталог есть, командой починки — объявленный скрипт `sizes`, а без него путь
688
+ к установленному пакету (его цитируют подпись отчёта и отказы, поэтому он обязан
689
+ работать уже сейчас), ссылкой на коммит адрес `origin`, метриками — `raw`, `min`,
690
+ `tok`. Метрика `min` считается настоящим сжатием (`"minify": {"engine": "esbuild"}`),
691
+ а `tok` — словарём (`"tokens": {"family": "openai", "encoding": "o200k_base"}`): без
692
+ этих необязательных зависимостей метрика честно отступает к другому счёту и прогон
693
+ отдаёт код 4 — правки настроек и тут не требуются.
694
+
695
+ Всё, что колонкой быть не может или в неё не поместилось (сам отчёт, замки
696
+ зависимостей, карты, собранное), называется в `skip` — поэтому первый же `size check`
697
+ полон, а не красен: «пути мимо колонок» появляются от новых правок, а не от того, что
698
+ проект ещё не описан. О том, что настройки выведены, инструмент говорит строкой в
699
+ stderr и называет команду, которая их закрепляет, — `--init`; закреплённое проходит
700
+ ту же проверку, что любой файл настроек, и дальше его правят глазами (сам `--init`
701
+ печатает, что закрепил, и что делать дальше — скрипты и проверку в CI). Без
702
+ закрепления профиль выводится заново на каждом запуске: числа не «поедут», но
703
+ повторить прежний замер — в том числе хуком и проверкой — можно только по файлу.
704
+
705
+ Закрепляется **то же, чем проект работает без файла**: вывод из проекта поверх
706
+ умолчаний. Поэтому в закреплённом файле видны и значения, которых в проекте никто не
707
+ писал, — тогда смена умолчаний в новой версии пакета не поедет по уже настроенному
708
+ проекту молча.
615
709
 
616
710
  > Subкоманды `size init` пока нет — CLI знает только флаги (`--init`, `--write`,
617
711
  > `--page`, `--data`, `--json`, без флага — проверка); полный список даёт `size --help`.
@@ -619,20 +713,21 @@ pnpm exec size --init # создаёт size-table.config.json
619
713
 
620
714
  ### 3. Что правится в конфиге
621
715
 
622
- Черновик знает про проект только размеры файлов — какие колонки важны, знает
623
- человек. Чаще всего правят:
716
+ Вывод знает про проект только то, что видно в дереве и истории, — какие колонки важны,
717
+ знает человек. Чаще всего правят:
624
718
 
625
719
  | Ключ | Что это |
626
720
  |---|---|
627
- | `columns` | колонки таблицы: `{label, paths: [...]}`; пути в одной колонке складываются (например, `src` целиком), `label` — то, что увидит человек |
721
+ | `columns` | колонки таблицы: `{label, paths: [...]}`; **колонка это файл**: список путей — её переименования (в ревизии берётся тот путь, который в ней есть), а не несколько файлов разом; `label` — то, что увидит человек |
628
722
  | `metrics` | из чего состоит число: `raw` (размер объекта git), `min` (минифицированная форма — какая именно, решает `minify.engine`), `tok` (токены), `gzip` |
629
723
  | `tokens.family`, `tokens.encoding` | словарь для `tok`: семейство (`openai`) и кодировка (`o200k_base` или `cl100k_base`) — кодировка меняет число, поэтому она и в настройках, и в подписи метрики |
630
724
  | `minify.engine` | чем считается `min`: `strip` (комментарии и отступы, точность не обещается) или `esbuild` (настоящее сжатие; форматы без минификатора — упрощение, и это видно в подписи метрики) |
631
- | `output` | файл таблицы (по черновику — `docs/size-table.html`) |
725
+ | `output` | файл таблицы (в выведенном профиле — `docs/size-table.html`, если каталог `docs/` есть, иначе в корне) |
632
726
  | `journal` | где искать разделы журнала, на которые ссылаются строки |
633
- | `links.commitUrl` | шаблон ссылки на коммит, например `https://github.com/org/repo/commit/{sha}` |
634
- | `skip` | пути, которые колонками быть не могут |
635
- | `fixCommand` | команда, которую цитирует подпись отчёта и подсказывает отказ; в черновике уже ваша |
727
+ | `links.commitUrl` | шаблон ссылки на коммит, например `https://github.com/org/repo/commit/{sha}`; выводится из адреса `origin` у GitHub и GitLab (у остальных хозяев — пусто, а не догадка) |
728
+ | `skip` | пути, которые колонкой не стали: и те, что ею быть не могут (сам отчёт, замки зависимостей), и те, что в колонки не поместились (выведенный профиль объявляет исключениями всё остальное — поэтому первый `check` полон) |
729
+ | `fixCommand` | команда, которую цитирует подпись отчёта и подсказывает отказ; в выведенном профиле ваш скрипт `sizes`, если он объявлен, иначе путь к установленному пакету внутри проекта (зов по имени пакета уходит в реестр — `REFACTOR.md` R-4.21) |
730
+ | `locale`, `title`, `heading` | язык текстов отчёта и его заголовки; пустые `title`/`heading` значат «взять из локали» |
636
731
  | `minify.guard` | расширения, где результат стриппера проверяется разбором; модуль в `.js` гард понимает сам, трогать его не нужно |
637
732
  | `hooks.enabled` | выключатель хука автообновления (`false` — хук остаётся на месте, но молчит; убирается он только `size uninstall-hook`) |
638
733
 
@@ -710,9 +805,10 @@ pnpm exec size doctor # 0 — делать нечего; иначе перв
710
805
  `.github/workflows/size-report.yml` без правок — сборка таблицы, сверка с файлом
711
806
  на диске, два снимка чисел (обычный и в среде без настроек git) и их сравнение.
712
807
  Секретов оно не требует. Для `npm`/`yarn` в самом файле сказано, какие две строки
713
- заменить. Рядом — `templates/size-report.config.json`, черновик настроек в дополнение
714
- к `pnpm exec size --init`: колонки в нём примерные (`README.md`, `package.json`),
715
- они есть почти в любом проекте, поэтому первый отчёт собирается сразу.
808
+ заменить. Рядом — `templates/size-report.config.json`, образец настроек: колонки в нём
809
+ примерные (`README.md`, `package.json`), они есть почти в любом проекте, поэтому
810
+ первый отчёт собирается сразу. Нужен он, только если хочется начать с правленого
811
+ файла: без файла настройки выводятся из проекта (`--init` закрепляет выведенное).
716
812
 
717
813
  Свой CI у пакета — `.github/workflows/ci.yml`: он гоняет у себя тот же список
718
814
  команд, что описан ниже, и его можно взять за образец для шага потребителя.
@@ -723,7 +819,7 @@ pnpm exec size doctor # 0 — делать нечего; иначе перв
723
819
  | 1 | таблица разошлась с историей (или правка на диске не закоммичена); у `size check` — путь истории не отслеживается и не исключён | `pnpm run sizes` и закоммитить таблицу; для `check` — дописать путь колонкой или в `skip` |
724
820
  | 2 | что-то в вызове или в проекте — **командная строка** (незнакомый ключ, ключ без значения, повтор ключа, два режима сразу, лишнее слово, команда и режим, неизвестная команда, несовместимый ключ, нет ответа в JSON, два ответа сразу, нет коммита); **настройки и проект** (нет файла настроек, настройки не разобраны, настройки неверны, нет git, не git-репозиторий, конфиг уже есть); **история** (нет такого коммита, коммит назван неточно, коммит вне истории); **хук** (чужой хук, чужой core.hooksPath, нечем звать инструмент); **измерение** (файл не JavaScript, минификатор не разобрал) | текст отказа называет причину и готовую команду — и она выполнима: это сторожит `test/refusals.test.js` |
725
821
  | 3 | неполная история (clone с `--depth`) | полный клон: `git fetch --unshallow` |
726
- | 4 | нет датчика | `minify.engine: "esbuild"`, а минификатора нет: числа получены упрощением. Отчёт собран, причина и починка — в тексте |
822
+ | 4 | нет датчика | `minify.engine: "esbuild"`, а минификатора нет: числа получены упрощением. Отчёт собран, причина и починка — в тексте; если при этом таблица расходится с историей, код остаётся **1** (нарушение старше приближения), а заметка о другом счёте печатается рядом |
727
823
  | 5 | внутренняя ошибка | это дефект инструмента: текст нужен нам, см. «Ловушки» ниже |
728
824
 
729
825
  ### 6. Отчёт обновляется сам после коммита (по желанию)
@@ -768,7 +864,17 @@ pnpm exec size uninstall-hook # снять и вернуть проект к
768
864
  при упрощении — `minify.guard`. Стеком такой случай не выглядит ни там, ни там;
769
865
  - **Минификатора нет** (установка без необязательных зависимостей, платформа без
770
866
  `esbuild`) — метрика честно отступает к упрощению: числа те же, что у `strip`,
771
- способ говорит об этом словами, а прогон отдаёт **код 4** с готовой починкой.
867
+ способ говорит об этом словами, а **сборка** (`--write`) отдаёт **код 4** с
868
+ готовой починкой. У **проверки** в этом случае ответ из двух частей, и он назван
869
+ здесь потому, что именно её советует CI: если отчёт на диске собран с настоящим
870
+ минификатором, а прогон идёт без него, точность изменилась — значит числа в
871
+ таблице больше не совпадают с историей, и проверка скажет про расхождение
872
+ (**код 1**), показав разошедшуюся строку подписи, **и тут же назовёт другой счёт**
873
+ заметкой с готовой починкой. Вердикт при этом остаётся за расхождением: код 4
874
+ утверждал бы, что разница объясняется датчиком, а это никто не проверял —
875
+ расхождение может быть и правкой мимо отчёта (тот же порядок, что у `size check` и
876
+ у `doctor`: нарушение старше приближения). Починка в обоих случаях — `pnpm run
877
+ sizes`; на этом окружении она вернёт **код 4**.
772
878
  Проверить это без переустановки можно окружением `SIZE_REPORT_NO_OPTIONAL=1` —
773
879
  тем же приёмом это делает `test/minify.test.js`;
774
880
  - **Разбор модуля — рабочий поток, поднятый один раз на прогон** (`REFACTOR.md`
@@ -831,8 +937,43 @@ R-4.1): пути, таблица файлов, зовы и ключи инстр
831
937
  починки взяты из конфига) не проверяет никто: если это важно, это одна проверка
832
938
  поверх `--data` в проекте.
833
939
 
940
+ ## Гейт против раздувания
941
+
942
+ **Список проверок — один, и он же в CI.** Профиль проверок задан в одном месте
943
+ (`tools/gates/run.js`): `pnpm run verify:fast` (десятки секунд — каждая правка),
944
+ `pnpm run verify` (полный — перед отправкой и в CI) и `pnpm run verify:slow`
945
+ (по расписанию — то же плюс покрытие). CI зовёт эту же команду, а не свой список:
946
+ работа `verify` (`.github/workflows/ci.yml`) на каждый пуш и запрос правки, работа
947
+ `verify-slow` — по расписанию. Совпадение стережёт `test/gates-verify.test.js`:
948
+ проверка, которой нет в профиле, в CI не пройдёт.
949
+
950
+ **Датчики ловят раздувание, а не стиль** (стиль — у линтера): размер и сложность
951
+ функций, размер модулей, дубли веток и функций (`sonarjs`), вес проверок (проверка
952
+ без утверждения, утверждение без сравнения, выключенная проверка), пометки долга,
953
+ клоны по токенам (`jscpd`), циклы и сироты связей (`dependency-cruiser`), просадка
954
+ покрытия против своей же базы (`c8`).
955
+
956
+ **Порог взят из замера, а не из головы, и он храповик.** По исходному замеру:
957
+ сложность функции p50 1 / p90 4 / p99 11 / max 27 — порог 12 (в базе осталось 5
958
+ функций); длина функции p50 7 / p90 27 / p99 73 / max 118 — порог 60 (9 в базе);
959
+ модуль p90 381 строка / max 907 — порог 450 (в базе не осталось ни одного: три
960
+ толстых файла — контракт, программа страницы и сборка фикстуры — разделены,
961
+ `WORKLOG.md` §59–§61). Всё, что выше порога
962
+ сегодня, лежит в базе (`.eslint-suppressions.json`) и работе не мешает; новое валит
963
+ прогон. Дубли — 13 клонов / 84 строки (0,67 %), связи — 112 модулей / 468 связей и ни
964
+ одной находки.
965
+
966
+ **Базы обновляет человек.** `pnpm run baseline:metrics`, `baseline:dup`,
967
+ `baseline:coverage` — и только с трейлером `Gate-Change:` в сообщении коммита: правка
968
+ гейт-файла без него красна и локально (хук `commit-msg`), и в CI (по каждому коммиту
969
+ диапазона). Иначе гейт ослаблялся бы тем же коммитом, который он останавливает.
970
+ Таблица замеров, отвергнутые инструменты (knip, ast-grep, size-limit, gitleaks) и
971
+ действия человека — в `WORKLOG.md` §58.
972
+
834
973
  ## Для ИИ-агента
835
974
 
975
+ - `pnpm run verify:fast` — перед каждой правкой, `pnpm run verify` — перед отправкой;
976
+ что не так и что нельзя трогать при красном — `AGENTS.md`.
836
977
  - `size check --json` — готово ли всё: какая часть истории покрыта, какие пути
837
978
  мимо колонок (с коммитом-первопричиной) и какие коммиты выпали без строки.
838
979
  - `size explain <коммит> --json` — почему у конкретного коммита нет строки: причина,
@@ -876,7 +1017,7 @@ pnpm test # быстрый прогон (каждая пр
876
1017
  # контракт данных и страница, сторож документации и выпуска
877
1018
  pnpm test:all # полный прогон (выкладка и CI): то же плюс интеграционные —
878
1019
  # сборка на дисках, сверка с деревом, хуки, метрики
879
- pnpm run suites:measure # перемерить стоимости файлов набора
1020
+ pnpm run suites:measure # замерить длительность каждого файла набора
880
1021
  pnpm run parity:live # паритет с живым проектом на клоне, две среды
881
1022
  node bin/size.js --data # контракт данных: страница и агент
882
1023
  node bin/size.js --page # минимальная страница отчёта