34 KiB
Промт: бэкенд, пак «честные числа и статусы» — строки 176 · 181 · 172-г · 187
Выдан оркестратором №18, 17.08.2026. Запуск — по слову владельца. Вес пака — лёгкий-средний: четыре точечных места, приёмка инлайн.
Что сознательно НЕ вошло (подписываю пропуск, чтобы ты не считал это забывчивостью и не «дочинил» молча): 146 (гейт свежести цен запирает read-only
reportи $0-resume) · 173 (backupStampсекундной точности маскирует «проект занят») · 177 (гигиена restore point уmigrate, ПЯТЬ пунктов: ретрай в ту же секунду = exit 1 вне полосы на цикле самолечения · рваная копия приkill -9· копии не подчищаются · SIGINT не прерывает · комментарийmigrate.go«no money touched» на no-op неверен — да, последний ровно темы этого пака, и всё равно не берём) — той же темы «движок врёт наружу», но в другой области кода (деплой и бэкап), и владелец выбрал МЕЛКИЙ пак. Увидишь их по пути — не трогай, а перечисли в отчёте.
0. Зачем (проблема, понятная без истории проекта)
Движок — CLI (tmctl), который переводит книгу платными вызовами LLM и которым снаружи управляет
процесс-супервизор (платформа). Наружу он сообщает три вещи: число денег, код выхода и
вердикт гейта. Сегодня в четырёх местах сообщаемое расходится с происходящим:
- Число, на которое оператор даёт согласие при пере-покупке, посчитано по СТАРЫМ ценам. Цены
провайдера сменились 16.08 (до ×4.4 в пике), проекция пере-покупки складывает исторический
cost_usd, а резерв и расчёт считают новой таблицей. Оператор соглашается на одну сумму, платит другую. Траты защищены потолками — врёт именно ЧИСЛО СОГЛАСИЯ (строка 181). - Паника Go-рантайма неотличима от успеха. Непойманная паника даёт exit 2, а 2 в контракте
означает «завершено, есть флагнутые главы» — потребитель записывает упавшему посреди работы
прогону «готово». Деньги при этом целы (считаются из
status --json), врёт статус (строка 176). - Ратифицированный порог приёмки классификатора не доехал до тестового рига. Владелец 15.08 подписал ВЫБОРОЧНУЮ форму порога, риг требует прежнюю — и сам предупреждает, что порог и выборка расходятся. Ближайший прогон вахты упадёт ложно (строка 172-г).
- Два комментария кода описывают снятое поведение — читатель кода получает неверный закон о том,
что попадает в
memory_version(строка 187).
Что решит твой результат: ни одно из четырёх мест больше не врёт, и каждое прибито тестом, который на до-фиксном коде ПАДАЕТ. После пака можно честно пере-прогонять старую книгу (сейчас нельзя — п.1) и честно читать исход прогона снаружи (п.2).
1. Зона записи и git
Пишешь ТОЛЬКО в backend/. Итог сессии — краткой записью в docs/PROGRESS.md, секция «Бэкенд»
(это единственный файл вне backend/, куда тебе можно писать).
Ты не коммитишь — лендит оркестратор. git add / git commit / git reset / git checkout /
git stash не звать вовсе; файлы застейдженными не оставлять.
Карв-аут к «пишешь только в backend/»: пробные проекты в ~/books/… — легитимная запись (они вне
git, и норма «персист, не scratch» требует, чтобы сырьё платной пробы легло рядом с проектом пробы, а
не в /tmp). Это касается только пункта 5.
⚠ В дереве прямо сейчас работают ДВЕ чужие живые сессии — полигон (eval/, docs/experiments/)
и платформа (platform/). Их незакоммиченные файлы читать можно, трогать — нельзя ни при каких
обстоятельствах. .env не читать никогда (там ключи). frontend/ не трогать.
2. Карта чтения (≤5, больше не нужно)
CLAUDE.md— гардрейлы и инвариант общности (Go не ветвится по языковой паре).backend/README.md+docs/architecture/12-go-style-notes.md§0 — норматив зоны.- Тела строк 176 · 181 · 172-г · 187 в таблице «Бэклог» файла
docs/PROGRESS.md— это ЗАКАЗ; в них лежатfile:lineотправные точки. Строки писала нейросеть: якоря могли уплыть, формулировка могла устареть — код первичен, расхождение строки с кодом называй вслух в отчёте. docs/architecture/05-decisions-log.md: D39.131 п.2а (ратифицированы exit 4 потолок и 5 graceful stop + полоса отказов 10–19 со словарём классов) · D39.134 п.2 (exit 13 финализирован; там же обещание полосы) · D39.137 (пере-пин цен — откуда взялась строка 181). ⚠ Коды 0–3 этими нотами НЕ ратифицировались — их источник Milestone 2 / R1-FL-A, действующая формулировка живёт комментариемcmd/tmctl/main.go:61-75; при расхождении верь коду. Греп по номеру: сначала живой файл, потом слайсыdocs/archive/architecture/.docs/experiments/00-provider-quirks.md— только если дойдёт до пункта 5 (живой прогон вахты). ⚠ Его ЦЕНОВАЯ фактура протухла: док до сих пор пишет «цены DeepSeek не изменились (flash $0.14/$0.28…)», а они сменились 16.08 (§0 этого промта и D39.137) — бери оттуда квирки поведения, не числа; расхождение назови в отчёте, чинить док не твоя зона.
Всё остальное — код, и его ты достаёшь сам.
Инварианты зоны, которые этот пак способен сломать (полный список — backend/README.md §Инварианты
и docs/architecture/12-go-style-notes.md; сюда вынесено то, что под ударом именно здесь):
- Деньги:
reserve → call → settle+checkpoint ОДНОЙ транзакцией,committed == SUM(checkpoints)переживаетkill -9, цена — по ФАКТИЧЕСКИ ответившей модели. Пункт 1 трогает ПРОЕКЦИЮ (read-путь), а не эту цепь; уехал в саму цепь — стоп и пинг. - Снапшот-дисциплина: всё, что влияет на wire-байты ИЛИ вердикты, свёрнуто в
snapshotID; правка = громкий--resnapshot= переоплата книги. Этот пак таких правок НЕ содержит по построению. - Эхо-мина DeepSeek: thinking НИКОГДА не отключать (
reasoning:"off"там осознанный no-op, гейтechoMineViolationвалит конфиг fail-fast). Касается пункта 5. - Язык кода — английский (решение владельца 20.07,
12-go-style-notes§1): комментарии, логи, тексты ошибок, usage-строка. Русским остаются три категории: wire/инъекц-промпты, которые читает модель · lang-data таблицы гейтов · тест-данные перевода (русские/китайские строки в фикстурах легитимны и англизировать их НЕ надо — пак пишет тесты, это его прямо касается). - Деньги на платном пути: пункт 5 гоняет живую цепь
reserve → call → settleчерезrunBankAttempt— цепь не трогать, леджер не сбрасывать, расход читать из леджера пробы.
3. Состав
Пункты 1–4 — «делай РОВНО так» по ПРЕДМЕТУ (что чинится, решено); форма фикса внутри — твоя, аргументируй в отчёте. Отступление от предмета = пинг, не интерпретация.
1. Строка 181 — число согласия на пере-покупку. Проекция пере-покупки обязана считать ТОЙ ЖЕ
ценовой таблицей, которой считают резерв и расчёт, а не складывать исторические суммы. Отправные
точки заказа: internal/pipeline/rebill.go (сложение исторического cost_usd; гейт согласия и его
порог), internal/pipeline/stagerun.go (резерв и сеттл новой таблицей). Тест обязателен и обязан
падать на до-фиксном коде (сценарий: единицы куплены по старой цене → цена сменилась → согласие
спрашивают на новую сумму).
⚠ ВСЕ тексты, описывающие проекцию как сумму исторических cost_usd, после фикса обязаны стать
правдой или уйти — не один комментарий. Минимум: rebill.go:81 («over-estimating is the safe
direction») · rebill.go:280 — операторское сообщение отказа гейта, где скобка «(the sum of their
stored cost_usd)» станет ложью в самом видимом месте, в тексте согласия · status.go:150-155 ·
status.go:609 · status.go:195 — комментарий поля ProjectedBookUSD («committed extrapolated
over the whole book») противоречит самой функции, которая committed книги сознательно НЕ использует
(rebill.go:177-178). Найдёшь ещё — тем же касанием.
⚠ Развилка источника чисел — размечаю, потому что она меняет смысл числа согласия: ChunkStatus
несёт только суммарный CostUSD без токенов (store/chunkstatus.go:21-33), а мост к usage
чекпоинта (FinalHash) существует только на ok-пути — flagged-строки, которые проекция тоже считает,
к своим usage не привязаны. Значит пере-прайсить можно ФАКТИЧЕСКИМИ токенами (там, где они
достижимы) либо ОЦЕНКОЙ той же функцией, что считает резерв. Выбери и аргументируй в отчёте —
без этого две сессии дадут два разных «честных» числа.
projected_book_usd у status — В СКОУПЕ, а не «проверь и назови»: заказ строки утверждает ту же
болезнь, и оставить её значит оставить status врущим. ⚠ Развилка, которую обязан разрешить явно:
сам ПОРОГ согласия тоже считается из этой проекции — починить числитель новой таблицей и оставить базу
порога исторической значит получить полу-исторический гейт. Реши обе половины вместе и назови решение
в отчёте; считаешь, что вторая половина не влезает — пинг, а не тихое сужение.
2. Строка 176 — паника не должна читаться как успех. Непойманная паника рантайма обязана приходить наружу кодом, который потребитель прочтёт как ПРОВАЛ.
⚠ Обе полосы кодов — ЗАМОРОЖЕНЫ, это шов, их словарь двигается только ратификацией.
- 0–5 не трогать (0–3 — Milestone 2 / R1-FL-A, живут комментарием
cmd/tmctl/main.go:61-75; 4 и 5 ратифицированы D39.131 п.2а). В частности exit 2 НЕ переносить: значение «завершено с флагами» зашито у потребителя намертво (platform/internal/ingest/exit.go:24,89·platform/internal/runs/reconcile.go:485→ready), и «развести номера, оставив полосу 10–19 нетронутой» сломает шов, не нарушив буквы этого запрета — каждый честно флагнутый прогон станетfailed, а чтение денег сломается вместе с ним. - 10–19 не расширять и для паники НЕ использовать — полоса обещает, дословно комментарием кода,
«nothing reached a provider, nothing was spent, nothing was written» (
cmd/tmctl/main.go:31-32; то же у потребителя —platform/internal/ingest/exit.go:36-37; направление ратифицировано D39.134 п.2, где формулировка короче — «полоса обещает „ничего не произошло, жди"»). Паника случается посреди оплаченной работы ⇒ посадить её на 19 значит соврать наоборот. - Безопасное направление: код, который сегодняшняя платформа читает как
failed— то есть 1 либо номер ВНЕ обеих полос. Выбрал не 1 — аргумент в отчёт и пинг, потому что это словарь шва.
⚠ Три ловушки, на которых фикс выглядит зелёным и молчит на настоящем пути:
- Воркеры. Платный путь исполняется пулом горутин (
internal/pipeline/waverun.go:260), аrecoverвmain/runловит только главную. Предмет — «паника ЛЮБОЙ горутины движка не читается как успех», и тест обязан это покрывать; фикс, падающий только на синтетической панике в main, заказ не закрывает. - ⚠ Готовый соблазн в репо, который даёт ПРОТИВОПОЛОЖНОЕ заказу:
internal/obs/safego.goлогирует панику и ПРОДОЛЖАЕТ — её докстринг (:9-12) прямо обещает «dies alone instead of taking the process down», тело recover —:16-22. Обернуть им воркеров = паника посреди оплаченной работы превратится в exit 0. Проглатывающий recover здесь запрещён: паника обязана стать ошибкой, доводящей прогон до провального кода. - Обёртка ошибки. Ошибка, рождённая из recover, не должна матчиться сентинелями словаря
(
errors.Is/Asпоcontext.Canceledи типамpipeline.*,cmd/tmctl/main.go:87-111) — иначе она тихо уедет в ЛЮБОЙ не-провальный код словаря при формально нетронутом словаре: 2 флаги · 3 стоп на подпись · 4 потолок (CeilingHalt— платформа читает его какpaused) · 5 graceful · 10–19 отказ (Refusal). Прибей это тестом. Стек паники обязан доехать до stderr: диагностика важнее лаконичности.
Попутно тем же касанием (сам заказ строки): usage-строка cmd/tmctl/invocation.go:96 не знает
команд migrate / backup / seed-lint. ⚠ Её байты запинены тестом замороженного контракта
(cmd/tmctl/invocation_test.go:23) — обновить тест ВМЕСТЕ со строкой здесь санкционировано
(санкция — тело строки 176 + D39.134 п.3, это секция «Строки» ноты, то есть реестр, а не отдельная
ратификация — статус называю честно): это не подгонка под зелень, и §4-запрет сюда не применяется.
Любая ДРУГАЯ правка замороженных байтов — пинг.
3. Строка 172-г — выборочный порог в риг вахты. Внести ратифицированную форму порога
(D39.136 п.6б: «≥4/5 прогонов при N=5 достигают 6/6») в internal/pipeline/live_reprobe_test.go
вместо действующего «6/6 на КАЖДОМ прогоне». Комментарий рига, объявляющий расхождение порога и
выборки открытым вопросом владельца, обязан быть переписан: вопрос ЗАКРЫТ, сошлись на выборочной
форме. Переписать надо и комментарий, и ТЕКСТ t.Fatalf рига (:187-190, «decide with the
owner…») — он тоже объявляет вопрос открытым. Риг под тегом live — компилируемость проверяется
make battery (в vet оба набора тегов).
⚠ Порог не «смягчить», а внести ратифицированный. Подгонять гейт под желаемый результат
недопустимо; если считаешь ратифицированную форму неверной — пиши пинг, не правку. Карв-аут к
§4-запрету: правка ЭТОГО гейта и есть заказ пункта, она санкционирована D39.136 п.6б — запрет
касается всего остального.
⚠ Ратифицирована форма только для N=5; риг принимает переопределение выборки через
TM_CLASSIFY6_N (live_reprobe_test.go:103-107). Не выводи иное N «из-под гейта» — это создало бы
тихий обход: прогон с N≠5 обязан ГРОМКО отказать («порог определён только на N=5») и не
засчитываться вахтой; нужен другой N — пинг, а не своя формула.
4. Строка 187 — два лживых комментария. internal/pipeline/snapshot.go («auto/draft исключены
per D8») и докстринг internal/membank/memory.go («Approved-only») описывают до-паковое поведение;
код с пака-20 сворачивает в хеш КАЖДУЮ строку любого статуса. $0, поведение не двигается; новый текст —
по-английски, как весь код зоны.
Диспозиция соседней строки 164 — назови её явно, не решай молча. 164 («status завышает
RebillUSD: read-путь не материализует base-банк ⇒ инъекция пустая ⇒ repin недоопознан») живёт в ТОЙ ЖЕ
функции проекции, что и пункт 1. Возможных исходов два, и оба легитимны: взять её этим паком (тогда
скажи цену и покрой тестом) либо оставить (тогда напиши в отчёте, почему разделение честнее). ⚠ Чего
делать нельзя — молча починить половину так, что строка 164 станет неверной, но останется открытой.
5. Живой прогон вахты весов — ТОЛЬКО при явной санкции владельца в твоём запуске. Если
санкция названа: прогнать риг из п.3 живьём — стендовый конфиг существует, абсолютным путём это
~/books/gu-zhenren/coldrun-b/reprobe/classify6/book.yaml (проверь сам, что он на месте и что это
именно проба классификатора):
TM_LIVE=1 TM_CLASSIFY6_CONFIG=~/books/gu-zhenren/coldrun-b/reprobe/classify6/book.yaml go test -tags live -run TestLiveClassifierHarmSet -v ./internal/pipeline/.
Сырьё персистить рядом с проектом пробы (риг это делает сам — файл classifier-6of6-<effort>.json), ЛЕДЖЕР не сбрасывать, принимать только finish=stop, потратить не больше названной
владельцем суммы. ⚠ Стартовать вне пиковых окон DeepSeek (01–04 и 06–10 UTC) — в пике вызов
стоит вдвое; статус этого правила называю честно — это РЕКОМЕНДАЦИЯ при открытом схемном решении
владельца (строка 172-а), а не ратифицированная норма; следуй ей, но не ссылайся как на закон.
⚠ Thinking у DeepSeek не отключать ни при каком
уровне эффорта — это эхо-мина, и гейт echoMineViolation обязан оставаться в силе.
Санкции нет в твоём запуске — пункт НЕ исполняется, и это не obstacle, а
норма: платные вызовы без явно названной суммы запрещены.
Ревью исполнением здесь — не только кода (мандат самопроверки, CLAUDE.md): прочитай СФОРМИРОВАННЫЕ
запросы к модели и ПОЛУЧЕННЫЕ ответы своими глазами, а не только агрегат рига — совпадает ли
отправленное с тем, что ты думаешь, что отправил (модель, уровень эффорта, thinking, состав промпта),
и подтверждают ли сырые ответы вердикт, который печатает риг. Расхождение — в отчёт, а не в сноску.
⚠ Носитель для этого назову, иначе требование вырожденное: артефакт рига несёт только ОТВЕТЫ
(classifier-6of6-*.json), тел запросов там нет — их достаёшь LOG_LLM_BODIES=1 + LOG_LEVEL=debug
(cmd/tmctl/main.go:155-159) либо дампом classifierMessages.
Чего НЕ делать (жёстко).
- Не трогать ничего, что двигает провод: тела промптов, состав инъекции, версии снапшота,
RequestHash,memory_version, голден. Этот пак wire-нейтрален — все правки обязаны оставить голден бит-в-бит; расхождение голдена = стоп и пинг, а не пере-съём голдена. - Не чинить попутно другие строки бэклога, даже если увидишь. Увидел — в отчёт списком находок, оркестратор заведёт строки.
- Не ветвить Go-логику по языковой паре (инвариант общности): ревью-вопрос «заработает ли на паре, которой в репо нет, без правки Go».
- Не запускать платных вызовов вне пункта 5.
4. Самопроверка ИСПОЛНЕНИЕМ (обязательный механизм, не перечитывание)
- Батарея:
cd backend && make battery— EXIT=0, и в её хвосте список SKIP: каждый скип объясни (скип, который ты не объяснил, читается как непокрытое место). - Голден бит-в-бит — командой (
go test ./internal/pipeline/ -run Golden -count=1), а не «батарея прошла»; результат в отчёт. - Фиксы пунктов 1 и 2 (включая usage-строку) прибиты тестом, падающим на до-фиксном коде — покажи
это ИСПОЛНЕНИЕМ: временно откати свой фикс (в рабочем дереве, без git-команд), покажи красный,
верни. Формулировка «тест добавлен» без красного до-фиксного прогона не принимается.
Для пунктов 3 и 4 требование НЕ применяется и имитировать его не надо: п.4 — правка двух
комментариев, поведения не двигает вовсе; п.3 живёт под тегом
live, и «красный до-фиксный прогон» там означал бы платный вызов, запрещённый вне п.5. Их доказательство другое: п.3 — компиляция ОБОИХ тег-сетов (make batteryгоняетvetпо обоим) плюс юнит на пороговую арифметику, если она выносима из платного пути; п.4 — цитата кода против цитаты комментария в отчёте. - Дифф
^func Test— исполнением:grep -rh '^func Test' backend --include='*_test.go' | wc -lдо и после; удалённых имён тестов быть не должно (проверь по именам, не по числу). - Править или удалять существующий тест/голден/гейт ради зелени — НЕДОПУСТИМО. Несогласие с тестом — пинг, а не правка.
- Адверсариальное ревью финалом — механизмом: субагенты РАЗРЕШЕНЫ и ожидаются, спавнь. ≥2
независимых ревьюера по финальному диффу, author≠reviewer: (а) денежная линза — по строке 181
пере-считать вручную, из сырого леджера, число согласия ДВУМЯ независимыми путями и сверить с тем,
что печатает движок. ⚠ Только на КОПИИ стендовой БД, вне живого проекта —
translateпишет в проект ещё до гейта согласия (пре-флайт бэкап), и «проба на живом» испортит и стенд, и улику; (б) линза шва — инвентарь ВСЕХ кодов выхода и их потребителей ЧТЕНИЕМ кода платформы, а не по твоему списку: изменил ли пункт 2 то, что платформа читает. ⚠ Ищи грепом по ВСЕМУplatform/(ExitCode,OutcomeOf,CompletedWithFlags, числовые константы кодов), а не по двум каталогам: потребители живут минимум в четырёх местах —internal/ingest/exit.goиsupervisor.go·internal/runs/reconcile.go·internal/runner/engine.go(exit 2 на канале расчёта денег) ·internal/books/parse.go(ДЕСТРУКТИВНЫЙ switch интейка: по коду удаляется загрузка пользователя). Перечисление каталогов — как раз то, что прячет последние два. Плюс свои посадки-мутации в новые тесты (не из ожидаемого тобой списка). Артефакт — таблица «находка → диспозиция» в отчёте; ноль находок = не верить прогону, спавнить третьего. Считаешь, что для этого пака вернее другие оси ревью, — замени с аргументом; молча сузить нельзя. - Записка-план: перед сдачей — таблица «пункт заказа 1–5 → исполнено (где) / отказ (почему)», механической сверкой против §3, а не по памяти.
- Сессия длинная — интервальная самоверификация субагентом против §3 и §4 в середине работы, не только в конце.
5. Дисциплина отчёта
- Заявление = команда. Каждое число и каждая категорика отчёта — с командой, которой получены;
приёмка их пере-ранит. Эрудиция не улика: либо
file:line/замер, либо пометка «мнение». - Перед отправкой отчёта сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии — не с памятью о том, как обычно бывает.
- Последний абзац отчёта — план или обещание? Значит, сделай это сейчас, а не пиши про это.
- Obstacle reporting — обязательная секция: что НЕ удалось · что не проверено · где остановился и почему. Пустая секция при непустой работе не бывает.
- Плотность, а не объём. Без саги, без пересказа диалога, без шаблонных оговорок.
6. Канал вопросов и право отказа
Промт против кода, доков или спеки — пинг через владельца, НЕ интерпретация: промт писала нейросеть, и он может быть неправ. Право сказать «этого делать не надо» — есть, с аргументом; такое возражение ценнее тихой девиации. Промежуточный вопрос, блокирующий работу, — тоже пинг: не гадай.
7. Старт и сдача
Эхо-протокол: первым деливераблом — ≤10 строк «скоуп / инварианты / чего не делаю», ДО работы.
Сдача: запись в docs/PROGRESS.md секция «Бэкенд» (записка-план · команды · таблица
адверсариального ревью · obstacle · список находок вне заказа), дерево НЕзакоммиченное.