textmachine/docs/BACKEND_HONESTY_PACK_SESSION_PROMPT.md

22 KiB
Raw Blame History

Промт: бэкенд, пак «честные числа и статусы» — строки 176 · 181 · 172-г · 187

Выдан оркестратором №18, 16.08.2026. Запуск — по слову владельца. Вес пака — лёгкий-средний: четыре точечных места, приёмка инлайн.

0. Зачем (проблема, понятная без истории проекта)

Движок — CLI (tmctl), который переводит книгу платными вызовами LLM и которым снаружи управляет процесс-супервизор (платформа). Наружу он сообщает три вещи: число денег, код выхода и вердикт гейта. Сегодня в четырёх местах сообщаемое расходится с происходящим:

  1. Число, на которое оператор даёт согласие при пере-покупке, посчитано по СТАРЫМ ценам. Цены провайдера сменились 16.08 (до ×4.4 в пике), проекция пере-покупки складывает исторический cost_usd, а резерв и расчёт считают новой таблицей. Оператор соглашается на одну сумму, платит другую. Траты защищены потолками — врёт именно ЧИСЛО СОГЛАСИЯ (строка 181).
  2. Паника Go-рантайма неотличима от успеха. Непойманная паника даёт exit 2, а 2 в контракте означает «завершено, есть флагнутые главы» — потребитель записывает упавшему посреди работы прогону «готово». Деньги при этом целы (считаются из status --json), врёт статус (строка 176).
  3. Ратифицированный порог приёмки классификатора не доехал до тестового рига. Владелец 15.08 подписал ВЫБОРОЧНУЮ форму порога, риг требует прежнюю — и сам предупреждает, что порог и выборка расходятся. Ближайший прогон вахты упадёт ложно (строка 172-г).
  4. Два комментария кода описывают снятое поведение — читатель кода получает неверный закон о том, что попадает в 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, больше не нужно)

  1. CLAUDE.md — гардрейлы и инвариант общности (Go не ветвится по языковой паре).
  2. backend/README.md + docs/architecture/12-go-style-notes.md §0 — норматив зоны.
  3. Тела строк 176 · 181 · 172-г · 187 в таблице «Бэклог» файла docs/PROGRESS.md — это ЗАКАЗ; в них лежат file:line отправные точки. Строки писала нейросеть: якоря могли уплыть, формулировка могла устареть — код первичен, расхождение строки с кодом называй вслух в отчёте.
  4. docs/architecture/05-decisions-log.md: D39.131 (половина 05 контракта кодов выхода) · D39.134 п.2 (полоса отказов 1019 и её обещание) · D39.137 (пере-пин цен — откуда взялась строка 181). Греп по номеру: сначала живой файл, потом слайсы docs/archive/architecture/.
  5. docs/experiments/00-provider-quirks.mdтолько если дойдёт до пункта 5 (живой прогон вахты).

Всё остальное — код, и его ты достаёшь сам.

3. Состав

Пункты 14 — «делай РОВНО так» по ПРЕДМЕТУ (что чинится, решено); форма фикса внутри — твоя, аргументируй в отчёте. Отступление от предмета = пинг, не интерпретация.

1. Строка 181 — число согласия на пере-покупку. Проекция пере-покупки обязана считать ТОЙ ЖЕ ценовой таблицей, которой считают резерв и расчёт, а не складывать исторические суммы. Отправные точки заказа: internal/pipeline/rebill.go (сложение исторического cost_usd; гейт согласия и его порог), internal/pipeline/stagerun.go (резерв и сеттл новой таблицей). Тест обязателен и обязан падать на до-фиксном коде (сценарий: единицы куплены по старой цене → цена сменилась → согласие спрашивают на новую сумму). ⚠ Комментарий rebill.go про «over-estimating is the safe direction» после фикса обязан стать правдой или уйти. projected_book_usd у statusВ СКОУПЕ, а не «проверь и назови»: заказ строки утверждает ту же болезнь, и оставить её значит оставить status врущим. ⚠ Развилка, которую обязан разрешить явно: сам ПОРОГ согласия тоже считается из этой проекции — починить числитель новой таблицей и оставить базу порога исторической значит получить полу-исторический гейт. Реши обе половины вместе и назови решение в отчёте; считаешь, что вторая половина не влезает — пинг, а не тихое сужение.

2. Строка 176 — паника не должна читаться как успех. Непойманная паника рантайма обязана приходить наружу кодом, который потребитель прочтёт как ПРОВАЛ.

Обе полосы кодов — ЗАМОРОЖЕНЫ, это шов, их словарь двигается только ратификацией.

  • 05 не трогать (ратифицированный словарь, D39.131; шапка cmd/tmctl/main.go). В частности exit 2 НЕ переносить: значение «завершено с флагами» зашито у потребителя намертво (platform/internal/ingest/exit.go:24,89 · platform/internal/runs/reconcile.go:484ready), и «развести номера, оставив полосу 1019 нетронутой» сломает шов, не нарушив буквы этого запрета — каждый честно флагнутый прогон станет failed, а чтение денег сломается вместе с ним.
  • 1019 не расширять и для паники НЕ использовать — полоса обещает «ничего не произошло, ничего не потрачено, ничего не записано» (D39.134 п.2г), а паника случается посреди оплаченной работы; посадить её на 19 значит соврать наоборот.
  • Безопасное направление: код, который сегодняшняя платформа читает как failed — то есть 1 либо номер ВНЕ обеих полос. Выбрал не 1 — аргумент в отчёт и пинг, потому что это словарь шва.

Ловушка, на которой фикс выглядит зелёным и молчит на настоящем пути: платный путь исполняется пулом воркер-горутин (internal/pipeline/waverun.go:260), а recover в main/run ловит только главную горутину. Предмет — «паника ЛЮБОЙ горутины движка не читается как успех», и тест обязан это покрывать; фикс, падающий только на синтетической панике в main, заказ не закрывает. Стек паники обязан доехать до stderr: диагностика важнее лаконичности.

Попутно тем же касанием (сам заказ строки): usage-строка cmd/tmctl/invocation.go:96 не знает команд migrate / backup / seed-lint. ⚠ Её байты запинены тестом замороженного контракта (cmd/tmctl/invocation_test.go:23) — обновить тест ВМЕСТЕ со строкой здесь санкционировано (ратифицированная правка контракта, D39.134 п.3): это не подгонка под зелень, и §4-запрет сюда не применяется. Любая ДРУГАЯ правка замороженных байтов — пинг.

3. Строка 172-г — выборочный порог в риг вахты. Внести ратифицированную форму порога (D39.136 п.6б: «≥4/5 прогонов при N=5 достигают 6/6») в internal/pipeline/live_reprobe_test.go вместо действующего «6/6 на КАЖДОМ прогоне». Комментарий рига, объявляющий расхождение порога и выборки открытым вопросом владельца, обязан быть переписан: вопрос ЗАКРЫТ, сошлись на выборочной форме. Риг под тегом live — компилируемость проверяется make batteryvet оба набора тегов). ⚠ Порог не «смягчить», а внести ратифицированный. Подгонять гейт под желаемый результат недопустимо; если считаешь ратифицированную форму неверной — пиши пинг, не правку. ⚠ Ратифицирована форма для N=5; риг допускает переопределение размера выборки переменной окружения — зафиксируй в коде и комментарии, что порог определён на N=5, а иное N выводит прогон из-под гейта (изобретать формулу для произвольного N — не твоя работа, это ратификация).

4. Строка 187 — два лживых комментария. internal/pipeline/snapshot.go («auto/draft исключены per D8») и докстринг internal/membank/memory.go («Approved-only») описывают до-паковое поведение; код с пака-20 сворачивает в хеш КАЖДУЮ строку любого статуса. $0, поведение не двигается.

5. Живой прогон вахты весов — ТОЛЬКО при явной санкции владельца в твоём запуске. Если санкция названа: прогнать риг из п.3 живьём (TM_LIVE=1 TM_CLASSIFY6_CONFIG=<стендовый book.yaml> go test -tags live -run TestLiveClassifierHarmSet -v ./internal/pipeline/), сырьё персистить рядом с проектом пробы, ЛЕДЖЕР не сбрасывать, принимать только finish=stop, потратить не больше названной владельцем суммы. ⚠ Стартовать вне пиковых окон DeepSeek (0104 и 0610 UTC) — в пике вызов стоит вдвое; суммы копеечные, но правило стоячее. Санкции нет в твоём запуске — пункт НЕ исполняется, и это не obstacle, а норма: платные вызовы без явно названной суммы запрещены.

Чего НЕ делать (жёстко).

  • Не трогать ничего, что двигает провод: тела промптов, состав инъекции, версии снапшота, 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 пишет в проект ещё до гейта согласия (пре-флайт бэкап), и «проба на живом» испортит и стенд, и улику; (б) линза шва — инвентарь ВСЕХ кодов выхода и их потребителей ЧТЕНИЕМ кода платформы (platform/internal/ingest/, platform/internal/runs/), а не по твоему списку: изменил ли пункт 2 то, что платформа читает. Плюс свои посадки-мутации в новые тесты (не из ожидаемого тобой списка). Артефакт — таблица «находка → диспозиция» в отчёте; ноль находок = не верить прогону, спавнить третьего. Считаешь, что для этого пака вернее другие оси ревью, — замени с аргументом; молча сузить нельзя.
  • Записка-план: перед сдачей — таблица «пункт заказа 15 → исполнено (где) / отказ (почему)», механической сверкой против §3, а не по памяти.
  • Сессия длинная — интервальная самоверификация субагентом против §3 и §4 в середине работы, не только в конце.

5. Дисциплина отчёта

  • Заявление = команда. Каждое число и каждая категорика отчёта — с командой, которой получены; приёмка их пере-ранит. Эрудиция не улика: либо file:line/замер, либо пометка «мнение».
  • Перед отправкой отчёта сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии — не с памятью о том, как обычно бывает.
  • Последний абзац отчёта — план или обещание? Значит, сделай это сейчас, а не пиши про это.
  • Obstacle reporting — обязательная секция: что НЕ удалось · что не проверено · где остановился и почему. Пустая секция при непустой работе не бывает.
  • Плотность, а не объём. Без саги, без пересказа диалога, без шаблонных оговорок.

6. Канал вопросов и право отказа

Промт против кода, доков или спеки — пинг через владельца, НЕ интерпретация: промт писала нейросеть, и он может быть неправ. Право сказать «этого делать не надо» — есть, с аргументом; такое возражение ценнее тихой девиации. Промежуточный вопрос, блокирующий работу, — тоже пинг: не гадай.

7. Старт и сдача

Эхо-протокол: первым деливераблом — ≤10 строк «скоуп / инварианты / чего не делаю», ДО работы. Сдача: запись в docs/PROGRESS.md секция «Бэкенд» (записка-план · команды · таблица адверсариального ревью · obstacle · список находок вне заказа), дерево НЕзакоммиченное.