92 lines
9.7 KiB
Markdown
92 lines
9.7 KiB
Markdown
# Промт: бэкенд, малое касание — `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-шаги миграций v8–v14 не идемпотентны на полу-применённой БД) — знать,
|
||
НЕ чинить этим касанием (отдельная строка); твоя команда не должна усугубить (никаких новых
|
||
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. Канал вопросов
|
||
|
||
Непонятно / конфликт промта с кодом → пинг оркестратору через владельца, НЕ интерпретация.
|