textmachine/platform/BACKLOG.md

22 KiB
Raw Permalink Blame History

Бэклог платформы (зонный)

Ведёт зона platform/ (решение владельца 02.08, D39.84: фронт и платформа держат СВОИ бэклоги; единый бэклог docs/PROGRESS.md остаётся трекером движка/полигона/доков и фронт/платформа-строк не принимает). Нормы те же: ID стабилен навсегда, каждая петля получает диспозицию. Запросы к ДВИЖКУ сюда не пишутся — они заходят строками единого бэклога через оркестратора (пример: строки 99102). Засеян оркестратором при лендинге D39.84 — дальше правит платформа-сессия.

Диспозиции после P1 (05.08) — раздел «Диспозиции бэклога зоны» архив-слайса зонного журнала (docs/archive/platform-PROGRESS-P0-P3.md). Коротко: П-6 и П-8 ЗАКРЫТЫ, П-7 закрыт по схеме и операциям (постановка холда воркером — часть П-1), П-4 отменён и поглощён П-7, П-5 переопределён под кредитную модель и ждёт правки спеки, П-1 продолжена, П-2/П-3 не трогали. Дублировать их здесь не стали — у строки один источник истины. ⚠ Сводка датирована 05.08 и задним числом не переписывается, но по П-5 она УСТАРЕЛА: спека с тех пор правлена, ручка построена — актуальное состояние в самой строке П-5 ниже. Адрес слайса file-относительный: platform/docs/archive/platform-PROGRESS-P0-P3.md:1086=## Диспозиции бэклога зоны.

ID Хвост Вес Источник
П-1 HTTP/SSE-слой и сервисная обвязка (экс-строка 96 единого бэклога) — ⚠ ЗАКРЫТА, живого хвоста нет (диспозиция 22.08, аудит доков). Что именно построено — platform/README.md §«Что здесь будет»; последний остаток, читающая поверхность, вынесен строкой П-17 и исполнен P7 (D39.153). Форма потока и аутентификации ратифицирована D39.84/D39.85 (docs/research/23-engine-platform-seam.md); порядок деплоя против schema-mismatch движка — deploy/README.md, §«Апгрейд ДВИЖКА» закрыта D39.81, D39.84, D39.85, STACK_DECISIONS §5
П-2 Глобальный брокер рейт-лимитов провайдеров (экс-строка 97): гарды движка per-процесс (pipeline/ratelimit.go:11, mistral ~48% отказов под параллелизмом), а лимит провайдера — на ВЕСЬ аккаунт: N прогонов = N независимых гардов против общего лимита; воркер отпрашивается у платформы перед вызовом ДО второго параллельного пользователя D39.81, D39.84
П-3 Очередь и конкурентность по книге — ⚠ ИСПОЛНЕНО P4 в той форме, которая была нужна: River на том же Postgres, MaxAttempts: 1 (довод — STACK_DECISIONS §19), одновременность по книге закрыта не лизами, а частичным уникальным индексом runs_one_live_per_book в БД. ОСТАЮТСЯ ровно те части, которые нужны только со ВТОРЫМ хостом: пиннинг книги к хосту и лизы book_leases с heartbeat — сегодня хост один, и лиза без второго хоста охраняет от самой себя до второго хоста STACK_DECISIONS §5 и §19, D39.84
П-4 Учёт токенов/денег per-user + бюджет-гейт ДО старта задачи — ⚠ ОТМЕНЁН и поглощён П-7 (диспозиция P1, шапка выше). ⚠ Оговорка жива и после поглощения: леджер = НИЖНЯЯ граница траты (строка 78 единого) отменён platform/README, D39.84
П-6 OAuth-вход популярных провайдеров (Google первым) + модель аккаунта (направление владельца 04.08) — ⚠ ЗАКРЫТ P1: authorization code + PKCE, собственная серверная сессия сохраняется (D39.84 — OAuth даёт только СОБЫТИЕ входа), ключ личности (provider, subject); код — internal/login/, состав и живой остаток — диспозиция из шапки. Писатель куки с __Host-атрибутами и dev-профиль — PD-8 P1, вместе с П-1 владелец 04.08
П-7 Кредитный баланс и фри-тир (решения владельца 05.08; ПРОДАЖИ НЕТ — бета на предоплаченных ключах владельца): append-only леджер в целых микро-долларах, грант фри-тира из админки (дефолт НОЛЬ, начисление руками — tmplatformctl grant), холд ДО спавна прогона + пер-книжный потолок движку (жёсткий стоп исполняет движок), расчёт на границе попытки. ⚠ Прежний «дефолт $5» ОТМЕНЁН словом владельца 16.08 (D39.138 п.2л, PD-104); возврат $5 идёт вместе с суточным агрегатным потолком. ⚠ Окон с обнулением НЕТ — баланс, а не подписка: черновик usage_windows в подписочной форме заменяется. Платёжный провайдер не проектируется до решения продавать. Разбор — docs/PLATFORM_DIRECTION.md §2 P1+, после П-6 владелец 0405.08
П-8 Минимальная админ-поверхность: начислить/списать кредиты аккаунту (одна запись леджера), посмотреть баланс и состояние прогонов — ⚠ ЗАКРЫТ P1 формой CLI: tmplatformctl grant/adjust/balance/logins/revoke (cmd/tmplatformctl/) вместе с П-7 владелец 05.08
П-5 API лимитов/использования + оповещение стопа по потолку (решение владельца 04.08, D39.100/ПТ-35, механизм «как Claude Code»): страница лимитов в настройках читает СТАТУС использования (не суммы — деньги на провод не идут, D39.84); стоп по потолку → статус paused + оповещение «перевод остановлен: лимиты исчерпаны». ⚠ ПЕРЕОПРЕДЕЛЁН под кредитную модель (диспозиция P1, шапка выше): окон и resets_at нет, ручка ПОСТРОЕНА, спека правлена — канон несёт docs/architecture/14-api-contract/openapi.yaml:815=operationId: getUsage с прямым «no period, no resets_at», маршрут смонтирован (platform/internal/httpapi/v0.go:89=path: "/usage", греп "/usage"; хендлер — символ func (h *v0) usage). Диспозиция П-5 с тремя поправками (колонка Run.paused_reason · скоуп revision · трактовка D39.84) — platform/docs/archive/platform-PROGRESS-P0-P3.md:861=потолок поднимает платформа) — ПРИНЯТ Ф3, вместе с П-1 D39.100, ПТ-35, контракт К-8
П-9 Загрузка книги POST /books (multipart)ИСПОЛНЕНО P5 (11.08): состав в platform/README.md §«Что здесь будет»; PD-72 закрыт своим тестом. Развилка «кто пишет стартовый book.yaml при интейке» (D39.110 §2b, шов books.ErrNotProvisioned) РЕШЕНА D39.130: бета = форма Б (стройка — П-14); движковая форма В (tmctl init) = строка 170 единого Ф3, следующий пак сессия P4 (границей промта D39.119 §5а), исполнено P5
П-10 Честная оценка «$/глава» от ДВИЖКА. Сейчас ставка — константа платформы ($0.03, провенанс exp08 v2 через D30.4, STACK_DECISIONS §20), и это осознанная бета-мера: движковой поверхности оценки не существует, а выдумывать её запрещено. Ставка решает только ДЛИНУ шкалы (деньги защищены холдом и потолком движка), но на книге, которая заметно дороже или дешевле средней, шкала врёт пользователю о том, сколько глав он покупает. Нужна оценка от движка по конкретной книге — запрос уходит строкой ЕДИНОГО бэклога через оркестратора, не сюда когда-нибудь (до первого платящего) сессия P4
П-11 Наблюдаемость раннераИСПОЛНЕНО P5 (11.08): prometheus/client_golang v1.24.1 на отдельном слушателе TM_PLATFORM_METRICS_ADDR; глубина очереди · возраст самого старого открытого холда · карантины · отставание тейлера · книги в интейке · длительность свипа и счётчик недоведённых проходов · запросы и задержки по паттерну маршрута. Ось наблюдаемости получила внешний эталон (практики именования Prometheus + золотые сигналы, ENGINEERING_STANDARDS §2) — половина PD-115, вторая половина строки остаётся открытой Ф3 сессия P4, исполнено P5
П-12 Квота интейка и ретеншен отклонённых книг. POST /books даёт аутентифицированному аккаунту писать на диск оператора: один аплоад ограничен (64 МиБ), число аплоадов — ничем. Отклонённая по вине источника книга каталог теряет, отклонённая по вине деплоя — сохраняет намеренно, и не чистит их никто. Строка регистра — PD-175 ⚠ ПРОДУКТОВАЯ ПОЛОВИНА СНЯТА ЦЕЛИКОМ — D39.176 п.1 (слово владельца 30.08): продуктовых КВОТ НЕТ и не будет, фри-тир-лимиты не проектируются, живём на покупке API и зачислениях из админки; «сколько книг входит во фри-тир» — больше НЕ развилка и НЕ ждёт владельца. Остаётся ИНЖЕНЕРНАЯ гигиена, решаемая зоной без чьего-либо слова: ретеншен отклонённых (rejected) книг · свип каталогов-сирот · потолок диска. Гейт у неё один и он не продуктовый — открытая регистрация, которой в закрытой бете нет. инженерная гигиена, до открытой регистрации сессия P5; продуктовая половина снята D39.176
П-13 Оценка размера книги в символах — у ДВИЖКА. character_count контракта платформа считает потоково на приёме (байты, не являющиеся продолжением UTF-8), что точно для UTF-8 и приблизительно для GB18030/UTF-16, которые движок принимает и декодирует сам. Манифест несёт source_bytes и encoding, но не число символов. Запрос уходит строкой ЕДИНОГО бэклога через оркестратора; строка регистра — PD-177 → строка 171 единого заведена 14.08 когда-нибудь сессия P5
П-15 ИСПОЛНЕНО P6 (14.08). Платформенная половина шва эмиттера (движковая залендена D39.131, 9cfe080) ИСПОЛНЕНО P6: коды выхода движка 4/5/1019 через ingest.OutcomeOf, потолок = paused двумя каналами (PD-113 закрыт), интейк судит по коду (PD-196 закрыт), exit 5 без намерения = прерывание и перезапуск (PD-152 закрыт); фолд unit_done ПРИСВАИВАНИЕМ по тройке (chapter, unit, wave) через unit_resolutions (миграция 00015; ратифицировано D39.131 п.2г, at-least-once); StreamVersion 1.1, Ceiling.Scope (book/day), внутренняя причина daily_ceiling, резюм дневного потолка = 409 (PD-157 половина); dev-супервизор читает ту же таблицу; foreign-hello adoption воспроизведён и закрыт (PD-200); порядок деплоя — deploy/README.md + tmplatformctl books --migratable. ⚠ Граница присваивания (PD-219): оно самолечит ПОВТОРНУЮ доставку, а не пропущенную — недодрейненный хвост новая попытка не перечитывает, её курсор начинается с размера журнала на допуске приёмка D39.131
П-14 ИСПОЛНЕНО P6 (14.08). Стройка интейка формы Б (ратификация D39.130) ИСПОЛНЕНО P6: рендер в ОДНОМ месте (books.provision); правила шаблона и его отказов — deploy/README.md §«Шаблон книги», требования к полям — backend/internal/config/book.go как справочник. При приходе формы В (строка 170 единого, tmctl init) рендер заменяется вызовом движка. Проверено настоящим tmctl manifest (3 главы) ратификация D39.130 (приёмка P5)
П-16 ИСПОЛНЕНО P6 (14.08). Дев-сид стенда: тестовый пользователь, чтобы фронт разрабатывался на живой платформе, а не на своих моках (слово владельца 14.08). Границы: фикстуры фронта остаются батарее его гейтов (детерминированные состояния — не работа стенда); полный снос frontend/src/mock/ — триггер Ф-29, после читающей поверхности ИСПОЛНЕНО P6: tmplatformctl seed и дев-вход TM_PLATFORM_DEV_LOGIN с четырьмя гардами непроходимости в проде — рецепт и разбор гардов в deploy/README.md §«Дев-стенд». Прогнано на стенде целиком владелец 14.08
П-17 ИСПОЛНЕНО P7 (1617.08). Читающая поверхность — остаток П-1, названный отдельной строкой ИСПОЛНЕНО P7 по канону 0.3.0; состав поверхности — platform/README.md §«Что здесь будет» и §«Карта зоны» (internal/readmodel). ⚠ submitBankDecisions СНЯТ 22.08 вместе с пер-термной моделью подписи — D39.144, слово владельца; см. PD-370. НЕ вошло и отложено в P8: updateBook/deleteBook/getRun, экспорт (createExport/getExport), эскроу (П-18) П-1, D39.84/85, контракт 14
П-18 Эскроу денег шва: write-ahead intent · uncertain · closing (строка 136 единого бэклога) — денежный промт, сознательно НЕ взятый ни P5, ни P6. Здесь же закрывается PD-154 (settled_at между Settle и MarkSettled): половина эскроу рядом с проектируемым целым — второй, более слабый ответ на тот же вопрос денежный промт строка 136, PD-154
П-19 Конверсия свободного от склейки блока под sqlc — ⚠ ИСПОЛНЕНО отдельной сессией 29.08 и заленджено (D39.172; PD-44 закрыт). Отдельной сессией, а не внутри пака, — решение владельца 22.08: изменений много, мешать их с чем-либо нельзя. Что построено (пин v1.31.1, sqlc.yaml, генерённый код в дереве, два гейта актуальности, override *.*_micro_usd на деньги) — docs/STACK_DECISIONS.md, строка «Кодоген SQL». ⚠ Живая ГРАНИЦА набора, которую стоит помнить при следующем касании: 42 места вызова / 41 текст SQL / 40 конвертируемых; observe.go структурно не конвертируется — River мигрирует river_job сам, и внесение чужой схемы в конфиг завело бы ВТОРОЙ её носитель (испр. D39.172), поэтому худший позиционный дрейф набора — семь int64 подряд — остался рукописным. Остальные 25 склеенных мест недостижимы по построению (lastRun — девять потребителей, nextRevisionOfThisBooksLibrary — восемь), и это ровно та часть, где рантайм-ошибки и случались. ⚠ Пак покупал типизированные скан-структуры и раннюю обратную связь, а НЕ корректность: класс «нет такой колонки» закрыт постоянным гейтом TestEverySQLStatementParsesAgainstTheMigratedSchema (пол — 140 операторов; после конверсии видит 172 — числа в PD-44) ИСПОЛНЕНО (D39.172) PD-44, фикс-лист приёмки P7 п. 4, решения владельца 20 и 22.08
П-20 Хвост PD-162 после P8-FIX: холд прогона, чей движок не ответит НИКОГДА, возвращается только рукой. Пак дал оператору и диагноз (tmplatformctl runs --stalled, гейдж tm_platform_runs_stalled), и терминальный вердикт (run abandon [--release-hold]), и это сознательная граница: автоматически решать про деньги прогона, о котором нельзя спросить, — та же ошибка «я не смог спросить = его нет», только со счётчиком впереди. Автоматический ответ на этот вопрос и есть эскроу — write-ahead intent · uncertain · closing (строка 136 единого бэклога, П-18). Пока эскроу нет, ручка остаётся ручной, и это записано, а не подразумевается вместе с П-18 P8-FIX
П-21 Ключ идемпотентности под конкуренцией даёт 500 (PD-369).Не работа пака P8-FIX и файл им не тронут — найдено попутно, прогоном батареи: собственный тест TestAClaimThatLostARaceToAReleaseIsRetriedAndNotAnError ФЛЕЙКОВЫЙ, 12 падения на ~80 прогонов. Причина не в тесте: ClaimIdempotency ретраит проигранную гонку ровно claimRounds = 3 раза, а восемь одновременных попыток одного ключа, каждая из которых сразу отдаёт ключ назад (как делает любой 4xx), могут отобрать гонку у одного проигравшего трижды подряд — и он получает внутреннюю ошибку вместо одного из четырёх контрактных ответов. Направление, а не решение: граница по ВРЕМЕНИ вместо числа раундов либо ErrKeyInFlight при исчерпании — это решение о контрактном поведении до первого чужого пользователя попутная находка сессии P8-FIX