@vernikr/size-report 1.2.0 → 1.3.1

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/CHANGELOG.md CHANGED
@@ -18,6 +18,97 @@
18
18
  где за основу взяты `fixtures/synthetic/config.json`, метрики — `raw`, `min`, `tok`, а
19
19
  способ минификации — `strip` или `esbuild`.
20
20
 
21
+ ## 1.3.1 — 2026-09-15
22
+
23
+ Правка раскладки страницы: на широком экране панель выбора встаёт слева от таблицы.
24
+
25
+ - **От 900 px панель — колонка слева от таблицы.** Метрики, дерево файлов и легенда
26
+ видны одновременно с числами: прокрутка нижних строк больше не уводит управление за
27
+ верх экрана, а таблица получает всё оставшееся по горизонтали (1060 px при окне
28
+ 1440 против 1372 у прежней раскладки в одну колонку). Узкое окно раскладку снимает:
29
+ там столбцы снова идут друг под другом, как было.
30
+ - Раскладка — сетка на `body` (`src/page/app.css`), а не обёртка в разметке: страница
31
+ собирается вклейкой глав, и форма страницы осталась в одном месте. Порог тот же,
32
+ что у адаптаций: между «широко» и «узко» нет промежутка без правил.
33
+ - Ни поведение, ни артефакт, ни данные не тронуты — правка только в оформлении
34
+ страницы (`--page`); статический артефакт (`--write`) этот выпуск не касается.
35
+
36
+ ### Что изменится в числах
37
+
38
+ **Ничего.** Этот выпуск не трогает ни измерение, ни форму отчёта: изменилась
39
+ раскладка страницы, а числа считает тот же движок теми же датчиками. Таблица ниже та
40
+ же, что у 1.3.0, до последней клетки.
41
+
42
+ | Файл | raw | min со strip | min с esbuild | tok |
43
+ |---|---|---|---|---|
44
+ | code.js | 735 | 276 | 185 | 168 |
45
+ | modern.js | 246 | 51 | 45 | 44 |
46
+ | config.mjs | 172 | 45 | 40 | 40 |
47
+ | заметки.md | 306 | 303 | 303 | 53 |
48
+ | crlf.txt | 63 | 60 | 60 | 10 |
49
+ | package.json | 87 | 69 | 69 | 34 |
50
+ | style.css | 156 | 55 | 43 | 38 |
51
+ | table.toml | 300 | 299 | 299 | 52 |
52
+ | empty.js | 0 | 0 | 0 | 0 |
53
+ | WORKLOG.md | 446 | 439 | 439 | 101 |
54
+ | **ИТОГО** | **2511** | **1597** | **1483** | **540** |
55
+
56
+ ## 1.3.0 — 2026-09-15
57
+
58
+ Выпуск выведенных настроек: чтобы получить отчёт, проект больше не обязан себя
59
+ описывать.
60
+
61
+ - **Файла настроек может не быть.** Их выводит сам инструмент — по проекту: колонками
62
+ крупнейшие файлы, по одному от каждого расширения (иначе отчёт состоял бы из одних
63
+ крупных `.md`, и ни один `.js` не попал бы под настоящее сжатие), журналом — первый
64
+ знакомый (`WORKLOG.md`, `CHANGELOG.md`, …), файлом отчёта — `docs/`, если каталог есть,
65
+ командой починки — объявленный скрипт `sizes`, а без него путь к установленному
66
+ пакету внутри проекта, ссылкой на коммит — адрес `origin` (GitHub или GitLab: у
67
+ остальных хозяев пусто, догадка вела бы не туда), метриками — `raw`, `min`, `tok`.
68
+ Команда починки и ссылка берутся готовыми, а не выдуманными: её цитируют подпись
69
+ отчёта и отказы, и зов скрипта, которого в проекте ещё нет, ответил бы «нет такого
70
+ скрипта» ровно там, где человеку нужна работающая команда.
71
+ - **Всё, что колонкой быть не может или в неё не поместилось, названо в `skip`** (сам
72
+ отчёт, замки зависимостей, карты, собранное): поэтому первый же `size check` полон, а не
73
+ красный, — «пути мимо колонок» появляются от новых правок, а не от того, что проект ещё
74
+ не настраивали. Колонка — это файл: список путей колонки движок читает как её
75
+ переименования, поэтому «папка целиком» колонкой не бывает.
76
+ - **О выведенных настройках сказано строкой** в stderr, с готовой командой `--init` —
77
+ она их закрепляет, и дальше их правят как обычные настройки. Закрепляется то же,
78
+ чем проект работает без файла (вывод поверх умолчаний), поэтому смена умолчаний в
79
+ новой версии пакета не поедет по уже настроенному проекту молча. Без закрепления
80
+ профиль выводится на каждом запуске: числа не «поехали», но повторить прежний замер
81
+ можно только закрепив его.
82
+ - **Отказ остался у названного файла:** `--config <файл>`, которого нет, — код 2 с той же
83
+ починкой `--init <файл>` (опечатку в пути покрывать догадкой нельзя). У умолчательного
84
+ имени отказа нет вовсе; коды выхода ни в одном другом случае не изменились.
85
+ - Номер **1.3.0** — по SemVer: появилась возможность, которой не было; схема данных
86
+ (`schema: 1`) та же.
87
+
88
+ ### Что изменится в числах
89
+
90
+ **У кого настройки есть — ничего.** Этот выпуск не трогает ни форму отчёта, ни счёт:
91
+ таблица ниже та же, что у 1.2.0.
92
+
93
+ **У кого настроек нет** — числа появятся там, где был отказ (код 2): их даст выведенный
94
+ профиль. Выведенное и закреплённое (`--init`) друг от друга не отличаются: файл — это тот
95
+ же профиль, только записанный. Повторить замер можно только по закреплённому: без файла
96
+ профиль выводится заново каждый запуск.
97
+
98
+ | Файл | raw | min со strip | min с esbuild | tok |
99
+ |---|---|---|---|---|
100
+ | code.js | 735 | 276 | 185 | 168 |
101
+ | modern.js | 246 | 51 | 45 | 44 |
102
+ | config.mjs | 172 | 45 | 40 | 40 |
103
+ | заметки.md | 306 | 303 | 303 | 53 |
104
+ | crlf.txt | 63 | 60 | 60 | 10 |
105
+ | package.json | 87 | 69 | 69 | 34 |
106
+ | style.css | 156 | 55 | 43 | 38 |
107
+ | table.toml | 300 | 299 | 299 | 52 |
108
+ | empty.js | 0 | 0 | 0 | 0 |
109
+ | WORKLOG.md | 446 | 439 | 439 | 101 |
110
+ | **ИТОГО** | **2511** | **1597** | **1483** | **540** |
111
+
21
112
  ## 1.2.0 — 2026-09-15
22
113
 
23
114
  Выпуск причины, а не измерения: проверка («таблица совпадает с историей?») теперь
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.1 (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
  стриппера понимает обе формы — скрипт и модуль, — поэтому подключение не требует
@@ -208,10 +216,10 @@
208
216
 
209
217
  | Прогон | Команда | Проверок |
210
218
  |---|---|---|
211
- | Быстрый — каждая правка | `pnpm test` | **57 из 135** |
212
- | Полный — выкладка и CI | `pnpm test:all` | **135** |
219
+ | Быстрый — каждая правка | `pnpm test` | **65 из 167** |
220
+ | Полный — выкладка и CI | `pnpm test:all` | **167** |
213
221
 
214
- Ни одна проверка не потеряна и не ослаблена: полный прогон запускает все 135 теми же
222
+ Ни одна проверка не потеряна и не ослаблена: полный прогон запускает все 167 теми же
215
223
  файлами, а быстрый берёт их часть. Умолчание — полный: файл становится быстрым только
216
224
  явно и с причиной, поэтому новое дорогое не может тихо уехать в быстрый. Стерегут это
217
225
  объявление `test/suites.test.js` (полнота классификации и причина у каждого файла) и
@@ -238,10 +246,15 @@ fixture`) и больше не зависит ни от того, держит
238
246
  пакета `npx size-table --write`).
239
247
 
240
248
  **Проверки идут сами (шаг 6 плана, `.github/workflows/ci.yml`).** На каждый пуш и
241
- на каждый запрос правки один job проходит семь шагов теми же командами, что и у
242
- себя локально: строгий линтер, набор проверок, тот же набор в среде, где настроек
243
- машины нет вовсе (`GIT_CONFIG_GLOBAL=/dev/null`), работу из собранного тарболла,
244
- сверку с историей проекта-потребителя и воспроизводимость обоих эталонов.
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` по расписанию.
245
258
  Секретов job не требует: история потребителя лежит в репозитории бандлом на той
246
259
  же ревизии, что записана в эталоне (`fixtures/live/`), а пересъём идёт во временный
247
260
  каталог и сверяется с закоммиченным — рабочее дерево остаётся чистым. Матрицы по
@@ -251,16 +264,27 @@ fixture`) и больше не зависит ни от того, держит
251
264
  прогоняет тот же полный набор, сверяет версию манифеста с тегом, проверяет работу
252
265
  из собранного пакета и отправляет его в реестр — без секрета и без кода из
253
266
  аутентификатора: публикация идёт по удостоверению GitHub Actions (trusted
254
- publishing), которое npm принимает вместо токена. Одна настройка делается человеком
255
- и один раз: на npmjs.com в настройках пакета (Trusted Publisher) нужно назвать
256
- владельца, репозиторий и файл `release.yml`. Черновой прогон из Actions («Run
257
- workflow»: по умолчанию он ничего не публикует) проходит весь список до самого пути
258
- публикации гоняет полный набор, проверяет работу из тарболла и собирает пакет на
259
- черновой версии (`1.1.1` → `1.1.2-draft.0`, чтобы реестр не отказал в уже выпущенном
260
- номере), и это же стережёт `test/release.test.js` со стороны текста. Только настроен
261
- ли издатель, он не проверяет: `--dry-run` не обменивается удостоверением и проходит
262
- вообще без учётных данных (проверено в пустом каталоге: код 0 без токена). Это делает
263
- первый настоящий тег.
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
+ тег, и он её дал.
264
288
 
265
289
  Первым же прогоном CI окупился: шаг живого паритета упал не на расхождении чисел,
266
290
  а на самой проверке — вывод процессов собирался как строка, и многобайтовый символ,
@@ -292,9 +316,20 @@ deльт задан один раз и по артефакту: рост зел
292
316
  папки три состояния — все её файлы включены, часть, ни одного, — и переключатель
293
317
  папки ведёт за собой всё поддерево; рядом стоит число файлов. Своего состояния у
294
318
  папки и у быстрой кнопки категории нет: обе переставляют галочки файлов, поэтому
295
- дерево, кнопки и таблица не могут разойтись. Список файлов длиной больше трети
296
- экрана прокручивается внутри панели, а не выталкивает таблицу: на живой истории
297
- (91 строка × 25 файлов) панель занимает 429 px, таблица начинается на 544-м.
319
+ дерево, кнопки и таблица не могут разойтись. Список файлов длиной больше экрана
320
+ прокручивается внутри панели, а не выталкивает таблицу: на живой истории
321
+ (87 строк × 12 колонок) дерево занимает 558 px из 883 у панели.
322
+
323
+ **На широком экране панель стоит слева от таблицы** (от 900 px, `src/page/app.css`).
324
+ Это не украшение: на десктопе бокового места много, а вертикального мало —
325
+ переключатели, дерево и числа видны одновременно, и прокрутка нижних строк не
326
+ уводит управление за верх экрана. Раскладка сделана сеткой на `body`: обёртки в
327
+ разметке нет, потому что страница собирается вклейкой глав и форма страницы должна
328
+ жить в одном месте. Порог один с адаптациями — промежутка без правил между «широко»
329
+ и «узко» нет. Измерено в настоящем Chrome на живой истории: при окне 1440×900
330
+ панель — колонка 300 px слева, таблица начинается на 346-м и на той же высоте
331
+ (103), её собственное окно 1060 × 602; при 1024 — те же 300 и 651; при 899
332
+ раскладка снимается и столбцы снова идут друг под другом (панель 79, таблица 557).
298
333
  Стрежет это проверка на числах таблицы, а не на разметке: выключение папки убирает
299
334
  ровно её колонки и ровно её объём из итога (`test/contract.test.js`). Проверено в
300
335
  настоящем Chrome: 9 папок до трёх уровней вложенности (`.github/workflows`,
@@ -358,8 +393,8 @@ deльт задан один раз и по артефакту: рост зел
358
393
  `src/metrics.js`), поэтому разойтись не могут. Минификатора нет (установка без необязательных
359
394
  зависимостей, платформа без него) — метрика отступает к упрощению, способ говорит
360
395
  об этом словами, а прогон отдаёт **код 4**, а не молчание: числа при этом те же, что
361
- у прежнего способа, — побайтово со эталоном. Черновик `--init` ведёт новые проекты
362
- сразу на сжатие; цена названа прямо в его подсказке. Файл, который минификатор не
396
+ у прежнего способа, — побайтово со эталоном. Выведенный профиль ведёт новые проекты
397
+ сразу на сжатие (и `--init` закрепляет то же самое); цена названа прямо в его подсказке. Файл, который минификатор не
363
398
  разобрал (разметка в `.js`, чужой синтаксис), — отказ кодом 2 с причиной от него
364
399
  самого и двумя готовыми выходами.
365
400
 
@@ -393,13 +428,13 @@ deльт задан один раз и по артефакту: рост зел
393
428
  живой истории (95 строк × 27 колонок, 1,23 МБ текста) тот же отчёт идёт
394
429
  **1,55 → 6,35 с** — умножается именно сбор истории, а не таблица: токенов в
395
430
  «сейчас» — **303 705**, то есть 4,05 Б на токен. Отсюда и цена набора проверок:
396
- **7,3–7,9 → 10,4 с** при 66 → 73 проверках (запас и новый бюджет — ниже). Черновик
397
- `--init` ведёт новые проекты сразу на токены.
431
+ **7,3–7,9 → 10,4 с** при 66 → 73 проверках (запас и новый бюджет — ниже). Выведенный
432
+ профиль ведёт новые проекты сразу на токены.
398
433
 
399
434
  **Волна 0 чистки пройдена** (`REFACTOR.md`): у отказов командной строки появились
400
435
  коды выхода и справка вместо стека, `--help` отвечает, `--page` и `--write`
401
436
  создают недостающий каталог, подсказка в отказе ведёт к работающей команде, а
402
- черновик `--init` больше не предлагает колонкой саму таблицу — иначе первая же
437
+ вывод настроек больше не предлагает колонкой саму таблицу — иначе первая же
403
438
  проверка настроек его отвергала.
404
439
 
405
440
  **Появились две команды: полнота и объяснение** (шаг 5 плана). `size check`
@@ -448,10 +483,24 @@ deльт задан один раз и по артефакту: рост зел
448
483
  | `CHANGELOG.md` | История выпусков и, у каждого выпуска, раздел «Что изменится в числах»: у кого числа поедут и почему |
449
484
  | `tools/parity-freeze.js` | Снимает эталон паритета (`pnpm run parity`): замороженной копией, на ревизии проекта из манифеста — `--json`, конфиг, хеш артефакта, хеш инструмента |
450
485
  | `tools/make-fixture.js` | Собирает синтетическую фикстуру (`pnpm run fixture`): детерминированную историю с ловушками плюс эталонные числа |
486
+ | `tools/synthetic/` | Сюжеты той сборки по предметам: `repo.js` — как говорим с git (закреплённые время, автор, настройки), `content.js` — что лежит в файлах, `history.js` — какие коммиты из этого получаются, `note.js` — записка к фикстуре со списком ловушек |
451
487
  | `tools/parity-live.js` | Сверяет движок с живым проектом на клоне: числа и артефакт (`pnpm run parity:live`) |
452
488
  | `tools/pack-check.js` | Собирает тарболл и проверяет, что из него всё работает: все исходники доехали, числа, артефакт и страница — как из репозитория (`pnpm run pack:check`) |
453
489
  | `tools/check-standards.js` | Проверяет, что оба эталона воспроизводятся: пересъём идёт в никуда и сверяется с закоммиченным (наши файлы — побайтово, бандл — по содержимому) и что бандл живой истории несёт `HEAD` (`pnpm run check:standards`) |
454
- | `.github/workflows/ci.yml` | CI: семь шагов на каждый пуш и запрос правки — те же команды, что локально, без секретов и матриц |
490
+ | `.github/workflows/ci.yml` | CI: работа `verify` на каждый пуш и запрос правки зовёт `pnpm run verify` тот же профиль, что локально; действия закреплены по SHA коммита |
491
+ | `.github/workflows/verify-slow.yml` | Slow-профиль по расписанию: то же плюс покрытие под c8 — дорогое не в каждом прогоне |
492
+ | `tools/gates/run.js` | Профили проверок — единственный список шагов: `fast` (каждая правка), `full` (перед отправкой и в CI), `slow` (+ покрытие); `--list` печатает команды |
493
+ | `tools/gates/metrics.js` | Датчик раздувания: правила размера и сложности, вес проверок, пометки долга — с храповиком подавлений ESLint (`.eslint-suppressions.json`) |
494
+ | `tools/gates/dup.js` | Датчик дублей: отпечатки клонов по содержимому (`dup-baseline.json`), взгляд против файла базы и против дерева `origin/main` |
495
+ | `tools/gates/deps.js` | Датчик связей: циклы, сироты, направление слоёв и неразрешимые импорты (`dependency-cruiser`) |
496
+ | `tools/gates/coverage.js` | Датчик покрытия: храповик по файлам против `coverage-baseline.json`, а не процент по репозиторию |
497
+ | `tools/gates/gatefiles.js` | Защита гейт-файлов: правка порогов, баз и обвязки без трейлера `Gate-Change:` — красный (хук `commit-msg` и CI по диапазону) |
498
+ | `tools/gates/common.js`, `tools/gate-probe.js` | Общее у датчиков (корень, разбор ключей, отчёты) и обвязка их проб: датчик зовётся командой, а не импортом |
499
+ | `.githooks/commit-msg`, `.githooks/pre-commit`, `.githooks/pre-push` | Хуки: защита гейт-файлов, быстрый профиль на правку и перед отправкой; ставятся `pnpm run hooks:install` (свой менеджер хуков не заводится) |
500
+ | `eslint.metrics.config.js`, `.eslint-suppressions.json` | Правила датчика раздувания и его база: пороги из замеров, всё, что выше, — в базе и разбирается постепенно |
501
+ | `.jscpd.json`, `dup-baseline.json` | Настройки и база датчика дублей: отпечаток считается по содержимому клона, поэтому база переносима |
502
+ | `.dependency-cruiser.cjs`, `.c8rc.json`, `coverage-baseline.json` | Правила графа связей, настройки снятия покрытия и его база по файлам |
503
+ | `AGENTS.md` | Короткая инструкция агенту репозитория: что запускать, что делать при красном, что нельзя менять |
455
504
  | `.github/workflows/release.yml` | Выпуск по тегу: тот же полный набор, сверка версии манифеста с тегом и публикация в реестр по удостоверению GitHub Actions — без секрета и без кода из аутентификатора |
456
505
  | `templates/` | То, что проект берёт как есть: `size-report.config.json` (черновик настроек), `ci.yml` (описание проверки) и `README.md` (куда что кладётся и что в них менять); едут в поставке и стерегутся `pack:check` и `test/templates.test.js` |
457
506
  | `fixtures/parity/` | Эталон с `safe-resets` на коммите `bd6ef9d`: 95 строк × 27 колонок. Копия реализации, которой он снят, в дереве не лежит — её байты живут в истории и берутся оттуда по требованию (`REFACTOR.md` R-1.5) |
@@ -465,11 +514,18 @@ deльт задан один раз и по артефакту: рост зел
465
514
  | `src/css.js` | Чтение оформления с диска: какие наборы стилей есть и какая у них роль |
466
515
  | `src/table.css` | Общая часть таблицы: геометрия клеток, липкие шапка и колонка, цвет дельт — одна на артефакт и страницу |
467
516
  | `src/artifact.css` | Оформление статического артефакта сверх общей части |
468
- | `src/page/app.css` | Оформление страницы сверх общей части: панель с деревом файлов, состояния пустоты, узкое окно |
469
- | `src/page/app.js` | Программа страницы: дерево файлов, разметка, состояние галочек, память выбора и ссылка; вклеивается в собранную страницу |
517
+ | `src/page/app.css` | Оформление страницы сверх общей части: панель с деревом файлов (на широком экране — колонка слева от таблицы), состояния пустоты, узкое окно |
518
+ | `src/page/state.js` | Состояние страницы: данные отчёта, вид галочек, паспорт записи, память браузера и обмен ссылкой глава программы страницы |
519
+ | `src/page/dom.js` | Узлы страницы: мелкие помощники разметки (`appEl`, `appBox`) — одни на панель и таблицу |
520
+ | `src/page/panel.js` | Панель выбора: галочки метрик и файлов, категории, дерево путей, легенда; перерисовку просит у главы сборки |
521
+ | `src/page/table.js` | Таблица страницы: клетка, подпись коммита, шапка и состояния пустоты — разметка поверх общего расчёта |
522
+ | `src/page/app.js` | Сборка и запуск страницы: таблица целиком, перерисовка по выбору читателя, первая отрисовка и смена якоря; вклеивается в собранную страницу
470
523
  | `src/page/build.js` | Сборка страницы: данные, оформление и программа в одном файле без внешних ссылок |
471
524
  | `src/git.js` | Единственная граница вызова git: закрепления настроек, блобы пачкой, история, сверка с диском |
472
- | `src/strip.js` | Снятие балласта: стрипперы комментариев и отступов и правила, какая форма к какому файлу, гард компиляции |
525
+ | `src/strip.js` | Снятие балласта: какая форма к какому файлу (расширение, стратегия) и что считать точным числом вход разбора форм |
526
+ | `src/strip/js.js` | Снятие комментариев и отступов в JS: проход по случаям (комментарий, регексп, строка, символ) — строки и шаблоны насквозь |
527
+ | `src/strip/forms.js` | Формы текста со своим снятием балласта: разметка, стили, строки файла и JSON |
528
+ | `src/strip/guard.js` | Гард стриппера: снятое обязано компилироваться — скриптом в процессе или модулем в рабочем потоке |
473
529
  | `src/parse.js` | Разбор модуля: рабочий поток на прогон и отступление к `node --check`, способ разбора последнего модуля |
474
530
  | `src/parse-worker.js` | Сам разбор внутри потока: разбирает текст без исполнения, сообщает, что модулей vm в Node нет |
475
531
  | `src/metrics.js` | Реестр метрик: что измеряется, нужен ли текст и насколько честна цифра; описание метрики для читателя — в одном месте |
@@ -486,11 +542,16 @@ deльт задан один раз и по артефакту: рост зел
486
542
  | `src/data.js` | Категории файлов и контракт со страницей (`--data`) |
487
543
  | `src/render.js` | Статический артефакт: клетки, таблица, примечание (стили — в `src/css.js`) |
488
544
  | `src/config.js` | Настройки проекта-потребителя: умолчания, чтение, проверка |
545
+ | `src/project.js` | Настройки, выведенные из самого проекта (дерево и история): колонки, журнал, исключения. Без файла настроек он и есть настройки; `--init` закрепляет его файлом |
489
546
  | `src/locales.js`, `src/refusal.js`, `src/tool.js` | Тексты отчёта; коды выхода и справка; имя и версия пакета |
490
- | `src/cli.js` | Режимы командной строки и разбор ключей; главный файл пакета |
547
+ | `src/cli.js` | Вход инструмента: разбор строки, чтение проекта и доставка запроса режиму; главный файл пакета |
548
+ | `src/args.js` | Грамматика командной строки: режимы, ключи и команды плюс проверки их сочетаний — отказ называет виновника и готовую команду |
549
+ | `src/modes.js` | Режимы: собрать таблицу, сверить её с историей, отдать данные или страницу, полноту покрытия и диагностику |
550
+ | `src/init.js` | Закрепление настроек файлом (`--init`): то, что проект вывел о себе сам, ложится файлом — и проходит ту же проверку, что первый запуск |
491
551
  | `test/api.test.js` | Публичный API пакета: список имён заморожен, разбиение не имеет права его менять |
492
552
  | `eslint.config.js` | Правила оформления: те же, что у проекта-потребителя, плюс запрет склейки операторов в строке (`pnpm run lint`, `pnpm run lint:strict`) |
493
553
  | `tools/harness.js` | Обвязка проверок: пути, клоны фикстуры (в том числе общий на набор и с CRLF), запуск инструмента, разбор отказов, хеши |
554
+ | `tools/page-harness.js` | Обвязка проверок контракта и страницы: данные контракта, собранная страница, чтение её в настоящем DOM, переключатели панели — одна на четыре набора |
494
555
  | `tools/suites.js` | Разделение набора: какие файлы идут в быстрый прогон (с причиной для каждого), почему каждый дорогой — в полном |
495
556
  | `tools/run-tests.js` | Прогон набора (`pnpm test`, `pnpm test:all`, `pnpm run suites:measure`): длительность каждого файла своим замером и сверка числа проверок |
496
557
  | `tools/docs-facts.js` | Чтение фактов из документации — один слой на четыре проверки сторожа: что документ называет (пути, зовы, адреса разделов) против того, что есть в репозитории |
@@ -504,7 +565,10 @@ deльт задан один раз и по артефакту: рост зел
504
565
  | `test/cli.test.js`, `test/cli-paths.test.js` | Отказы командной строки: справка, настройки, коды выхода — и куда инструмент пишет |
505
566
  | `test/refusals.test.js` | Отказы исполняются: каждый вызван прогоном, сверены код выхода и обещанные фразы (свои клоны — для чужого хука, обрезанной истории и ветки мимо отчёта), и **совет выполняется** — команда даёт обещанный код, не падает стеком, а где объявлено «отказ ушёл», тот же зов после неё отвечает другим |
506
567
  | `test/refusals-catalog.test.js` | Сторож каталога отказов: у каждого места отказа в исходниках есть пункт, у каждого пункта — объявленный совет, а отказы, отданные другой проверке, ею в самом деле утверждаются (названные файл и строка проверяются) |
507
- | `test/contract.test.js` | Контракт данных и страница: числа против эталона, производные против чисел артефакта, дерево файлов против путей, память выбора и ссылка против перезахода, чужого отчёта и чужого адреса, пометки приближения против подписи метрики |
568
+ | `test/contract-data.test.js` | Контракт данных: числа против эталона, состав полей против производных, пометки приближения против подписи метрики |
569
+ | `test/contract-derived.test.js` | Производные против чисел артефакта: итоги строки, дельты клетки и дельта итога — на коде, который лежит в дереве |
570
+ | `test/page-view.test.js` | Собранная страница: вклейка без копий расчёта, самодостаточность, дерево файлов, состояния пустоты, оформление и переключатели |
571
+ | `test/page-choice.test.js` | Память выбора и обмен ссылкой: перезаход, чужой отчёт, чужая и битая запись, смена адреса на открытой странице |
508
572
  | `test/module.test.js` | Модуль в расширении `.js`: измеряется без правок настроек; гард стриппера жив (доказано мутацией) и не обвиняет невиновного |
509
573
  | `test/guard.test.js` | Разбор модуля: идёт потоком, оба пути дают один вердикт, отступление работает без файла потока, сотни разборов дешевле запуска |
510
574
  | `test/runner.test.js` | Чтение вывода процесса: куски склеиваются буферами, а не приклеиваются к строке — многобайтовый символ на границе кусков не превращается в два символа-заменителя |
@@ -513,12 +577,14 @@ deльт задан один раз и по артефакту: рост зел
513
577
  | `test/changelog.test.js` | Сторож выпуска: версия в `CHANGELOG.md` — версия манифеста, а таблица «что изменится в числах» — это замер на фикстуре, сверенный с живым прогоном |
514
578
  | `test/release.test.js` | Сторож выпуска из CI: он начинается тегом, версия берётся из манифеста, секрета и одноразового кода не требует, prerelease не уезжает в `latest`, перед публикацией идёт полный набор — и подсказка на npmjs.com называет этот же файл |
515
579
  | `test/suites.test.js` | Сторож разделения набора: полнота классификации (быстрый — явно, полный — с причиной), причина у каждого файла, что быстрый прогон остаётся частью набора |
580
+ | `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` | Пробы датчиков: искусственное нарушение → датчик красный, снятие → снова зелёный; прогон зовёт датчик командой, а не импортом, поэтому доказывает и код возврата |
581
+ | `test/gates-verify.test.js` | Сторож единственного списка: команды профиля против рабочих процессов, хуков и `templates/ci.yml` — проверки, которой нет в профиле, в CI быть не может |
516
582
  | `test/check.test.js` | Полнота и объяснение на настоящих коммитах фикстуры: непокрытый путь, «только отчёт», «число не сдвинулось», «мимо колонок», слияние — и что починка настроек не двигает числа |
517
583
  | `test/doctor.test.js` | Диагностика на пяти состояниях проекта: без настроек (2), полное покрытие (0), неполное (1), обрезанная история (3), нет датчика (4) — и блок покрытия равен ответу `size check`, а не считается вторым разом |
518
584
  | `test/hook.test.js` | Хуки на свежем клоне: ставятся только командой, дают отдельный коммит отчёта (в том числе после слияния), повторный запуск молчит, чужая работа и индекс не тронуты, в CI и при отказе инструмента ничего не делают, снятие возвращает проект к прежнему |
519
585
  | `test/templates.test.js` | Шаблоны: черновик настроек проходит проверку инструмента и собирает настоящий отчёт; описание проверки разбирается и зовёт только существующие команды и ключи |
520
586
  | `test/minify.test.js`, `test/tokens.test.js` | Настоящее сжатие и токены: числа против упрощения, кодировка как часть числа, честность подписи, работа без необязательной зависимости (код 4) и шов `SIZE_REPORT_NO_OPTIONAL` |
521
- | `package.json` | Манифест пакета: имя `@vernikr/size-report`, версия `1.1.1`, список поставки — только существующее |
587
+ | `package.json` | Манифест пакета: имя `@vernikr/size-report`, версия `1.2.0`, список поставки — только существующее |
522
588
 
523
589
  Оба каталога эталонов снимаются заново теми же инструментами: `pnpm run parity` и
524
590
  `pnpm run fixture` дают те же файлы. Побайтово сверяется наше — конфиг, эталонные
@@ -550,7 +616,7 @@ JSX и TSX выход зависит от настройки jsx сам
550
616
  уже собранных значениях (`data`, `render`, `page`), а настройки, тексты и отказ —
551
617
  по краям, потому что их знает любой и они не знают никого. Оба отчёта считаются на
552
618
  сборке: страница получает исходники общего расчёта и своей программы вклеенными
553
- (`src/derived.js`, `src/page/app.js`), потому что открывается она с диска, без
619
+ (`src/derived.js`, `src/page/*.js`), потому что открывается она с диска, без
554
620
  сервера и без сети. Остальное — по шагам 2–6 (`PLAN.md` §5).
555
621
 
556
622
  ## Как подключить к своему проекту
@@ -573,7 +639,7 @@ pnpm add -D @vernikr/size-report
573
639
  ```
574
640
 
575
641
  Пакет **опубликован в реестре**, и публично: `npm view @vernikr/size-report
576
- version` отвечает `1.1.1`, `npm access get status @vernikr/size-report` — `public`,
642
+ version` отвечает `1.3.1`, `npm access get status @vernikr/size-report` — `public`,
577
643
  а анонимный запрос тарболла — код 200. `npm i -D` и `yarn add -D` принимают то же
578
644
  имя; ни ключа, ни ссылки на репозиторий не нужно.
579
645
 
@@ -581,11 +647,11 @@ version` отвечает `1.1.1`, `npm access get status @vernikr/size-report`
581
647
  реестра, но остаётся привязанной к ревизии:
582
648
 
583
649
  ```bash
584
- pnpm add -D github:vernikr/size-report#v1.2.0
650
+ pnpm add -D github:vernikr/size-report#v1.3.1
585
651
  ```
586
652
 
587
653
  Без сети (или если тянуть из codeload нечем) — тарболл: `pnpm pack` в клоне
588
- пакета, затем `pnpm add -D ./vernikr-size-report-1.2.0.tgz`.
654
+ пакета, затем `pnpm add -D ./vernikr-size-report-1.3.1.tgz`.
589
655
 
590
656
  **Почему тег, а не sha.** Короткий sha pnpm разрешает только через видимые рефы, а
591
657
  `git ls-remote` отдаёт одни верхушки веток: пока ревизия — верхушка, короткий sha
@@ -593,7 +659,7 @@ pnpm add -D github:vernikr/size-report#v1.2.0
593
659
  <sha> to a commit`. Это не рассуждение, а проба: короткий пин `6530237` ставился,
594
660
  пока `main` стоял на нём, и перестал — на следующем же коммите, а тот же sha
595
661
  целиком поставился. Имя ветки (`#main`) или тег принимаются оба, но ветка —
596
- движущаяся цель, а тег постоянен: этот выпуск стоит на теге `v1.1.1`, он же и в
662
+ движущаяся цель, а тег постоянен: этот выпуск стоит на теге `v1.3.1`, он же и в
597
663
  примере (сорок знаков тоже годятся, но их придётся брать глазами из истории).
598
664
 
599
665
  Ревизия в примере — не украшение, а часть утверждения: она закреплена за тем, что
@@ -618,30 +684,39 @@ GIT_SSH_COMMAND=false`): установка 3,4 с, дальше `size --write`
618
684
  области владельца (`@vernikr/size-report`), а не только отправка архива; что
619
685
  затронуло переименование — `PLAN.md` §10, чем доказана выкладка — `WORKLOG.md` §53.
620
686
 
621
- ### 2. Черновик настроек
687
+ ### 2. Настройки: их можно не заводить
622
688
 
623
689
  ```bash
624
- pnpm exec size --init # создаёт size-table.config.json
690
+ pnpm exec size --write # таблица; настроек нет — их выведет сам инструмент
691
+ pnpm exec size --init # закрепить выведенное в size-table.config.json
625
692
  ```
626
693
 
627
- Черновик собирается по самому проекту: колонки крупнейшие файлы по списку
628
- расширений исходников (список печатается тут же «расширения в проекте»), журнал
629
- по знакомым именам (`WORKLOG.md`, `CHANGELOG.md`, …), вывод в `docs/`, если он
630
- есть, `fixCommand` под ваш менеджер пакетов.
631
-
632
- > **Документации в черновике нет, и это заметно сразу.** Список расширений
633
- > исходники (`.js`, `.css`, `.json`, …), а `README.md` и журнал в него не входят:
634
- > колонкой доки делает человек, потому что «важный документ» — не про расширение.
635
- > Поэтому первый же `size check` на проекте с README и журналом честно скажет, что
636
- > эти пути не отслеживаются и не исключены (код 1), — это и есть тот шаг, где доки
637
- > становятся колонками или попадают в `skip` (`§5`). Проверено покомандно на
638
- > живом проекте: `WORKLOG.md` §54. Он же печатает
639
- следующие три шага. Черновик ведёт метрику `min` на настоящее сжатие
640
- (`"minify": {"engine": "esbuild"}`) и сразу включает метрику `tok`
641
- (`"tokens": {"family": "openai", "encoding": "o200k_base"}`): и минификатор, и
642
- словарь едут необязательными зависимостями и ставятся обычной установкой, а без
643
- них инструмент работает и сам скажет об этом (код 4) правки настроек не
644
- требуются.
694
+ Начинать с настроек не нужно: без файла инструмент выводит их из проекта — колонками
695
+ берёт крупнейшие файлы, по одному от каждого расширения (иначе отчёт состоял бы из
696
+ одних крупных `.md`, и ни один `.js` не попал бы под настоящее сжатие), журналом —
697
+ первый знакомый (`WORKLOG.md`, `CHANGELOG.md`, …), файлом отчёта `docs/`, если
698
+ каталог есть, командой починки — объявленный скрипт `sizes`, а без него — путь
699
+ к установленному пакету (его цитируют подпись отчёта и отказы, поэтому он обязан
700
+ работать уже сейчас), ссылкой на коммит адрес `origin`, метриками `raw`, `min`,
701
+ `tok`. Метрика `min` считается настоящим сжатием (`"minify": {"engine": "esbuild"}`),
702
+ а `tok` словарём (`"tokens": {"family": "openai", "encoding": "o200k_base"}`): без
703
+ этих необязательных зависимостей метрика честно отступает к другому счёту и прогон
704
+ отдаёт код 4 правки настроек и тут не требуются.
705
+
706
+ Всё, что колонкой быть не может или в неё не поместилось (сам отчёт, замки
707
+ зависимостей, карты, собранное), называется в `skip` поэтому первый же `size check`
708
+ полон, а не красен: «пути мимо колонок» появляются от новых правок, а не от того, что
709
+ проект ещё не описан. О том, что настройки выведены, инструмент говорит строкой в
710
+ stderr и называет команду, которая их закрепляет,`--init`; закреплённое проходит
711
+ ту же проверку, что любой файл настроек, и дальше его правят глазами (сам `--init`
712
+ печатает, что закрепил, и что делать дальше — скрипты и проверку в CI). Без
713
+ закрепления профиль выводится заново на каждом запуске: числа не «поедут», но
714
+ повторить прежний замер — в том числе хуком и проверкой — можно только по файлу.
715
+
716
+ Закрепляется **то же, чем проект работает без файла**: вывод из проекта поверх
717
+ умолчаний. Поэтому в закреплённом файле видны и значения, которых в проекте никто не
718
+ писал, — тогда смена умолчаний в новой версии пакета не поедет по уже настроенному
719
+ проекту молча.
645
720
 
646
721
  > Subкоманды `size init` пока нет — CLI знает только флаги (`--init`, `--write`,
647
722
  > `--page`, `--data`, `--json`, без флага — проверка); полный список даёт `size --help`.
@@ -649,20 +724,21 @@ pnpm exec size --init # создаёт size-table.config.json
649
724
 
650
725
  ### 3. Что правится в конфиге
651
726
 
652
- Черновик знает про проект только размеры файлов — какие колонки важны, знает
653
- человек. Чаще всего правят:
727
+ Вывод знает про проект только то, что видно в дереве и истории, — какие колонки важны,
728
+ знает человек. Чаще всего правят:
654
729
 
655
730
  | Ключ | Что это |
656
731
  |---|---|
657
- | `columns` | колонки таблицы: `{label, paths: [...]}`; пути в одной колонке складываются (например, `src` целиком), `label` — то, что увидит человек |
732
+ | `columns` | колонки таблицы: `{label, paths: [...]}`; **колонка это файл**: список путей — её переименования (в ревизии берётся тот путь, который в ней есть), а не несколько файлов разом; `label` — то, что увидит человек |
658
733
  | `metrics` | из чего состоит число: `raw` (размер объекта git), `min` (минифицированная форма — какая именно, решает `minify.engine`), `tok` (токены), `gzip` |
659
734
  | `tokens.family`, `tokens.encoding` | словарь для `tok`: семейство (`openai`) и кодировка (`o200k_base` или `cl100k_base`) — кодировка меняет число, поэтому она и в настройках, и в подписи метрики |
660
735
  | `minify.engine` | чем считается `min`: `strip` (комментарии и отступы, точность не обещается) или `esbuild` (настоящее сжатие; форматы без минификатора — упрощение, и это видно в подписи метрики) |
661
- | `output` | файл таблицы (по черновику — `docs/size-table.html`) |
736
+ | `output` | файл таблицы (в выведенном профиле — `docs/size-table.html`, если каталог `docs/` есть, иначе в корне) |
662
737
  | `journal` | где искать разделы журнала, на которые ссылаются строки |
663
- | `links.commitUrl` | шаблон ссылки на коммит, например `https://github.com/org/repo/commit/{sha}` |
664
- | `skip` | пути, которые колонками быть не могут |
665
- | `fixCommand` | команда, которую цитирует подпись отчёта и подсказывает отказ; в черновике уже ваша |
738
+ | `links.commitUrl` | шаблон ссылки на коммит, например `https://github.com/org/repo/commit/{sha}`; выводится из адреса `origin` у GitHub и GitLab (у остальных хозяев — пусто, а не догадка) |
739
+ | `skip` | пути, которые колонкой не стали: и те, что ею быть не могут (сам отчёт, замки зависимостей), и те, что в колонки не поместились (выведенный профиль объявляет исключениями всё остальное — поэтому первый `check` полон) |
740
+ | `fixCommand` | команда, которую цитирует подпись отчёта и подсказывает отказ; в выведенном профиле ваш скрипт `sizes`, если он объявлен, иначе путь к установленному пакету внутри проекта (зов по имени пакета уходит в реестр — `REFACTOR.md` R-4.21) |
741
+ | `locale`, `title`, `heading` | язык текстов отчёта и его заголовки; пустые `title`/`heading` значат «взять из локали» |
666
742
  | `minify.guard` | расширения, где результат стриппера проверяется разбором; модуль в `.js` гард понимает сам, трогать его не нужно |
667
743
  | `hooks.enabled` | выключатель хука автообновления (`false` — хук остаётся на месте, но молчит; убирается он только `size uninstall-hook`) |
668
744
 
@@ -740,9 +816,10 @@ pnpm exec size doctor # 0 — делать нечего; иначе перв
740
816
  `.github/workflows/size-report.yml` без правок — сборка таблицы, сверка с файлом
741
817
  на диске, два снимка чисел (обычный и в среде без настроек git) и их сравнение.
742
818
  Секретов оно не требует. Для `npm`/`yarn` в самом файле сказано, какие две строки
743
- заменить. Рядом — `templates/size-report.config.json`, черновик настроек в дополнение
744
- к `pnpm exec size --init`: колонки в нём примерные (`README.md`, `package.json`),
745
- они есть почти в любом проекте, поэтому первый отчёт собирается сразу.
819
+ заменить. Рядом — `templates/size-report.config.json`, образец настроек: колонки в нём
820
+ примерные (`README.md`, `package.json`), они есть почти в любом проекте, поэтому
821
+ первый отчёт собирается сразу. Нужен он, только если хочется начать с правленого
822
+ файла: без файла настройки выводятся из проекта (`--init` закрепляет выведенное).
746
823
 
747
824
  Свой CI у пакета — `.github/workflows/ci.yml`: он гоняет у себя тот же список
748
825
  команд, что описан ниже, и его можно взять за образец для шага потребителя.
@@ -871,8 +948,43 @@ R-4.1): пути, таблица файлов, зовы и ключи инстр
871
948
  починки взяты из конфига) не проверяет никто: если это важно, это одна проверка
872
949
  поверх `--data` в проекте.
873
950
 
951
+ ## Гейт против раздувания
952
+
953
+ **Список проверок — один, и он же в CI.** Профиль проверок задан в одном месте
954
+ (`tools/gates/run.js`): `pnpm run verify:fast` (десятки секунд — каждая правка),
955
+ `pnpm run verify` (полный — перед отправкой и в CI) и `pnpm run verify:slow`
956
+ (по расписанию — то же плюс покрытие). CI зовёт эту же команду, а не свой список:
957
+ работа `verify` (`.github/workflows/ci.yml`) на каждый пуш и запрос правки, работа
958
+ `verify-slow` — по расписанию. Совпадение стережёт `test/gates-verify.test.js`:
959
+ проверка, которой нет в профиле, в CI не пройдёт.
960
+
961
+ **Датчики ловят раздувание, а не стиль** (стиль — у линтера): размер и сложность
962
+ функций, размер модулей, дубли веток и функций (`sonarjs`), вес проверок (проверка
963
+ без утверждения, утверждение без сравнения, выключенная проверка), пометки долга,
964
+ клоны по токенам (`jscpd`), циклы и сироты связей (`dependency-cruiser`), просадка
965
+ покрытия против своей же базы (`c8`).
966
+
967
+ **Порог взят из замера, а не из головы, и он храповик.** По исходному замеру:
968
+ сложность функции p50 1 / p90 4 / p99 11 / max 27 — порог 12 (в базе осталось 5
969
+ функций); длина функции p50 7 / p90 27 / p99 73 / max 118 — порог 60 (9 в базе);
970
+ модуль p90 381 строка / max 907 — порог 450 (в базе не осталось ни одного: три
971
+ толстых файла — контракт, программа страницы и сборка фикстуры — разделены,
972
+ `WORKLOG.md` §59–§61). Всё, что выше порога
973
+ сегодня, лежит в базе (`.eslint-suppressions.json`) и работе не мешает; новое валит
974
+ прогон. Дубли — 13 клонов / 84 строки (0,67 %), связи — 112 модулей / 468 связей и ни
975
+ одной находки.
976
+
977
+ **Базы обновляет человек.** `pnpm run baseline:metrics`, `baseline:dup`,
978
+ `baseline:coverage` — и только с трейлером `Gate-Change:` в сообщении коммита: правка
979
+ гейт-файла без него красна и локально (хук `commit-msg`), и в CI (по каждому коммиту
980
+ диапазона). Иначе гейт ослаблялся бы тем же коммитом, который он останавливает.
981
+ Таблица замеров, отвергнутые инструменты (knip, ast-grep, size-limit, gitleaks) и
982
+ действия человека — в `WORKLOG.md` §58.
983
+
874
984
  ## Для ИИ-агента
875
985
 
986
+ - `pnpm run verify:fast` — перед каждой правкой, `pnpm run verify` — перед отправкой;
987
+ что не так и что нельзя трогать при красном — `AGENTS.md`.
876
988
  - `size check --json` — готово ли всё: какая часть истории покрыта, какие пути
877
989
  мимо колонок (с коммитом-первопричиной) и какие коммиты выпали без строки.
878
990
  - `size explain <коммит> --json` — почему у конкретного коммита нет строки: причина,