textmachine/docs/BACKEND_MIGRATE_SESSION_PROMPT.md

92 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Промт: бэкенд, малое касание — `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.md`
`docs/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 → повтор» вместо стоп-мира. Поведение на совпадающей
схеме не меняется ни на байт.
- Тесты: фикстура/БД старой версии → `migrate``OpenReadOnly`/`status` работает; лок занят →
exit 12; повторный вызов — no-op; version-mismatch у `status` даёт новый различимый исход.
**Обязательный money-тест:** фикстура старой схемы с ЖИВЫМИ резервациями → `migrate`
`committed` не сдвинулся ни на цент, `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. Канал вопросов
Непонятно / конфликт промта с кодом → пинг оркестратору через владельца, НЕ интерпретация.