148 KiB
Отчёт бэкенд-пака «денежный стоп перестаёт быть аварией» (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 под построенное имя.
Две честные оговорки, обе в доке поля:
- Это ВЕРХНЯЯ граница, а не минимум.
reservedвключает ещё идущие вызовы, а settle заменяет резервацию стоимостью, которая никогда не больше. «Пополни на столько — и отказанный вызов пройдёт» верно всегда; «меньше не хватит» — нет. При пустом полёте (обычный терминальный отказ) они совпадают. - Только 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, и ложь, по которой покупатель
стал бы действовать.
⛔ И ЧИТАТЕЛЬ С ПИСАТЕЛЕМ ПРИБИТЫ ОДНИМ АКТОМ — по норме, которую оркестратор передал в ходе пака. Две пары:
- Размер вызова.
baseMaxTokensForвынесен изrunStageв ОДНУ функцию, которой пользуются и исполнитель, и проекция. Проекция, считающая размер по-своему, разъехалась бы с резервациями, которые прогон реально делает, — и разъезд был бы невидим, обе стороны выглядят как числа. - Цена на двух поверхностях. ⛔ Полная батарея поймала у меня ровно этот класс:
TestManifestServesTheReadModelsIdenticallyпокраснел —statusотвечал ценой при наличии сайдкара иnilбез него. Манифест — АКСЕЛЕРАТОР, а акселератор, меняющий ОТВЕТ, это второй источник истины. Лечение:readModelPriceотвечает на обоих путях, и обе ветки кончаются в однойprojectBook.
5. Секция, которую требует §5-тер(а): ГДЕ ПРОМТ НАПИСАН НЕ ТАК
Читаю промт последней и называю расхождения — не как вежливость, а как заказ владельца.
- ⛔ §4(3) внутренне противоречив. Он называет число, которое должно ехать наружу
(
denied estimate=$…), и следующей фразой запрещает его класс («цены моделей, стадий и вызовов не выходят»). Исполнить дословно значило бы нарушить соседнее предложение. Разрешено заменой на недостачу; согласовано. - ⛔ §4(1) заказывает «обрамление как ДАННЫХ», которое в текстовой форме нельзя выполнить, не нарушив
§4(6). Единственный дом для такой фразы —
lang/data/injection.txt, а он побайтно входит вEmbeddedVersion()→cutTag→unit.idкаждой книги. Промт этого не знал; сделано структурно. - ⚠ §3 п.1 карты чтения указывает на секцию, которой уже нет — но это НЕ дефект промта, и правку
вношу по указанию оркестратора. «
15-money-path.md§«ОТКРЫТАЯ РАЗВИЛКА 04.09»» переименована в «РАЗБОР 04.09: КНИГУ НЕЛЬЗЯ ДОЧИТАТЬ…» — и переименовал её оркестратор УЖЕ ПОСЛЕ выдачи промта. То есть на момент выдачи карта была верна, а сломал её последующий редакторский акт. Разница не формальная: «промт написан не так» лечится редактурой промтов, а «карта сломана после выдачи» — дисциплиной того, кто правит доки под работающей сессией. Секцию нашла грепом по заголовкам. - ⚠ §4(2) «защёлка на уровне ВОЛНЫ» обоснована доводом, который больше не работает. Промт пишет:
«Без защёлки W воркеров дадут W путей ошибки вместо одной остановки». Довод снят самой конструкцией:
haltErr— отдельный слот с первым-побеждает, путь ошибки один при любом числе воркеров. Защёлка ОСТАВЛЕНА (оркестратор подтвердил другим доводом — продуктовым: книгу читают подряд, и непрерывный префикс из шести глав лучше решета из восьми), но её цена названа: она читает «этот ОДИН элемент не влез» как «денег нет ни на что», а при разбросе резервов внутри волны 2.976× (замер разбора 04.09) и удвоении бюджета на ретрае это ложно — юниты, которые ВЛЕЗАЛИ, не начинаются. Заведено строкой бэклога. - ⚠ §4-тер риск (б) назван мягче, чем он есть. «частичная волна под потолком — НОВАЯ форма для этих счётчиков» — на деле счётчики не просто новую форму получают, они СОВРУТ по шести полям из семи, и лечение не в поправке, а в отказе их публиковать (см. §2-бис).
- ⚠ §5 «модель называй имени» — опечатка («по имени»). Названо: помощник — Fable 5, один агент, долгий разговор, два круга.
- ✅ §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 — НЕ ОДИН ВЕРДИКТ, А ТРИ РАЗНЫХ, и различать их обязательно (разведение оркестратора,
которое моя формулировка «выживание ЕСТЬ сообщение о недостижимости» и позволила):
- посадка эквивалентна оригиналу — дефект ПОСАДКИ, гарантия цела;
- посадка не воспроизводит прежний мир ЦЕЛИКОМ — починка из двух частей, а вернули одну (см. 31);
- посадка недостижима, потому что код МЁРТВ — находка о КОДЕ, добытая прибором (см. 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 — что НЕ удалось, что не проверено, что осталось гипотезой
- ⛔ Клин юнита НЕ починен, и это признанный долг, а не забытое. Отказ РЕЗЕРВАЦИИ на ретрае не
деградирует И не оставляет следа: он уходит наверх из цикла попыток (
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. - ⚠ ⭐-уточнение оркестратора («защёлка на свежем, продолжение на ретрае») ОТКЛОНЕНО, и не по цене.
Его посылка — «текст у юнита ЕСТЬ, он лишь флагован» — ложна: см. п. 1, строки
chunk_statusнет, для читателя это такой же ПРОБЕЛ. Различитель дёшев (номер попытки есть прямо в точке отказа), но покупает не то, ради чего предлагался. - ⚠ Цена защёлки НЕ ЗАМЕРЕНА, только выведена. Что юниты, которые влезали, не начинаются — следует из удвоения бюджета ретрая и из замеренного разбором 04.09 разброса резервов 2.976× внутри волны. На реальной книге я это не мерила: пак не включает платного прогона.
- ⚠ Числа проекции не сверены с фактом на реальной паре.
expected_usdстроится из фертильностиzh-ruи ценmodels.yaml; насколько он попадает в настоящий счёт — вопрос замера, а не чтения. То, что предъявлено: он не константа, растёт с длиной главы, катится в сумму по главе и книге, иstep_max_usdне меньше резервации, которую исполнитель делает для первого вызова. - ⚠ Порог «сеттлить ли поздно отменённый вызов оценкой» НЕ выставлен, и это сознательный отказ. Промт разрешал латентность кандидатом при условии «порог обязан быть обоснован ЗАМЕРОМ, а не назначен». Замера нет, платный прогон паком не выдан ⇒ порог назначить нечем, и я его не назначала.
- ⛔ ПРОВОДКА
settleCannotHelpНЕ ПРОВЕРЕНА, и я показываю, ПОЧЕМУ она почти ничего не решает, а не отговариваюсь. Предикат запинен своим тестом;if, который его спрашивает, — нет. ⚠ И здесь надо развести ДОСТИЖИМОСТЬ и ЭФФЕКТ — первую редакцию этого абзаца я написала неверно, и поправил меня оркестратор по строке кода. Предикат стоит ПЕРЕД входом в ожидание (stagerun.go:530-531), поэтому когда он ИСТИНЕН, полёт не спрашивают вовсе: путь достижим при любом состоянии полёта, включая пустое. Утверждать «достижим только при непустом» — значит утверждать недостижимость состояния, которого код не держит, и следующая сессия построила бы на этом пин. Верно другое: он что-то МЕНЯЕТ только при непустом полёте — при пустом книгу и без него останавливаетwaitNothingInFlight. А состояние «отказ, который никакой settle не поднимет, ПРИ непустом полёте» почти недостижимо. Но вызов в полёте сам был допущен при том жеcommittedи сопоставимой оценке — если место нашлось ему, оно найдётся и этому: два условия противоречат друг другу, пока оценки не разойдутся существенно (разнородные размеры чанков, либо черновик и редактура в одной волне — а они не встречаются никогда). А когда полёт пустеет, книгу останавливаетwaitNothingInFlightи без предиката. ⇒ он ОПТИМИЗАЦИЯ (не держать волну открытой весь провайдерский таймаут ради известного ответа), а не несущая гарантия, и в §2 он теперь описан так. - ⛔ ЧТО ТРИ ОПЦИОНАЛЬНЫХ САЙТА ПЕРЕДАЮТ ИМЕННО
false— не прибито ничем, и я это НАЗЫВАЮ, а не заявляю. Правило «кто может ждать» теперь живёт в шлюзе и запинено тестом с обеих сторон (опциональный отворачивается со счётчиком, обязательный ждёт и просыпается), плюс посадкой. Но АРГУМЕНТ, которыйescalation.go,repair.goиterminologist.goпередают в этот шлюз, проверяется только компилятором и комментарием у каждого вызова. Недостающий артефакт назван точно: интеграционная фикстура, в которой ОПЦИОНАЛЬНЫЙ вызов отказан, ПОКА сосед в полёте. Собрать её дёшево не вышло: у эскалации хоп случается после сеттла своего же черновика, а потолок, впускающий два параллельных черновика, впускает и хоп; репайр живёт внутри воркера редакторской волны и требует дефекта; терминолог идёт между волнами, где в полёте по построению никого. Выдумывать доказательство черезsleepя не стала — назвать дыру честнее. - ⚠ Гонка
Reserve-вне-мьютекса структурна, но не воспроизводима. ПосадкаMS-reserve-under-the-gate— см. §6.5; если она пережила, это НЕ значит, что дефекта нет, а значит, что без хука в планировщик его нечем поймать. Выдумывать «доказательство» черезsleepя не стала. - ⚠ Платформенная сторона не проверена ни на чём. Читает ли платформа
shortfall_micro_usd, не считает ли её sweep живой юнит уpaused-прогона аномалией (что убило бы «доделать допущенное» на её стороне) — чужая зона, я туда не ходила. Передано пингом. - ⚠
statusтеперь читает манифест ДВАЖДЫ, и я это не лечу.readModelChunksзагружает сайдкар, иreadModelPriceзагружает его снова — аloadManifestвалидирует ключ черезsourceSHA256по ВСЕМУ исходнику, то есть на 23-мегабайтной книге это второе полное чтение файла и второй хеш на каждыйstatus. Не ingest (те 1.4 с), но и не $0. Лечение — вернуть загруженный документ изreadModelChunksвместо второй загрузки — трогает сигнатуру, которой пользуются четыре вызывающих, и шире этого пака. ⚠ Туда же уходит TOCTOU между двумя загрузками: конкурентныйtranslate, записавший сайдкар между ними, даёт согласованный ответ по СОВПАДЕНИЮ источника, а не по построению. - ⚠ Две величины в
expected_usdНЕ смоделированы, и обе названы в доке, а не спрятаны. (а) Доля РАЗМЫШЛЕНИЯ в completion: на провайдере с subset-биллингом рассуждение входит в completion, но датума о его доле в конфиге нет — считается текст без него, то есть в сторону занижения. Именно из-за этого у редакторской модели существует флор 16000 (models.yaml), так что величина не мала. (б) На стадии k>0 размер входа — токены провайдера (est_out) противEstimateTokens(знаки/3) у исполнителя; совпадают только при ~3 знаках на токен. На боевом плече обе глушит модельный флор; на плече без флора — нет. - ⚠ Взаимодействие с
--max-unitsпод денежным стопом проверено только чтением.res.Volumeпри денежном стопе не прикладывается,scope.reconcileне зовётся; что объёмный грант и денежный потолок в одном прогоне ведут себя корректно во ВСЕХ комбинациях — не предъявлено прогоном. - ⚠ ПРАВКА V2-7 НЕ ДОТЯГИВАЕТСЯ ДО УЖЕ ЗАПИСАННОГО САЙДКАРА, и я это называю, а не чиню.
structureвычисляется при РЕЗКЕ и складывается в сайдкар; правило, по которомуdeclaredтеперь требует, чтобы формат провёл ВСЕ границы, применяется к новым резкам. Файл, нарезанный сборкой между81a89e9и дофиксом, несётdeclared, посчитанный СТАРЫМ правилом, проходит версию (поле аддитивно, версия намеренно не двигалась — см. запись 44) и отдаётся читателю как есть. Лечение — двинутьmanifestVersion, то есть выбросить каждый сохранённый сайдкар и пере-резать каждую книгу; цена несоразмерна окну в несколько часов на движке, который ещё не отгружен. ⚠ Но правило общее и его стоит держать: аддитивное поле, чьё ПРАВИЛО потом меняется, перестаёт быть аддитивным — с этого момента у него есть версия, просто ненаписанная. - ⚠ Что предупреждение о дропнутом ряде (запись 51) кто-то ПРОЧИТАЕТ — не предъявлено. Оно уходит
в
r.Log.Warnрядом с предупреждением о ДЕКЛИНЕ, то есть ровно туда же, куда движок уже говорит о похожем событии; но доходит ли этот уровень до оператора платформы или тонет в логе прогона — вопрос чужой зоны, и я туда не ходила. - ⚠ Каталог мутаций гонялся ЦЕЛИКОМ только на финальном дереве. Промежуточные прогоны я дважды останавливала САМА, обнаружив, что копия устарела от собственных правок: результат о дереве, которого больше нет, — это ровно тот класс, за который смена расплачивалась («утверждение о дереве живёт до следующего коммита»). Цвета в §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), ни одной строки в чужой зоне, ни одного платного вызова. Батарея и каталог прогнаны на дереве ПОСЛЕ дофикса, не до.