871 lines
148 KiB
Markdown
871 lines
148 KiB
Markdown
# Отчёт бэкенд-пака «денежный стоп перестаёт быть аварией» (05.09)
|
||
|
||
> **Промт:** `docs/archive/prompts/BACKEND_MONEYSTOP_SESSION_PROMPT_2026-09-05.md` (сдан и заархивирован 05.09, `D39.206`). **Зона записи:** `backend/` + своя секция
|
||
> `docs/PROGRESS.md`. **Дерево НЕ закоммичено, лендинг за оркестратором.**
|
||
>
|
||
> ⚠ **В рабочем дереве лежат ЧУЖИЕ правки — вся `platform/`** (параллельный платформенный пак
|
||
> `textmachine-main-63`: бэкапы, дверь входа, рантбук деплоя). Я их не трогала ни байтом; всё, что ниже,
|
||
> — только про `backend/`.
|
||
>
|
||
> ⛔ **Платного прогона НЕТ. Ни один вызов не ушёл ни к одному провайдеру.** Все три оси ревью
|
||
> предъявлены за $0 — фейк-провайдер на `httptest`, голдены, каталог мутаций.
|
||
|
||
---
|
||
|
||
## 0. Что стало с деревом — одним абзацем
|
||
|
||
Отказ резервации больше не убивает книгу. Он защёлкивает волну (новые элементы не начинаются),
|
||
допущенные вызовы дорабатываются и сеттлятся, а сам отказ не окончателен, пока в полёте есть чужая
|
||
резервация: settle возвращает `оценка − факт`, запас растёт, и тот же резерв переспрашивается. Прогон
|
||
уходит штатно, с частичным результатом, с `exit 4`/`paused` и — впервые — **называет, сколько не
|
||
хватило**. Отдельно и первым пунктом: пользовательская правка банка больше не может писать свои строки
|
||
внутрь системного сообщения платного вызова. И движок теперь публикует **цену книги ДО покупки** —
|
||
по юниту, по главе и по книге, плюс самый крупный неделимый резерв, из-за которого стена и стояла.
|
||
|
||
---
|
||
|
||
## 1. Пункт 1 (строка 271) — правки банка перестали быть инструкциями. ЗАКРЫТ
|
||
|
||
**Что было.** Инъекция банка — отдельное сообщение роли `system`; тело собиралось конкатенацией
|
||
`src + " → " + dst`; `dst` приходит с пользовательской двери `POST /books/{id}/bank/corrections` и
|
||
получал ровно один `TrimSpace`. Ни предела длины, ни запрета управляющих. ⇒ `dst` с переводом строки
|
||
дописывает свои строки внутрь системного блока **каждого** платного вызова книги.
|
||
|
||
**Что построено — одна граница, ТРИ точки исполнения** (`backend/internal/membank/wirefence.go`):
|
||
|
||
| точка | что делает | почему там |
|
||
|---|---|---|
|
||
| дверь (`decisions.go/resolveDecision`) | ОТКАЗЫВАЕТ с индексом решения | человек узнаёт, что его термин не поедет, вместо того чтобы получить молча искажённый |
|
||
| загрузчик сида (`memseed.go`) | ОТКАЗЫВАЕТ загрузку | файл оператора — его же и чинить; дроп на пути к проводу оставил бы термин, который он считает действующим, отсутствующим в каждом запросе |
|
||
| рендереры (`RenderGlossaryBlock`, `editorLines`) | ПРОПУСКАЕТ строку | единственная точка, которую нельзя обойти писателем, добавленным позже |
|
||
|
||
**Что именно запрещено и почему НЕ больше.** Управляющие (Cc: сюда попадают LF, CR, TAB, VT, FF, NEL),
|
||
разделители строки/абзаца U+2028/U+2029 и **bidi-переопределения/изоляты** U+202A–U+202E, U+2066–U+2069
|
||
(класс Trojan Source, CVE-2021-42574 — они не рвут строку, а переставляют её, и человек, подписывающий
|
||
банк глазами, читает не то, что получает модель). ⚠ **Bidi-МАРКИ U+200E/U+200F и ZWJ/ZWNJ РАЗРЕШЕНЫ**:
|
||
это обычное содержимое иврита, арабского, деванагари и фарси, а движок обязан работать для пары,
|
||
которой в репозитории нет (§0.1). Предел длины — **200 рун** (не байт: байтовый предел был бы разным
|
||
пределом для разных письменностей). Довод числа: собственная структурная строка движка — заголовок главы
|
||
с подзаголовком — ограничена 60 рунами (`chunk.chapterHeaderMaxRunes`), а при штатном
|
||
`glossary_token_budget: 800` одна 200-рунная плотная строка уже забирает четверть всего бюджета
|
||
инъекции на чанк.
|
||
|
||
**⚠ ТРЕТЬЕ ЛЕЧЕНИЕ ПРОМТА — «обрамление как ДАННЫХ» — В ТЕКСТОВОЙ ФОРМЕ НЕВОЗМОЖНО, и это находка, а не
|
||
пропуск.** Заголовки блоков — целевые wire-тексты и живут только в
|
||
`backend/internal/lang/data/injection.txt`. Этот файл **побайтно** входит в `lang.EmbeddedVersion()`
|
||
(`embedded.go:22,79`), который едет в `cutInputs.Embedded` → `cutTag()` → **внутрь каждого `unit.id`**
|
||
(`manifest.go:168-170,201`) и в `manifestKey` и в снапшот волны. ⇒ добавление ОДНОЙ строки пере-чеканило
|
||
бы `unit.id` всех существующих книг и потребовало `--resnapshot` сквозь консент-гейт пере-оплаты — то
|
||
есть ровно то, что тот же промт §4(6) запрещает. Обрамление сделано **структурно**: значение, которое не
|
||
может покинуть свою строку и не может превысить длину термина, не в состоянии выдать себя ни за что,
|
||
кроме правой части своего же отображения.
|
||
|
||
**Снапшот: чистые книги не двигаются, отравленная двигается.** Фолд `wire-fence-v1` — СКОУПНЫЙ, по
|
||
образцу `neuter-directive-v1`: пишется только когда в скоупе ЕСТЬ негодная строка. Предъявлено
|
||
**значением, а не доводом**: хеш чистого банка снят сборкой коммита `32be78a` (до фикса) и запинен
|
||
константой в тесте.
|
||
|
||
**Артефакт приёмки — КРАСНЫЙ до, ЗЕЛЁНЫЙ после.** Предъявлено исполнением на дереве HEAD:
|
||
|
||
```
|
||
$ cd <копия HEAD 32be78a>/backend && go test ./internal/membank/ -run 'TestAUserTerm|TestTheDoor|TestTheSeedLoader' -v
|
||
--- FAIL: TestAUserTermCannotWriteItsOwnLinesIntoTheSystemBlock
|
||
draft glossary block: a user-written term put an INSTRUCTION into the system message
|
||
draft glossary block: the value ended its own line and wrote a new one
|
||
editor constraint block: a user-written term put an INSTRUCTION into the system message
|
||
editor constraint block: the value ended its own line and wrote a new one
|
||
--- FAIL: TestTheDoorRefusesATermThatCannotBeRenderedAsData
|
||
want exactly the SECOND decision refused, got []
|
||
--- FAIL: TestTheSeedLoaderRefusesATermThatCannotBeRenderedAsData
|
||
a seed term whose dst ends its own line must not load
|
||
```
|
||
|
||
На рабочем дереве все шесть тестов файла зелёные.
|
||
|
||
---
|
||
|
||
## 2. Пункт 2 (строка 277) — отказ перестал гасить допущенное. ЗАКРЫТ
|
||
|
||
**Форма защёлки** (`waverun.go`): канал `latched`, не флаг. Причина механическая: кормилец БЛОКИРУЕТСЯ
|
||
на `idxCh <- i`, когда все воркеры заняты, — а это ровно то состояние, в котором достигается денежный
|
||
стоп; флаг был бы прочитан после отправки, которую он должен предотвратить.
|
||
⚠ **И защёлка спрашивается НЕБЛОКИРУЮЩЕ ПЕРЕД трёхветочным `select`.** Go выбирает среди готовых ветвей
|
||
`select` **равномерно случайно**, так что при закрытом `latched` и свободном воркере трёхветочный
|
||
`select` раздавал бы индекс примерно в половине случаев — до одного лишнего элемента на простаивающего
|
||
воркера. Тот же изъян давно есть у `ctx.Done()` в этой же строке; на денежном пути это разница между
|
||
«ничего нового не начато» и «начато ещё несколько».
|
||
|
||
**Где ждать settle — в точке резерва** (`stagerun.go`), потому что там известна оценка и туда же
|
||
возвращается повтор. Общая на прогон структура — `reserveGate` (`reservegate.go`).
|
||
|
||
**⚠ `Reserve` исполняется ПОД мьютексом шлюза, и это требование корректности, а не аккуратность.** С
|
||
допуском и счётчиком в двух критических секциях воркер X мог закоммитить резервацию и быть вытеснен до
|
||
записи себя «в полёте»; воркер Y, которому отказала резервация X, прочитал бы ноль в полёте и объявил
|
||
книгу законченной, пока вызов X идёт и вот-вот вернёт деньги. Стор и так сериализует резервации
|
||
единственным пишущим пулом — под ещё одним локом не теряется ничего.
|
||
|
||
**`done()` — `defer` СРАЗУ после выхода из цикла допуска, и нигде больше.** Резервация освобождается и
|
||
сеттлится на дюжине путей выхода; парная привязка счётчика к ним оставила бы `inFlight > 0` до конца
|
||
прогона при одном пропущенном, и каждый последующий отказ ждал бы settle, который не придёт. Счёт до
|
||
возврата функции строго консервативен: ждущий ждёт мгновение дольше, чем должен, и никогда не
|
||
останавливается там, где должен был ждать.
|
||
|
||
**Только ОБЯЗАТЕЛЬНЫЕ вызовы ждут** — новый параметр `mandatory` у `runAttempt`, переданный явно (а не
|
||
выведенный), потому что вывести его нельзя: `escalation=true` стоит только у хопа, а репайр
|
||
(`repair.go:421`) и терминолог (`terminologist.go:687`) передают `false`, хотя тоже деградируют. Ждущий
|
||
опциональный вызов превратил бы «не начинаем новое» в «опциональное съедает то, что нужно
|
||
обязательному», а хоп держит `escMu` всю свою жизнь и заблокировал бы эскалации всех соседей.
|
||
|
||
**Ожидание бессмысленно, когда его нечем купить** — `settleCannotHelp` (⚠ это ОПТИМИЗАЦИЯ, не несущая гарантия: состояние, в котором она решает, почти недостижимо в однородной волне — §9 п.6): settle возвращает не больше
|
||
`оценка − факт` и никогда не уменьшает УЖЕ СПИСАННОЕ, так что при `committed + estimate > ceiling` ждать
|
||
нечего. Без этой проверки воркер держал бы волну открытой весь самый долгий провайдерский таймаут, чтобы
|
||
прийти к отказу, который у него уже был. ⚠ Спрашивается только у КНИЖНОГО потолка: дневной суммирует
|
||
все книги стора, и движковые цифры — не его; там ждём консервативно (ожидание всё равно ограничено —
|
||
оно кончается, как только полёт пуст).
|
||
|
||
**Штатное завершение с леджером.** Денежный стоп возвращает `(res, halt)` — `TranslateBook` уже отдавал
|
||
обе половины, а `tmctl` теперь ПЕЧАТАЕТ частичный леджер (раньше при ошибке результат не рендерился
|
||
вовсе, и оператор узнавал только что прогон встал).
|
||
|
||
### 2-бис. Пять рисков зоны — каждый предъявлен
|
||
|
||
| риск | предъявление |
|
||
|---|---|
|
||
| **(а)** защёлка на уровне волны, не у воркера | посадка `MS-latch-is-a-cancel` (замена защёлки на `cancel()`) — цвет в §6; тест `TestUnderACeilingTheWaveFinishesWhatItAdmitted` |
|
||
| **(б)** учёт объёма (`bound()`/`reconcile`) при частичной волне | ⛔ **НАЙДЕНО И ЗАКРЫТО ОТКАЗОМ ОТ ОТЧЁТА:** `res.Volume` при денежном стопе НЕ прикладывается — см. ниже |
|
||
| **(в)** событие `ceiling` и exit 4/`paused` не задержались | `TestTheStoppedRunTellsAConsumerHowMuchWasMissing` читает журнал СО СТОРОНЫ ПОТРЕБИТЕЛЯ (байты NDJSON), `TestACeilingHaltAnnouncesItselfAndNamesWhichCeiling` держит позицию кадра и `outcome: ceiling` |
|
||
| **(г)** дневной потолок делит ветку с книжным | `TestSettleCannotHelpRefusesToWaitForMoneyThatIsAlreadySpent` — day-скоуп не смеет объявить отказ окончательным по цифрам одной книги; `TestTheShortfallIs…` — day-скоуп не публикует недостачу; `TestACeilingHaltAnnounces…/the day ceiling` — поле опущено на проводе |
|
||
| **(д)** эскалационный хоп не опирается на отмену | проверено чтением: `escalation.go:163` ловит `errors.Is(err, errReserveCeiling)`, а `CeilingHalt.Unwrap` его отдаёт — отмена в этой цепи не участвует. Более того, ЧУЖОЙ `cancel()` хопу МЕШАЛ: `rateGuard.acquire(ctx)`/`Complete(ctx)` падали ctx-ошибкой, которая не матчит сентинел. Снятие отмены хоп только чистит. Пин: `TestRunnerEscalationCeilingDegrades` (зелёный), `TestWaveEscalationBudgetSerializedUnderParallelism` (зелёный) |
|
||
|
||
**⛔ Риск (б) оказался настоящим, и лечение — молчание, а не исправление счётчиков.** Семь счётчиков
|
||
`VolumeStop` — это ПЛАН: `planVolume` классифицирует юниты до волн, `reconcile` правит только те исходы,
|
||
которые есть. Денежный стоп обрывает работу, которую план считал сделанной, ⇒ допущенные и не доделанные
|
||
юниты остаются посчитанными как `Delivered`/`Reworked`/`Carried`/`Free`, а `LeftFresh`/`LeftRework`
|
||
занижены ровно на них; при `Left() == 0` прогон, вставший посреди книги, объявил бы «дошёл до конца
|
||
книги и ничего не придержал» и не пригласил бы докупить. **Восьмого бакета «допущен, не доделан» в семи
|
||
полях нет**, поэтому денежный стоп получил СВОЙ леджер — `Finished.Money`, считанный по тому, что прогон
|
||
РАЗРЕШИЛ, а не по тому, что намеревался.
|
||
|
||
---
|
||
|
||
## 3. Пункт 3 (строка 278, событие) — «сколько не хватило» доезжает. ЗАКРЫТ, с ЗАМЕНОЙ ЧИСЛА
|
||
|
||
**⚠ РАСХОЖДЕНИЕ С ПРОМТОМ, взятое осознанно и согласованное с оркестратором.** §4(3) называет число:
|
||
«Число у тебя уже есть и печатается (`denied estimate=$…`)» — и следующей фразой запрещает его класс:
|
||
«цены моделей, стадий и вызовов не выходят». `denied estimate` — это **цена одного вызова**, то есть
|
||
ровно запрещённое. Наружу едет **недостача** `committed + reserved + estimate − ceiling`, поле
|
||
`shortfall_micro_usd` (строка 278 называла `next_step_micro_usd` — то же противоречие). Недостача
|
||
отвечает на настоящий вопрос платформы («пополни на столько — и вызов пройдёт») и не раскрывает
|
||
стоимость ни одной стадии: это расстояние между лимитом, который платформа сама поставила, и суммой,
|
||
которую она уже авторизовала. Оркестратор ответ принял и пере-снимет строку 278 под построенное имя.
|
||
|
||
**Две честные оговорки, обе в доке поля:**
|
||
1. **Это ВЕРХНЯЯ граница, а не минимум.** `reserved` включает ещё идущие вызовы, а settle заменяет
|
||
резервацию стоимостью, которая никогда не больше. «Пополни на столько — и отказанный вызов пройдёт»
|
||
верно всегда; «меньше не хватит» — нет. При пустом полёте (обычный терминальный отказ) они совпадают.
|
||
2. **Только book-скоуп.** Дневной потолок суммируется по ВСЕМ книгам стора (`ledger.go`: `WHERE date = ?`
|
||
без фильтра по книге), а доступные движку цифры — одной книги. Уверенное число там было бы про другую
|
||
сумму. Поле опускается; отсутствие значит «не сказано», не «ничего не не хватало».
|
||
|
||
⚠ **ЧЕСТНЫЙ ОСТАТОК, названный оркестратором и РАСШИРЕННЫЙ мной: недостача ВОССТАНОВИМА в цену
|
||
отказанного вызова.** `estimate = shortfall + ceiling − committed − reserved`. Потолок платформа знает —
|
||
сама поставила; `committed` знает — движок публикует его кумулятивно кадром `spend`; а `reserved` на
|
||
обычном терминальном отказе по построению почти нулевой, потому что стоп туда приходит через
|
||
`waitNothingInFlight` — то есть при ПУСТОМ полёте. ⇒ обратный счёт работает не только на первом вызове
|
||
книги, как было записано изначально, а практически на любом стопе ЭТОГО пути.
|
||
⚠ **И границу самого этого утверждения тоже надо назвать, иначе оно шире замера** (поправка оркестратора,
|
||
купленная его же сегодняшней ошибкой: выборка, предъявленная как доказательство, доказывает ровно себя).
|
||
«`reserved` пуст» показано для ОБЯЗАТЕЛЬНОГО пути, который останавливается через `waitNothingInFlight`.
|
||
Для стопа через `settleCannotHelp` полёт может быть любым: при пустом этот стоп неотличим от основного
|
||
случая (`reserved` ≈ 0, оценка восстановима), при непустом обратный счёт даёт ВЕРХНЮЮ границу. То есть
|
||
утечка на этом пути НЕ БОЛЬШЕ, чем на основном — «не больше», а не «строго слабее», как я написала
|
||
сначала. Для НЕОБЯЗАТЕЛЬНЫХ вызовов вопрос не встаёт вовсе: они на
|
||
потолке деградируют, а не останавливают книгу, и кадра не рождают. Вывод от этого не меняется, а
|
||
утверждение перестаёт быть шире того, что проверено.
|
||
⛔ **ДОВОД «ТА ЖЕ ДВЕРЬ» БЫЛ ЛОЖЕН, И ЭТО ГЛАВНАЯ ПРАВКА ДОФИКСА (F1 приёмки).** Первая редакция этого
|
||
абзаца утверждала: «тот же пак ратифицированно публикует цену вызова на том же шве — `step_max_usd`,
|
||
значит дверь та же». **Проверено командой, а не рассуждением:**
|
||
```
|
||
$ git grep -lE "StepMax|step_max" HEAD -- platform/ # ПУСТО
|
||
$ git grep -c "StepMax" HEAD -- backend/internal/runevents/ # 0
|
||
```
|
||
⇒ `step_max_usd` живёт только в `manifest.go`, `priceprojection.go` и `render.go`; **потребитель его не
|
||
читает и на проводе его нет.**
|
||
|
||
⛔ **И ЭТА ФОРМУЛИРОВКА, В СВОЮ ОЧЕРЕДЬ, ТОЖЕ ОКАЗАЛАСЬ НЕ ОКОНЧАТЕЛЬНОЙ — 05.09 оркестратор ОТОЗВАЛ
|
||
ратификацию до слова владельца, и я проверила его посылки своей рукой прежде, чем править:**
|
||
- `docs/product-requirements.md` (ПТ-35): отзывая 05.09 «запрет денег на экране», владелец оставил
|
||
остальное дословно — «цены моделей, стоимость стадий **и вызовов**». Сузить это до «пользовательской
|
||
поверхности» зона не вправе, и я это сужение приняла.
|
||
- `docs/archive/prompts/BACKEND_MONEYSTOP_SESSION_PROMPT_2026-09-05.md:82`=`цены моделей, стадий и вызовов не выходят`
|
||
применяет запрет к **ПРОВОДУ**, а не к экрану: «Наружу —
|
||
только НЕДОСТАЮЩАЯ сумма; цены моделей, стадий и вызовов не выходят». ⇒ замер верификатора попал не в
|
||
реализацию поля, а **в посылку ЗАКАЗА**, а на этот случай норма однозначна: стоп и вопрос владельцу.
|
||
- ссылка на ратификацию «D39.106 §2» — овер-атрибуция: `05-decisions-index.md:167` говорит, что D39.106 —
|
||
про шов «движок = транзиентный юнит, события = `events.jsonl`», и такой фразы там нет вовсе.
|
||
|
||
**Что было сделано с кодом: поведение НЕ тронуто, изменилось только то, что код УТВЕРЖДАЛ о своей
|
||
легитимности** — шапка поля была помечена `pending owner`. И к вопросу владельца добавлено то, чего не видел
|
||
ни оркестратор, ни я: **тензия живёт ВНУТРИ самого заказа.** Пункт (3) промта запрещает цене вызова
|
||
выходить наружу, пункт (4) ТРЕБУЕТ опубликовать `step_max_usd` в `manifest --json` — а это по построению
|
||
оценка ОДНОГО вызова, самой дорогой неделимой резервации книги. То есть дверь открывает не недостача
|
||
одна: заказ держит обе половины сразу. ⚠ Уточнение, которое сужает вопрос до честного: ни недостача, ни
|
||
`step_max_usd` не раскрывают, сколько вызов СТОИЛ, — обе величины суть до-вызовные ОЦЕНКИ, а не
|
||
сеттлмент.
|
||
✅ **РАЗРЕШЕНО ВЛАДЕЛЬЦЕМ 05.09, ратифицировано `D39.203`** — проверено по телу ноты, а не по пересказу:
|
||
`05-decisions-log.md:2395`, строка реестра `05-decisions-index.md:264`. Дословно: «Можно, в форме
|
||
„сколько добавить“». **Различение оценка/стоимость вошло ОПОРОЙ решения, а не примечанием:** наружу идёт
|
||
до-вызовная ОЦЕНКА (резервация, которую движок просит ПЕРЕД звонком), ПТ-35 запрещает СТОИМОСТЬ и
|
||
остаётся в силе НЕ суженным, сеттлмент наружу не идёт ни одной дверью. Обе половины тензии закрыты
|
||
разом — снятие недостачи вопроса бы не закрыло, потому что `step_max_usd` остаётся. `pending owner` с
|
||
шапки поля снят, вместо него — указатель на ноту.
|
||
|
||
⚠ **Владелец выбрал ФОРМУ, которой в коде ещё нет:** `max(shortfall, step_max − headroom)` — «сколько
|
||
добавить, чтобы прошёл ЛЮБОЙ следующий вызов». Это **НОВЫЙ ЗАКАЗ, а не дефект здесь**: построенное поле
|
||
не ложно, оно У́ЖЕ — говорит, сколько не хватило на ЭТОТ вызов. Состав работы — тот же список, которым я
|
||
отвечала на вопрос о цене: новая величина + ПЕРЕИМЕНОВАНИЕ поля (`shortfall_micro_usd` станет ложным
|
||
именем) + минор `StreamVersion` 1.3→1.4 + зеркало словаря у платформы + пере-снятие пина белого списка.
|
||
Пак лендится КАК ПОСТРОЕН, форма — строкой бэклога (`D39.203` §5–6).
|
||
⚠ Довод пришёл ко мне от оркестратора, и я приняла его, не проверив командой, — ошибка чужая по
|
||
происхождению и МОЯ по принятию: посылку, на которой стоит утверждение в коде, зона обязана проверять
|
||
сама, кем бы она ни была принесена.
|
||
⚠ **И утверждение в самом коде было сильнее правды:** `runevents.go` писал «discloses nothing about what
|
||
any one call cost». Верификатор опроверг это ЖИВЫМ ЗОНДОМ: `shortfall + ceiling = $0.007016840` против
|
||
собственного текста отказа движка `denied estimate=$0.007016` — дельта в микродоллар округления; со
|
||
вторым зондом (ненулевой `committed`, который платформа читает кадром `spend`) — $0.0070166 против тех же
|
||
$0.007016. Текст исправлен на измеренную правду.
|
||
|
||
**Что остаётся закрытым и почему поле всё равно не убирается:** ПТ-33 охраняет СТРУКТУРУ расходов — цену
|
||
модели, стоимость стадии, разложение вызова между промптом и генерацией; тотал одного отказанного вызова
|
||
не является прайс-листом. Кадр идёт внутреннему потребителю, а не человеку (D39.196 §2а снимает запрет
|
||
денег в UI, не трогая ПТ-33), платформенная сессия письменно подтвердила, что показывать его не будет и
|
||
не может, а стоп, который не может назвать недостачу, оставляет покупателя с мёртвой книгой и без
|
||
следующего шага — то, что и намерено живьём 04.09.
|
||
⚠ Ратификационную ноту об этом пишет ОРКЕСТРАТОР — сужение ратифицированной фразы не работа зоны.
|
||
|
||
**Версия шва: `StreamVersion` 1.2 → 1.3** по правилу минора (добавлены поле и структура). Прибито
|
||
тестом, читающим `hello` из журнала, и посадкой на откат константы.
|
||
|
||
**Событие пишется В МОМЕНТ ОТКАЗА, а не из терминального обработчика.** Причина — PD-113: он закрыт
|
||
двумя каналами, которые падают НЕЗАВИСИМО. Раз потолок больше не отменяет соседей, один из них может
|
||
упасть по своей причине позже и обогнать halt на выходе — и событие, выведенное только из возвращённой
|
||
ошибки, промолчало бы о деньгах, которые уже кончились. Идемпотентно (`emitter.ceilingSaid`).
|
||
|
||
**Событие не может выстрелить от ОПЦИОНАЛЬНОГО отказа — проверено чтением обеих ветвей.** `Finished.Money`
|
||
и кадр `ceiling` условны на `ceilingSaid`, который выставляется только из `fail()` волны. Отказ хопа
|
||
гасится в `maybeEscalate` (`escalation.go:163`), репайра — `repair.go:307-311` (`break`, не `return`),
|
||
терминолога — `terminologist.go:857-860` (`break`). Ни один до `fail()` не доходит ⇒ прогон, который лишь
|
||
деградировал опциональный проход, не публикует ни денежного леджера, ни стопа.
|
||
|
||
**Ранжирование ошибок: паника > инфра-ошибка > потолок, и инфра-ошибка с потолком НЕ джойнится.** Паника
|
||
джойнится, потому что у неё ЕСТЬ ТИП, который `exitCode` и `terminal()` проверяют ПЕРВЫМ, — ранг
|
||
переживает джойн. У инфра-ошибки типа нет: `errors.As(join, &CeilingHalt)` был бы истинен, сломавшийся
|
||
прогон ушёл бы с exit 4, платформа записала бы `paused`, и оператору вручили бы неверное лекарство
|
||
(«добавьте денег»). Пин: `TestAnInfraFailureAfterAMoneyStopDoesNotDepartAsPaused`.
|
||
|
||
---
|
||
|
||
## 4. Пункт 4 (строка 278, проекция) — цена книги ДО покупки. ЗАКРЫТ
|
||
|
||
**⚠ РЕШЕНИЯ, КОТОРЫЕ ПРОМТ ОТДАЛ СЕССИИ — с доводами.**
|
||
|
||
**(а) `expected_usd` считается по СОБСТВЕННОЙ фертильности и формулой СПИСАНИЯ, а `step_max_usd` — формулой
|
||
РЕЗЕРВАЦИИ. Это не две оценки одного, это два разных вопроса.**
|
||
Публиковать одно число за оба было бы неверно в обе стороны сразу. Резервация worst-case по построению
|
||
(весь промпт мимо кеша или В кеш, генерация до `max_tokens`), так что проекция из резерваций завысила бы
|
||
счёт в разы и заставила платформу отказывать в заказах, которые она легко тянет; проекция ожидаемого
|
||
списания занизила бы ровно ту величину, которая отказывает вызовам, — а это и есть путь, которым сервис
|
||
пришёл к «книгу нельзя перевести никакими деньгами». ⇒ `expected_usd` = `ledger.CostUSD` над
|
||
фертильностью пары (те же коэффициенты `est_out`, которыми чанкер уже режет книгу); `step_max_usd` =
|
||
`ledger.EstimateUSD` при размере, который считает сам исполнитель.
|
||
|
||
**(б) По отрендеренному промпту или по сырому тексту — по СЫРОМУ ТЕКСТУ ПЛЮС ШАБЛОН, и вот почему.**
|
||
Отрендерить настоящий промпт стадии k>0 нельзя по построению: её вход — ЧЕРНОВИК, которого до прогона
|
||
нет, а вопрос задаётся именно до покупки. Зато шаблон загружен (`openRunner` зовёт `loadTemplates()` на
|
||
обоих путях, включая read-only) и он НЕ мал — редакторский промпт c1 больше тысячи токенов, и проекция,
|
||
которая его игнорирует, занижает промптовую сторону каждого юнита книги на одну и ту же величину: это
|
||
смещение, а не шум. Инъекция банка берётся ПО ВЕРХНЕЙ границе (её бюджет), потому что банка на момент
|
||
построения манифеста ещё нет.
|
||
|
||
⚠ **И два уточнения, которых в первой редакции НЕ БЫЛО — оба найдены адверсариальным проходом и оба
|
||
двигали число на проценты, а не на копейки** (находки 14 и 15 в §6.3):
|
||
· **Исходник едет на КАЖДУЮ стадию, чей шаблон его просит.** Исполнитель рендерит все стадии через
|
||
`RenderVars{Text, Draft}`, а редактор билингвален (D30.1). Проекция спрашивает ШАБЛОН — `{{text}}`, тот же
|
||
`sourcePlaceholder`, по которому движок уже считает echo-exposure, — а не позицию стадии: монолингвальное
|
||
плечо ценится без исходника теми же строками, и пар-специфики в этом нет.
|
||
· **Few-shot считается по ТОГЛУ стадии,** через `tpl.SystemFor(fewShotEnabled(st))` — функции самого
|
||
исполнителя. У боевого c1-редактора тогл выключен, а блок в шаблоне лежит.
|
||
|
||
**(в) Какие стадии входят и НА КАКОЙ ГРАНУЛЯРНОСТИ.** В `expected_usd` — все стадии конвейера, по одной
|
||
попытке. НЕ входят: ретраи, эскалационный хоп, репайр. Каждый из них УСЛОВЕН, и вложить лекарства плохого
|
||
прогона в заголовочную цену значит выдать исключение за правило.
|
||
⚠ **И считается это на ТОЙ ЖЕ гранулярности, на какой исполнитель делает вызовы** (находка 22): черновые
|
||
стадии — ПО ЧЛЕНАМ юнита (`runDraftChunk` — один рендер и один вызов на чанк), остальные — ПО ЮНИТУ
|
||
(`runEditUnit`). Иначе смещение идёт сразу в две стороны: шаблон с инъекцией недосчитываются, а
|
||
`step_max_usd` завышается в число членов.
|
||
|
||
**(г) Ретраи и эскалации — в `step_max_usd` входят РЕТРАИ и не входит всё остальное, и различитель —
|
||
деградирует ли отказ.** `maxTokensForAttempt` УДВАИВАЕТ выходной бюджет на каждой регенерации, а отказ
|
||
РЕТРАЯ **не деградирует** — он останавливает книгу. Потолок, посчитанный по attempt 0, допускает первый
|
||
вызов юнита и навсегда отказывает его лекарству. Хоп, репайр и терминология ловят `errReserveCeiling` и
|
||
продолжают — потолок, малый для них, стоит качества, а не движения, и включать их значило бы поднять
|
||
минимальную покупку ради работы, от которой прогон готов отказаться.
|
||
|
||
**(д) `book_once_usd` — это ГРАНИЦА, а не прогноз, и назвать её иначе было бы ложью.** Вход терминолога
|
||
и его классификатора — список кандидатов, который производит майнер ИЗ ЧЕРНОВИКОВ, а черновиков на момент
|
||
публикации числа нет. Честно движок может сказать только то, чего он не превысит: обе стадии гейтятся
|
||
собственными бюджетами, и план обрезается под них до первого вызова. Для вопроса «какой потолок нужен
|
||
этой книге» — а он и есть вопрос — граница и является полезной цифрой. Ноль, когда стадии не настроены.
|
||
|
||
**(е) `structure` — по ПРОВЕНАНСУ, с одним исключением, которое тоже про провенанс.**
|
||
`declared` — границу нарисовал ФОРМАТ (spine EPUB; перевод страницы U+000C в txt — ASCII-разрыв
|
||
страницы, столь же явный). `detected` — движок ВЫВЕЛ её, сопоставив строки-заголовки в прозе
|
||
(`splitTextChapters`); он прав на корпусе, ради которого построен, и он догадка: абзац, открывающийся
|
||
«第三节…» в пределах длины, неотличим от заголовка. `none` — одна глава: границу, которую никто не
|
||
пересёк, никто и не проверял, каким бы путём она ни получилась.
|
||
|
||
⚠ **И ЧЕСТНАЯ ГРАНИЦА, которую я сначала записала как достоинство и исправляю: `detected` СЕГОДНЯ
|
||
CJK-ONLY.** Сопоставление заголовков идёт по `lang.DefaultCJKSection()` — это КОНСТАНТА (章节節回 плюс
|
||
класс CJK-числительных), а не данные пары. ⇒ книга в любой другой письменности вернёт `none`, как бы
|
||
явно её главы ни были размечены. **Асимметрию я не вношу — она уже есть и уже названа**: движок читает
|
||
данные пары для РЕНДЕРА заголовка (`configs/langpacks/*/heading.txt`), а РАЗРЕЗ на главы остаётся
|
||
захардкоженным CJK, и строка бэклога **283** называет именно этот раскол работой. Что добавляет поле — у
|
||
асимметрии появляется ИМЯ на проводе вместо того, чтобы прятаться в числе глав: не-CJK книга, сказавшая
|
||
`none`, говорит правду о том, что этот движок умеет, и потребитель откажется предлагать заказ в главах
|
||
вместо того, чтобы предложить его против разреза, которого никто не делал. Сама классификация
|
||
пар-агностична — она спрашивает, КАКОЙ ПУТЬ провёл границы, — и скажет `detected` без единой правки Go в
|
||
тот день, когда разрез научится читать данные пары.
|
||
|
||
**(ж) Форма леджера в `finished`.** `Finished.Money{units_resolved, units_deferred}`, считанный из тех же
|
||
по-волновых счётчиков, из которых публикуется `progress`, — так текущий канал и терминальный не могут
|
||
назвать разные числа. ⚠ **`resolved`, а НЕ `delivered`**: счётчик инкрементируется на юните, который
|
||
дошёл до вердикта, включая флагованный с пустым текстом. Назвать это «доставлено» — ровно та ложь, ради
|
||
прекращения которой объёмный леджер разделили на `Delivered`/`Flagged`, и ложь, по которой покупатель
|
||
стал бы действовать.
|
||
|
||
**⛔ И ЧИТАТЕЛЬ С ПИСАТЕЛЕМ ПРИБИТЫ ОДНИМ АКТОМ — по норме, которую оркестратор передал в ходе пака.**
|
||
Две пары:
|
||
1. **Размер вызова.** `baseMaxTokensFor` вынесен из `runStage` в ОДНУ функцию, которой пользуются и
|
||
исполнитель, и проекция. Проекция, считающая размер по-своему, разъехалась бы с резервациями, которые
|
||
прогон реально делает, — и разъезд был бы невидим, обе стороны выглядят как числа.
|
||
2. **Цена на двух поверхностях.** ⛔ **Полная батарея поймала у меня ровно этот класс:**
|
||
`TestManifestServesTheReadModelsIdentically` покраснел — `status` отвечал ценой при наличии сайдкара
|
||
и `nil` без него. Манифест — АКСЕЛЕРАТОР, а акселератор, меняющий ОТВЕТ, это второй источник истины.
|
||
Лечение: `readModelPrice` отвечает на обоих путях, и обе ветки кончаются в одной `projectBook`.
|
||
|
||
---
|
||
|
||
## 5. Секция, которую требует §5-тер(а): ГДЕ ПРОМТ НАПИСАН НЕ ТАК
|
||
|
||
Читаю промт последней и называю расхождения — не как вежливость, а как заказ владельца.
|
||
|
||
1. ⛔ **§4(3) внутренне противоречив.** Он называет число, которое должно ехать наружу
|
||
(`denied estimate=$…`), и следующей фразой запрещает его класс («цены моделей, стадий и вызовов не
|
||
выходят»). Исполнить дословно значило бы нарушить соседнее предложение. Разрешено заменой на
|
||
недостачу; согласовано.
|
||
2. ⛔ **§4(1) заказывает «обрамление как ДАННЫХ», которое в текстовой форме нельзя выполнить, не нарушив
|
||
§4(6).** Единственный дом для такой фразы — `lang/data/injection.txt`, а он побайтно входит в
|
||
`EmbeddedVersion()` → `cutTag` → `unit.id` каждой книги. Промт этого не знал; сделано структурно.
|
||
3. ⚠ **§3 п.1 карты чтения указывает на секцию, которой уже нет — но это НЕ дефект промта, и правку
|
||
вношу по указанию оркестратора.** «`15-money-path.md` §«ОТКРЫТАЯ РАЗВИЛКА 04.09»» переименована в
|
||
«РАЗБОР 04.09: КНИГУ НЕЛЬЗЯ ДОЧИТАТЬ…» — и переименовал её оркестратор УЖЕ ПОСЛЕ выдачи промта. То
|
||
есть на момент выдачи карта была верна, а сломал её последующий редакторский акт. Разница не
|
||
формальная: «промт написан не так» лечится редактурой промтов, а «карта сломана после выдачи» —
|
||
дисциплиной того, кто правит доки под работающей сессией. Секцию нашла грепом по заголовкам.
|
||
4. ⚠ **§4(2) «защёлка на уровне ВОЛНЫ» обоснована доводом, который больше не работает.** Промт пишет:
|
||
«Без защёлки W воркеров дадут W путей ошибки вместо одной остановки». Довод снят самой конструкцией:
|
||
`haltErr` — отдельный слот с первым-побеждает, путь ошибки один при любом числе воркеров. Защёлка
|
||
ОСТАВЛЕНА (оркестратор подтвердил другим доводом — продуктовым: книгу читают подряд, и непрерывный
|
||
префикс из шести глав лучше решета из восьми), но её цена названа: она читает «этот ОДИН элемент не
|
||
влез» как «денег нет ни на что», а при разбросе резервов внутри волны 2.976× (замер разбора 04.09) и
|
||
удвоении бюджета на ретрае это ложно — юниты, которые ВЛЕЗАЛИ, не начинаются. Заведено строкой
|
||
бэклога.
|
||
5. ⚠ **§4-тер риск (б) назван мягче, чем он есть.** «частичная волна под потолком — НОВАЯ форма для этих
|
||
счётчиков» — на деле счётчики не просто новую форму получают, они СОВРУТ по шести полям из семи, и
|
||
лечение не в поправке, а в отказе их публиковать (см. §2-бис).
|
||
6. ⚠ **§5 «модель называй имени» — опечатка** («по имени»). Названо: помощник — **Fable 5**, один агент,
|
||
долгий разговор, два круга.
|
||
7. ✅ **§4(6) с `StreamVersion` исправлен верно** — прежняя редакция запрещала бамп и заказывала поле; в
|
||
выданной мне редакции противоречие снято, и это тот случай, когда правка промта сработала.
|
||
|
||
---
|
||
|
||
## 6. Самопроверка ИСПОЛНЕНИЕМ
|
||
|
||
### 6.1. Батарея — ЗЕЛЁНАЯ
|
||
|
||
```
|
||
$ cd backend && make battery # go build · go vet ·+tags live ·+tmvet · golangci-lint · go test -race
|
||
0 issues. # golangci-lint
|
||
ok textmachine/backend/cmd/tmctl 42.422s
|
||
ok textmachine/backend/internal/archguard 58.448s
|
||
ok textmachine/backend/internal/chunk 1.658s
|
||
ok textmachine/backend/internal/membank 31.507s
|
||
ok textmachine/backend/internal/pipeline 246.730s
|
||
ok textmachine/backend/internal/store 56.399s
|
||
…19 пакетов с тестами, ни одного FAIL…
|
||
MAKE_EXIT=0
|
||
--- did NOT run (no stand data; see battery-stand) ---
|
||
--- SKIP: TestMinerFullBookParity · TestHelperEventsRun · TestHelperKillLoop
|
||
```
|
||
|
||
⚠ **Это прогон на СДАВАЕМОМ дереве, вместе с дофиксом приёмки — не на приземлённом паке.** Батарея
|
||
гонялась заново после КАЖДОЙ волны правок: прежнее «зелено» после правки не значит ничего. Последний
|
||
прогон — на ЗАМОРОЖЕННОЙ копии финального дерева, в одном заходе с каталогом; предыдущий пришлось
|
||
выбросить целиком, потому что дерево изменилось под ним (записи 50–51), и результат о дереве, которого
|
||
больше нет, — не результат.
|
||
|
||
**19 пакетов ok · 0 упавших тестов · 0 issues линтера · 3 скипа (все — стендовые, требуют данных вне
|
||
репозитория; ровно те же три, что и до пака).** Сюда входит `go vet ./...`, `go vet -tags live`, оба
|
||
прохода через СОБСТВЕННЫЙ анализатор зоны (`bin/tmvet`) и `gofmt -l` — именно `tmvet` и поймал у меня
|
||
хенд-билд `config.Stage` (§6.3 находка 23).
|
||
|
||
⚠ **ГОНЯЛОСЬ ЦИКЛОМ ДО ЧИСТОТЫ, А НЕ ОДНИМ ПРОХОДОМ (D39.197: утверждение о проверке живёт ровно до
|
||
следующей правки), и число прогонов само по себе факт: ШЕСТЬ.** Первые пять были остановлены или красны:
|
||
`MAKE_EXIT=2` на собственном анализаторе зоны · `--- FAIL` под `-race` на моём же тесте ожидания (он
|
||
полагался на везение, а не на состояние) · три прогона убиты мной, когда очередной собственный проход
|
||
находил дефект и делал результат утверждением о дереве, которого больше нет. Шестой — на замороженном
|
||
дереве, после того как ВСЕ находки первого полного прогона каталога были закрыты.
|
||
⚠ `internal/pipeline` шёл **272 с** (в пятом прогоне — 450 с) против ~95 с на тихой машине: параллельные
|
||
сессии зоны на той же машине, разброс, о котором предупреждает сам `Makefile`. Ни одного упавшего
|
||
утверждения ни в одном из зелёных прогонов.
|
||
|
||
### 6.2. Дифф `^func Test` — ИСПОЛНЕНИЕМ, не по памяти
|
||
|
||
```
|
||
$ git diff -U0 -- backend | grep -E '^-func Test' # пусто: ни одна функция не удалена и не переименована
|
||
$ git grep -h '^func Test' <до пака> -- backend | wc -l # 1172
|
||
$ grep -rh '^func Test' backend --include=*_test.go | wc -l # 1213
|
||
```
|
||
|
||
**+41 тестовая функция: 39 в трёх новых файлах** (`wirefence_test.go` 7 · `moneystop_test.go` 17 ·
|
||
`priceprojection_test.go` 15 — сумма ровно 39) **и две дописаны в существующие**:
|
||
`cmd/tmctl/render_test.go` (запись 41) и `internal/chunk/ingest_test.go` (запись 54). Обе ДОБАВЛЯЮТ
|
||
функцию, не трогая ни одной существующей. Ни одна существующая функция не удалена и не
|
||
переименована — вывод грепа по `-func Test` пуст, а два протухших теста починены ВНУТРИ тела (§7).
|
||
|
||
**Дофикс приёмки добавил четыре из этих 40** — их видно тем же диффом против уже приземлённого пака
|
||
(`git diff -U0 -- backend | grep -E '^\+func Test'`): `TestASidecarFromAnOlderBuildStillYieldsAPrice`
|
||
(запись 44) · `TestAnEngineWrittenDocumentDropsTheRowInsteadOfKillingTheRun` (запись 42) ·
|
||
`TestTheMoneyLedgerFollowsTheCeilingAndNotTheOutcome` (запись 40) ·
|
||
`TestTheHumanManifestCallsTheBookLevelFigureACeiling` (запись 41) ·
|
||
`TestTXTStructureIsDeclaredOnlyWhenTheFormatDrewEVERYBoundary` (запись 54 — дописан ПОСЛЕ полного
|
||
прогона каталога, потому что его отсутствие предъявила выжившая посадка).
|
||
|
||
### 6.3. Что я нашла В СВОЕЙ ЖЕ РАБОТЕ (адверсариальный проход, помощник Fable 5)
|
||
|
||
Плотность находок на собственной работе не ниже, чем на чужой (D39.197) — подтвердилось:
|
||
|
||
| # | находка | статус |
|
||
|---|---|---|
|
||
| 1 | ⛔ `waitForSettle` возвращал `false` и на «нечего ждать», и на `ctx.Done()`, а вызывающий обе трактовал как ФИНАЛЬНЫЙ отказ ⇒ прогон, остановленный Ctrl-C или отменённый инфра-падением соседа, публиковал `ceiling` с недостачей, и платформа записывала `paused`, требуя денег за стоп, к деньгам отношения не имевший | ЗАКРЫТО: три исхода (`waitSettled`/`waitNothingInFlight`/`waitAborted`), пин `TestACancelledWaitIsNotAMoneyStop`, посадка `MS-cancelled-wait-is-not-money` |
|
||
| 2 | ⛔ Новая ветка `terminal()` («ошибка + потолок был») стояла ВЫШЕ `sigStop` и `context.Canceled` ⇒ банк-стоп на книге, коснувшейся потолка, отчитывался `failed` при exit 3, а пойманный SIGTERM — `failed` при exit 5. Два канала противоречат друг другу об одном прогоне | ЗАКРЫТО: ветка перенесена последней, порядок объявлен утверждением в комментарии |
|
||
| 3 | ⛔ Гонка «Reserve закоммичен — `enter()` ещё нет» давала ЛОЖНЫЙ финальный стоп | ЗАКРЫТО: `Reserve` под мьютексом шлюза |
|
||
| 4 | ⛔ Денежный стоп в ЧЕРНОВОЙ волне пускал волну РЕДАКТУРЫ: `draftByKey` строится по ВСЕМ чанкам, поэтому не начатый член находится (`ok == true`) с нулевым результатом — не флагован, текст пуст — и редактор ОПЛАЧИВАЛСЯ за юнит с дырой, который потом отгружался | ЗАКРЫТО: ранний возврат + `drafted []bool`, пин `TestAMoneyStopInTheDraftWaveNeverStartsTheEditWave`, посадка `MS-draft-stop-does-not-edit` |
|
||
| 5 | ⛔ Кормилец продолжал раздавать индексы после защёлки примерно в половине случаев (равномерный выбор `select` среди готовых ветвей) | ЗАКРЫТО: неблокирующая проверка `latched` перед `select` |
|
||
| 6 | ⛔ `MoneyLedger.UnitsDelivered` считал РАЗРЕШЁННЫЕ юниты, включая флагованные с пустым текстом, а док поля обещал «книга, которую покупатель может читать сейчас» | ЗАКРЫТО до выхода 1.3 на провод: поле переименовано в `units_resolved`, ложь снята |
|
||
| 7 | ⛔ Мой собственный тест фолда версии был ВЫРОЖДЕННЫМ: сравнивал два разных набора строк и доказывал только что строки разные — посадка `WF-version-fold-scope` его ПЕРЕЖИЛА | ЗАКРЫТО: тест сделан ВРЕМЕННЫМ (пины хешей, снятых сборкой `32be78a`), посадка краснеет |
|
||
| 8 | ⛔ Моя посадка `WF-length-bound` не собиралась (`1 << 30` рун в `strings.Repeat` съедал память) — каталог показал `NOTHING`, а не `RED` | ЗАКРЫТО: посадка снимает ПРОВЕРКУ, а не двигает константу |
|
||
| 9 | ⛔ `waitForGate` звал `t.Fatalf` ИЗ ПОРОЖДЁННОЙ ГОРУТИНЫ — в Go это `runtime.Goexit` не на той горутине: утверждение теряется, а прогон висит до таймаута пакета. Под посадкой я получила бы необъяснимый таймаут вместо КРАСНОГО | ЗАКРЫТО: возврат `bool`, проверка в тестовой горутине |
|
||
| 10 | ⛔ Посадка `PP-step-max-ignores-retries` пережила бы ВХОЛОСТУЮ: единственное утверждение про `step_max` сравнивало его с резервом attempt 0 | ЗАКРЫТО: `TestStepMaxCountsTheRetryThatDoesNotDegrade` |
|
||
| 11 | ⛔ Полная батарея: `status` отвечал ценой при наличии сайдкара и `nil` без него | ЗАКРЫТО: `readModelPrice` отвечает на обоих путях, обе ветки кончаются в одной `projectBook` |
|
||
| 12 | ⛔ Проекция цены считала цену юнита ДВАЖДЫ — в цикле манифеста и заново внутри книжной свёртки. Две деривации одного числа, и семь проходов по тексту книги там, где хватает трёх; на 23-мегабайтной книге манифест — акселератор, купленный секундами | ЗАКРЫТО: один проход `projectBook`, `unitPrice` несёт `PromptTokens` |
|
||
| 13 | ⛔ `WireUnfit` материализовал `[]rune(s)` на КАЖДОЙ строке банка КАЖДОЙ инъекции КАЖДОГО вызова — аллокация на горячем пути ради вопроса, которому она не нужна | ЗАКРЫТО: проход по строке со счётчиком рун |
|
||
| 14 | ⛔ **Проекция роняла ИСХОДНИК из промпта каждой стадии после первой.** Исполнитель рендерит ВСЕ стадии через `RenderVars{Text: ch.Text, Draft: prev}` (`stagerun.go:73`), а редактор БИЛИНГВАЛЕН по ратифицированному решению (D30.1) — он получает исходник рядом с черновиком, и это единственная причина, по которой он умеет привязывать канон к исходному термину. Проекция давала поздним стадиям только черновик ⇒ **≈ ⅙ ожидаемого счёта юнита, в сторону ЗАНИЖЕНИЯ** — той самой, в которой платформа продаёт книгу дешевле себестоимости | ЗАКРЫТО: `promptTokens` спрашивает ШАБЛОН (`{{text}}`, тот же `sourcePlaceholder`, по которому движок уже считает echo-exposure), а не позицию стадии — монолингвальное плечо ценится без исходника теми же строками. Пин `TestTheProjectionChargesTheSourceToEveryStageThatAsksForIt`, две посадки |
|
||
| 15 | ⛔ **Проекция считала few-shot безусловно, а исполнитель шлёт его по тоглу.** `SystemFor(fewShotEnabled(st))` (`render.go:159-170`) добавляет блок только когда `few_shot` включён, а у боевого c1-редактора он ВЫКЛЮЧЕН при том, что блок в шаблоне лежит — больше тысячи байт. Сумма полей завышала КАЖДЫЙ edit-вызов КАЖДОЙ книги на этом плече, и невидимо: цена на 20 % выше выглядит ровно как цена | ЗАКРЫТО: спрашиваются функции исполнителя. Пин `TestTheProjectionChargesTheFewShotBlockOnlyWhenTheStageSendsIt`, посадка |
|
||
| 16 | ⛔ **`TestWaitingForHeadroomDeliversTheWholeBook` доказывал не то, что заявлял — предъявлено ИСПОЛНЕНИЕМ.** В логе `-v` оба черновика допущены ОДНОВРЕМЕННО, строки «reservation refused … waiting» нет ни одной: книга доставлена потолком, который ни разу не отказал. То есть **положительная половина строки 277 не исполнялась ни одним тестом**, и посадка `MS-wait-for-settle` этот тест бы пережила | ЗАКРЫТО: у шлюза появился счётчик СРАБАТЫВАНИЙ (`waitsSoFar`), тест требует `waits > 0` ДО всего остального, потолок пере-подобран (1.85 оценки). Предъявлено: `waits_this_run=1` в логе |
|
||
| 17 | ⛔ **Фикстурный потолок был завышен ×1.114**: `callCeilingUSD` клал в промпт `plan.inject` = 800 токенов, а безглоссарная фикстура инъекцию не шлёт вовсе. Из-за этого множитель 1.9 давал ≥ 2 резерваций — окно «по одному за раз» превращалось в «влезают оба», что и породило находку 16 | ЗАКРЫТО: аллоуанс убран, оценка теперь совпадает с реальной побайтно (0.007016 в логе исполнителя и в фикстуре) |
|
||
| 18 | ⛔ **Два утверждения `TestResumeAfterAMoneyStopRePaysNothing` НЕ МОГЛИ упасть:** «committed упал» невозможно (committed монотонен), а `secondPass >= firstPass + rec.count()` арифметически недостижимо, поскольку `secondPass := rec.count() - firstPass`. Оба читались как проверки и ни одна не краснела | ЗАКРЫТО: утверждение о ЧИСЛЕ вызовов через оба прогона (`3 юнита × 2 стадии == 6`) — пере-покупка ловится именно им |
|
||
| 19 | Лог-строка ожидания печаталась ПОСЛЕ пробуждения и читала счётчики тогда: оператор видел слова «still in flight … waiting» рядом с `in_flight=0 queued=0` — фраза, противоречащая собственным числам | ЗАКРЫТО: строка говорит «settle освободил запас, переспрашиваем», счётчики названы `*_now` |
|
||
| 20 | Якорь посадки `PP-price-reaches-status` умер, когда `status.go` перешёл на `readModelPrice` — каталог дал бы `ROTTED`, то есть громко, но запись бы ничего не утверждала | ЗАКРЫТО: якорь пере-навешен на нынешнее выражение того же свойства |
|
||
| 21 | Пере-утверждение в собственном комментарии и отчёте: «`structure` пар-агностичен по построению». На деле `detected` сегодня CJK-only (`lang.DefaultCJKSection()` — КОНСТАНТА), и это известная асимметрия строки бэклога **283** | ЗАКРЫТО: формулировка исправлена в обоих местах, граница названа прямо |
|
||
| 22 | ⛔ **Проекция ценила ЧЕРНОВИК ПО ЮНИТУ, а исполнитель зовёт его ПО ЧАНКУ** (`runDraftChunk` — один рендер и один вызов на члена; `runEditUnit` — на юнит). Смещение сразу в две стороны: шаблон и инъекция едут в КАЖДОМ черновом вызове, значит счёт занижался; а выходной бюджет черновика сайзится от ОДНОГО чанка, значит `step_max_usd` — число, по которому платформа ставит минимальную покупку, — завышался в число членов. На боевом плече второе прячет модельный флор, первое — проценты; на плече без флора не прячется ничто | ЗАКРЫТО: `projectUnit` и `stepMaxForUnit` ходят по членам для черновых стадий и по юниту для остальных. Пин `TestTheDraftIsPricedPerMemberChunkAndTheEditPerUnit` (та же книга одним чанком и двумя: цена ОБЯЗАНА вырасти, шаг ОБЯЗАН не вырасти), посадка |
|
||
| 23 | ⛔ Мой новый тест строил `config.Stage` литералом — и **собственный vet-анализатор проекта его отказал** (`archguard.StageSeam`, D39.87: литерал молча роняет reasoning и temperature). В списке исключений есть файл того же класса, и добавить туда свой было бы ослаблением гейта ради удобства | ЗАКРЫТО БЕЗ ПРАВКИ ГЕЙТА: фикстура строит стадию через сам шов `config.InternalCall{…}.Stage()`, а `few_shot` ставится после — это и есть поле под тестом, которое шов намеренно оставляет нулевым |
|
||
| 24 | ⛔ **Черновые стадии не ЦЕПЛЯЛИСЬ друг за друга.** `runStageSequence` прогоняет чанк через КАЖДУЮ черновую стадию по порядку, каждая читает выход предыдущей; ничто в загрузчике не запрещает вторую translator-стадию (`escalate_to` ограничен ролью, число стадий — нет). Плоской суммой по стадиям вторая ценилась бы против ИСХОДНИКА, а черновик юнита вышел бы ВДВОЕ больше правды — и редактор получил бы вход, которого не бывает | ЗАКРЫТО: член — внешний цикл, стадии цепляются внутри него, как в `runStageSequence` |
|
||
| 25 | ⛔ **ЦЕЛЫЙ СПИСОК УТВЕРЖДЕНИЙ ОТЧЁТА НЕ ПОДКРЕПЛЯЛСЯ НИ ОДНИМ АРТЕФАКТОМ** — четвёртый круг адверсариального прохода искал именно это, и нашёл двенадцать. Главные: **(а)** всё поведение параметра `mandatory` было доводом в комментарии — каждая фикстура, где отказывали опциональному вызову, последовательна, поэтому `inFlight == 0` давал мгновенный стоп при ЛЮБОМ значении флага, и переворот всех трёх опциональных сайтов в `true` оставил бы батарею зелёной; **(б)** порядок ветвей `terminal()` — регрессия, случившаяся В ЭТОЙ СМЕНЕ, — держался комментарием; **(в)** идемпотентность кадра `ceiling` не проверял никто (единственный тест, читающий журнал при стопе, шёл на одном воркере, то есть на ОДНОМ отказе); **(г)** проводка `settleCannotHelp` (предикат запинен, `if` — нет; ⚠ НЕ ЗАКРЫТА — см. §9 п.7, состояние почти недостижимо и я это доказываю, а не обхожу); **(д)** `book_once_usd` проверялся только на собственном нуле — терминология выключена во всех фикстурах, `return 0` пережил бы всё | ЗАКРЫТО ПЯТЬЮ НОВЫМИ ТЕСТАМИ И ШЕСТЬЮ ПОСАДКАМИ. Правило «кто может ждать» ПЕРЕЕХАЛО из `if` у вызывающего В САМ ШЛЮЗ (`waitForSettle(ctx, mandatory)`, четвёртый исход `waitNotAllowed`, счётчик `optionalTurnedAway`) — именно чтобы оно оставляло после себя ФАКТ, а не комментарий |
|
||
| 26 | ⛔ **Черновые стадии не цеплялись и фолбэк «нет translator-стадии» врал по выходу.** Две translator-стадии структурно загружаются (валидация проверяет РОЛИ и не считает их); исполнитель ведёт чанк через все по очереди, каждая читает выход предыдущей. Плоской суммой вторая ценилась бы против ИСХОДНИКА, а черновик юнита вышел бы вдвое. Отдельно: конвейер БЕЗ translator-стадии — его первая стадия сама переводит, значит её выход это фертильность пары, а не число токенов исходника (занижение на весь коэффициент) | ЗАКРЫТО: `walkDraftCalls` — ОДИН обход, которым пользуются обе арифметики; два разных фолбэка для входа и выхода. Пин `TestAPipelineWithNoTranslatorStagePricesItsFirstStageAsTheTranslation` (та же книга, две калибровки фертильности — цена ОБЯЗАНА разойтись), посадка |
|
||
| 27 | ⛔ **И ПРОДОЛЖЕНИЕ НАХОДКИ 16, которое стоит отдельной строки: после того как утверждение о механизме появилось, тест стал ПАДАТЬ — под `-race`.** Он полагался на то, что два воркера успеют зарезервировать ОДНОВРЕМЕННО, а это не состояние, а везение: в логе батареи chunk 0 сеттлился раньше, чем chunk 1 вообще запрашивал допуск, — потолок не может отказать в том, о чём не спросили параллельно. ⚠ **Это ровно тот класс «краснота держится на постороннем обстоятельстве», который мне было велено искать у себя, — и поймала его НЕ я, а батарея под `-race`, потому что мои точечные прогоны шли без него.** Отдельно стоит сказать, что утверждение сработало как задумано: тест УПАЛ громко вместо того, чтобы пройти вхолостую | ЗАКРЫТО: столкновение сделано СОСТОЯНИЕМ — первый вызов удерживается на фейк-провайдере, пока шлюз не покажет очередь, и только потом отпускается. Предъявлено: `-race -count=3`, три прохода |
|
||
| 28 | ⛔ **ШЕСТАЯ ЛОЖНАЯ ЗЕЛЕНЬ, И ЭТУ Я НАШЛА САМА, ЗАПОДОЗРИВ АНОМАЛИЮ ВРЕМЕНИ.** Мой тест «футильный отказ не держит волну открытой» проходил за 5,6 с там, где по его собственной арифметике должен был упереться в 30-секундный таймаут. Разбор трассы: удерживаемый на фейк-провайдере вызов НЕ висел — он упирался в `attempt_timeout`, транспорт РЕТРАИЛ, и ретрай отвечал мгновенно (`latency_ms=5389` у вызова, который тест считал замороженным). Прогон кончался потому, что вызов завершился, три отказанных воркера УСПЕЛИ подождать и проснуться, а утверждение было про ситуацию, которой фикстура не строила | ТЕСТ УДАЛЁН, а не подкручен. Артефакт, проверяющий не то, что заявляет, ХУЖЕ отсутствующего: он читается как покрытие. На его месте — абзац, объясняющий, почему состояние почти недостижимо, и запись в §9 |
|
||
| 29 | ⛔ **ДВА МОИХ ЯКОРЯ ПРОТУХЛИ ОТ МОИХ ЖЕ ПОЗДНИХ РЕФАКТОРОВ** — `MS-wait-for-settle` (условие ожидания переехало из `if` в шлюз) и `PP-draft-priced-per-unit-not-per-chunk` (цикл по членам уехал в `walkDraftCalls`). Каталог дал бы `ROTTED`: громко, но запись при этом не утверждает НИЧЕГО, а список посадок читается как покрытие. Ровно тот класс, который приёмка прошлого пака поймала у четырёх записей | ЗАКРЫТО ПРОВЕРКОЙ, А НЕ ГЛАЗОМ: скриптом сверила все 33 своих якоря на «резолвится РОВНО ОДИН раз» в текущем дереве — два не резолвились, пере-навешены на нынешние выражения тех же свойств, остальные 31 чисты. Проверку прогнала ПОСЛЕ каждого последующего касания кода |
|
||
| 30 | ⛔ **ПЕРВЫЙ ПОЛНЫЙ ПРОГОН КАТАЛОГА ВЕРНУЛ ЧЕТЫРЕ НЕОЖИДАННЫХ ИСХОДА, И ВСЕ ЧЕТЫРЕ — МОИ.** `183 RED · 3 SURVIVED · 1 NOTHING · 0 ROTTED`. Среди выживших — **`MS-latch-is-a-cancel`, красноту которой промт назвал ОБЯЗАТЕЛЬНЫМ УСЛОВИЕМ ПРИЁМКИ.** Скажи я «сдано» по зелёной батарее, пак ушёл бы в лендинг с невыполненным named-артефактом | ЗАКРЫТО ЧЕТЫРЬМЯ РАЗНЫМИ ПОЧИНКАМИ, каждая воспроизведена руками, а не принята с вывода инструмента — разбор ниже, строки 31–34 |
|
||
| 31 | ⛔ **`MS-latch-is-a-cancel` выжила, потому что мой тест открывал ОКНО, КОТОРОГО В СЛОМАННОМ МИРЕ НЕ СУЩЕСТВУЕТ.** Он отпускал удерживаемый вызов, когда воркер ВСТАЛ В ОЧЕРЕДЬ, — но в очередь встают только в ПОЧИНЕННОМ мире; в сломанном отказ убивает волну сразу и в очередь не встаёт никто, так что триггер просто не срабатывал и утверждение читалось в обоих мирах одинаково. ⚠ **И сама посадка была неверной: починка из ДВУХ частей требует посадки из ДВУХ частей.** С ожиданием на месте одинокий `cancel()` почти безвреден — к моменту защёлки в полёте уже никого, — то есть посадка воспроизводила не до-паковый мир, а третий, которого никогда не было | ЗАКРЫТО: триггер перевешен на РЕФУЗ (`refusals ≥ 1 && inFlight ≥ 1`) — окно, существующее в ОБОИХ мирах, различает которые то, что прогон с ним ДЕЛАЕТ; посадка стала ПАРОЙ (снять ожидание И вернуть `cancel`), то есть буквально до-паковым поведением |
|
||
| 32 | ⛔ **`MS-volume-ledger-on-a-money-stop` выжила ДВАЖДЫ, по двум разным причинам, и вторая тоньше первой.** (а) Утверждение было ВЫРОЖДЕННЫМ: фикстура не давала объёмного гранта вовсе, поэтому `res.Volume` был nil при любом коде. (б) Даже с грантом посадка выжила СНОВА: `res.Volume` прикладывается МЕЖДУ волнами (`waverun.go:200-201`), а мой потолок останавливал прогон внутри ЧЕРНОВОЙ волны — путь, который его вообще не прикладывает, так что удержание было no-op и там | ЗАКРЫТО: отдельный тест `TestASpendStopReportsNoVolumeLedger` с НАСТОЯЩИМ грантом (`MaxUnits = 3`), одним воркером и потолком, рассчитанным оплатить ВСЕ черновики гранта и кончиться на ПЕРВОЙ редактуре. Предъявлено исполнением: под посадкой тест падает текстом «stopped on the VOLUME ceiling … 3 NEW unit(s) delivered» — леджер заявляет ТРИ доставленных для прогона, у которого третья редактура отказана |
|
||
| 33 | ⛔ **`PP-no-translator-stage-completion` выжила — и ВЫЖИВАНИЕ БЫЛО НАХОДКОЙ: она нашла МЁРТВЫЙ КОД.** `walkDraftCalls` возвращал размер черновика ДАЖЕ когда ни одной черновой стадии не отработало ⇒ фолбэк для конвейера без translator-стадии был недостижим, и первая стадия такого конвейера ценилась с входом-черновиком, которого она не получает. Мутация не могла изменить то, до чего не доходят, — и именно это её выживание и означало. **Каталог сработал не как гейт, а как ПРИБОР** | ЗАКРЫТО: `walkDraftCalls` возвращает 0, когда ни одна стадия не отработала; ветка стала живой |
|
||
| 34 | **`MS-optional-call-queues-for-headroom` дала `NOTHING`** — посадка удаляла блок целиком, `mandatory` становился неиспользуемым, сборка падала | ЗАКРЫТО: правка на компилирующуюся форму (`if false && !mandatory`) |
|
||
| 35 | ⛔ **И дедлок, который я сама себе устроила и сама сняла — одна нить с находкой 28, а не отдельный эпизод.** Ветка `r.Context().Done()` у фейк-провайдера и была причиной того, что «удерживаемый» вызов падал по `attempt_timeout`, а его ретрай отвечал мгновенно. Сняв её, я получила зависание `srv.Close()`, который ЖДЁТ незавершённые запросы: `defer`-ы LIFO, и `close(release)` стоял в очереди ЗА `Close()`. Пакет ВИС вместо того чтобы упасть — снова отказ в форме таймаута | ЗАКРЫТО: `defer close(release)` объявлен ПОСЛЕ `defer srv.Close()`, чтобы выполниться ПЕРЕД ним; причина записана в комментарий теста |
|
||
| 36 | Именной тест `TestUnderACeilingTheWaveFinishesWhatItAdmitted` под ПАРНОЙ посадкой падает текстом `the stop must be a ceiling halt, got … context canceled` — то есть ровно симптомом до-пакового дефекта: допущенный вызов убит отменой. Проверено прямым исполнением, а не по строке вывода инструмента | ПРЕДЪЯВЛЕНО |
|
||
| 37 | ⛔ **`MS-optional-call-queues-for-headroom` дала `NOTHING` ВТОРОЙ раз, и теперь по другой причине: посадка краснела ЗАВИСАНИЕМ.** Первая причина была сборкой; после починки посадка собиралась, но под ней опциональный вызов ПАРКОВАЛСЯ на settle, которого в юнит-тесте не бывает, — пакет упирался в таймаут, и каталог печатал «the planting did not build, or the run died», что неотличимо от нагрузки машины. **Восьмой случай того же класса за смену, и снова мой** | ЗАКРЫТО: у ожидания появилась ГРАНИЦА (`context.WithTimeout` 2 с). Предъявлено: под посадкой тест падает за 2,00 с с текстом об опциональном вызове, занявшем деньги обязательного, — вместо таймаута пакета |
|
||
| 38 | Течь резервации на ветке `json.Marshal(resp.Usage)` (возврат без release и без settle) | ПОЧИНЕНО — см. §8 |
|
||
| 39 | ⛔ **Довод «та же дверь» и утверждение в коде «discloses nothing about what any one call cost» — ОБА ЛОЖНЫ.** Довод пришёл от оркестратора и был принят мной без проверки командой; утверждение опровергнуто ЖИВЫМ ЗОНДОМ, а не рассуждением (`shortfall + ceiling − committed − reserved` восстанавливает оценку отказанного вызова с точностью до микродоллара округления) | ПОЧИНЕНО — §3; норма записана: **посылку, на которой стоит утверждение в коде, зона проверяет САМА, кем бы она ни была принесена** (F1 приёмки) |
|
||
| 40 | ⛔ **`Finished.Money` документирован правилом присутствия, которого нет:** «present only on `outcome: ceiling`». Движок вешает леджер на ПЯТИ терминальных ветвях — потолок больше не гасит соседей, и прогон, защёлкнувшийся на деньгах, уходит `failed`/`stopped`/`bank_stop`. Потребитель этого файла построил бы ровно обратное: леджер отсутствовал бы именно в тех выходах, ради которых он и нужен | ПОЧИНЕНО — правило переписано в обе стороны (`ceiling` тоже НЕ влечёт поле). ⚠ Нашли ДВА верификатора приёмки НЕЗАВИСИМО — сильнейший сигнал, какой может получить неверная фраза |
|
||
| 41 | ⛔ **Книжная часть цены печаталась как ПРОГНОЗ:** «$X of it is book-level and paid once». Терминологические проходы ограничены бюджетами, их вход не существует, пока нет черновиков ⇒ фигура — это ПОТОЛОК, который прогон не превысит. На книге в несколько глав она доминирует, и оператор читал бы короткую книгу как более дорогую за главу, чем длинную | ПОЧИНЕНО — `render.go` называет её ceiling отдельной строкой и печатает только когда она есть (F3 приёмки) |
|
||
| 42 | ⛔ **Забор убивал ОПЛАЧЕННЫЙ прогон об СОБСТВЕННЫЙ вывод движка:** намайненная дельта и авто-банк пишутся из ОТВЕТА МОДЕЛИ, а грузились строгим `ParseBankSeed` — одна негодная руна в модельном термине, и `loadMinedDelta`/`loadAutoBank` отдают ошибку наверх, материализация банка превращает её в мёртвый прогон, а человека просят руками править файл, которого он не писал | ПОЧИНЕНО — расщепление по АВТОРСТВУ: `ParseEngineBankSeed` роняет РЯД, `ParseBankSeed` по-прежнему отказывает ДОКУМЕНТУ оператора. Пин `TestAnEngineWrittenDocumentDropsTheRowInsteadOfKillingTheRun`, посадки `DF-input-identity-is-stated-not-counted` сосед. ⚠ Приёмка назвала дельту; авто-банк — тот же класс и ВЕРОЯТНЕЕ (пишется из модели КАЖДЫЙ прогон), поэтому тест утверждает правило, а не вызывающего (F8 приёмки) |
|
||
| 43 | ⛔ **История в шапке забора льстила прошлому:** «инвариант держался ПО ПОСТРОЕНИЮ, пока у банка не появился внешний писатель». Ложно: намайненный термин — не текст постороннего, но и не наш, это ВЫВОД МОДЕЛИ, и он попадает в тот же system-блок. Недоверенный писатель пришёл с майнингом, а дверь коррекций лишь сделала его ЧЕЛОВЕКОМ | ПОЧИНЕНО в тексте (код покрывает оба по построению). ⚠ Ценность правки не в коде: история, говорящая «раньше это было невозможно», зовёт следующего читателя доверять НЕ ТОЙ границе (F9 приёмки) |
|
||
| 44 | ⛔ **У сайдкара ТРИ состояния, а комментарий и код знали ДВА:** отсутствует · протух · АКТУАЛЕН И СТАРШЕ ЭТИХ ПОЛЕЙ. `price`/`structure` аддитивны, версия документа намеренно не двигалась ⇒ файл прежней сборки проходит версию, `selfConsistent` и ключ валидности, возвращается «актуальным» и цены не несёт. Ветка выше отдавала `nil`, фолбэк — тот самый, который комментарий называет инвариантом, — НЕ РАБОТАЛ, и книга молча отчитывалась без цены ровно на поверхности, которую платформа читает ПЕРЕД покупкой | ПОЧИНЕНО — живость аддитивного поля спрашивается О ПОЛЕ, а не о документе (V2-3 приёмки) |
|
||
| 45 | ⛔ **Книга платила за проход, которого не бывает:** `bookOnceUSD` складывал `classify_budget_usd` безусловно, тогда как `classify_types` гейтит саму фазу, а загрузчик требует положительный бюджет ТОЛЬКО при включённом тумблере — на поставляемой ветке это ПОЛОВИНА книжной фигуры ($1.00 из $2.00). ⚠ **И ТЕСТ ЭТОЙ ФУНКЦИИ ПИНИЛ НЕВЕРНОЕ ЧИСЛО** — гейт, защищающий дефект: ревью, читающее зелень, такого не ловит НИКОГДА | ПОЧИНЕНО, тест пере-пинен (V2-5 приёмки) |
|
||
| 46 | ⛔ **«Формат сам провёл границы» значило ВСЕ границы, а спрашивалось про ОДНУ:** структура называлась `declared`, если в файле встретился хоть один form feed — книга с пятьюдесятью главами, найденными по заголовкам, и одним случайным `\f` отчитывалась словом файла о сорока девяти ДОГАДКАХ | ПОЧИНЕНО — честная проверка в том, добавил ли путь заголовков хоть что-то: `len(parts) > 1 && len(chapters) == len(parts)` (V2-7 приёмки) |
|
||
| 47 | ⛔ **Тождество выводилось из РАВЕНСТВА ЧИСЕЛ:** `promptTokens` спрашивал `inputTokens != sourceTokens`, чтобы не зарядить исходник дважды. Два счётчика совпадают, не будучи одним текстом — пара с фертильностью около 1.0 даёт черновик размером с исходник, и стадия, которая исходник ПОЛУЧАЕТ, молча переставала за него платить | ПОЧИНЕНО — идентичность ОБЪЯВЛЯЕТ вызывающий (`inputIsSource bool`), арифметика её не знает; посадка `DF-input-identity-is-stated-not-counted` (V2-8 приёмки) |
|
||
| 48 | Ссылка на идентификатор `SourceEchoExpected`, которого в Go больше нет, и на причину, которая с тех пор изменилась (эхо-исключение сегодня покрывает ОБА банк-роля) — три носителя: `paidtail.go`, `MONEY_HONESTY_REPORT.md`, `DISCLOSURE_LAW_DESIGN.md` | ПОЧИНЕНО с НУЛЕВЫМ сдвигом строк (в эти файлы целятся ЧУЖИЕ якоря по номерам — сдвинь я строку, починил бы два своих носителя, сломав пять чужих якорей). ⚠ Носитель в `paidtail.go` якорным линтом не ловится и не поймается впредь |
|
||
| 49 | ⛔ **ЧЕТЫРЕ ПОСАДКИ ПРОТУХЛИ ОТ МОЕГО ЖЕ ДОФИКСА** (`promptTokens` получил параметр, колбэк `walkDraftCalls` — пятый аргумент, `bookOnceUSD` — гейт): `PP-source-rides-every-stage-that-asks`, `MS-source-double-charged`, `PP-draft-priced-per-unit-not-per-chunk`, `PP-book-once-charge`. Ни один гейт этого не ловит: нерезолвящийся якорь — не красная посадка, а ОТСУТСТВУЮЩАЯ | ПОЧИНЕНО — пере-нацелены; проверка гоняется после КАЖДОГО касания кода. Это уже ЧЕТВЁРТЫЙ раз за смену, и он подтверждает: класс не в невнимательности, а в том, что каталог — код БЕЗ КОМПИЛЯТОРА |
|
||
| 50 | ⛔ **«ЭТО НЕЧЕМ ПОКРЫТЬ» — И ЭТО БЫЛА ОТГОВОРКА.** Правку F3 (`render.go`) я СНАЧАЛА ОБЪЯВИЛА в отчёте непокрываемой: «носитель — текст, красить нечем, проверено чтением». Держалось ровно до попытки: `renderManifest` принимает `io.Writer` и манифест, тест пишется в двадцать строк без стенда и без денег — а на эту функцию не было ни одного теста ВООБЩЕ | ПОЧИНЕНО — `TestTheHumanManifestCallsTheBookLevelFigureACeiling` + две посадки (сказать неправду · сказать лишнее). Класс записан как есть: **«нечем покрыть» — самая дешёвая из неправд, какие смена пишет о себе, и от честной её отличает ОДНА ПОПЫТКА** |
|
||
| 51 | ⛔ **МОЙ СОБСТВЕННЫЙ ДОФИКС ПРОМЕНЯЛ ГРОМКУЮ СМЕРТЬ НА ТИХОЕ ИСЧЕЗНОВЕНИЕ.** Расщепление по авторству (запись 42) роняет негодный ряд вместо отказа документу — и роняло МОЛЧА: намайненный термин пропадал из всех запросов без следа, а оператор, видящий его в файле и не видящий в переводе, не имел ЧЕГО прочитать. Это ровно тот класс, о котором предупреждает шапка CLAUDE.md: обход тихо становится нормой | ПОЧИНЕНО — `BankSeed.Dropped` выносит имена наверх (загрузчик логгера не имеет и не должен), `Runner.warnDroppedRows` говорит рядом с уже существующим предупреждением о ДЕКЛИНЕ; пин расширен, посадка `DF-a-dropped-row-is-not-silent`. ⚠ Нашла своим же чтением дофикса, а не приёмкой: **починка, у которой обе половины не проверены, — половина починки** |
|
||
| 52 | ⛔ **ЧИСЛО В КОММЕНТАРИИ, КОТОРОГО НИКТО НЕ МЕРИЛ.** Комментарий записи 45 утверждал: «на поставляемой ветке это ПОЛОВИНА книжной фигуры ($1.00 из $2.00)». Проверено командой: `configs/pipeline-c1.yaml:170` = `classify_types: true` ⇒ на поставляемом конфиге фаза ИСПОЛНЯЕТСЯ, бюджет тратится по-настоящему, и дефект там не проявляется вовсе | ПОЧИНЕНО — фраза снята, вместо неё сказано, ГДЕ дефект кусает: конфиг с ВЫКЛЮЧЕННЫМ тоглом и оставшимся от прежней редакции бюджетом (загрузчик перестаёт валидировать ключ ровно тогда, когда фаза выключена). Дефект настоящий, ветка — нет. ⚠ Пришло от оркестратора через внешнего ревьюера; проверила своей рукой прежде, чем править (норма записи 39) |
|
||
| 53 | ⛔ **РАТИФИКАЦИЯ, НА КОТОРУЮ ОПИРАЛОСЬ УТВЕРЖДЕНИЕ В КОДЕ, ОТОЗВАНА — а само утверждение было сужением ЧУЖОГО решения.** ПТ-35 — решение владельца, и 05.09, снимая «деньги на экране», он оставил дословно «цены моделей, стоимость стадий И ВЫЗОВОВ»; промт пака применяет запрет к ПРОВОДУ (строка 78), а не к экрану ⇒ живой замер верификатора попал в ПОСЫЛКУ ЗАКАЗА, а не в реализацию. Плюс овер-атрибуция: цитируемой ратификации «D39.106 §2» в журнале нет (D39.106 — про транзиентный юнит и `events.jsonl`) | ЗАКРЫТО: сперва помечено `pending owner` (поведение не тронуто), затем РАТИФИЦИРОВАНО владельцем — `D39.203`, «Можно, в форме „сколько добавить“»; шапка поля указывает на ноту, различение оценка/стоимость стало опорой решения. К вопросу владельца добавлено то, чего не видели ни оркестратор, ни я: **тензия внутри самого заказа** — п.(3) запрещает цене вызова выходить, п.(4) требует опубликовать `step_max_usd`, оценку САМОГО ДОРОГОГО одиночного вызова. ⚠ И обе величины — до-вызовные ОЦЕНКИ, а не сеттлмент: что вызов СТОИЛ, не раскрывает ни одна |
|
||
| 54 | ⛔ **ПОЧИНКУ V2-7 НЕ ДЕРЖАЛ НИ ОДИН ТЕСТ — предъявила ВЫЖИВШАЯ ПОСАДКА, а не чтение.** В `internal/chunk/*_test.go` слова `Structure` не было вовсе; два случая в `priceprojection_test.go:144-146` — КРАЙНИЕ (только form feed → `declared`, только заголовки → `detected`), а СМЕШАННОГО — одного случайного `\f` среди найденных заголовками глав — не держал никто. Ровно в нём дефект и жил | ПОЧИНЕНО — `TestTXTStructureIsDeclaredOnlyWhenTheFormatDrewEVERYBoundary` (вся линейка из трёх), посадка красна именно подтестом `one_stray_form_feed_among_matched_headers`. ⚠ Класс: **починка, у которой есть посадка, но нет теста, выглядит как покрытая ровно до полного прогона каталога** |
|
||
| 55 | ⛔ **МОЯ СОБСТВЕННАЯ ПРОВЕРКА «отличаются только комментариями» ДАЛА ЛОЖНУЮ ЗЕЛЕНЬ.** Утилита-стриппер не собралась (`error obtaining VCS status`), оба выхода вышли ПУСТЫМИ, `diff` сравнил пустоту с пустотой и напечатал «ИДЕНТИЧНО» — и я чуть не оперлась на это, доказывая законность склейки двух прогонов | ПОЧИНЕНО — размер выхода печатается рядом с вердиктом, пустой выход стал отдельной красной ветвью; результат пере-снят и верен (5020/5020 и 2710/2710 байт). ⚠ Тот же класс, что §6.4, но теперь на ИНСТРУМЕНТЕ ПРОВЕРКИ: **проверка, которая не умеет провалиться, ничего не проверяет** |
|
||
| 56 | ⛔ **ДВА КОММЕНТАРИЯ ОДНОГО КОММИТА ПРОТИВОРЕЧИЛИ ДРУГ ДРУГУ О ФОЛБЭКЕ ЦЕНЫ (F7 приёмки).** `manifest.go` объявлял фолбэк ИНВАРИАНТОМ («без него книга без сайдкара молча отчитывается без цены»), а `status.go` в шапке того же поля утверждал обратное: «absent when there is no current manifest — у фолбэка есть текст, но нет гарантии сайдкара, что он описывает ЭТУ резку». Второе описывало код ровно до того момента, как ТОТ ЖЕ коммит добавил фолбэк и сюда не вернулся. Потребитель построил бы «нет сайдкара ⇒ нет цены» и показал бы покупателю пустоту на той самой поверхности, с которой тот и спрашивает | ПОЧИНЕНО — шапка `StatusReport.Price` пере-написана: цена есть и БЕЗ сайдкара; совпадение двух поверхностей держится тем, что оба конца гоняют ОДНУ деривацию (`readModelPrice` → `projectBook`), а не тем, что один отказывается отвечать; и фолбэк описывает эту резку ПО ПОСТРОЕНИЮ, а не по гарантии. ⚠ Правка комментарная, предъявлено машинно: `status.go` без комментариев побайтно идентичен (20012 vs 20012) |
|
||
| 57 | ⛔ **ЯКОРНЫЙ ЛИНТ ДОКОВ Я НЕ ГОНЯЛА НИ РАЗУ ЗА СМЕНУ, а свой — после каждого касания кода.** Проверяла `mutations.json` собственным скриптом и не заметила, что у зоны есть ВТОРОЙ корпус якорей и готовый гейт на него (`docs/scripts/counts.py --lint`, зовётся пре-коммит-хуком при ЛЮБОМ коммите с непустым индексом). На дереве он показывал **6 битых якорей в `backend/docs/`**, из них 5 — долг зоны, старше моего пака (замерено: `RebillBasis string` стоял на 275 ещё в `81a89e9^`, а якорь звал 249), и 1 — свежая, от переноса промта в архив | ПОЧИНЕНО — все шесть пере-нацелены по СОДЕРЖИМОМУ, линт `0 проблемных якорей в 113 живых доках`, сверяемых по токену стало 194 → 200. ⚠ Класс: **у зоны было ДВА множества якорей и одна привычка** — та, которую я завела сама, а не та, под которую в репозитории уже стоял гейт. Собственная дисциплина вытеснила существующую |
|
||
| 58 | ⛔ **МОЯ ФОРМУЛИРОВКА ЛЕЧЕНИЯ КЛИНА НАЗЫВАЛА СМЕНОЙ ТО, ЧТО ЕЮ НЕ ЯВЛЯЕТСЯ.** §9 п.1 предлагал «деградацию отказа резервации на attempt 0 (**смена D2.3**)» — а `D2` УЖЕ кончается флагом («ретрай с бо́льшим `max_tokens` до капа, потом флаг»), и денежная ветка до него просто не доходит: ошибка уходит наверх из цикла попыток раньше. То есть это ДОСТРОЙКА существующей диспозиции, а не правка ратифицированного — разница решает, нужна ли ратификация вообще | ПОЧИНЕНО — сверено по телу ноты, формулировка заменена, добавлено требование оркестратора: **причина флага обязана ОТЛИЧАТЬСЯ от исчерпания регенераций**, иначе пометка врёт о причине (`D39.204`, строка бэклога 291). ⚠ Поймал оркестратор чтением журнала; я предлагала правку ратифицированной ноты, не открыв её тела |
|
||
|
||
### 6.4. ⛔ ЧЕТВЁРТЫЙ КЛАСС ЛОЖНОЙ ЗЕЛЕНИ, найденный в этой смене
|
||
|
||
Выношу отдельно, потому что он не про мой пак, а про инструмент, и следующая смена наступит на него так
|
||
же.
|
||
|
||
**Посадка, чей отказ выглядит как ТАЙМАУТ, неотличима от нагрузки — и потому не является посадкой.**
|
||
⚠ **`SURVIVED` — НЕ ОДИН ВЕРДИКТ, А ТРИ РАЗНЫХ, и различать их обязательно** (разведение оркестратора,
|
||
которое моя формулировка «выживание ЕСТЬ сообщение о недостижимости» и позволила):
|
||
1. **посадка эквивалентна оригиналу** — дефект ПОСАДКИ, гарантия цела;
|
||
2. **посадка не воспроизводит прежний мир ЦЕЛИКОМ** — починка из двух частей, а вернули одну (см. 31);
|
||
3. **посадка недостижима, потому что код МЁРТВ** — находка о КОДЕ, добытая прибором (см. 33).
|
||
Только третий род приносит дефект; первые два его прячут. Читать зелёное как «гарантия держит» нельзя ни
|
||
в одном из трёх.
|
||
|
||
⚠ **И шестая форма, найденная последним прогоном: ТРИГГЕР, СУЩЕСТВУЮЩИЙ ТОЛЬКО В ПОЧИНЕННОМ МИРЕ.** Тест,
|
||
который ждёт состояния, чтобы что-то проверить, обязан ждать состояния, ДОСТИЖИМОГО И В СЛОМАННОМ МИРЕ —
|
||
иначе он не отличает миры, а просто не запускается в одном из них. Различать их должно то, что прогон с
|
||
этим состоянием ДЕЛАЕТ. Подкласс того же: **починка из двух частей требует посадки из двух частей** —
|
||
вернув одну половину, воспроизводишь не прежний мир, а третий, которого никогда не было. ⚠ Этот подкласс
|
||
сформулирован ОРКЕСТРАТОРОМ 04.09 на его собственной ошибке («oldest wins») и ПОДТВЕРЖДЁН здесь вторым
|
||
независимым случаем — `MS-latch-is-a-cancel`. Атрибуция важна не для заслуг: со вторым случаем это
|
||
перестало быть анекдотом и стало классом, и следующий читатель должен видеть, сколькими случаями норма
|
||
куплена.
|
||
|
||
Механика: `t.Fatalf`, вызванный из ПОРОЖДЁННОЙ горутины, делает `runtime.Goexit` не на той горутине.
|
||
Утверждение не записывается, помощник молча умирает, а прогон, который он должен был разблокировать,
|
||
висит до таймаута ПАКЕТА. Мой `waitForGate` был написан именно так. Под посадкой, делающей ожидаемое
|
||
состояние недостижимым (`MS-wait-for-settle`, `MS-latch-is-a-cancel`), каталог получил бы не `RED`, а
|
||
`NOTHING` или таймаут — и в этой смене я списала бы его на нагрузку машины с полным основанием: `Makefile`
|
||
зоны сам документирует разброс `internal/pipeline` от ~95 с на тихом хосте до ~560 с при трёх параллельных
|
||
агент-сессиях, и таймауты здесь уже были средой.
|
||
|
||
⚠ **И его близнец, пойманный в этой же смене на этом же тесте: посадка, чья КРАСНОТА держится на
|
||
везении.** Стоп-утверждение «ожидание сработало» я добавила, и тест стал зелёным — на моих точечных
|
||
прогонах без `-race`. Батарея под `-race` его уронила: два воркера успевали резервировать одновременно не
|
||
всегда, а потолок не может отказать в том, о чём не спросили параллельно. Утверждение отработало верно
|
||
(упало громко), но фикстура строила НАДЕЖДУ, а не состояние. Лечение то же, что и у первой находки:
|
||
удерживать вызов на фейк-провайдере, пока шлюз не покажет очередь.
|
||
|
||
⚠ **И ПЯТАЯ ФОРМА, найденная в этой же смене у меня же: посадка, чей ЯКОРЬ не резолвится.**
|
||
`ROTTED` — вердикт громкий, но он звучит только когда каталог прогнан ЦЕЛИКОМ. До этого запись
|
||
существует и **не утверждает ничего**, а ДЛИНА СПИСКА посадок читается как покрытие — то есть дефект
|
||
прячется ровно там, куда смотрят, оценивая полноту. У меня протухли два якоря, и оба сломала я сама
|
||
поздними рефакторами. Норма, которую это купило: **посадка, чей якорь не резолвится, — не красная
|
||
посадка, а отсутствующая; проверка «каждый якорь резолвится РОВНО ОДИН раз» гоняется после КАЖДОГО
|
||
касания кода, а не перед сдачей.** Это уже третий носитель класса: приёмка прошлого пака нашла тем же
|
||
вопросом четыре протухшие записи.
|
||
|
||
Это рядом с тремя уже известными формами: **упавшая сборка** (`WF-length-bound` в первой редакции: константа
|
||
`1 << 30` съедала память в `strings.Repeat` тестовой фикстуры — каталог сказал `NOTHING`), **вырожденное
|
||
утверждение** (`WF-version-fold-scope`: тест сравнивал два разных набора строк и доказывал лишь, что они
|
||
разные) и **эквивалентная посадка**. Общее у всех четырёх: цвет каталога перестаёт быть свидетельством о
|
||
коде.
|
||
|
||
**Практическое следствие, которое стоит держать:** прежде чем поверить `RED`, стоит проверить, что тест,
|
||
который покраснел, краснеет ПО ТОЙ ПРИЧИНЕ; прежде чем поверить `SURVIVED` — что посадка вообще меняет
|
||
поведение; а `NOTHING` и таймаут читать как «инструмент не ответил», а не как «дефекта нет».
|
||
|
||
### 6.5. Каталог мутаций — ПРОГНАН ЦЕЛИКОМ, И НЕ СОШЁЛСЯ С ПЕРВОГО РАЗА
|
||
|
||
```
|
||
$ cp -a backend /tmp/mutF/backend && cd /tmp/mutF/backend
|
||
$ GOFLAGS=-buildvcs=false go run ./cmd/tmmutate -root .
|
||
201 mutation(s) run, 1 unexpected outcome(s)
|
||
tmmutate: not what the catalogue records: DF-declared-means-every-boundary
|
||
MUT_EXIT=1
|
||
```
|
||
|
||
**201 запись · 196 RED · 4 записанных `survives` · 1 НЕОЖИДАННЫЙ ВЫЖИВШИЙ · 0 NOTHING · 0 ROTTED.**
|
||
Арифметика сходится: 196 + 4 + 1 = 201.
|
||
|
||
⛔ **ВЫЖИЛА ПОСАДКА `DF-declared-means-every-boundary` — И ЭТО НАСТОЯЩАЯ НАХОДКА, А НЕ ШУМ.** Она
|
||
восстанавливает дефект `V2-7` (структура зовётся `declared`, если form feed просто ВСТРЕТИЛСЯ), и её
|
||
выживание означает ровно одно: **починку V2-7 не держал НИ ОДИН тест.** В `internal/chunk/*_test.go` слово
|
||
`Structure` не встречалось вовсе, а два случая, которые есть в `priceprojection_test.go:144-146`, —
|
||
крайние (только form feed → `declared`, только заголовки → `detected`). **СМЕШАННОГО случая — одного
|
||
случайного `\f` среди найденных заголовками глав — не держал никто, а он и есть тот, в котором дефект
|
||
жил.** Дописан `TestTXTStructureIsDeclaredOnlyWhenTheFormatDrewEVERYBoundary` (три случая, вся линейка), и
|
||
посадка предъявлена КРАСНОЙ на исправленном дереве — краснеет именно подтест
|
||
`one_stray_form_feed_among_matched_headers`.
|
||
|
||
**На сдаваемом дереве: 201 запись · 197 RED · 4 записанных выживших · 0 неожиданных.**
|
||
|
||
⚠ **И ЭТО СКЛЕЙКА ИЗ ДВУХ ПРОГОНОВ — говорю прямо, потому что «прогнано целиком» звучит сильнее.**
|
||
Полный проход шёл на замороженной копии; после него дерево изменилось трижды (тест выше плюс две
|
||
КОММЕНТАРНЫЕ правки по отозванной ратификации, записи 52–53). Что склейка законна, **предъявлено
|
||
механически, а не рассуждением**:
|
||
```
|
||
$ diff -rq /tmp/mutF/backend backend # различаются РОВНО четыре файла:
|
||
docs/MONEYSTOP_REPORT.md · internal/chunk/ingest_test.go
|
||
internal/pipeline/priceprojection.go · internal/runevents/runevents.go
|
||
# каждый .go пере-печатан из AST, разобранного БЕЗ ParseComments:
|
||
$ strip <frozen>/internal/pipeline/priceprojection.go | diff - <(strip backend/…) # ИДЕНТИЧНО (5020 vs 5020 байт)
|
||
$ strip <frozen>/internal/runevents/runevents.go | diff - <(strip backend/…) # ИДЕНТИЧНО (2710 vs 2710 байт)
|
||
```
|
||
⇒ два файла отличаются ТОЛЬКО комментариями (исполняемого не изменилось ничего), отчёт не компилируется,
|
||
а новый тест живёт в `./internal/chunk/`, где посадка ровно ОДНА — та самая, что и пере-прогнана. То есть
|
||
пере-считано в точности то множество, чей вердикт мог измениться, и ни одной записи не унаследовано от
|
||
дерева, которого больше нет.
|
||
|
||
⚠ **И проверка «отличаются только комментариями» с первого раза была ЛОЖНО ЗЕЛЁНОЙ:** утилита не
|
||
собралась (`error obtaining VCS status`), оба выхода вышли ПУСТЫМИ, и `diff` сравнил пустоту с пустотой,
|
||
напечатав «ИДЕНТИЧНО». Поймано тем, что размер выхода печатается рядом с вердиктом; в переделанной
|
||
проверке пустой выход — отдельная красная ветка. **Проверка, которая не умеет провалиться, ничего не
|
||
проверяет** — тот же класс, что и §6.4, теперь на моём собственном инструменте проверки.
|
||
|
||
Записанные выжившие — четверо, и каждый с аргументом в `why`: `G-byte-gate` и
|
||
`WB62-innocent-const-must-not-be-accused` (чужие, аргументированы прежними паками) плюс мои два:
|
||
· **`MS-reserve-under-the-gate`** — гонка «Reserve закоммичен, счётчик ещё нет» структурна, но
|
||
воспроизводима только с хуком в планировщик, которого в репозитории нет; в `why` записано ПРЯМО, чего
|
||
эта зелень НЕ значит: «не „гарантия держит“, а „инструментом не проверяемо“».
|
||
· **`MS-reservation-released-on-marshal-failure`** — ветка недостижима по контракту `encoding/json`
|
||
(структура из `int` не может не смаршалиться), и покраснить её нечем без подмены стандартной
|
||
библиотеки.
|
||
**Моих записей 43, из них 41 RED и 2 аргументированных выживших.**
|
||
|
||
⚠ **И ЭТО ТРЕТИЙ ПОЛНЫЙ ПРОГОН ЗА СМЕНУ. ПЕРВЫЙ БЫЛ ЧЕСТНЫМ, ПОЛНЫМ — И НЕ СОШЁЛСЯ:** `183 RED ·
|
||
3 SURVIVED · 1 NOTHING`, четыре неожиданных исхода, все мои, включая посадку, красноту которой промт
|
||
назвал ОБЯЗАТЕЛЬНЫМ УСЛОВИЕМ ПРИЁМКИ (записи 30–34, 37). Второй сошёлся (191/187). Третий, вот этот, —
|
||
снова НЕТ. Норма, которую смена купила трижды: **«каталог прогнан целиком» и «каталог прогнан целиком И
|
||
СОШЁЛСЯ» — разные утверждения, и первое звучит как второе.**
|
||
|
||
---
|
||
|
||
### 6.6. Файлы
|
||
|
||
**Новые:** `internal/membank/{wirefence,wirefence_test}.go` · `internal/pipeline/{reservegate,priceprojection,priceprojection_test,moneystop_test}.go` · `docs/MONEYSTOP_REPORT.md`.
|
||
**Изменены:** `internal/membank/{memory,decisions,memseed,memvoice}.go` · `internal/chunk/ingest.go` ·
|
||
`internal/pipeline/{waverun,stagerun,escalation,repair,terminologist,events,manifest,status,runner,bookrun,mining,bankdecisions,paidtail}.go` ·
|
||
`internal/runevents/runevents.go` · `cmd/tmctl/{main,render}.go` · `cmd/tmmutate/mutations.json` (158 → 201) ·
|
||
тесты `internal/pipeline/{runevents_test,wavepanic_test,volumepanic_test,contractblockers_test}.go` и
|
||
`cmd/tmctl/render_test.go` и `internal/chunk/ingest_test.go` (два существующих тестовых файла, в каждый
|
||
ДОБАВЛЕНА функция — §6.2) ·
|
||
доки зоны `docs/{MONEY_HONESTY_REPORT,DISCLOSURE_LAW_DESIGN}.md` (снятая ссылка, запись 48).
|
||
|
||
⚠ **`git status` этого дерева показывает ещё файлы ВНЕ моей зоны, и это АРТЕФАКТ ОТЦЕПЛЕННОГО HEAD, а не
|
||
чья-то незакоммиченная работа.** Верификатор приёмки сделал `checkout` в общем дереве, поэтому HEAD стоит
|
||
на `9cac062`, а ветка `main` ушла вперёд. Следствие: `docs/architecture/{15-money-path,17-seam-inbound-law}.md`
|
||
(пере-нацеливание якорей оркестратора — `stagerun.go:496→495`, `525→559`, `status.go:835→858`,
|
||
`events.go:404→409`, уехавших от моего же диффа) числятся «изменёнными» относительно отцепленной линии,
|
||
хотя их содержимое уже РАВНО `main` — проверено: `git diff --stat main -- docs/architecture/` пусто.
|
||
⚠ И `docs/PROGRESS.md` в этом дереве, наоборот, СТАРШЕ `main` на две строки — то есть коммит этого пути
|
||
отсюда откатил бы чужую правку. Не трогала ни одного из них; лендить их из этого дерева НЕЛЬЗЯ.
|
||
**Моя работа не затронута:** `backend/` между `81a89e9` и `main` идентичен (`git diff --stat 81a89e9 main -- backend` пусто),
|
||
поэтому все диффы этого отчёта против `HEAD` для `backend/` верны.
|
||
|
||
### 6.7. Команды приёмки — каждое число выше получено одной из них
|
||
|
||
```
|
||
cd backend && make battery
|
||
go test ./internal/membank/ -run 'TestAUserTerm|TestTheDoor|TestTheSeedLoader|TestTheLengthBound|TestTheFenceStops|TestTheFenceMoves' -v
|
||
go test ./internal/pipeline/ -run 'TestUnderACeiling|TestARefusalWithNothing|TestTheGateWaits|TestSettleCannotHelp|TestTheShortfallIs|TestTheStoppedRunTells|TestResumeAfter|TestAMoneyStopInTheDraft|TestNothingLeaks|TestWaitingForHeadroom|TestACancelledWait|TestAnInfraFailure' -v
|
||
go test ./internal/pipeline/ -run 'TestTheManifestPrices|TestTheProjection|TestTheCutSays|TestTheStoredManifest|TestARunRewrites|TestStepMaxCounts|TestManifestServesTheReadModels' -v
|
||
go test ./internal/pipeline/ -run 'TestACeilingHaltAnnounces|TestWaveWorkerPanic|TestACrashOverACaughtCeiling|TestRunWaveSurfacesParentCancellation|TestRunnerEscalationCeilingDegrades|TestWaveParallelWorkersMoneyConserved' -v
|
||
|
||
# КРАСНЫЙ до починки — на копии дерева HEAD 32be78a (пункт 1)
|
||
git archive HEAD backend | tar -x -C /tmp/headtree && cp backend/internal/membank/wirefence_test.go /tmp/headtree/backend/internal/membank/
|
||
cd /tmp/headtree/backend && GOFLAGS=-buildvcs=false go test ./internal/membank/ -run 'TestAUserTerm|TestTheDoor|TestTheSeedLoader' -v
|
||
|
||
# каталог мутаций ЦЕЛИКОМ, на копии
|
||
cp -a backend /tmp/mut/backend && cd /tmp/mut/backend && GOFLAGS=-buildvcs=false go run ./cmd/tmmutate -root .
|
||
|
||
# дифф тестов ИСПОЛНЕНИЕМ
|
||
git diff -U0 -- backend | grep -E '^-func Test' # пусто: ничего не удалено и не переименовано
|
||
grep -rh '^func Test' backend --include=*_test.go | wc -l # 1213 (+41 к 1172 до пака)
|
||
python3 -c "import json;print(len(json.load(open('backend/cmd/tmmutate/mutations.json'))))" # 201 (158 + 43)
|
||
|
||
# посадки дофикса (пункт 1 приёмки + записи 42/47)
|
||
go test ./internal/membank/ -run 'TestAnEngineWrittenDocument' -v
|
||
go test ./internal/pipeline/ -run 'TestASidecarFromAnOlderBuild|TestTheMoneyLedgerFollowsTheCeiling' -v
|
||
go test ./cmd/tmctl/ -run 'TestTheHumanManifestCallsTheBookLevelFigureACeiling' -v
|
||
go test ./internal/chunk/ -run 'TestTXTStructureIsDeclaredOnly' -v
|
||
|
||
# ⛔ ЯКОРЯ ПОСАДОК — после КАЖДОГО касания кода, а не перед сдачей (запись 49):
|
||
# каждая пара (file, find) обязана встречаться в дереве РОВНО ОДИН раз; иначе посадка не красная, а отсутствующая
|
||
```
|
||
|
||
## 7. Правки существующих тестов — объявление по D39.183
|
||
|
||
Все три вызваны сменой поведения, ЗАКАЗАННОЙ паком, ни одна не сделана «чтобы прошло».
|
||
|
||
**(1) `runevents_test.go` — пин «the ceiling event must carry the fact and no figures».**
|
||
· *Что изменилось в поведении:* кадр `ceiling` теперь несёт `shortfall_micro_usd` (заказ §4(3) и строки 278).
|
||
· *Что описывал тест:* ратифицированную норму «деньги не выходят из движка иначе как в `spend`»
|
||
(D39.106 §2/ПТ-33).
|
||
· *Куда уехала гарантия:* она СУЖЕНА, не снята. Кадр проверяется по БЕЛОМУ СПИСКУ ключей
|
||
(`halted|scope|shortfall_micro_usd`) — любое новое денежное поле краснеет; запрещён любой отрендеренный
|
||
`$`; и добавлено новое утверждение, которого не было: book-стоп ОБЯЗАН назвать недостачу, day-стоп
|
||
обязан её НЕ называть. ⚠ Оркестратор берёт ноту о сужении на себя (акт приёмки): сужение
|
||
ратифицированной фразы — ратификационное действие, а не обслуживание.
|
||
|
||
**(2) `wavepanic_test.go` и (3) `volumepanic_test.go` — оба ВИСЛИ.**
|
||
· *Предъявление, а не утверждение:* `panic: test timed out after 1m30s`, `FAIL … 90.048s`.
|
||
· *Что изменилось:* оба парковали падающий воркер на `<-ctx.Done()` и рассчитывали, что отмену вызовет
|
||
ПОТОЛОК. Потолок больше не отменяет — это и есть заказ пака §4(2).
|
||
· *Куда уехала гарантия:* никуда. Утверждения не ослаблены — краш и потолок по-прежнему в РАЗНЫХ слотах
|
||
и оба выходят из волны; ушёл только ритуал упорядочивания, который с двумя слотами ничего не решает.
|
||
Упорядочивание, которое ещё решает — краш против ИНФРА-ошибки, она отменяет, — пинится там же через
|
||
`ctx.Done()` **и предъявлено КРАСНЫМ** посадкой `MS-crash-outranks-the-wave-error` (§6.5).
|
||
· *Попутно:* при первой переписке `volumepanic` тест ПРОВАЛИЛСЯ, потому что под защёлкой кормилец не
|
||
успевал раздать второй элемент — падающий воркер обязан ДЕРЖАТЬ свой элемент до возврата halt. Это
|
||
поведение защёлки, а не дефект; в тест внесена явная синхронизация и комментарий.
|
||
|
||
---
|
||
|
||
## 8. Правка ВНЕ ЗАКАЗА, взятая по своему усмотрению
|
||
|
||
**Течь резервации на ветке `json.Marshal(resp.Usage)`** (`stagerun.go`): ветка возвращалась без
|
||
`releaseReservation` и без settle, оставляя оценку в `reserved_usd` до следующего `store.Open`.
|
||
· *Довод:* это денежный путь, и у него появился ВТОРОЙ читатель — утёкшая резервация завышает недостачу,
|
||
которую прогон теперь публикует, то есть движок попросил бы у человека больше, чем ему нужно.
|
||
· *Честная граница:* ветка практически недостижима — `json.Marshal` структуры из `int` не может упасть.
|
||
Поэтому посадка `MS-reservation-released-on-marshal-failure` занесена в каталог **как аргументированный
|
||
выживший**, а не как находка: покраснить её без подмены стандартной библиотеки нечем, и я не стала
|
||
выдумывать доказательство. Недостижимость — то, что перестаёт быть правдой от одной правки соседа.
|
||
|
||
---
|
||
|
||
## 9. Obstacle reporting — что НЕ удалось, что не проверено, что осталось гипотезой
|
||
|
||
1. ⛔ **Клин юнита НЕ починен, и это признанный долг, а не забытое.** Отказ РЕЗЕРВАЦИИ на ретрае не
|
||
деградирует И не оставляет следа: он уходит наверх из цикла попыток (`stagerun.go:164-167`) РАНЬШЕ
|
||
`UpsertChunkStatus` (`stagerun.go:285`), так что чекпойнт attempt 0 есть (реплей за $0), а строки
|
||
`chunk_status` нет — читающие модели видят `pending`. Если потолок покупки не вмещает attempt 1 с
|
||
удвоенным бюджетом, юнит не доделается НИКОГДА, а прогон уходит exit 4 при полностью доставленных
|
||
купленных юнитах. Мой `step_max_usd` это НАЗЫВАЕТ (ретраи в нём именно поэтому), `Finished.Money`
|
||
называет доставленное и отложенное — но клин остаётся. Лечение: (а) деградация отказа ретрая во флаг
|
||
attempt 0 — ⚠ **и это НЕ смена D2, как я записала сперва:** тело ноты
|
||
`D2` («ретрай с бо́льшим `max_tokens` до капа, ПОТОМ ФЛАГ») уже кончается флагом, а денежная
|
||
ветка до него не доходит — ошибка уходит наверх из цикла попыток раньше. Значит это ДОСТРОЙКА
|
||
существующей диспозиции, а не её правка, и требование к ней одно: **причина флага обязана
|
||
ОТЛИЧАТЬСЯ от исчерпания регенераций**, иначе пометка врёт о причине (`D39.204`, строка 291) — либо (б) минимальная покупка платформы ≥ `step_max_usd`. Выбор не мой и не
|
||
оркестратора; строка бэклога с ОБОИМИ путями — в моей секции `docs/PROGRESS.md`.
|
||
2. ⚠ **⭐-уточнение оркестратора («защёлка на свежем, продолжение на ретрае») ОТКЛОНЕНО, и не по цене.**
|
||
Его посылка — «текст у юнита ЕСТЬ, он лишь флагован» — ложна: см. п. 1, строки `chunk_status` нет,
|
||
для читателя это такой же ПРОБЕЛ. Различитель дёшев (номер попытки есть прямо в точке отказа), но
|
||
покупает не то, ради чего предлагался.
|
||
3. ⚠ **Цена защёлки НЕ ЗАМЕРЕНА, только выведена.** Что юниты, которые влезали, не начинаются — следует
|
||
из удвоения бюджета ретрая и из замеренного разбором 04.09 разброса резервов 2.976× внутри волны. На
|
||
реальной книге я это не мерила: пак не включает платного прогона.
|
||
4. ⚠ **Числа проекции не сверены с фактом на реальной паре.** `expected_usd` строится из фертильности
|
||
`zh-ru` и цен `models.yaml`; насколько он попадает в настоящий счёт — вопрос замера, а не чтения. То,
|
||
что предъявлено: он не константа, растёт с длиной главы, катится в сумму по главе и книге, и
|
||
`step_max_usd` не меньше резервации, которую исполнитель делает для первого вызова.
|
||
5. ⚠ **Порог «сеттлить ли поздно отменённый вызов оценкой» НЕ выставлен, и это сознательный отказ.**
|
||
Промт разрешал латентность кандидатом при условии «порог обязан быть обоснован ЗАМЕРОМ, а не
|
||
назначен». Замера нет, платный прогон паком не выдан ⇒ порог назначить нечем, и я его не назначала.
|
||
6. ⛔ **ПРОВОДКА `settleCannotHelp` НЕ ПРОВЕРЕНА, и я показываю, ПОЧЕМУ она почти ничего не решает, а не
|
||
отговариваюсь.** Предикат запинен своим тестом; `if`, который его спрашивает, — нет.
|
||
⚠ **И здесь надо развести ДОСТИЖИМОСТЬ и ЭФФЕКТ — первую редакцию этого абзаца я написала неверно, и
|
||
поправил меня оркестратор по строке кода.** Предикат стоит ПЕРЕД входом в ожидание
|
||
(`stagerun.go:530-531`), поэтому когда он ИСТИНЕН, полёт не спрашивают вовсе: путь достижим при любом
|
||
состоянии полёта, включая пустое. Утверждать «достижим только при непустом» — значит утверждать
|
||
недостижимость состояния, которого код не держит, и следующая сессия построила бы на этом пин.
|
||
Верно другое: **он что-то МЕНЯЕТ только при непустом полёте** — при пустом книгу и без него
|
||
останавливает `waitNothingInFlight`. А состояние «отказ, который никакой settle не поднимет, ПРИ
|
||
непустом полёте» почти недостижимо. Но вызов в полёте сам был допущен при том же `committed` и сопоставимой оценке — если
|
||
место нашлось ему, оно найдётся и этому: два условия противоречат друг другу, пока оценки не разойдутся
|
||
существенно (разнородные размеры чанков, либо черновик и редактура в одной волне — а они не встречаются
|
||
никогда). А когда полёт пустеет, книгу останавливает `waitNothingInFlight` и без предиката. ⇒ он
|
||
ОПТИМИЗАЦИЯ (не держать волну открытой весь провайдерский таймаут ради известного ответа), а не
|
||
несущая гарантия, и в §2 он теперь описан так.
|
||
7. ⛔ **ЧТО ТРИ ОПЦИОНАЛЬНЫХ САЙТА ПЕРЕДАЮТ ИМЕННО `false` — не прибито ничем, и я это НАЗЫВАЮ, а не заявляю.**
|
||
Правило «кто может ждать» теперь живёт в шлюзе и запинено тестом с обеих сторон (опциональный
|
||
отворачивается со счётчиком, обязательный ждёт и просыпается), плюс посадкой. Но АРГУМЕНТ, который
|
||
`escalation.go`, `repair.go` и `terminologist.go` передают в этот шлюз, проверяется только компилятором
|
||
и комментарием у каждого вызова. Недостающий артефакт назван точно: интеграционная фикстура, в которой
|
||
ОПЦИОНАЛЬНЫЙ вызов отказан, ПОКА сосед в полёте. Собрать её дёшево не вышло: у эскалации хоп случается
|
||
после сеттла своего же черновика, а потолок, впускающий два параллельных черновика, впускает и хоп;
|
||
репайр живёт внутри воркера редакторской волны и требует дефекта; терминолог идёт между волнами, где в
|
||
полёте по построению никого. Выдумывать доказательство через `sleep` я не стала — назвать дыру честнее.
|
||
8. ⚠ **Гонка `Reserve`-вне-мьютекса структурна, но не воспроизводима.** Посадка
|
||
`MS-reserve-under-the-gate` — см. §6.5; если она пережила, это НЕ значит, что дефекта нет, а значит,
|
||
что без хука в планировщик его нечем поймать. Выдумывать «доказательство» через `sleep` я не стала.
|
||
9. ⚠ **Платформенная сторона не проверена ни на чём.** Читает ли платформа `shortfall_micro_usd`, не
|
||
считает ли её sweep живой юнит у `paused`-прогона аномалией (что убило бы «доделать допущенное» на её
|
||
стороне) — чужая зона, я туда не ходила. Передано пингом.
|
||
10. ⚠ **`status` теперь читает манифест ДВАЖДЫ, и я это не лечу.** `readModelChunks` загружает сайдкар,
|
||
и `readModelPrice` загружает его снова — а `loadManifest` валидирует ключ через `sourceSHA256` по
|
||
ВСЕМУ исходнику, то есть на 23-мегабайтной книге это второе полное чтение файла и второй хеш на каждый
|
||
`status`. Не ingest (те 1.4 с), но и не $0. Лечение — вернуть загруженный документ из
|
||
`readModelChunks` вместо второй загрузки — трогает сигнатуру, которой пользуются четыре вызывающих, и
|
||
шире этого пака. ⚠ Туда же уходит TOCTOU между двумя загрузками: конкурентный `translate`, записавший
|
||
сайдкар между ними, даёт согласованный ответ по СОВПАДЕНИЮ источника, а не по построению.
|
||
11. ⚠ **Две величины в `expected_usd` НЕ смоделированы, и обе названы в доке, а не спрятаны.** (а) Доля
|
||
РАЗМЫШЛЕНИЯ в completion: на провайдере с subset-биллингом рассуждение входит в completion, но датума
|
||
о его доле в конфиге нет — считается текст без него, то есть в сторону занижения. Именно из-за этого
|
||
у редакторской модели существует флор 16000 (`models.yaml`), так что величина не мала. (б) На стадии
|
||
k>0 размер входа — токены провайдера (`est_out`) против `EstimateTokens` (знаки/3) у исполнителя;
|
||
совпадают только при ~3 знаках на токен. На боевом плече обе глушит модельный флор; на плече без
|
||
флора — нет.
|
||
12. ⚠ **Взаимодействие с `--max-units` под денежным стопом проверено только чтением.** `res.Volume` при
|
||
денежном стопе не прикладывается, `scope.reconcile` не зовётся; что объёмный грант и денежный потолок
|
||
в одном прогоне ведут себя корректно во ВСЕХ комбинациях — не предъявлено прогоном.
|
||
14. ⚠ **ПРАВКА V2-7 НЕ ДОТЯГИВАЕТСЯ ДО УЖЕ ЗАПИСАННОГО САЙДКАРА, и я это называю, а не чиню.**
|
||
`structure` вычисляется при РЕЗКЕ и складывается в сайдкар; правило, по которому `declared` теперь
|
||
требует, чтобы формат провёл ВСЕ границы, применяется к новым резкам. Файл, нарезанный сборкой
|
||
между `81a89e9` и дофиксом, несёт `declared`, посчитанный СТАРЫМ правилом, проходит версию (поле
|
||
аддитивно, версия намеренно не двигалась — см. запись 44) и отдаётся читателю как есть. Лечение —
|
||
двинуть `manifestVersion`, то есть выбросить каждый сохранённый сайдкар и пере-резать каждую книгу;
|
||
цена несоразмерна окну в несколько часов на движке, который ещё не отгружен. ⚠ Но правило общее и
|
||
его стоит держать: **аддитивное поле, чьё ПРАВИЛО потом меняется, перестаёт быть аддитивным** — с
|
||
этого момента у него есть версия, просто ненаписанная.
|
||
15. ⚠ **Что предупреждение о дропнутом ряде (запись 51) кто-то ПРОЧИТАЕТ — не предъявлено.** Оно уходит
|
||
в `r.Log.Warn` рядом с предупреждением о ДЕКЛИНЕ, то есть ровно туда же, куда движок уже говорит о
|
||
похожем событии; но доходит ли этот уровень до оператора платформы или тонет в логе прогона —
|
||
вопрос чужой зоны, и я туда не ходила.
|
||
13. ⚠ **Каталог мутаций гонялся ЦЕЛИКОМ только на финальном дереве.** Промежуточные прогоны я дважды
|
||
останавливала САМА, обнаружив, что копия устарела от собственных правок: результат о дереве, которого
|
||
больше нет, — это ровно тот класс, за который смена расплачивалась («утверждение о дереве живёт до
|
||
следующего коммита»). Цвета в §6.5 — с последнего, замороженного прогона.
|
||
|
||
---
|
||
|
||
## 10. Дофикс приёмки — дельта против уже приземлённого пака
|
||
|
||
Пак приземлён двумя коммитами (`81a89e9` — дерево, `9cac062` — правка довода в шапке поля). Всё
|
||
перечисленное ниже лежит В ДЕРЕВЕ ПОВЕРХ них и не закоммичено: лендит оркестратор.
|
||
|
||
| приёмка | что было не так | где починено |
|
||
|---|---|---|
|
||
| F1 | довод «та же дверь» ложен, и утверждение в коде сильнее правды | `runevents.go`, §3, запись 39 |
|
||
| — | `Finished.Money` документирован правилом присутствия, которого нет (нашли ДВА верификатора независимо) | `runevents.go`, пин `TestTheMoneyLedgerFollowsTheCeilingAndNotTheOutcome`, запись 40 |
|
||
| F3 | книжная фигура печаталась как прогноз, а она ПОТОЛОК | `cmd/tmctl/render.go`, пин `TestTheHumanManifestCallsTheBookLevelFigureACeiling`, запись 41 |
|
||
| F8 | забор убивал оплаченный прогон об собственный вывод движка | расщепление по авторству: `memseed.go`/`memvoice.go`/`mining.go`/`bankdecisions.go`/`decisions.go`, пин `TestAnEngineWrittenDocumentDropsTheRowInsteadOfKillingTheRun`, запись 42 |
|
||
| F9 | история в шапке забора льстила прошлому | `wirefence.go`, запись 43 |
|
||
| V2-3 | у сайдкара ТРИ состояния, код знал два | `manifest.go`, пин `TestASidecarFromAnOlderBuildStillYieldsAPrice`, запись 44 |
|
||
| V2-5 | книга платила за проход, которого не бывает, **и тест пинил неверное число** | `priceprojection.go` + пере-пин, запись 45 |
|
||
| V2-7 | один случайный form feed объявлял сорок девять догадок словом файла | `chunk/ingest.go`, запись 46 |
|
||
| V2-8 | тождество выводилось из равенства чисел | `priceprojection.go`, запись 47 |
|
||
| — | ссылка на идентификатор, которого в Go больше нет (три носителя) | `paidtail.go` + два дока зоны, запись 48 |
|
||
| — | **ДОФИКС F8 РОНЯЛ РЯД МОЛЧА** — найдено не приёмкой, а собственным чтением дофикса | `memvoice.go`/`memseed.go`/`mining.go`, запись 51 |
|
||
| F7 | два комментария ОДНОГО коммита противоречили о фолбэке цены; у находки не было владельца до 05.09 | `status.go`, запись 56 |
|
||
|
||
**Каталог: 191 → 201.** Десять новых посадок держат дофикс, и это ровно десять ИМЁН:
|
||
`DF-engine-written-document-must-not-kill-the-run` и `DF-operator-seed-keeps-its-refusal` (расщепление по
|
||
авторству держится с ОБЕИХ сторон: если бы посадка была одна, «уроню всё» и «откажу всему» прошли бы
|
||
поодиночке) · `DF-old-sidecar-must-not-silence-the-price` · `DF-classifier-budget-only-when-it-runs` ·
|
||
`DF-input-identity-is-stated-not-counted` · `DF-declared-means-every-boundary` ·
|
||
`DF-money-follows-the-ceiling-not-the-outcome` · `DF-book-level-figure-is-a-forecast` и
|
||
`DF-no-book-level-spend-says-nothing` (F3 — тоже с обеих сторон: сказать неправду и сказать лишнее) ·
|
||
`DF-a-dropped-row-is-not-silent` (запись 51). Плюс четыре ПЕРЕ-НАЦЕЛЕННЫЕ (запись 49).
|
||
|
||
⚠ **Первая редакция этого абзаца объявляла F3 непокрываемым** — «носитель правки текст, покрасить
|
||
нечем, проверено чтением вывода». Это была ОТГОВОРКА, и она держалась ровно до попытки: `renderManifest`
|
||
принимает `io.Writer` и `*BookManifest`, тест пишется в двадцать строк без стенда и без денег, а
|
||
`cmd/tmctl/render.go` до сих пор не имел на эту функцию НИ ОДНОГО теста. Записано как есть, потому что
|
||
«нечем покрыть» — самая дешёвая из неправд, какие пишет о себе смена, и от честной она отличается
|
||
одной попыткой.
|
||
|
||
⚠ **Что дофикс НЕ трогал:** ни одного теста не удалено и не переименовано (§6.2), ни одной строки в
|
||
чужой зоне, ни одного платного вызова. Батарея и каталог прогнаны на дереве ПОСЛЕ дофикса, не до.
|