fix(ночь): «свободных пунктов нет» больше не значит «задача сделана»; сторож был зелен случайно
Находка М-406, правила 244-246. Смена 27 передала список «пять доступных настоящих долгов». Правило 243 велит проверять такой список замером, каждую строку отдельно. Проверил — ДОСТУПНЫМ ОКАЗАЛСЯ ОДИН. Третья смена подряд с той же долей: 14 ложных из 33 долгов, 3 из 4 разъездов, 4 из 5. - `zadachaKonchena` — ✅ настоящий, закрыт этим коммитом; - `reshyonnyeSNevypolnimymi` — 🔒 заперт словом владельца: единственный источник невыполнимых пунктов — ответ владельца «нет» в `questions.mjs`, а он мёртв целиком; - `mailFailedSince` — 🔒 заперт им же: замысел строки 750/1073 называет его дословно («затея видит, что сводка не ушла») частью правила ворот «отклик владельца»; - `readResult` — ❌ не долг, а МЁРТВЫЙ ОСТАТОК. Дороги «запусти, дождись конца, возьми итог» в затее нет: `startWorker` отдаёт живого ребёнка. Четверо читающих потоки делают ДРУГОЕ (надзиратель — живьём по строке; `budget` — ищет замеры предела; судья — берёт последнее событие `result` и текст приговора; `dengi-bez-zapisi` — первую цену), а расход затея нарочно не считает, а спрашивает. Не снесён намеренно: это решение владельца. ЧТО ПОЧИНЕНО. В блоке 5а надзирателя стояло упрощение `otlozhennyePunkty.size === 0` — «отложенных нет, значит всё засчитано». Сюда приходят по ДВУМ причинам, и вторая — не «пунктов не осталось», а «пункт ЕСТЬ, но его файлы держит живой сосед и занять их из-под руки не вышло». Отложенных при этом нет — и на НЕДОДЕЛАННОЙ задаче поднимался флаг приёмщику, а владельцу уходило «Пункты задачи кончились и все засчитаны». Доказано вырезанием: сторож написан первым и покраснел (`items_all_verified` = true на недоделанной задаче), после починки зелёный, при возврате упрощения — снова красный, и ровно один, посчитанный ДО прогона. Прибор №6 подтвердил независимо: имя ушло из приговора. 🔴 ГЛАВНОЕ, ЧТО КУПИЛА СМЕНА, И КУПЛЕНО ЧУЖИМ КРАСНЫМ. Полный прогон после починки дал красное у чужого сторожа `primechanie-ne-rashoditsya-s-pravdoy.test.mjs`. Правило 242 велит сперва спросить, что он бережёт. Он бережёт согласие примечания с правдой и по замыслу ПРАВ. Шапку я поправил — он всё равно краснел. Причина: сторож брал `items.mjs` СЫРЫМ файлом и искал фразу-маркер «НЕТ НИ ОДНОГО», а она стоит там в ЖИВОМ КОДЕ — в тексте ошибки «в плане задачи нет ни одного пункта». Пока шапка врала тем же словом, обе половины совпадали и сторож молчал: он был зелёным ПО ДВУМ ПРИЧИНАМ РАЗОМ, и одна из них случайная. ⇒ Заведён третий рез `tolkoPrimechaniya`, обратный `bezPrimechaniy`, в тот же дом и к той же развилке «какой брать». Обратность стережётся отдельной проверкой (что вырезал один — то оставил второй). Все три места сторожа переведены на него, хотя слепота задевала одно: у двух других фразы-маркера в коде сегодня нет — но это удача, а не защита. 244. У СТОРОЖА, ЧИТАЮЩЕГО ФАЙЛ СЫРЫМ, СПРОСИ: НЕ ОТВЕЧАЕТ ЛИ ЗА ПРИМЕЧАНИЕ ТО, ЧТО ПРИМЕЧАНИЕМ НЕ ЯВЛЯЕТСЯ. Обратная сторона 234: тот про «рез срезал зерно», этот про «отсутствие реза подсунуло шелуху». 245. У КРАСНОГО ЧУЖОГО СТОРОЖА ТРИ ИСХОДА, А НЕ ДВА. Третий: прав он по замыслу, но слеп по способу — тогда чинят САМОГО СТОРОЖА. Спроси, что он измерил, и то ли это, что он берётся охранять. 246. `grep -i` НА РУССКИХ БУКВАХ РЕГИСТР НЕ ПРИВОДИТ: «НЕТ НИ ОДНОГО» → 0, «нет ни одного» → 1, node тем же выражением → нашёл. Мерь тем же телом, каким меряет проверяемый. ПОПУТНО три примечания приведены к правде (правило 235): у `zadachaKonchena` живой зовущий теперь ЕСТЬ И ОН ОДИН (второго не будет, пока нет куска 7 — писать «зовут двое» значило бы чинить враньё половиной правды); `readResult` назван мёртвым остатком; `reshyonnyeSNevypolnimymi` помечен запертым, а не «некому сделать». Проверок 2929 в 108 файлах (было 2927), ноль красных, код 0. Прибор №6: вывозится 613, точных долгов 29 — число то же, состав другой: `zadachaKonchena` ушёл, на его место встал новый рез (класс правила 231, прибор говорит это сам). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10828,3 +10828,95 @@ const s = `ушло ${dollary(x)} денег`; → после чистки:
|
||||
🟢 **Два реза сведены в один дом.** `bezPrimechaniy` перенесён к грубому `tolkoKod`
|
||||
с развилкой «какой брать». Врозь они и стоили смене 26 сторожа, который физически не мог
|
||||
покраснеть: искала команду, живущую строкой, а взяла рез, который строки режет.
|
||||
|
||||
---
|
||||
|
||||
## М-406. Смена 28: сторож был зелёным по ДВУМ причинам разом, и одна из них случайная
|
||||
|
||||
**Развилка окупилась ВОСЬМОЙ раз подряд.** Ночи не было; все шесть приборов сошлись знак
|
||||
в знак с числами смены 27.
|
||||
|
||||
### Правило 243 сработало на своём же списке — и опять сняло больше половины
|
||||
|
||||
Смена 27 передала список «пять доступных настоящих долгов». Проверил каждую строку
|
||||
отдельно, как велит свежее правило. **Доступным оказался ОДИН.**
|
||||
|
||||
| долг | приговор замера смены 28 |
|
||||
|---|---|
|
||||
| `readResult` (`spawn.mjs`) | ❌ **не долг, а мёртвый остаток**. Дороги, которой он писан — «запусти, дождись конца, возьми итог», — в затее нет: `startWorker` отдаёт ЖИВОГО ребёнка. Все четверо читающих потоки делают ДРУГОЕ (надзиратель — живьём по строке; `budget` — ищет замеры предела; судья — берёт последнее событие `result` и текст приговора; `dengi-bez-zapisi` — первую цену). Расход, ради которого он писан, затея **нарочно не ведёт**: не считает, а спрашивает у прибора системы |
|
||||
| `mailFailedSince` (`mailer.mjs`) | 🔒 **заперт словом владельца.** Замысел, строки 750 и 1073: «затея видит, что сводка не ушла (отправлялка записала свою неудачу) — тут признака жизни мало, нужно ЯВНОЕ слово». Это часть правила ворот «отклик владельца» |
|
||||
| **`zadachaKonchena` (`items.mjs`)** | ✅ **настоящий, доступный, закрыт этой сменой** |
|
||||
| `reshyonnyeSNevypolnimymi` (`items.mjs`) | 🔒 **заперт тем же словом.** Единственное место, откуда пункт становится невыполнимым, — ответ владельца «нет» в `questions.mjs`, а он мёртв целиком. Заводить ленджер невыполнимых — значит строить провод к источнику, которого нет |
|
||||
| `kudaOtnesyon` | ❌ ложная тревога, закрыта сменой 26 |
|
||||
|
||||
🔑 **Третья смена подряд с той же долей:** 14 ложных из 33 долгов, 3 из 4 разъездов,
|
||||
теперь 4 из 5. Списки в этом хозяйстве стабильно наполовину чужие — и стабильно
|
||||
разбираются замером за час.
|
||||
|
||||
### 🟢 Что закрыто: «свободных пунктов нет» ≠ «задача сделана»
|
||||
|
||||
В блоке 5а надзирателя стояло упрощение `otlozhennyePunkty.size === 0` — «отложенных нет,
|
||||
значит всё засчитано». Оно врало в **третьем случае, безнадзорном**.
|
||||
|
||||
Сюда приходят по ДВУМ разным причинам:
|
||||
|
||||
1. пунктов вправду не осталось — всё закрыто или отложено;
|
||||
2. пункт **есть**, но его файлы держит живой сосед, и занять их из-под руки не вышло.
|
||||
|
||||
Во втором случае отложенных нет — и на **недоделанной задаче** поднимался флаг приёмщику,
|
||||
а владельцу уходило «Пункты задачи кончились и все засчитаны». Ровно та беда, от которой
|
||||
предостерегает примечание строкой выше того же файла.
|
||||
|
||||
**Доказано вырезанием:** сторож написан первым, покраснел (`items_all_verified` = `true`
|
||||
на недоделанной задаче), после починки зелёный, при возврате упрощения — снова красный,
|
||||
и ровно один, посчитанный до прогона. **Прибор №6 подтвердил независимо:** имя ушло
|
||||
из приговора.
|
||||
|
||||
### 🔴🔴 ГЛАВНОЕ, ЧТО КУПИЛА СМЕНА: правило 242 сработало, и ответ оказался ТРЕТЬИМ
|
||||
|
||||
После починки полный прогон дал красное **у чужого сторожа** — `primechanie-ne-rashoditsya-s-pravdoy.test.mjs`,
|
||||
проверка 4. Вчерашнее правило 242 велит не откатываться сразу, а спросить: что он бережёт?
|
||||
|
||||
Он бережёт согласие примечания с правдой, и по замыслу он **прав**: правило подключили —
|
||||
шапка обязана это признать. Шапку я поправил. Он всё равно краснел.
|
||||
|
||||
**Причина:** сторож брал `items.mjs` **сырым файлом** и искал в нём фразу-маркер
|
||||
«НЕТ НИ ОДНОГО». А такая фраза стоит там **в живом коде** — в тексте ошибки
|
||||
`'в плане задачи нет ни одного пункта — считать нечего'`.
|
||||
|
||||
⇒ Пока шапка врала тем же словом, **обе половины совпадали, и сторож молчал**. То есть он
|
||||
был зелёным **по двум причинам разом, и одна из них случайная**. Напиши смена 26 шапку
|
||||
другими словами — он краснел бы на пустом месте; подключи кто-то правило и поправь шапку
|
||||
честно — он краснеет, не умея сказать почему. Ровно это и случилось со мной.
|
||||
|
||||
**244. 🔑 У СТОРОЖА, ЧИТАЮЩЕГО ФАЙЛ СЫРЫМ, СПРОСИ: НЕ ОТВЕЧАЕТ ЛИ ЗА ПРИМЕЧАНИЕ ТО,
|
||||
ЧТО ПРИМЕЧАНИЕМ НЕ ЯВЛЯЕТСЯ.** Это обратная сторона правила 234. То говорит: рез может
|
||||
срезать вместе с шелухой зерно. Это говорит: **отсутствие реза может подсунуть шелуху
|
||||
вместо зерна.** Ищешь слова файла О СЕБЕ — режь всё, кроме примечаний.
|
||||
|
||||
**245. 🔑 У КРАСНОГО ЧУЖОГО СТОРОЖА ТРИ ИСХОДА, А НЕ ДВА.** Смена 27 знала два: «прав он —
|
||||
откатись» и «прав ты — чини своё». Есть третий, и он встретился первым же: **прав он
|
||||
по замыслу, но слеп по способу — тогда чинят САМОГО СТОРОЖА.** Отличить просто: спроси,
|
||||
что именно он измерил, и совпадает ли измеренное с тем, что он берётся охранять. Здесь
|
||||
он брался охранять слова файла о себе, а мерил весь файл целиком.
|
||||
|
||||
**246. 🪤 `grep -i` НА РУССКИХ БУКВАХ РЕГИСТР НЕ ПРИВОДИТ.** Замерено дважды на одном
|
||||
файле: `grep -c -i "НЕТ НИ ОДНОГО"` → **0**, `grep -c -i "нет ни одного"` → **1**, `node`
|
||||
тем же выражением → нашёл. Мой первый замер сказал «фразы в файле нет» — и я чуть не пошёл
|
||||
искать причину не там. 🔑 **Мерь тем же телом, каким меряет тот, кого проверяешь.**
|
||||
|
||||
### Что сделано
|
||||
|
||||
🟢 **Долг куска 7 закрыт** — надзиратель зовёт правило вместо упрощения.
|
||||
🟢 **Слепота сторожа правила 235 закрыта** — заведён третий рез `tolkoPrimechaniya`,
|
||||
обратный `bezPrimechaniy`, и обратность стережётся отдельной проверкой. Все три места
|
||||
сторожа переведены на него, хотя слепота задевала одно: у двух других фразы-маркера
|
||||
в коде сегодня нет — но это **удача, а не защита**.
|
||||
🟢 **Три примечания приведены к правде** (правило 235): `items.mjs` — «живой зовущий есть,
|
||||
и он ОДИН» (второго нет и не будет, пока нет куска 7); `spawn.mjs` — `readResult` назван
|
||||
мёртвым остатком с поимённым разбором всех четверых, кто делает похожее; `items.mjs` —
|
||||
`reshyonnyeSNevypolnimymi` помечен запертым, а не «некому сделать».
|
||||
|
||||
🔴 **`readResult` НЕ снесён намеренно.** Снос необратимее записи, слова владельца на него
|
||||
нет, а вместе с правилом ушли бы три его проверки. Это отдельное решение владельца,
|
||||
а не побочный ход чужой работы.
|
||||
|
||||
@@ -140,6 +140,24 @@ app/vitest.config.tools.mjs:56 → globalSetup: ['../tools/night/storozh-boevo
|
||||
|
||||
---
|
||||
|
||||
## 🔴🔴 ПОПРАВКА СМЕНЫ 28 (07.08.2026): «доступных пять» — на деле ОДИН
|
||||
|
||||
Смена 27 передала следующей список «осталось доступных пять». Правило 243 велит проверять
|
||||
такой список замером, каждую строку отдельно. Проверил. **Доступным оказался один.**
|
||||
|
||||
| строка списка | приговор замера |
|
||||
|---|---|
|
||||
| **`zadachaKonchena`** | ✅ **настоящий и доступный — ЗАКРЫТ сменой 28.** Упрощение `otlozhennyePunkty.size === 0` врало в третьем случае: пункт есть, но его файлы держит живой сосед. Отложенных нет — и на НЕДОДЕЛАННОЙ задаче поднимался флаг приёмщику. Доказано вырезанием, прибор №6 подтвердил независимо |
|
||||
| `reshyonnyeSNevypolnimymi` | 🔒 **заперт словом владельца, а не «некому сделать»**. Единственный источник невыполнимых пунктов — ответ владельца «нет» в `questions.mjs`, а он мёртв целиком. Ленджер невыполнимых строить некуда: источника нет |
|
||||
| `mailFailedSince` | 🔒 **заперт словом владельца.** Замысел, строки 750 и 1073: «затея видит, что сводка не ушла — тут признака жизни мало, нужно ЯВНОЕ слово». Это часть правила ворот «отклик владельца», и подключение меняет ночь владельца |
|
||||
| `readResult` | ❌ **не долг, а мёртвый остаток.** Дороги «запусти, дождись конца, возьми итог» в затее нет: `startWorker` отдаёт живого ребёнка. Четверо читающих потоки делают ДРУГОЕ, разбор поимённо — в примечании над самим правилом. Не снесён намеренно: это решение владельца |
|
||||
| `kudaOtnesyon` | ❌ ложная тревога, уже закрытая выше (раздел Г) |
|
||||
|
||||
🔑 **Третья смена подряд, где список наполовину чужой:** 14 ложных из 33 долгов, 3 из 4
|
||||
разъездов, 4 из 5 «доступных». Правило проверяют мыслью, список — замером.
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Чему научил разбор
|
||||
|
||||
**237. У ПРИБОРА, СУДЯЩЕГО «КТО ЭТО ЗОВЁТ», СПРОСИ СНАЧАЛА — А ВСЕ ЛИ ДОРОГИ ЗОВА ОН ЗНАЕТ.**
|
||||
|
||||
+20
-14
@@ -363,27 +363,33 @@ export function deferredTaskGate({
|
||||
// ленджера невыполнимых пунктов затея на 02.08 не заводит нигде. Значит правило готово,
|
||||
// а живой провод к нему — нет: кто будет его делать, обязан сперва завести такой ленджер,
|
||||
// иначе звать это правило будет нечем.
|
||||
//
|
||||
// 🔒 ЗАМЕРЕНО 07.08.2026, СМЕНА 28 — И ЭТО НЕ «НЕКОМУ СДЕЛАТЬ», А «НЕЛЬЗЯ НАЧАТЬ».
|
||||
// Единственное место затеи, откуда пункт вообще становится невыполнимым, — ответ владельца
|
||||
// «нет» в `questions.mjs` (`item_marked_impossible`). Механизм вопросов владельцу мёртв
|
||||
// С ОБЕИХ СТОРОН и ждёт его слова: подключение меняет ЕГО ночь. Значит и это правило
|
||||
// заперто тем же решением — заводить ленджер невыполнимых, пока их неоткуда взять,
|
||||
// значит строить провод к источнику, которого нет.
|
||||
export function reshyonnyeSNevypolnimymi(verified = [], impossible = []) {
|
||||
return [...new Set([...verified, ...impossible].map(String))];
|
||||
}
|
||||
|
||||
// ── кусок 7, задача 6 ────────────────────────────────────────────────────────────────
|
||||
// 🔴🔴 ЗОВУЩИХ У ЭТОГО ПРАВИЛА НЕТ НИ ОДНОГО. ЗАМЕРЕНО 06.08.2026, СМЕНА 26.
|
||||
// 🟢 ЖИВОЙ ЗОВУЩИЙ ЕСТЬ, И ОН ОДИН. ЗАМЕРЕНО 07.08.2026, СМЕНА 28.
|
||||
//
|
||||
// 🪤 Ниже стояло — и стоит до сих пор, дочитайте абзац, — «Зовут его двое». Это неправда
|
||||
// в НАСТОЯЩЕМ времени: живых зовущих ноль, зовут только проверки (11). Оставляю прежний
|
||||
// текст как есть нарочно, потому что он верно описывает ЗАМЫСЕЛ, — но читать его надо
|
||||
// как «зваться оно должно двумя», а не «зовётся».
|
||||
//
|
||||
// 🟢 А вот в `supervisor.mjs` про то же самое сказано ЧЕСТНО: «ДОЛГ КУСКА 7 (позиция 7):
|
||||
// ИМЕННО ЭТА СТРОКА заменяется на зов». Будущее время вместо настоящего — и примечание
|
||||
// из вранья становится наказом. 🔑 Разница между тем и другим тут ровно в одном глаголе.
|
||||
// Сегодня вместо этого правила там стоит упрощение `otlozhennyePunkty.size === 0`.
|
||||
//
|
||||
// 🔴 ЕДИНСТВЕННОЕ место затеи, где записано, что значит «задача кончена». Зовут его двое:
|
||||
// 🔴 ЧИСЛО ЗДЕСЬ НАЗВАНО ТОЧНО, И ЭТО НЕ ПРИДИРКА. Замысел велит звать правило ДВОИМ:
|
||||
// блок 5а куска 4 (вслед за засчитанной заявкой) и блок 6а куска 7 (каждый круг).
|
||||
// Второй дороги к этому правилу не заводить: разъедутся молча, и приёмщик пойдёт снимать
|
||||
// недоделанную задачу — а владелец утром прочтёт «сделано».
|
||||
// Подключён ПЕРВЫЙ — `supervisor.mjs`, блок 5а. Второго нет: куска 7 в затее нет вовсе.
|
||||
// 🪤 Напиши здесь «зовут его двое» — и это снова стало бы правилом 235: задуманное
|
||||
// в настоящем времени читается как доказательство подключения, будучи его заменой.
|
||||
// Прежняя смена честно пометила ту же строку как враньё; чинить враньё половиной правды
|
||||
// нельзя. Пока куска 7 нет — здесь стоит ОДИН.
|
||||
//
|
||||
// 🔴 ЕДИНСТВЕННОЕ место затеи, где записано, что значит «задача кончена». Второй дороги
|
||||
// к этому правилу не заводить: разъедутся молча, и приёмщик пойдёт снимать недоделанную
|
||||
// задачу — а владелец утром прочтёт «сделано». Ровно эту беду и держал прежний зов:
|
||||
// вместо правила там стояло упрощение `otlozhennyePunkty.size === 0`, и на пункте,
|
||||
// чьи файлы держит живой сосед, оно отвечало «всё засчитано» (замерено вырезанием).
|
||||
//
|
||||
// 🔴 Третий довод `otlozhennye` обязателен по существу (находка Н8). Отложенный пункт лежит
|
||||
// в списке `done` очереди — чтобы она не выдала его снова, — но в `items-verified.jsonl`
|
||||
|
||||
@@ -50,7 +50,15 @@ const chitat = (imya) => readFileSync(join(NOCH, imya), 'utf8');
|
||||
// 🆕 Смена 27 перенесла сам рез в `schyot-zovushchih.mjs` — К ГРУБОМУ `tolkoKod`, чтобы оба
|
||||
// стояли рядом с развилкой «какой брать». Врозь они и стоили смене 26 этой ошибки. Проверка
|
||||
// 1Б ниже стережёт его по-прежнему, откуда бы он ни ввозился.
|
||||
import { bezPrimechaniy } from './schyot-zovushchih.mjs';
|
||||
// 🆕🔴 ВТОРОЙ РЕЗ, И ОН ОБРАТНЫЙ ПЕРВОМУ. ЗАВЕДЁН СМЕНОЙ 28 ПО КРАСНОМУ, КОТОРОЕ ЭТОТ
|
||||
// СТОРОЖ ВЫДАЛ САМ. Правда о подключении ищется в КОДЕ (`bezPrimechaniy` — режет примечания),
|
||||
// а слова файла о себе — в ПРИМЕЧАНИЯХ. Пока вторую половину брали сырым файлом, за шапку
|
||||
// мог ответить живой код: в `items.mjs` фраза-маркер «нет ни одного» стоит в тексте ошибки
|
||||
// (`'в плане задачи нет ни одного пункта — считать нечего'`), и сторож считал её словами
|
||||
// шапки. Он был зелёным по ДВУМ причинам разом, и одна — случайная.
|
||||
// 🔑 Тот же урок, что у первого реза, только с другого конца: у датчика спрашивай не «что
|
||||
// он режет», а «не отвечает ли за примечание то, что примечанием не является».
|
||||
import { bezPrimechaniy, tolkoPrimechaniya } from './schyot-zovushchih.mjs';
|
||||
|
||||
export { bezPrimechaniy };
|
||||
|
||||
@@ -88,11 +96,36 @@ describe('примечание о подключении не расходитс
|
||||
expect(bezPrimechaniy('/* про questions */ const a = 1;')).toContain('const a = 1');
|
||||
});
|
||||
|
||||
it('1В. 🔴 обратный рез: за примечание не отвечает живой код — иначе сторож зелен случайно', () => {
|
||||
// 🔴 СВОЯ НАХОДКА СМЕНЫ 28, КУПЛЕННАЯ КРАСНЫМ ЭТОГО ЖЕ СТОРОЖА. Проверка 4 ниже брала
|
||||
// `items.mjs` СЫРЫМ файлом и искала в нём фразу-маркер «НЕТ НИ ОДНОГО». А такая фраза
|
||||
// стоит там в живом коде — в тексте ошибки «в плане задачи нет ни одного пункта».
|
||||
// Значит шапку файла подтверждала строка кода, которая о подключении не говорит НИЧЕГО.
|
||||
// Пока шапка врала тем же словом, обе половины совпадали и сторож молчал; подключи
|
||||
// правило и поправь шапку честно — и он краснеет, не умея сказать почему.
|
||||
expect(tolkoPrimechaniya("const r = { reason: 'в плане нет ни одного пункта' };")).not.toContain('ни одного');
|
||||
expect(tolkoPrimechaniya('// 🔴 ЗОВУЩИХ НЕТ НИ ОДНОГО')).toContain('НЕТ НИ ОДНОГО');
|
||||
expect(tolkoPrimechaniya('/* многострочное\n НЕ ПОДКЛЮЧЁН */ const a = 1;')).toContain('НЕ ПОДКЛЮЧЁН');
|
||||
expect(tolkoPrimechaniya('/* многострочное */ const a = 1;')).not.toContain('const a = 1');
|
||||
// 🪤 Два реза обязаны быть ОБРАТНЫ друг другу, а не просто разными: что вырезал один,
|
||||
// то оставил второй. Без этой строки они могли бы разойтись молча, и половина файла
|
||||
// не попала бы ни к тому, ни к другому.
|
||||
const obrazec = "// шапка\nconst a = 'тело';";
|
||||
expect(bezPrimechaniy(obrazec)).toContain('тело');
|
||||
expect(bezPrimechaniy(obrazec)).not.toContain('шапка');
|
||||
expect(tolkoPrimechaniya(obrazec)).toContain('шапка');
|
||||
expect(tolkoPrimechaniya(obrazec)).not.toContain('тело');
|
||||
});
|
||||
|
||||
it('2. 🔴 вопросы владельцу: надзиратель про них молчит — и файл так и говорит', () => {
|
||||
// 🪤 Тоже по очищенному коду: напиши кто-нибудь в примечании надзирателя слово
|
||||
// «questions» — и плоский поиск объявил бы механизм подключённым. См. проверку 4.
|
||||
// 🆕 Вторая половина — обратным резом, как у проверки 4. У `questions.mjs` фразы-маркера
|
||||
// в живом коде сегодня НЕТ (замерено сменой 28), то есть сторож тут зелен по существу,
|
||||
// а не по удаче. Рез ставится всё равно: напиши кто-нибудь завтра текст ошибки со словом
|
||||
// «не подключён» — и эта проверка ослепла бы ровно так же, как ослепла четвёртая.
|
||||
const podklyucheno = bezPrimechaniy(chitat('supervisor.mjs')).includes('questions');
|
||||
expect(primechanieSoydyotsya(podklyucheno, chitat('questions.mjs')),
|
||||
expect(primechanieSoydyotsya(podklyucheno, tolkoPrimechaniya(chitat('questions.mjs'))),
|
||||
podklyucheno
|
||||
? 'механизм вопросов ПОДКЛЮЧИЛИ, а примечание всё ещё говорит «не подключён» — поправьте шапку questions.mjs'
|
||||
: 'примечание в questions.mjs обещает подключение, которого нет',
|
||||
@@ -104,7 +137,8 @@ describe('примечание о подключении не расходитс
|
||||
// а режем только примечания — иначе сторож ослепнет насовсем (разбор у `bezPrimechaniy`).
|
||||
const cli = bezPrimechaniy(chitat('cli.mjs'));
|
||||
const podklyucheno = /['"`]answer['"`]|['"`]ack['"`]/.test(cli);
|
||||
expect(primechanieSoydyotsya(podklyucheno, chitat('mailer.mjs')),
|
||||
// 🆕 Вторая половина — обратным резом (смена 28), по тому же доводу, что у проверок 2 и 4.
|
||||
expect(primechanieSoydyotsya(podklyucheno, tolkoPrimechaniya(chitat('mailer.mjs'))),
|
||||
podklyucheno
|
||||
? 'команды ответа владельца ЗАВЕЛИ, а примечание в mailer.mjs всё ещё говорит «писать нечем»'
|
||||
: 'примечание в mailer.mjs обещает команды, которых у cli.mjs нет',
|
||||
@@ -118,8 +152,12 @@ describe('примечание о подключении не расходитс
|
||||
// за работу — ровно та мина, от которой написан прибор зовущих, и я написал про неё
|
||||
// строкой выше за минуту до того, как в неё угодил.
|
||||
// ⇒ Примечания вырезаем ДО поиска, чужим проверенным чистильщиком.
|
||||
// 🆕🔴 И ВТОРАЯ ПОЛОВИНА — ЧЕРЕЗ ОБРАТНЫЙ РЕЗ (смена 28). Сырой файл сюда подавать
|
||||
// нельзя: фраза-маркер «нет ни одного» стоит в `items.mjs` в ТЕКСТЕ ОШИБКИ живого кода,
|
||||
// и пока шапка врала тем же словом, обе половины совпадали случайно. Правду ищем
|
||||
// в коде, слова файла о себе — в примечаниях; смешивать их и значит ослепнуть.
|
||||
const podklyucheno = /zadachaKonchena\s*\(/.test(bezPrimechaniy(chitat('supervisor.mjs')));
|
||||
expect(primechanieSoydyotsya(podklyucheno, chitat('items.mjs')),
|
||||
expect(primechanieSoydyotsya(podklyucheno, tolkoPrimechaniya(chitat('items.mjs'))),
|
||||
podklyucheno
|
||||
? 'правило «задача кончена» ПОДКЛЮЧИЛИ — поправьте шапку items.mjs, там сказано «зовущих нет ни одного»'
|
||||
: 'примечание в items.mjs говорит о зовущих, которых нет',
|
||||
|
||||
@@ -101,7 +101,7 @@ function obrazetsLiTut(vydano) {
|
||||
return PERED_OBRAZTSOM.test(t) || SLOVA_PERED_OBRAZTSOM.test(t);
|
||||
}
|
||||
|
||||
// ━━━ 🔴🔴 ДВА РЕЗА, И ОНИ НЕ ВЗАИМОЗАМЕНИМЫ. РАЗВИЛКА — ЗДЕСЬ ━━━
|
||||
// ━━━ 🔴🔴 ТРИ РЕЗА, И ОНИ НЕ ВЗАИМОЗАМЕНИМЫ. РАЗВИЛКА — ЗДЕСЬ ━━━
|
||||
//
|
||||
// Стоят рядом НАРОЧНО. Смена 26 обожглась ровно оттого, что они лежали в разных файлах:
|
||||
// искала команду, живущую строкой (`case 'answer':`), взяла грубый `tolkoKod` — и своими
|
||||
@@ -111,8 +111,13 @@ function obrazetsLiTut(vydano) {
|
||||
// |---|---|
|
||||
// | ИМЯ СО СКОБКОЙ (зов, `имя(`) | `tolkoKod` — грубый: срежет примечания, строки и образцы |
|
||||
// | СТРОКУ (`'events.jsonl'`, команду, ключ) | `bezPrimechaniy` — режет ТОЛЬКО примечания |
|
||||
// | ЧТО ФАЙЛ ГОВОРИТ О СЕБЕ (шапку, наказ) | `tolkoPrimechaniya` — обратный второму |
|
||||
//
|
||||
// 🔑 Спрашивай у реза не «что он режет», а «не режет ли он вместе с шелухой зерно».
|
||||
// 🆕🔑 И второй вопрос, купленный сменой 28: «а не отвечает ли за примечание то, что
|
||||
// примечанием не является». Сторож правила 235 брал файл СЫРЫМ и искал в нём слова шапки —
|
||||
// а слово нашлось в тексте ошибки живого кода. Он был зелёным по двум причинам разом,
|
||||
// и одна из них случайная. Ищешь слова файла о себе — бери третий рез, а не сырой текст.
|
||||
|
||||
/**
|
||||
* Вырезать ТОЛЬКО примечания, оставив строковые значения нетронутыми.
|
||||
@@ -127,6 +132,24 @@ export function bezPrimechaniy(text) {
|
||||
.replace(/\/\/[^\n]*/g, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Оставить ТОЛЬКО примечания, выбросив весь живой код. Обратный `bezPrimechaniy`:
|
||||
* что вырезал тот, то оставляет этот, и наоборот. Обратность стережётся проверкой —
|
||||
* `primechanie-ne-rashoditsya-s-pravdoy.test.mjs`, 1В.
|
||||
*
|
||||
* Берут его там, где надо спросить «а что файл ГОВОРИТ О СЕБЕ»: слова о подключении,
|
||||
* наказы следующей смене, шапка. По сырому файлу такой вопрос задавать нельзя — за шапку
|
||||
* ответит живой код, случайно совпавший словами (замерено сменой 28 на `items.mjs`).
|
||||
*
|
||||
* 🪤 Та же краевая грубость, что у обратного, и с тем же следствием наоборот: `//` внутри
|
||||
* строкового значения (ссылка `https://`) этот рез сочтёт началом примечания и утащит
|
||||
* хвост строки в «слова о себе». Сторожам это безразлично, берущему для нового дела — нет.
|
||||
*/
|
||||
export function tolkoPrimechaniya(text) {
|
||||
const najdeno = String(text ?? '').match(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g);
|
||||
return najdeno ? najdeno.join('\n') : '';
|
||||
}
|
||||
|
||||
export function tolkoKod(text) {
|
||||
let out = '';
|
||||
let i = 0;
|
||||
|
||||
@@ -615,6 +615,30 @@ export function masterskayaOtsechena({ potok, zakazanTools = false, zakazanMcp =
|
||||
};
|
||||
}
|
||||
|
||||
// 🔴🔴 ЖИВЫХ ЗОВУЩИХ НЕТ, И ЭТО НЕ ДОЛГ, А МЁРТВЫЙ ОСТАТОК. ЗАМЕРЕНО 07.08.2026, СМЕНА 28.
|
||||
//
|
||||
// Правило берёт ГОТОВЫЙ вывод целиком и разбирает его ПОСЛЕДНЮЮ строку. Дороги, которой
|
||||
// оно писано — «запусти работника, дождись конца, возьми итог», — в затее больше нет:
|
||||
// `startWorker` отдаёт ЖИВОГО ребёнка, а не текст. Все четверо, кто читает потоки, названы
|
||||
// поимённо, и каждый делает ДРУГУЮ работу — сводить их сюда было бы не починкой, а поломкой:
|
||||
//
|
||||
// · `supervisor.mjs` — разбирает поток ЖИВЬЁМ, по строке, по мере прихода: попутно кладёт
|
||||
// кусок на диск до всякого разбора и считает замеры запаса. Ждать конца ему нельзя;
|
||||
// · `budget.mjs` (`probeUtilization`) — перебирает ВСЕ строки, ища замеры предела,
|
||||
// а не итог. Последняя строка ему не нужна вовсе;
|
||||
// · `sudya-nochi.mjs` (`itogIzPotoka`) — берёт ПОСЛЕДНЕЕ событие вида `result` (а не
|
||||
// последнюю строку: после итога в потоке бывает хвост), понимает `is_error` и считает
|
||||
// пустой приговор за «не судил». Отдаёт ТЕКСТ приговора, а не объект события;
|
||||
// · `dengi-bez-zapisi.mjs` (`razborSleda`) — берёт ПЕРВУЮ цену из следа-улики.
|
||||
//
|
||||
// 🪤 Расход (`total_cost_usd`), ради которого правило писалось, затея НАРОЧНО не ведёт:
|
||||
// остаток запаса она не считает, а СПРАШИВАЕТ у прибора системы (`supervisor.mjs`, разбор
|
||||
// потока). Подключать отсюда второй счёт денег значило бы завести вторую дорогу к тому же
|
||||
// смыслу — ровно то, от чего хозяйство предупреждает в пяти местах.
|
||||
//
|
||||
// 🔴 Не снесено НАМЕРЕННО, и причина названа: снос — необратимее записи, слова владельца
|
||||
// на него нет, а вместе с правилом ушли бы три его проверки. Сносить это — отдельное
|
||||
// решение владельца, не побочный ход чужой работы.
|
||||
export function readResult(stdout) {
|
||||
const text = String(stdout ?? '').trim();
|
||||
if (!text) return null;
|
||||
|
||||
@@ -80,7 +80,7 @@ import { readJsonOrBroken } from './state.mjs';
|
||||
// (кусок 1, задача 11), и ввозить его не надо: второй такой же рядом развёл бы два
|
||||
// правила на одно слово владельца. Здесь ввозятся только руки — кто читает каталог
|
||||
// команд, кто снимает исполненную и кто называет непонятое слово.
|
||||
import { readCommand, consumeCommand, unknownCommands, COMMAND_WORDS, closeItem, afterItemDone, afterTaskDone } from './items.mjs';
|
||||
import { readCommand, consumeCommand, unknownCommands, COMMAND_WORDS, closeItem, afterItemDone, afterTaskDone, zadachaKonchena } from './items.mjs';
|
||||
// --- дополнение к ввозам в шапке файла (кусок 4, задача 5) ---------------------
|
||||
// 🔴 Замок «пункт закрыт» написан в item-audit.mjs, а ЗОВЁТ его этот файл, каждый круг,
|
||||
// у каждого работника. До этой задачи он зеленел на своих проверках и в живой ночи
|
||||
@@ -2235,11 +2235,29 @@ export async function supervise({
|
||||
// в ночь»: одна ночь оставила бы два противоречащих знака, и утром владелец
|
||||
// не знал бы, которому верить. Знак должен быть ОДИН.
|
||||
//
|
||||
// 🔴🔴 ДОЛГ КУСКА 7 (позиция 7): ИМЕННО ЭТА СТРОКА заменяется на зов
|
||||
// `zadachaKonchena({ items, verified, otlozhennye }).konchena`, и вместе
|
||||
// с долгом 5 (невыполнимые пункты), у которого своего ленджера в затее
|
||||
// пока нет вовсе. Не переводи её тот, кто правит блок 5а.
|
||||
const vseZaschitany = otlozhennyePunkty.size === 0;
|
||||
// 🟢 ДОЛГ КУСКА 7 (позиция 7) ЗАКРЫТ 07.08.2026, СМЕНА 28. Здесь стояло
|
||||
// упрощение `otlozhennyePunkty.size === 0` — «отложенных нет, значит всё
|
||||
// засчитано». Оно врало в ТРЕТЬЕМ случае, безнадзорном: сюда приходят
|
||||
// не только когда пунктов не осталось, но и когда пункт ЕСТЬ, а его файлы
|
||||
// держит живой сосед и занять их из-под руки не вышло (`svobodnyy.item`
|
||||
// гасится выше словами «их уводят из-под руки»). Отложенных при этом нет —
|
||||
// и на НЕДОДЕЛАННОЙ задаче поднимался флаг приёмщику, а владельцу уходило
|
||||
// «Пункты задачи кончились и все засчитаны». Замерено сторожем в
|
||||
// `supervisor.test.mjs`: без правила `items_all_verified` = true.
|
||||
//
|
||||
// 🔴 Правило спрашивает ДРУГОЕ и потому ловит этот случай: не «нет ли
|
||||
// отложенных», а «ВСЕ ЛИ пункты плана засчитаны». Оно же закрывает пустой
|
||||
// план — прежде «ни одного пункта» читалось как «все засчитаны».
|
||||
//
|
||||
// 🪤 Долг 5 (невыполнимые пункты) сюда НЕ подключён и подключён быть не может:
|
||||
// единственный источник невыполнимых — ответ владельца в `questions.mjs`,
|
||||
// а он мёртв целиком и ждёт слова владельца. Довода `impossible` у правила
|
||||
// нет вовсе — оно считает по засчитанным и отложенным, и этого хватает.
|
||||
const vseZaschitany = zadachaKonchena({
|
||||
items: punktyProgona.map(p => p?.id),
|
||||
verified: closedItemsOfRun(runDirPath),
|
||||
otlozhennye: otlozhennyePunkty,
|
||||
}).konchena;
|
||||
|
||||
if (!vseZaschitany) {
|
||||
const otlozheno = [...otlozhennyePunkty].join(', ');
|
||||
|
||||
@@ -2798,6 +2798,42 @@ describe('очередь пунктов и конец задачи (провер
|
||||
expect(run.items_all_verified).toBe(false);
|
||||
expect(run.reviewer_starts).toBe(false);
|
||||
});
|
||||
|
||||
it('🔴🔴 пункт НЕ засчитан и НЕ отложен, но его файлы держит СОСЕД — приёмщику флага НЕТ', async () => {
|
||||
// 🔴🔴 ТРЕТИЙ СЛУЧАЙ, И ОН БЫЛ БЕЗНАДЗОРНЫМ. Два соседних выхода охранялись: «все
|
||||
// засчитаны» (флаг есть) и «пункт отложен» (флага нет). А «свободных пунктов не
|
||||
// нашлось» бывает по ДВУМ причинам, и вторая — не «пунктов не осталось», а «пункт
|
||||
// есть, но его файлы заняты живым соседом, и занять их из-под руки не вышло»
|
||||
// (`svobodnyy = { item: null, … 'занять его файлы не удалось' }` в блоке 5а).
|
||||
//
|
||||
// Прежнее упрощение `otlozhennyePunkty.size === 0` в этом случае отвечало «все
|
||||
// засчитаны» — потому что отложенных вправду нет. И тогда на НЕДОДЕЛАННОЙ задаче
|
||||
// поднимался флаг приёмщику, а владельцу уходило «Пункты задачи кончились и все
|
||||
// засчитаны». Пункт 2 при этом не тронут никем: сосед его только держит.
|
||||
//
|
||||
// 🔑 Ломается ТИХО и целиком невидимо — ровно тот класс, ради которого правило
|
||||
// `zadachaKonchena` и написано: оно спрашивает не «нет ли отложенных», а «все ли
|
||||
// пункты плана ЗАСЧИТАНЫ», и это разные вопросы.
|
||||
zayavit('1');
|
||||
// Сосед живой: карточка с углом на диске и веткой — иначе `liveForeignCorners`
|
||||
// его не увидит, и замок не удержится (замерено в блоке про живых соседей выше).
|
||||
const sosedRoot = join(dir, 'sosed-2');
|
||||
mkdirSync(sosedRoot, { recursive: true });
|
||||
writeJsonAtomic(join(rdir, 'workers', 'w-2', 'worker.json'), {
|
||||
worker_id: 'w-2', run_id: 'r-1', status: 'working', workspace: sosedRoot, base_branch: 'feat/x',
|
||||
});
|
||||
// 🪤 Заявку кладём тем же способом, каким её кладёт жизнь, а не рукой в файл: ключ
|
||||
// сравнения путей соберётся сам, и проверка не начнёт зависеть от его устройства.
|
||||
expect(zanyatFayly(rdir, 'w-2', ['app/B.php'], {}).ok).toBe(true);
|
||||
planPunktov4([
|
||||
{ id: '1', title: 'Первый', tests: ['a.test.mjs'], files: ['app/A.php'], long: false },
|
||||
{ id: '2', title: 'Второй', tests: ['b.test.mjs'], files: ['app/B.php'], long: false },
|
||||
], ['w-1', 'w-2']);
|
||||
await runSupervisor({ tickMs: 1000, script: [() => {}], head: () => 'sha-1', runTests: ZELENO });
|
||||
const run = readJson(join(rdir, 'run.json'));
|
||||
expect(run.items_all_verified).toBe(false);
|
||||
expect(run.reviewer_starts).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('непризнанный пункт: работник узнаёт, и попыток у него две', () => {
|
||||
|
||||
Reference in New Issue
Block a user