21 KiB
ДОФИКС бэкенд-паку «число согласия на пути второй покупки» — по приёмке оркестратора №22 (03.09.2026)
Роль: бэкенд, та же сессия, что отработала пак. Зона записи:
backend/+ своя секция «Бэкенд» вdocs/PROGRESS.md. Чужие зоны читаешь, не правишь. Не коммитишь и не трогаешь индекс и историю — запреты и причина:CLAUDE.md, «Git-координация мультисессий». ⛔ В индексе сейчас лежит НЕЗАКОММИЧЕННАЯ работа двух смен (46 файлов). Ни одной пишущей git-команды: ниcheckout, ниrestore, ниstash, ниreset, ниclean, ниadd, ниcommit.
Что это и почему оно пришло
Пак принят с дофиксом. Приёмка шла не чтением отчёта: десять линз (пять на зону), каждая обязана была проверять исполнением, каждая находка ушла опровергателю с мандатом её ОПРОВЕРГНУТЬ своей командой. По бэкенду 20 находок, 19 пережили опровержение.
⚠ Твой код в основном подтверждён — ниже отдельная секция «что приёмка проверила и признала», и её переделывать не нужно. Дофикс про другое: пак создал один дефект в том, что сам чинил, оставил ложным одно РАТИФИЦИРОВАННОЕ утверждение, и три свойства, объявленные твоими, тестом не прибиты — мутации приёмки их пережили.
Карта чтения — три позиции
- Этот файл. Твой прежний промт
docs/BACKEND_CONSENT_SESSION_PROMPT.md— уже прочитан, перечитывать целиком не надо. - Свой отчёт в
docs/PROGRESS.md, секция «Бэкенд», запись 03.09 — по пунктам Б7–Б10 правится он сам. - Улики ниже. Каждая проверена приёмкой исполнением; воспроизведи их сам — если что-то не воспроизводится, это находка, неси её, а не подгоняй работу под текст.
Б1 [важное] — стоп-строка и лог лгут ровно в том случае, который пак СОЗДАЛ
bound() теперь истинен при Carried > 0. Значит прогон, дошедший до конца книги и лишь
дописавший начатое, печатает оператору самопротиворечие:
backend/internal/pipeline/volume.go:220 — «stopped on the VOLUME ceiling (--max-units %d),
not on money and not at the end of the book» — префикс БЕЗУСЛОВЕН, внутри самого fmt.Sprintf
и тут же в том же предложении: «Still in the book: 0 unit(s) NEVER delivered»
Воспроизведено живым прогоном настоящего Runner (две главы, первый прогон умирает на редакторе,
второй с MaxUnits=2): Left()=0 granted()=0 Paid()=2, и обе половины предложения печатаются.
Тот же текст едет в лог waverun.go:173, а комментарий у вызова waverun.go:169-170 («Only set when
the ceiling actually held work back») протух — в файле, который пак правил.
Делай РОВНО так: предложение разводится на два случая — грант кончился / книга кончилась. Решаешь сам: формулировки обеих веток и нужен ли отдельный текст для смешанного случая. ⚠ Пункт 2 пака закрывался ИМЕННО как дисклоужер — объявлять его закрытым до этой правки нельзя.
Б2 [важное] — ратифицированный контракт кадра finished.volume стал ЛОЖНЫМ
D39.181 п.2 объявляет ПРИСУТСТВИЕ поля булевым признаком «грант придержал работу»:
backend/internal/runevents/runevents.go:189 «present only when a volume grant actually held work back»
backend/internal/runevents/runevents.go:202-204 «its PRESENCE is the boolean: the engine attaches this
only when the grant actually held something back»
те же утверждения — backend/internal/pipeline/events.go:584-588 и bookrun.go:91
Кадр теперь приезжает и у прогона, дошедшего до конца книги (left_fresh:0, left_rework:0), и
carried на провод не выходит вовсе — то есть читатель различить два состояния НЕ МОЖЕТ ничем.
Пак переписал соседние поля ТОЙ ЖЕ структуры (Delivered, Free), а утверждение тремя строками выше
оставил. Сегодня безопасно только потому, что потребителя нет: platform/internal/ingest/events.go
декодирует Finished{Outcome} и всё.
Делай РОВНО так: текст контракта приводится в соответствие с поведением ВО ВСЕХ ЧЕТЫРЁХ местах.
Решаешь сам: формулировка. ⛔ Поля на провод НЕ добавлять — это класс «новое поле в
опубликованном документе», он требует ратификации и минора StreamVersion, и владелец 03.09 уже
сказал «оставить как есть». Если считаешь, что честный контракт без поля невозможен, — пинг, а не
поле.
Б3 [важное] — три свойства объявлены твоими и НЕ прибиты: мутации приёмки пережили весь пакет
Все три посажены приёмкой на копии дерева, каждая SURVIVED на ./internal/pipeline/, и в том же
прогоне соседние посадки дали RED — то есть положительный контроль стоит.
- (а)
volume.go:191—case class == unitFresh && s.stop.Delivered > s.stop.Carried:. Возврат к форме> 0(и эквивалентной>= 1) переживает пакет. СуществующийTestReconcileKeepsCarriedInsideDelivered(volumeslot_test.go:554) гоняет только веткиunitFreeи до этой ветки не доходит. ⚠ Отчёт числит за этой находкой «посадку Y3», которой в репозитории нет — см. Б7. - (б)
rebill.go:450— подменаprojectionBasis(proj)наprojectionBasis(book)переживает пакет. Ветка-близнец (scoped-клауза,:505) от той же подмены КРАСНЕЕТ черезTestTheScopedClauseOfTheRefusalCarriesItsOwnBasis— значит пакет эту подмену ловить умеет. - (в)
rebill.go:323—if p.ModelMovedRows > 0заменяется на>= 0, и «0 of the N unit(s) were bought from a model…» уезжает в КАЖДЫЙ текст согласия; пакет зелёный. У близнецаHistoricalRowsотрицательный пин ЕСТЬ (reprice_test.go:569-572проверяет, что чистая сумма НЕ содержит «originally billed»), у новой оговорки — нет нигде.
Делай РОВНО так: каждое свойство получает тест, который от своей мутации краснеет, И запись в
backend/cmd/tmmutate/mutations.json. Решаешь сам: форма тестов и формулировки записей.
⚠ Приёмка сажала СВОИ формы мутаций — не копируй их, посади свою и убедись, что она красная.
Б4 [мелочь] — докстринг classifyUnits не пере-считан под собственную правку
volume.go:637: «sorts every output unit into the three things this run can do to it», а unitClass
несёт ЧЕТЫРЕ члена, и добавленный паком unitCarried присваивается ВНУТРИ самой этой функции.
Вредно адресно: читатель, ищущий, куда попадает начатый-но-не-отгруженный юнит, по слову «three»
решит, что четвёртого класса нет, — и пойдёт искать в ветку unitFresh, ровно туда, где жил дефект
строки 232. Делай РОВНО так, правка на одно слово.
Б5 [мелочь] — пример в backend/README.md:73 противоречит новой семантике
Строка обещает «оплатить не больше 10 выходных юнитов», тогда как тело README:11 новую семантику раскрывает верно. Делай РОВНО так: привести пример к телу.
Б6 [мелочь, по желанию] — шов store → репрайсер прибит с одной стороны
Подмена c.escalation на 0 в ledger.go:380 переживает ./internal/store/, а ./internal/pipeline/
краснеет через TestAnEscalatedRowIsRePricedAtTheNewHopEndToEnd. Дефектом сегодня не является — пин
живёт там, где живёт свойство. Но твой же довод по фильтру книги («мутация пережила обе батареи по
отдельности, значит одной мало» — оттого FC1-book-filter-store И -pipeline) применим и здесь.
Решаешь сам: заводить ли запись каталога на store-стороне; отказ аргументируй.
Б7 [среднее] — отчёт ссылается на ПЯТЬ несуществующих посадок как на живые гейты
Y2-silent-when-nothing-left · Y3 · Z2 · X29 · Y4. Каталог получил ровно ШЕСТЬ записей
(FC1-*×4, FC5-*×2); 35 заявленных собственных посадок жили в scratch-каталогах
(own_mutations.json, round2_mut.json, round3_mut.json, draftonly_mut.json), которых на машине
нет. Проверено грепом с границами слова, положительный контроль на ту же форму: FC1-book-filter-store
находится в двух местах.
Читатель отчёта принимает пять несуществующих гейтов за живые — а один из них «закрывает» гарантию,
чья мутация переживает (Б3а). Это ровно та болезнь, ради которой tmmutate и лежит в репозитории.
Делай РОВНО так: либо снять ссылки, либо посадить недостающие записи в каталог. Решаешь сам: какие из пяти стоят записи, а какие были разведкой.
Б8 [мелочь] — четыре числа отчёта не печатаются командами, которые он для них называет
- «денежного пути в каталоге было 0 файлов, стало 5» — каталог даёт 18 → 21 различных файла, то
есть ТРИ новых (
reprice.go,volume.go,ledger.go), шесть записей; греп из промта печатает 7. Ни одно прочтение не даёт 5. - «на входе 3 попадания» у грепа
can never quote a different number— на входном коммите в названной области попадание ОДНО. - «все 18 строк» линта — гейт печатает 19, и девятнадцатый создан самим отчётом
(строка, цитирующая
docs/PROGRESS.md:679). - «в файле 485 строк» — 499: число снято до того, как отчёт дорос.
Делай РОВНО так: пере-считать каждое своей командой и вписать команду рядом с числом.
⚠ python3 docs/scripts/counts.py --lint гнать ПОСЛЕДНИМ действием, после того как отчёт дописан,
и починить 19-й якорь — иначе гейт остаётся красным по вине самого отчёта.
Б9 [мелочь] — неполнота самообъявления по D39.183
TestCheckpointsOfOneGenerationSumToTheRowsCost (reprice_test.go:639) адаптирован под третий возврат
usd(), а в блоке самообъявления не назван, хотя остальные четыре адаптации под ту же смену сигнатур
перечислены поимённо. Ослабления нет — правка механическая. Это ровно класс, который бэклог держит
строкой 248. Делай РОВНО так: дописать имя.
Б10 [к сведению, НЕ заказ] — что приёмка нашла и что сознательно НЕ чинится этим дофиксом
Назови это в отчёте, чтобы оно не умерло молча вместе с паком.
Carriedна провод не выходит, а подсказка, дописанная паком в докстрингDelivered, ключуется наdelivered > max_units— и в форме «весь остаток перенесён»delivered == max_units, то есть подсказка НЕ срабатывает и разложить кадр читателю нечем. Связано с открытым вопросом владельца о семантике переноса; чинится вместе с ним, а не отдельно.- В перенос попадает и юнит, который ФЛАГНУЛСЯ и текста не отдал — он «начат и не отгружен»,
значит каждая следующая покупка снова платит за его пере-атаку ВНЕ гранта. Приёмка это вывела из
classifyUnitsи подтвердила зелёными тестами пака, но цикла из трёх последовательных покупок не гонял никто. Это вход в решение владельца, а не дефект — не чини, но не потеряй.
Границы — что НЕ делать
- ⛔ Не добавлять поле на провод (см. Б2) и не двигать
StreamVersion, форму манифеста, схему хранилища, версию контракта. - ⛔ Не менять семантику переноса слота. Владелец 03.09 отложил её словом «подумаем на этот счёт». В коде сегодня «перенос ограничен грантом», и приёмка это ЗАМЕРИЛА: грант 2 оплачивает 4 выходных юнита, 6 вызовов провайдера. Это открытый вопрос владельца, а не дефект — трогать нельзя.
- ⛔ Не править
docs/architecture/15-money-path.mdи телоD39.170— они противоречат новой семантике--max-units, это правда, но это зона оркестратора, и он их правит сам. Ты правильно сделал, что не полез. - ⛔ Не трогать чужие зоны,
.env, соседний worktree.
Что приёмка ПРОВЕРИЛА и признала — переделывать не нужно
- Батарея зелёная:
EXIT=0,0 issues, три известных SKIP, ни одного FAIL. Тестов 1083 → 1102, удалённых имён НОЛЬ (пофамильный дифф). - Все ШЕСТЬ каталожных мутаций пере-посажены и все шесть КРАСНЫЕ.
- Четыре обязательства
D39.150 п.1исполнены ВСЕ, включая четвёртое: обещание точности снято изstatus.go, шапкаreprice.goзаявляет обратное прежнему. - Инвариант направления ошибки держится по построению: в
repriceCheckpointцена только повышается — пол по ответившей модели, подъём до сегодняшней. - Правки существующих тестов проверены на МОТИВ: подгонки нет, все стали СИЛЬНЕЕ (проверка трёх полей → проверка всей структуры; добавленное утверждение про basis).
- Границы соблюдены: схема, манифест, контракт не двинуты;
runevents/тронут только комментариями; два новых поляstatus --jsonаддитивны — платформа декодирует аллоулист. - Кратность оценки
$0-строки названа честно комментарием и ратифицирована «round up when it cannot be known» — приёмка это принимает.
Мандат самопроверки — исполнением
- Каждый пункт Б1–Б9 проверяется своей командой, и в отчёте стоит её выход цитатой, а не пересказ.
- Мутации Б3 сажаются на копии дерева (
cp -a backend /tmp/…) и предъявляются КРАСНЫМИ. Посадка, не покрасневшая, — находка, а не провал: так и пиши. - Отдельный проход по своей готовой работе свежим контекстом. Субагенты разрешены явно, модель задавай ИМЕНЕМ и знай, сколько их работает.
- ⚠ «Прошёл, находок нет» отчётом НЕ является: ноль находок обосновывается тем, ЧТО искалось и какими командами.
Отчёт
В свою секцию docs/PROGRESS.md. Пройди по каждому пункту Б1–Б9 и отчитайся по каждому — эта тема
в смене проваливалась несколько раз на том, что исполнялся первый пункт, а нумерованный остаток
бросался. Каждое число — с командой получения, которая его печатает; приёмка пере-ранит.
Обязательная секция «Что НЕ удалось и что НЕ проверено» — третий исход законен.
Конфликт этого наряда с кодом — пинг, не интерпретация: наряд писан по дереву индекса, код первичен.
Прямой канал
Механизм — CLAUDE.md, «Связь между сессиями»: свой блок в /tmp/textmachine-channel, сверка с
ListAgents перед отправкой; канала нет — нормальный случай, сессии подряд не опрашивать.