fix(ночь): прибор объявлял мёртвым сторожа, которого поднимает сам прогон
Разбор 33 «точных долгов» глазами (работа 2 из промта смены 26). Настоящих оказалось 20, ложных тревог 13 — почти сорок процентов. Одна ложная тревога была ОПАСНОЙ. `setup` и `teardown` из `storozh-boevogo-hozyaystva.mjs` шли в приговоре с самой твёрдой пометкой прибора — «не зовёт никто вовсе, даже проверки». У этой пометки, в отличие от «зовут только проверки», нет оговорки «посмотри глазами». А зовёт их САМ ПРОГОН: файл назван в его настройке, `app/vitest.config.tools.mjs` строка 56, `globalSetup`. Зова в коде нет и быть не может — соглашение об имени скобками не выглядит никогда. То есть прибор, написанный против класса «написано, но не работает», подталкивал снести работающее. Прочти следующая смена этот приговор буквально — и пошла бы вырезать сторожа боевого хозяйства как мёртвый код. РЕЗ УЗКИЙ НАРОЧНО, и это главное в правке. Наружу из затеи торчат три дороги, зовом имени считается ОДНА: - `globalSetup` — прогон зовёт `setup`/`teardown` сам по соглашению — засчитываем; - `setupFiles` — файл просто исполняется сверху вниз — имён не зовёт; - наряд `package.json` (`node tools/night/utro.mjs`) — имён не зовёт, там рот. Широкий рез тут не ослепил бы прибор, а ЗАТКНУЛ: засчитай мы наряды — все имена `cli.mjs` и `utro.mjs` вышли бы из долга разом, и живая находка `merkaCheloveka` пропала бы из приговора молча. ДОКАЗАНО ВЫРЕЗАНИЕМ, ДВАЖДЫ. Убрал встройку в обход — покраснела ровно одна, сквозная (число красных посчитано ДО прогона, сошлось). Расширил рез на `setupFiles` — покраснел встречный сторож послаблений. Голый шов закрыт нарочно: правило умеет читать настройку, обход умеет судить, и обе половины были бы зелены, даже если обход правила не спрашивает вовсе. Ловится только сквозной проверкой на подложном уголке. Номера новому пятну не даю: нумерация в шапке прибора сама себе противоречит — одно и то же пятно (ввоз модуля целиком) названо там и «шестым», и «№9». Имя вместо номера. Разбор всех 33 поимённо — docs/superpowers/zamer/2026-08-18-razbor-33-dolgov.md. Оттуда же два новых долга, ни один не начат: - механизм вопросов владельцу мёртв С ОБЕИХ СТОРОН: команд `cli.mjs answer` и `cli.mjs ack`, которыми примечание обещает класть ответ, в `cli.mjs` нет вовсе; - живая сверка настроек не спрашивает `kudaOtnesyon` — замерено подложным ключом: выдуманный ключ прошёл молча, а правило при этом честно ответило «не отнесён никуда». Приговор прибора: 33 -> 31 точных долга, 610 вывозится. Проверок 2916 в 106 файлах, ноль красных, код 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
# Разбор 33 долгов прибора «кто зовёт это в жизни» — глазами, по одному
|
||||
|
||||
**Смена 26, 06.08.2026.** Работа №2 из промта смены 26: «Прибор их называет за 21 секунду,
|
||||
но приговор он выносит по устройству, а не по замыслу. Правило чтения: спроси у каждого
|
||||
„а для кого он писался?“».
|
||||
|
||||
> 🔴 **ГЛАВНОЕ ЧИСЛО.** Из 33 «точных долгов» настоящих оказалось **20**, ложных тревог —
|
||||
> **13, то есть почти сорок процентов**. И одна ложная тревога была **опасной**: прибор
|
||||
> объявил мёртвым сторожа, который поднимается на каждом прогоне.
|
||||
|
||||
---
|
||||
|
||||
## 🔴🔴 Опасная ложная тревога: прибор подталкивал снести живое
|
||||
|
||||
`setup` и `teardown` из `storozh-boevogo-hozyaystva.mjs` шли в приговоре с самой твёрдой
|
||||
пометкой прибора — **«не зовёт никто вовсе, даже проверки»**. У этой пометки, в отличие
|
||||
от «зовут только проверки», нет оговорки «посмотри глазами».
|
||||
|
||||
А зовёт их **сам прогон**, потому что файл назван в его настройке:
|
||||
|
||||
```
|
||||
app/vitest.config.tools.mjs:56 → globalSetup: ['../tools/night/storozh-boevogo-hozyaystva.mjs']
|
||||
```
|
||||
|
||||
Зова в коде нет и быть не может: соглашение об имени скобками не выглядит никогда.
|
||||
|
||||
🔑 **Прибор против класса «написано, но не работает» сам подталкивал снести работающее.**
|
||||
Прочти следующая смена этот приговор буквально — и пошла бы вырезать сторожа боевого
|
||||
хозяйства как мёртвый код.
|
||||
|
||||
**Починено этой же сменой** (см. ниже «Что сделано»). Приговор упал 33 → 31.
|
||||
|
||||
---
|
||||
|
||||
## Разбор по одному
|
||||
|
||||
### 🟢 Ложные тревоги — 13
|
||||
|
||||
| имя | файл | почему не долг |
|
||||
|---|---|---|
|
||||
| `setup`, `teardown` | `storozh-boevogo-hozyaystva.mjs` | 🔴 поднимает **сам прогон** через `globalSetup` |
|
||||
| `schyotDolga`, `strokaDolga`, `perebrat`, `svezhiePoddelki`, `otkazPoddelki` | `lezhachie-sledy.mjs` | **тело сторожа есть сама проверка**: вторая половина сторожа течи, заведена 04.08 по прямому решению владельца; краснеет на полном прогоне |
|
||||
| `PREDEL_ZHIVOGO_ZAPUSKA`, `PREDEL_SAMOY_PROGRAMME` | `predel-zhivogo-zapuska.mjs` | писались **для проверок**, их и должны звать только они (названо ещё в промте) |
|
||||
| `readEvents` | `heartbeat.mjs` | работа делается **напрямую**: события читают `sudya-nochi.mjs:146` и `zhiv-li-progon.mjs:113` своим путём |
|
||||
| `vorotaVetkiNochi` | `vetka-nochi-ne-otstayot.mjs` | лишняя обёртка над половинками, которые зовут напрямую (названо в промте) |
|
||||
| `schyotZovushchih` | `schyot-zovushchih.mjs` | счёт по одному имени; обход зовущих ходит своей дорогой |
|
||||
| `readProbeAttempts`, `readSpawnAttempts` | `schyot-zamerov.mjs` | читалки журналов для проверок; живой счёт замеров идёт своим путём |
|
||||
|
||||
🪤 **Четыре из них — один и тот же новый род:** «работа делается, но мимо обёртки».
|
||||
Две дороги к одному файлу могут разъехаться молча — это мелкий долг, но не мёртвый код.
|
||||
|
||||
### 🔴 Настоящие долги — 20
|
||||
|
||||
**А. Механизм «спросить владельца и принять ответ» — 11 имён, мёртв С ОБЕИХ СТОРОН**
|
||||
|
||||
| имя | файл |
|
||||
|---|---|
|
||||
| `askOwner`, `mustAsk`, `answerQuestion`, `applyAnswer`, `assumptionOpen`, `deadlineHit`, `otvetDnyom`, `priznakiPunkta`, `prostoyLiVpravdu` | `questions.mjs` |
|
||||
| `recordOwnerAck`, `OWNER_ACTION_KINDS` | `mailer.mjs` |
|
||||
|
||||
🔴 **Смена 25 нашла половину.** Вторая половина хуже: примечание в `mailer.mjs` говорит,
|
||||
что действия владельца «пишет ПОМОЩНИК теми же командами, что кладут ответ: `cli.mjs answer`
|
||||
и `cli.mjs ack`». **Таких команд в `cli.mjs` нет вовсе** — прибор 4 называет семь, и этих
|
||||
двух среди них нет (замерено).
|
||||
|
||||
⇒ Владельцу не только нечем задать вопрос — **ему некуда положить ответ**.
|
||||
|
||||
**Б. Долги, названные вслух там, где их надо подключить — 2**
|
||||
|
||||
| имя | где сказано |
|
||||
|---|---|
|
||||
| `zadachaKonchena` | `supervisor.mjs:2239`: «ДОЛГ КУСКА 7 (позиция 7): **ИМЕННО ЭТА СТРОКА** заменяется на зов». Вместо правила стоит упрощение `otlozhennyePunkty.size === 0` |
|
||||
| `reshyonnyeSNevypolnimymi` | `items.mjs`: «правило готово, а живой провод к нему — нет: кто будет его делать, обязан сперва завести ленджер невыполнимых» |
|
||||
|
||||
🪤 **Но примечание в `items.mjs` при этом врёт** — про `zadachaKonchena` там сказано
|
||||
«**зовут его двое**: блок 5а куска 4 и блок 6а куска 7». Замерено командой: живого зова нет
|
||||
ни одного. Это правило 235 в чистом виде — задуманное записано в настоящем времени.
|
||||
|
||||
**В. Три находки смены 25 — 4 имени**
|
||||
|
||||
| имя | что это |
|
||||
|---|---|
|
||||
| `mayTouchFakt` (`file-claims.mjs`) | 🔴 защита «файл не заявлен — значит ничей»; **ждёт слова владельца** |
|
||||
| `merkaCheloveka`, `merkaAvariynoy` (`utro.mjs`) | 🟡 мерка человечности; 🆕 **мерок ДВЕ, а не одна** — вторая для аварийной бумаги |
|
||||
|
||||
**Г. Новый долг, найденный этой сменой — 1**
|
||||
|
||||
| имя | что это |
|
||||
|---|---|
|
||||
| `kudaOtnesyon` (`sverka-nastroek-s-resheniyami.mjs`) | 🔴 «ключ живой настройки не отнесён никуда — и это беда» |
|
||||
|
||||
🔴 **Замерено, а не предположено.** Подложил в настройку выдуманный ключ и позвал живую сверку:
|
||||
|
||||
```
|
||||
Сверка сказала про выдуманный ключ: НЕТ — прошёл молча
|
||||
А само правило говорит: null
|
||||
```
|
||||
|
||||
⇒ Правило готово и верно отвечает «не отнесён никуда». Живая сверка его не спрашивает.
|
||||
Заведи кто-нибудь новый ключ в настройках ночи — он пройдёт мимо владельца молча.
|
||||
|
||||
**Д. Прочее — 2**
|
||||
|
||||
| имя | что это |
|
||||
|---|---|
|
||||
| `mailFailedSince` (`mailer.mjs`) | «не ушла ли сводка с такого-то часа» — не зовёт никто |
|
||||
| `readResult` (`spawn.mjs`) | разбор последней строки вывода работника — не зовёт никто |
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Чему научил разбор
|
||||
|
||||
**237. У ПРИБОРА, СУДЯЩЕГО «КТО ЭТО ЗОВЁТ», СПРОСИ СНАЧАЛА — А ВСЕ ЛИ ДОРОГИ ЗОВА ОН ЗНАЕТ.**
|
||||
Зов приходит не только из кода: из настройки прогона (`globalSetup`), из наряда, по соглашению
|
||||
об имени. Прибор, знающий одну дорогу, объявляет мёртвым живое — и приговор его тем твёрже,
|
||||
чем меньше дорог он знает.
|
||||
|
||||
**238. СТОРОЖ, ЧЬЁ ТЕЛО ЕСТЬ САМА ПРОВЕРКА, ЖИВ — ЕСЛИ ПРОВЕРКИ ГОНЯЮТ.** Пять имён
|
||||
`lezhachie-sledy.mjs` не зовёт ни один рабочий файл, и это правильно: сторож течи работает
|
||||
тем, что краснеет на полном прогоне. Вопрос «для кого писался» тут отвечается не «для
|
||||
проверок», а «**чем он доставляется**».
|
||||
|
||||
**239. 🪤 РАБОТА, ИДУЩАЯ МИМО СВОЕЙ ЖЕ ОБЁРТКИ, — НЕ МЁРТВЫЙ КОД, А РАЗЪЕЗД.** Четыре
|
||||
случая из тринадцати: обёртка есть, а зовут напрямую. Сносить нельзя, оставлять как есть —
|
||||
значит держать две дороги к одному месту, которые однажды разойдутся молча.
|
||||
|
||||
---
|
||||
|
||||
## Что сделано этой сменой
|
||||
|
||||
🟢 **Слепое пятно «зов из настройки» закрыто.** Прибор читает `globalSetup` настройки
|
||||
прогона и не сажает `setup`/`teardown` такого файла в точный долг. Рез **узкий нарочно**:
|
||||
|
||||
| дорога наружу | считается зовом имени? |
|
||||
|---|---|
|
||||
| `globalSetup` — прогон зовёт `setup`/`teardown` сам | ✅ да |
|
||||
| `setupFiles` — файл исполняется сверху вниз | ❌ нет, имён не зовёт |
|
||||
| наряд `package.json` (`node tools/night/utro.mjs`) | ❌ нет, там рот |
|
||||
|
||||
🔴 **Почему рез узкий — это главное.** Широкий рез здесь не ослепил бы прибор, а **заткнул**:
|
||||
засчитай мы наряды — все имена `cli.mjs` и `utro.mjs` вышли бы из долга разом, и живая
|
||||
находка `merkaCheloveka` пропала бы из приговора молча.
|
||||
|
||||
**Доказано вырезанием, дважды:**
|
||||
|
||||
1. убрал встройку в обход → покраснела ровно одна, сквозная (**считал заранее — сошлось**);
|
||||
2. расширил рез на `setupFiles` → покраснел встречный сторож послаблений.
|
||||
|
||||
**Голый шов закрыт нарочно:** правило умеет читать настройку, обход умеет судить — и обе
|
||||
половины были бы зелены, даже если обход правила не спрашивает вовсе. Ловится только
|
||||
сквозной проверкой на подложном уголке.
|
||||
|
||||
**Числа:** проверок 2916 в 106 файлах (было 2911), ноль красных, код 0.
|
||||
Приговор прибора: **610 вывозится, 31 точный долг** (был 33).
|
||||
@@ -110,6 +110,53 @@ describe('обход зовущих: спрашиваем у всего выво
|
||||
}
|
||||
});
|
||||
|
||||
it('3Б. 🔴 кого поднимает САМ ПРОГОН — не долг, хоть зова в коде и нет', () => {
|
||||
// 🔴 БЕДА ЗАМЕРЕНА НА ЖИВОМ (06.08.2026). Обход объявил `setup`/`teardown` сторожа
|
||||
// боевого хозяйства «не зовёт никто вовсе, даже проверки» — самым твёрдым своим
|
||||
// приговором, у которого нет оговорки «посмотри глазами». А их зовёт сам прогон:
|
||||
// файл назван в его настройке (`globalSetup`). Прочти это следующая смена буквально —
|
||||
// и пошла бы сносить живого сторожа как мёртвый код.
|
||||
//
|
||||
// 🪤 ЗАЧЕМ ЗДЕСЬ ОТДЕЛЬНАЯ ПРОВЕРКА, ЕСЛИ САМО ПРАВИЛО УЖЕ ПОКРЫТО (13–16 у соседа).
|
||||
// Потому что покрыты были бы ДВЕ ЧЕСТНЫЕ ПОЛОВИНКИ, а дыра живёт МЕЖДУ ними: правило
|
||||
// умеет читать настройку, обход умеет судить — и обе зелены, даже если обход правила
|
||||
// не спрашивает вовсе. Голый шов ловится только сквозной проверкой.
|
||||
const dir = mkdtempSync(join(tmpdir(), 'obhod-zovushchih-nastroyka-'));
|
||||
try {
|
||||
writeFileSync(join(dir, 'moy-storozh.mjs'), [
|
||||
'export function setup() { return 1; }',
|
||||
'export function teardown() { return 2; }',
|
||||
'export function nikomuNeNuzhnoe() { return 3; }',
|
||||
].join('\n'), 'utf8');
|
||||
const nastroyka = join(dir, 'vitest.config.tools.mjs');
|
||||
writeFileSync(nastroyka, [
|
||||
'export default {',
|
||||
' test: {',
|
||||
" globalSetup: ['../tools/night/moy-storozh.mjs'],",
|
||||
' },',
|
||||
'};',
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
const itog = obhodZovushchih(dir, { nastroykaProgona: nastroyka });
|
||||
const dolgi = itog.tochnyeDolgi.map((v) => v.imya).sort();
|
||||
|
||||
// 🔴 Главное: два имени по соглашению вышли из точного долга…
|
||||
expect(dolgi).not.toContain('setup');
|
||||
expect(dolgi).not.toContain('teardown');
|
||||
// …и ушли не в тишину, а под глаза — с пометкой, что их поднимает прогон.
|
||||
const podnyatye = itog.posmotretGlazami.filter((v) => v.podnimaetProgon).map((v) => v.imya).sort();
|
||||
expect(podnyatye).toEqual(['setup', 'teardown']);
|
||||
|
||||
// 🔴 ВСТРЕЧНЫЙ СТОРОЖ ПОСЛАБЛЕНИЙ, и он тут главнее первого. Прощение выдано ДВУМ
|
||||
// именам по соглашению, а не всему файлу: сосед по тому же файлу, которого не зовёт
|
||||
// никто, обязан остаться в долгу. Иначе один `globalSetup` укрыл бы от прибора целый
|
||||
// файл — и прибор стал бы выгораживать мёртвое, то есть врать в другую сторону.
|
||||
expect(dolgi).toContain('nikomuNeNuzhnoe');
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('4. 🔴 пустое место не выдаётся за ноль долгов (правило 233)', () => {
|
||||
// Каталог без единого рабочего файла — это НЕ «долгов нет», это «считать было нечего».
|
||||
const dir = mkdtempSync(join(tmpdir(), 'obhod-zovushchih-pusto-'));
|
||||
|
||||
@@ -324,13 +324,58 @@ function vvoziatLiModulCelikom(syroy, rabochie, rodnoyFayl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ━━━ СЛЕПОЕ ПЯТНО, НАЙДЕНО 06.08.2026: ЗОВ ИДЁТ ИЗ НАСТРОЙКИ, А НЕ ИЗ КОДА ━━━
|
||||
//
|
||||
// 🔴 Прибор смотрит только внутрь затеи — и потому объявил `setup`/`teardown` сторожа
|
||||
// боевого хозяйства «не зовёт никто вовсе, даже проверки». А зовёт их САМ ПРОГОН, потому
|
||||
// что файл назван в его настройке: `globalSetup: ['../tools/night/storozh-…mjs']`. Зова
|
||||
// в коде нет и быть не может — соглашение об имени зовом со скобками не выглядит никогда.
|
||||
//
|
||||
// 🔴🔴 Приговор «не зовёт никто вовсе» — самый твёрдый, какой выдаёт прибор: оговорки
|
||||
// «посмотри глазами» у него нет. Прочти его следующая смена буквально — и пошла бы сносить
|
||||
// живого сторожа как мёртвый код. Прибор против «написано, но не работает» подталкивал
|
||||
// снести работающее.
|
||||
//
|
||||
// 🔴 РЕЗ УЗКИЙ НАРОЧНО. Наружу из затеи торчат три дороги, зовом имени считается ОДНА:
|
||||
// · `globalSetup` — прогон зовёт у файла `setup`/`teardown` сам ⇒ засчитываем;
|
||||
// · `setupFiles` — файл просто исполняется сверху вниз ⇒ имён не зовёт;
|
||||
// · наряд в `package.json` (`node tools/night/utro.mjs`) ⇒ имён не зовёт, там рот.
|
||||
// Широкий рез тут не ослепил бы прибор, а ЗАТКНУЛ: засчитай мы наряды — все имена `cli.mjs`
|
||||
// и `utro.mjs` вышли бы из долга разом, и живая находка `merkaCheloveka` пропала бы молча.
|
||||
export const IMENA_PO_SOGLASHENIYU = ['setup', 'teardown'];
|
||||
|
||||
/**
|
||||
* Файлы затеи, которые ПОДНИМАЕТ САМ ПРОГОН по своей настройке (`globalSetup`).
|
||||
* Ответ — набор имён файлов без пути. Настройки нет — пустой набор, а не падение:
|
||||
* прибор зовут и на подложных уголках во временном месте машины.
|
||||
* 🔴 Путь доводом, а не приклеенный: иначе правило нечем показать на подложной настройке,
|
||||
* а сторож, которого не видели красным, не сторожит ничего.
|
||||
*/
|
||||
export function faylyPodnimaemyeNastroykoy(putNastroyki) {
|
||||
let text;
|
||||
try { text = readFileSync(putNastroyki, 'utf8'); } catch { return new Set(); }
|
||||
const kusok = /globalSetup\s*:\s*(\[[^\]]*\]|['"`][^'"`]*['"`])/.exec(text);
|
||||
if (!kusok) return new Set();
|
||||
const out = new Set();
|
||||
for (const put of kusok[1].matchAll(/['"`]([^'"`]+)['"`]/g)) {
|
||||
const imya = put[1].split(/[\\/]/).pop();
|
||||
if (imya) out.add(imya);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Спросить у каждого вывозимого: кто зовёт тебя в жизни?
|
||||
//
|
||||
// 🔴 `byloLiChtoSchitat` стоит здесь не для красоты (правило 233): каталог без единого
|
||||
// рабочего файла даёт ноль долгов ровно так же, как безупречное хозяйство, и отличить
|
||||
// их по числу нельзя. Пустое место, посчитанное числом, становится нулём само собой.
|
||||
export function obhodZovushchih(dir) {
|
||||
export function obhodZovushchih(dir, { nastroykaProgona = null } = {}) {
|
||||
const vsyo = vsyoVyvozimoe(dir);
|
||||
// Кого поднимает сам прогон своей настройкой. Умолчание — настоящая настройка затеи;
|
||||
// доводом её подменяют в проверках, чтобы показать правило на подложке.
|
||||
const podnimaemye = faylyPodnimaemyeNastroykoy(
|
||||
nastroykaProgona ?? join(dir, '..', '..', 'app', 'vitest.config.tools.mjs'),
|
||||
);
|
||||
const bezZovushchih = [];
|
||||
const sZovushchimi = [];
|
||||
const tochnyeDolgi = [];
|
||||
@@ -376,9 +421,12 @@ export function obhodZovushchih(dir) {
|
||||
const vvezeno = vvezenoLiImya(syroy, rabochie, v.imya, v.fayl);
|
||||
const celikom = vvoziatLiModulCelikom(syroy, rabochie, v.fayl);
|
||||
|
||||
const zapis = { ...v, vProverkah, doma, vvezeno, celikom };
|
||||
// Поднимает ли этот файл сам прогон, и зовётся ли имя у такого файла по соглашению.
|
||||
const podnimaetProgon = podnimaemye.has(v.fayl) && IMENA_PO_SOGLASHENIYU.includes(v.imya);
|
||||
|
||||
const zapis = { ...v, vProverkah, doma, vvezeno, celikom, podnimaetProgon };
|
||||
bezZovushchih.push(zapis);
|
||||
if (!vvezeno && !celikom && doma === 0) tochnyeDolgi.push(zapis);
|
||||
if (!vvezeno && !celikom && doma === 0 && !podnimaetProgon) tochnyeDolgi.push(zapis);
|
||||
else posmotretGlazami.push(zapis);
|
||||
}
|
||||
|
||||
@@ -420,7 +468,7 @@ export function slovaObhoda(itog) {
|
||||
|
||||
if (itog.tochnyeDolgi.length > 0) {
|
||||
v.push('━━ 🔴 ТОЧНО ДОЛГ: имя никуда не ввезено, дома не зовётся, явных зовов нет ━━');
|
||||
v.push('Ни один из четырёх обманов прибора тут невозможен — обманываться нечем.');
|
||||
v.push('Ни один из пяти обманов прибора тут невозможен — обманываться нечем.');
|
||||
v.push('');
|
||||
for (const b of itog.tochnyeDolgi) {
|
||||
const hvost = b.vProverkah > 0
|
||||
@@ -434,20 +482,27 @@ export function slovaObhoda(itog) {
|
||||
if (itog.posmotretGlazami.length > 0) {
|
||||
v.push(`━━ 🟡 ПОСМОТРЕТЬ ГЛАЗАМИ (${itog.posmotretGlazami.length}) ━━`);
|
||||
v.push('Явных зовов нет, но имя ввезено, либо его модуль ввозят целиком, либо оно');
|
||||
v.push('зовётся дома — значит прибор мог ослепнуть, и ноль тут ничего не доказывает.');
|
||||
v.push('зовётся дома, либо файл поднимает сам прогон — значит прибор мог ослепнуть,');
|
||||
v.push('и ноль тут ничего не доказывает.');
|
||||
const podnyatye = itog.posmotretGlazami.filter((b) => b.podnimaetProgon);
|
||||
if (podnyatye.length > 0) {
|
||||
v.push(`🔴 Из них ${podnyatye.length} поднимает САМ ПРОГОН своей настройкой (globalSetup) —`);
|
||||
v.push(`зова в коде у них нет и быть не может: ${podnyatye.map((b) => b.imya).join(', ')}.`);
|
||||
}
|
||||
v.push('');
|
||||
}
|
||||
|
||||
v.push('━━ 🔴 КАК ЭТО ЧИТАТЬ ━━');
|
||||
v.push('Ноль зовущих сам по себе — НЕ приговор. Прибор отвечает на вопрос «есть ли явный');
|
||||
v.push('зов со скобками в чужом рабочем файле», а не на вопрос «подключено ли». Четыре');
|
||||
v.push('обмана, названные в шапке прибора:');
|
||||
v.push('зов со скобками в чужом рабочем файле», а не на вопрос «подключено ли». Пять');
|
||||
v.push('обманов, названных в шапке прибора:');
|
||||
v.push(' · это постоянная — скобок она не получает никогда, и ноль у неё почти всегда враньё;');
|
||||
v.push(' · её передали значением (`имя = имя,` в доводах) — скобок тоже нет;');
|
||||
v.push(' · её зовут в её же файле — родной файл прибор пропускает нарочно;');
|
||||
v.push(' · модуль ввезли целиком, и зов идёт через точку (`m.имя()`).');
|
||||
v.push('Сито выше отсеивает все четыре: в «точно долг» попадает лишь то, что никуда');
|
||||
v.push('не ввезено и дома не зовётся — обмануться там физически нечем.');
|
||||
v.push(' · модуль ввезли целиком, и зов идёт через точку (`m.имя()`);');
|
||||
v.push(' · файл поднимает САМ ПРОГОН по своей настройке, и зовёт `setup`/`teardown` без кода.');
|
||||
v.push('Сито выше отсеивает все пять: в «точно долг» попадает лишь то, что никуда не ввезено,');
|
||||
v.push('дома не зовётся и прогоном не поднимается — обмануться там физически нечем.');
|
||||
return v.join('\n');
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { tolkoKod, zovyImeni, schyotZovushchih } from './schyot-zovushchih.mjs';
|
||||
import {
|
||||
tolkoKod, zovyImeni, schyotZovushchih,
|
||||
faylyPodnimaemyeNastroykoy, IMENA_PO_SOGLASHENIYU,
|
||||
} from './schyot-zovushchih.mjs';
|
||||
|
||||
// Короткий заход: очистить образец от примечаний и строк, посчитать зовы имени.
|
||||
// Ровно то, что делает `schyotZovushchih` над содержимым каждого файла, только без ФС.
|
||||
@@ -203,3 +206,84 @@ describe('счёт зовущих: семь слепых пятен, назва
|
||||
expect(skolko("const m = await import('./foo.mjs');\nm.zovi();", 'foo')).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// СЛЕПОЕ ПЯТНО, НАЙДЕНО 06.08.2026 СМЕНОЙ 26: ЗОВ ИДЁТ ИЗ НАСТРОЙКИ, А НЕ ИЗ КОДА
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// 🔴 БЕДА, ЗАМЕРЕННАЯ ЖИВЬЁМ. Обход объявил `setup` и `teardown` из
|
||||
// `storozh-boevogo-hozyaystva.mjs` точным долгом со словами «не зовёт никто вовсе, даже
|
||||
// проверки». А это САМЫЙ ЖИВОЙ сторож затеи: он поднимается на КАЖДОМ полном прогоне,
|
||||
// потому что назван в настройке прогона:
|
||||
// app/vitest.config.tools.mjs → globalSetup: ['../tools/night/storozh-boevogo-hozyaystva.mjs']
|
||||
// Прогон зовёт у такого файла `setup` и `teardown` САМ, ПО СОГЛАШЕНИЮ ОБ ИМЕНИ, — зова
|
||||
// в коде нет и быть не может. Прибор смотрит только внутрь затеи и настройки не видит вовсе.
|
||||
//
|
||||
// 🔴🔴 ЧЕМ ЭТО ОПАСНО, И ЭТО ГЛАВНОЕ. Приговор «не зовёт никто вовсе» — самый твёрдый
|
||||
// из всех, какие прибор выдаёт: у него нет оговорки «посмотри глазами». Прочитай его
|
||||
// следующая смена буквально — и пошла бы сносить сторожа боевого хозяйства как мёртвый код.
|
||||
// То есть прибор против класса «написано, но не работает» сам подталкивал снести работающее.
|
||||
//
|
||||
// 🪤 НОМЕРА ЭТОМУ ПЯТНУ НЕ ДАЮ НАРОЧНО. Нумерация в шапке прибора сама себе противоречит:
|
||||
// одно и то же пятно (ввоз модуля целиком) названо там и «шестым», и «№9». Плодить девятое
|
||||
// поверх этого значило бы закрепить путаницу. Имя вместо номера: «зов из настройки».
|
||||
//
|
||||
// 🔴 РЕЗ УЗКИЙ НАРОЧНО — правило 234 («не режет ли датчик вместе с шелухой зерно»), только
|
||||
// с другой стороны: слишком широкий рез здесь не ослепит прибор, а ЗАТКНЁТ ЕГО. Из затеи
|
||||
// наружу торчат ТРИ дороги, и зовом имени считается лишь одна:
|
||||
// · `globalSetup` — прогон зовёт `setup`/`teardown` по соглашению ⇒ ЗОВ, засчитываем;
|
||||
// · `setupFiles` — файл просто исполняется сверху вниз ⇒ имён не зовёт;
|
||||
// · наряд `package.json` (`node tools/night/utro.mjs`) ⇒ имён не зовёт, там рот.
|
||||
// Засчитай мы вторую и третью — все имена `cli.mjs` и `utro.mjs` разом вышли бы из долга,
|
||||
// и `merkaCheloveka` (живая находка смены 25) исчезла бы из приговора молча.
|
||||
describe('счёт зовущих: зов из НАСТРОЙКИ прогона, а не из кода (найдено 06.08.2026)', () => {
|
||||
function podlozhnayaNastroyka(telo) {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'nastroyka-progona-'));
|
||||
const put = join(dir, 'vitest.config.tools.mjs');
|
||||
writeFileSync(put, telo, 'utf8');
|
||||
return { dir, put };
|
||||
}
|
||||
|
||||
it('13. файл из globalSetup назван — прогон поднимает его сам', () => {
|
||||
const { dir, put } = podlozhnayaNastroyka([
|
||||
'export default {',
|
||||
' test: {',
|
||||
" globalSetup: ['../tools/night/storozh-boevogo-hozyaystva.mjs'],",
|
||||
' },',
|
||||
'};',
|
||||
].join('\n'));
|
||||
try {
|
||||
const podnimaet = faylyPodnimaemyeNastroykoy(put);
|
||||
expect(podnimaet.has('storozh-boevogo-hozyaystva.mjs')).toBe(true);
|
||||
} finally { rmSync(dir, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
it('14. ВСТРЕЧНЫЙ СТОРОЖ: setupFiles зовом имён НЕ считается — рез узкий', () => {
|
||||
// 🔴 Тут и живёт цена ошибки: `setupFiles` исполняет файл целиком, но ни `setup`,
|
||||
// ни любого другого имени по соглашению не зовёт. Засчитай мы его — прибор начал бы
|
||||
// выгораживать мёртвое, то есть врать в ту самую сторону, от которой написан.
|
||||
const { dir, put } = podlozhnayaNastroyka([
|
||||
'export default {',
|
||||
' test: {',
|
||||
" setupFiles: ['../tools/night/nastroyka-storozha-zhivogo-dereva.mjs'],",
|
||||
' },',
|
||||
'};',
|
||||
].join('\n'));
|
||||
try {
|
||||
const podnimaet = faylyPodnimaemyeNastroykoy(put);
|
||||
expect(podnimaet.has('nastroyka-storozha-zhivogo-dereva.mjs')).toBe(false);
|
||||
expect(podnimaet.size).toBe(0);
|
||||
} finally { rmSync(dir, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
it('15. настройки нет вовсе — пусто, а не падение', () => {
|
||||
// Прибор обязан работать и там, где настройки прогона нет: его зовут на подложных
|
||||
// уголках во временном месте машины, и никакой настройки рядом с ними не лежит.
|
||||
expect(faylyPodnimaemyeNastroykoy(join(tmpdir(), 'takoy-nastroyki-net-12345.mjs')).size).toBe(0);
|
||||
});
|
||||
|
||||
it('16. имена по соглашению названы поимённо и список ЗАКРЫТ', () => {
|
||||
// 🪤 Список закрыт нарочно: прогон зовёт у globalSetup-файла ровно эти два имени.
|
||||
// Допиши сюда третье «на всякий случай» — и оно начнёт выгораживать мёртвое.
|
||||
expect(IMENA_PO_SOGLASHENIYU).toEqual(['setup', 'teardown']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user