textmachine/docs/archive/prompts/BACKEND_DOFIX_2026-09-03.md

22 KiB
Raw Permalink Blame History

ДОФИКС бэкенд-паку «число согласия на пути второй покупки» — по приёмке оркестратора №22 (03.09.2026)

⚠⚠ АРХИВ. НАРЯД ОТРАБОТАН — ИНСТРУКЦИИ ОТСЮДА НЕ ИСПОЛНЯЮТСЯ. Выдан 03.09 по приёмке пака «число согласия», отработан той же сессией, принят приёмкой второго круга (D39.187). Исход: девять пунктов из десяти исполнены и подтверждены исполнением; остаток — развилка операторской строки прибита с одной стороны, граница bound() без пина, три числа отчёта. Остаток НЕ потерян: он пунктом 6 выданного пака BACKEND_WIREBATCH_SESSION_PROMPT.md. ⚠ Часть якорей мертва по построению — пак снял тексты, на которые они указывали.

Роль: бэкенд, та же сессия, что отработала пак. Зона записи: backend/ + своя секция «Бэкенд» в docs/PROGRESS.md. Чужие зоны читаешь, не правишь. Не коммитишь и не трогаешь индекс и историю — запреты и причина: CLAUDE.md, «Git-координация мультисессий». В индексе сейчас лежит НЕЗАКОММИЧЕННАЯ работа двух смен (46 файлов). Ни одной пишущей git-команды: ни checkout, ни restore, ни stash, ни reset, ни clean, ни add, ни commit.

Что это и почему оно пришло

Пак принят с дофиксом. Приёмка шла не чтением отчёта: десять линз (пять на зону), каждая обязана была проверять исполнением, каждая находка ушла опровергателю с мандатом её ОПРОВЕРГНУТЬ своей командой. По бэкенду 20 находок, 19 пережили опровержение.

Твой код в основном подтверждён — ниже отдельная секция «что приёмка проверила и признала», и её переделывать не нужно. Дофикс про другое: пак создал один дефект в том, что сам чинил, оставил ложным одно РАТИФИЦИРОВАННОЕ утверждение, и три свойства, объявленные твоими, тестом не прибиты — мутации приёмки их пережили.

Карта чтения — три позиции

  1. Этот файл. Твой прежний промт docs/BACKEND_CONSENT_SESSION_PROMPT.md — уже прочитан, перечитывать целиком не надо.
  2. Свой отчёт в docs/PROGRESS.md, секция «Бэкенд», запись 03.09 — по пунктам Б7Б10 правится он сам.
  3. Улики ниже. Каждая проверена приёмкой исполнением; воспроизведи их сам — если что-то не воспроизводится, это находка, неси её, а не подгоняй работу под текст.

Б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:191case 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:323if 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, и девятнадцатый создан самим отчётом (строка, цитировавшая мёртвый якорь в самом отчёте).
  • «в файле 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. Каждый пункт Б1Б9 проверяется своей командой, и в отчёте стоит её выход цитатой, а не пересказ.
  2. Мутации Б3 сажаются на копии дерева (cp -a backend /tmp/…) и предъявляются КРАСНЫМИ. Посадка, не покрасневшая, — находка, а не провал: так и пиши.
  3. Отдельный проход по своей готовой работе свежим контекстом. Субагенты разрешены явно, модель задавай ИМЕНЕМ и знай, сколько их работает.
  4. «Прошёл, находок нет» отчётом НЕ является: ноль находок обосновывается тем, ЧТО искалось и какими командами.

Отчёт

В свою секцию docs/PROGRESS.md. Пройди по каждому пункту Б1Б9 и отчитайся по каждому — эта тема в смене проваливалась несколько раз на том, что исполнялся первый пункт, а нумерованный остаток бросался. Каждое число — с командой получения, которая его печатает; приёмка пере-ранит. Обязательная секция «Что НЕ удалось и что НЕ проверено» — третий исход законен. Конфликт этого наряда с кодом — пинг, не интерпретация: наряд писан по дереву индекса, код первичен.

Прямой канал

Механизм — CLAUDE.md, «Связь между сессиями»: свой блок в /tmp/textmachine-channel, сверка с ListAgents перед отправкой; канала нет — нормальный случай, сессии подряд не опрашивать.