textmachine/docs/archive/prompts/BACKEND_MIGRATE_SESSION_PROMPT_2026-08-14.md

10 KiB
Raw Permalink Blame History

АРХИВ — ОТРАБОТАН. Исполнен бэкенд-сессией 14.08.2026, принят приёмкой оркестратора №17 (D39.134, лендинг d55edd4, 15.08): строка 174 закрыта, exit 13 финализирован. Отчёт — PROGRESS «Бэкенд» 1415.08; фикс-лист приёмки — там же. Инструкции отсюда не исполнять.

Промт: бэкенд, малое касание — tmctl migrate (строка 174: деадлок деплоя v15)

Выдан оркестратором №16 14.08.2026. Запуск — по слову владельца (запуск = ратификация варианта А строки 174). Лёгкий класс: короткий промт, инлайн-приёмка — но самопроверка исполнением обязательна, это путь у денег и данных.

0. Какую проблему решаем

Движок TextMachine — CLI-процесс (tmctl) со своим SQLite на книгу. Read-only команды (status, report) требуют ТОЧНОГО совпадения версии схемы («schema vN … expects vM», internal/store/store.go:135-143) и мигрировать не вправе; мигрирует схему только write-команда. Платформа (platform/) зовёт tmctl status --json ПЕРЕД каждым спавном прогона и для расчёта денег. Следствие — деадлок деплоя: апгрейд бинаря движка запирает ВСЕ существующие книги — status отказывает старой схеме, а write-команда, которая мигрировала бы, не наступает никогда (строка 174 единого бэклога, найдена приёмкой D39.131). Прямо сейчас это блокирует деплой свежезаленденного эмиттер-бинаря (D39.131) на стенд платформы.

Твой результат: $0-команда tmctl migrate — явный write-open книги без прогона. Платформа вставит её деплой-шагом «стоп прогонов → migrate по каждой книге → новый бинарь в работу».

1. Онбординг

CLAUDE.md (корень; гардрейлы: зона записи ТОЛЬКО backend/, ты НЕ коммитишь, .env не читать, чужие незакоммиченные файлы не трогать) → backend/README.mddocs/architecture/12-go-style-notes.md (норматив общности §0). Entry-points (код первичен): cmd/tmctl/main.go (диспетчер команд), internal/store/store.go (Open — write-путь: миграции + recoverReservations; OpenReadOnly — отказ по версии).

Эхо-протокол: ≤10 строк «что я понял» до работы.

2. Задача (делай РОВНО так по границам; форму внутри — реши сам и аргументируй)

tmctl migrate --config <book.yaml> (алиасы/форма флагов — как у соседних команд):

  • Открывает проект write-путём (store.Open) — миграции применяются, recoverReservations проходит, — печатает schema vN -> vM (или «already at vM») и выходит. Никаких LLM-вызовов, сети, трат: $0-команда, как manifest/status.
  • Идемпотентна: повторный вызов на свежей схеме — успех и no-op.
  • Exit-коды — по свежей конвенции полосы отказов (D39.131: 10 конфиг · 12 лок · 19 безымянный); успех 0.
  • Типизированный отказ read-only путей при несовпадении схемы (вторая половина задачи, ресёрч 14.08): status/report на файле чужой версии сегодня падают текстом — сделай исход МАШИНОРАЗЛИЧИМЫМ: свой exit-код (выбери место в конвенции, занятые классы не переиспользуй)
    • found/expected машиночитаемо (JSON/стандартная строка — реши форму). Цель: платформа сможет самолечиться «поймала → migrate → повтор» вместо стоп-мира. Поведение на совпадающей схеме не меняется ни на байт.
  • Тесты: фикстура/БД старой версии → migrateOpenReadOnly/status работает; лок занят → exit 12; повторный вызов — no-op; version-mismatch у status даёт новый различимый исход. Обязательный money-тест: фикстура старой схемы с ЖИВЫМИ резервациями → migratecommitted не сдвинулся ни на цент, reserved обнулён ровно по правилам recovery, схема = head; повторный вызов денег не трогает. Плюс дифф ^func Test исполнением, не памятью.

3. Границы и известные мины (приоры — опровергаются замером)

  • Деньги: recoverReservations обнуляет остаточный reserved_usd — на этом свойстве стоит ратифицированная формула потолка платформы (PD-158: аргумент = committed + прирост, БЕЗ reserved). migrate перед спавном делает status честнее, формулу не ломает — ПРОВЕРЬ это утверждение по коду и НАЗОВИ в отчёте явно; расхождение = пинг, не тихий фикс.
  • Гейт prices_checked не должен убивать миграцию. LoadModels жёстко отказывает конфигу с ценами старше 120 дней (internal/config/models.go:209-214), и строка 146 единого бэклога уже фиксирует: этот гейт стреляет на read-only/$0-путях, где перекупки нет. Если migrate соберёшь поверх полного конфиг-стека, протухшие цены превратят $0-миграцию в отказ — и деплой-деадлок вернётся ровно в той форме, которую команда лечит. Открывай store/книгу БЕЗ валидации свежести цен (или обоснуй, почему она тут уместна) — и назови выбор в отчёте.
  • Строка 49а (ALTER-шаги миграций v8v14 не идемпотентны на полу-применённой БД) — знать, НЕ чинить этим касанием (отдельная строка); твоя команда не должна усугубить (никаких новых ALTER, только вызов существующего механизма). Опция «реши сам»: проверь, транзакционно ли ПРИМЕНЕНИЕ шагов миграции (DDL в SQLite транзакционен) — если нет, обёртка применения (тела миграций не трогать!) закрыла бы класс «крэш посреди migrate = полу-применённая БД с деньгами» вперёд; бери только если ложится чисто, иначе — строкой.
  • Бэкап: write-open уже делает бэкап файла (backupStamp) — проверь, что путь migrate его наследует; помни коллизию секундной метки (строка 173) — не наступи, чинить не обязан.
  • Settle-инвариант: платформа считает деньги из committed/леджера; reserved легитимно равен нулю в любой момент (advisory, восстановим). Назови в отчёте явно, что migrate, случившийся между выходом процесса и расчётом платформы, денег расчёта не искажает.
  • Снапшоты/голдены/RequestHash не двигаются — команда не трогает промпты и провод; голден-батарея обязана остаться бит-в-бит.
  • Общность §0: ничего пар-/книго-специфичного.

4. Самопроверка и сдача

cd backend && make battery EXIT=0; самопроверка исполнением (живой прогон команды на копии проектной БД двух версий — не чтением диффа); адверсариальная вычитка собственного диффа с установкой опровергать (субагент разрешён явно); «заявление = команда». Отчёт короткий: что построено · команды проверок · взаимодействие с PD-158 · чего не делал. Итог — пингом в docs/PROGRESS.md секция «Бэкенд» — ТОЛЬКО append своей записи (файл держат параллельные сессии: CURRENT-STATE и чужие секции не трогать, перед правкой git status); дерево НЕ коммитить — лендит оркестратор.

5. Канал вопросов

Непонятно / конфликт промта с кодом → пинг оркестратору через владельца, НЕ интерпретация.