diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index c8e5f26a..913e2ccc 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -79,7 +79,7 @@ | 142 | **Механизм «гейты флагают → точечная починка по флагу» как прод-путь**: exp20 §3.3 — $0.0002/дефект, 12/12 (при ИДЕАЛЬНОЙ детекции); механизм конкурентов (research/22:179), у нас петля пака-16 за `enabled:false` (строка 13); детекторы «выдуманное слово»/«инверсия» прототипированы эксп-21. **ДИСПОЗИЦИЯ D39.117:** ОТКЛОНЁН как замена редактора (`C2/A_law` −3.75 Holm 0.0006; «$0.0002 чинит всё» мерилось при идеальных флагах и при реальной детекции пало), ПРИНЯТ формой «механический ПРЕ-ГЕЙТ перед редактором» — брак 5 клеток против 10/23 при половинной цене ($0.00298/$0.00602); ремонт ТОЛЬКО с банком (без банка ломает канон 0.917→0.895) | владелец → бэкенд | скоро (форма решена D39.117; ⚠ **порядок задан D39.190 п.3 — стройка ПОСЛЕ шагов 0–1 полигона**, а не по первой свободной смене) | бэкенд-промт | D39.108, **D39.117**, D39.190 п.3 | | 143 | **Абсорбция норматива судейства в `09-pilot-protocol.md`**: разброс инструмента ДО замера · объявленный порог различимости · MQM-lite/error-span первичен, pairwise только для стиля · декой обязателен · запрет судьи семейства жильца арма | полигон/оркестратор | скоро ($0) | правка дока | D39.108, exp20 §7 | | 144 | **Вахта: grok `reasoning_tokens` ВНЕ `completion_tokens`** — формула цены харнессов может недосчитывать (до $0.0325 на exp20); вендор-сверка биллинга xAI при следующем прогоне с grok | полигон | когда-нибудь (следующий xAI-прогон) | вендор-чек | D39.108 (приёмка) | -| 146 | **Два техдолга движка без строки трекера** (свип 08.08): `prices_checked` (120 д) гейтит read-only `report` и $0-resume — свежесть цен проверять только на ПЛАТНОМ пути (`03-implementation-notes:244`) · `cache_ttl` ↔ `cache_write_per_m`: цена записи в кэш задана под ОДИН TTL (5 м), при другом TTL расчёт врёт (`:246`). Оба жили только в справочнике якорей, то есть вне единственного трекера · **приёмка D39.134:** ценовой гейт держит и ДЕТЕКТОР самолечения деплоя — на стенде с протухшими ценами `status` даёт 10, а не 13, и цикл «поймал 13 → migrate → повтор» не наступает | бэкенд | когда-нибудь | малое касание | свип доков 08.08, D39.134 | +| 146 | **Два техдолга движка без строки трекера** (свип 08.08): `prices_checked` (120 д) гейтит read-only `report` и $0-resume — свежесть цен проверять только на ПЛАТНОМ пути (`docs/architecture/03-implementation-notes.md:114`; ⚠ адрес испр. 10.09 — прежний `:244` мёртв, в файле 115 строк) · `cache_ttl` ↔ `cache_write_per_m`: ⚠ **испр. 10.09:** цена записи в кэш НЕ ЗАДАНА вовсе — `cache_write_per_m` встречается в `backend/configs/models.yaml` 11 раз и ненулевых среди них **0** (контроль: `attempt_s` — 8 вхождений); премия за длинный TTL антропиковская, а провайдера этого вида в конфиге нет ⇒ долг не может сработать, пока не появится провайдер с платной записью (`docs/architecture/03-implementation-notes.md:115`; прежний адрес `:246` мёртв). Оба жили только в справочнике якорей, то есть вне единственного трекера · **приёмка D39.134:** ценовой гейт держит и ДЕТЕКТОР самолечения деплоя — на стенде с протухшими ценами `status` даёт 10, а не 13, и цикл «поймал 13 → migrate → повтор» не наступает | бэкенд | когда-нибудь | малое касание | свип доков 08.08, D39.134 | | 147 | **Транспортные частичные из research/21 без носителя** (свип 08.08): п.16 HTTP/1.1-escape на последней попытке как ДИАГНОСТИКА тихого усечения 2–8k [PARTIAL, GATED] · п.17 assistant-prefill continuation на `finish=length` вместо ре-рана всего вызова [PARTIAL, DEFER]. Оба помечены в ресёрче как отложенные и ни в одной строке трекера не появились | бэкенд/полигон | когда-нибудь | замер → решение | свип доков 08.08, research/21 §16–17 | | 150 | **Sol-арбитраж — внешний контур вместо слепого чтения владельца**: пере-суд расходящихся контрастов эксп-21 (`A/F`: luna +0.84 против gemini +3.03 — расхождение 14 sd шума судьи-замены) и слепых пар эксп-22 руками владельца через подписку (третье семейство, high reasoning); пакеты готовит эксп-22 Ф0.4 (слепые метки · оба порядка · декой), транскрипты персистятся как голоса внешнего судьи; luna-клетки Sol не судит (своё семейство) | владелец + полигон | скоро (пакет арбитража — из существующего сырья) | эксп-22 Ф0.4 + руки владельца | **D39.117** | | 151 | **Отказной режим связки**: эхо-мина черновика воспроизводима на материале (глава 11 — 3/3 попыток; частота 31% на zh), связка покрыла 9/10 подряд идущих глав против 10/10 у однопроходки — единственный довод ПРОТИВ связки во всём эксп-21; движку нужен fallback при невозможности годного черновика (вариация ре-гена · эскалация модели черновика · деградация в однопроходку) | владелец → бэкенд | когда-нибудь (до МАСШТАБА) | СТОП-решение | **D39.117**, exp21 §14 | diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index f1f65c0e..fb410eb4 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,6 +1,6 @@ # Журнал прогресса -> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-08, голова D39.234 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №23** (единственный носитель — здесь; роль передана 06.09, №22 закрыт нотой передачи D39.217; ⚠ испр. 06.09: коммит передачи `c992ee5` бампнул голову и НЕ тронул номер — гейта на номер очереди нет вовсе, `counts.py` сверяет только голову, поэтому носитель разошёлся молча). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ИСПОЛНЕН, испр. 08.09 (висел как «первым» неделю после исполнения):** (0а) ревизия документации на протухшее ОТРАБОТАНА 01.09 воркфлоу `docs-staleness-revision-A` (15 срезов), провенанс находок — `D39.185`; (0б) планы доработок в бэкенд и платформу — исполняются ПАКАМИ, за 07–08.09 закрыты два движковых (`D39.225`, `D39.226`); (0в) вынос неактуального в архив идёт батчами `DOC_CLEANUP_PLAN.md` (Б14/Б15/Б17 живы). ⇒ строка ниже — не заказ, а история: (0а) ревизия документации на ПРОТУХШЕЕ — по всем зонам; (0б) планы доработок в БЭКЕНД и ПЛАТФОРМУ; (0в) вынос неактуального в АРХИВ (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **94** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 109 (major 1), всего рядов 465 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном). +> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-10, голова D39.234 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №23** (единственный носитель — здесь; роль передана 06.09, №22 закрыт нотой передачи D39.217; ⚠ испр. 06.09: коммит передачи `c992ee5` бампнул голову и НЕ тронул номер — гейта на номер очереди нет вовсе, `counts.py` сверяет только голову, поэтому носитель разошёлся молча). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ИСПОЛНЕН, испр. 08.09 (висел как «первым» неделю после исполнения):** (0а) ревизия документации на протухшее ОТРАБОТАНА 01.09 воркфлоу `docs-staleness-revision-A` (15 срезов), провенанс находок — `D39.185`; (0б) планы доработок в бэкенд и платформу — исполняются ПАКАМИ, за 07–08.09 закрыты два движковых (`D39.225`, `D39.226`); (0в) вынос неактуального в архив идёт батчами `DOC_CLEANUP_PLAN.md` (Б14/Б15/Б17 живы). ⇒ строка ниже — не заказ, а история: (0а) ревизия документации на ПРОТУХШЕЕ — по всем зонам; (0б) планы доработок в БЭКЕНД и ПЛАТФОРМУ; (0в) вынос неактуального в АРХИВ (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **94** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 109 (major 1), всего рядов 465 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном). > ⛔ **ПЯТЬ ПОТОКОВ РАБОТЫ — состояние на 07.09 (пере-снято лендингами смены №23).** > **(1) ПОЛИГОН — «ремонт прибора»** (`docs/POLYGON_INSTRUMENT_REPAIR_SESSION_PROMPT.md`, строки 319 · 300 · 265 · 143): > ⚠ **ЕДИНСТВЕННЫЙ ПОТОК, НЕ СДВИНУВШИЙСЯ ЗА СМЕНУ — сессия по промту так и не стартовала.** Блокирующая линза diff --git a/docs/architecture/01-decisions.md b/docs/architecture/01-decisions.md index c66155d2..e02975f7 100644 --- a/docs/architecture/01-decisions.md +++ b/docs/architecture/01-decisions.md @@ -14,7 +14,7 @@ - Goroutines/context — идеальная модель для тысяч параллельных LLM-вызовов; один статический бинарь без рантайма — идеально для локального режима у пользователя. - **Отвергнуто:** C++/userver — ноль переиспользования, производительность здесь не монетизируется (мы ждём сеть, а не CPU), а код пишет Claude, не владелец. Python — только оффлайн-евалы и Python-сайдкар качества (CometKiwi, морфология русского — в Go-экосистеме зрелого лемматизатора нет). TypeScript — для будущего IDE-фронтенда. -Пакеты выделены и построены — `backend/internal/{llm,ledger,obs,store}`. Первый новый нативный адаптер — **Gemini** (судья/апекс, нативный контекст-кэш `cachedContent` — НЕ anthropic-`cache_control`) ⚠ **это ПЛАН, а не построенное (испр. 05.09):** на HEAD в `backend/internal/llm/` живут `provider_anthropic.go` · `provider_openai.go` · `provider_local.go`, `provider_gemini.go` НЕТ; честная формулировка — `STACK.md` («нативный адаптер — Ф2»); нативный Anthropic-адаптер оставлен в коде **DEPRECATED-референсом** (Anthropic выброшен за дороговизну — Р4/D3, git `c7f8a95`), как образец для Gemini-адаптера, а не как код к написанию. Известная ловушка из vojo: per-adapter семантика reasoning-токенов (недоучёт до 30–44% расходов). +Пакеты выделены и построены — `backend/internal/{llm,ledger,obs,store}`. Первый новый нативный адаптер — **Gemini** (судья/апекс, нативный контекст-кэш `cachedContent` — НЕ anthropic-`cache_control`) ⚠ **это ПЛАН, а не построенное (испр. 05.09):** на HEAD в `backend/internal/llm/` живут `provider_anthropic.go` · `provider_openai.go` · `provider_local.go`, `provider_gemini.go` НЕТ; честная формулировка — `docs/STACK.md` («нативный адаптер — Ф2»); нативный Anthropic-адаптер оставлен в коде **DEPRECATED-референсом** (Anthropic выброшен за дороговизну — Р4/D3, git `c7f8a95`), как образец для Gemini-адаптера, а не как код к написанию. Известная ловушка из vojo: per-adapter семантика reasoning-токенов (недоучёт до 30–44% расходов). ## Р2. Ядро пайплайна — конфигурируемый граф стадий; выбор ядра решает пилот. **Принято (каркас) / гипотеза (ядро)** @@ -82,12 +82,12 @@ COGS (прямые ключи, **актуально — [experiments/08-cost-mod - **Prompt caching — главный рычаг** *(→ опровергнуто exp08: кэш ≈2% стоимости; ревизия D31)*, и раскладка промпта фиксируется под него (разрешение конфликта Р3↔Р5 из ревью): **стабильный кэшируемый префикс** = системный промпт роли + book brief + style sheet + резюме книги/арок (меняется редко, раз в главу-арку); **волатильный хвост после кэш-брейкпоинта** = селективный глоссарий + STM + чанк. На стадии edit кэшу ловить почти нечего (уникальный черновик в промпте) — ожидаемый cache-hit считать по стадиям, а не «≥50% в среднем». - **Батчуемость — свойство стадии**: Analyst, Terminologist, судья/оценки, книжный QA — батчуемы (−50%); черновик и редактура чанков — последовательны (STM/резюме создают зависимость, DelTA-паттерн). «С batch ≈$2» относится только к батчуемым стадиям. Batch API — Фаза 2 (к судье), не Фаза 0. - **Бюджет премиум-токенов на книгу**: полный проход премиум-моделью не должен идти сплошняком (актуальные числа — в абзаце COGS выше, exp08). Принцип сохраняется для любой премиум-эскалации (Gemini 3.1 Pro): дорогая модель — только адресно по сигналу судьи/гейтов, в пределах $-потолка (формула ниже). -- **Формула премиум-бюджета** (уточнение по запросу бэкенд-сессии, заменяет эвристику «≤2×»): в коде — конфигурируемый $-потолок на книгу, `premium_budget_usd = clamp(price_target × (1 − target_margin) − est_base_cogs, 0, hard_cap)`; для локального режима без прайса — дефолты под СЕЛЕКТИВНЫЙ apex ([exp08](../experiments/08-cost-model-v2.md)): ранобэ/роман ~$2 (полный apex жив), вебновелла — потолок-на-главу или явная `apex_fraction`; полный apex вебновеллы (~$82) дефолтом НЕ ставить (переполняет $30). «≤2× input-объёма» остаётся прикидочной эвристикой в доках, не контрактом кода. +- **Формула премиум-бюджета** (уточнение по запросу бэкенд-сессии, заменяет эвристику «≤2×»): ⚠ **испр. 10.09: в коде этой формулы НЕТ и посылки её тоже нет.** Замер: `premium_budget_usd` · `price_target` · `target_margin` · `hard_cap` — **0 хитов** по `backend/` и `platform/` (контроль: `budget_usd` — 121 хит, механизм бюджета существует). Более того, ВХОД формулы спилен: ставки за главу больше нет ни константой, ни ручкой (эррата 04.09-а к `D39.179`). Сегодняшний книжный потолок ПЛОСКИЙ — `--ceiling-usd` (`backend/cmd/tmctl/main.go`, греп `--ceiling-usd is NOT a per-run budget`). ⇒ читать как СПЕКУ, не как построенное; вопрос переоткроется, только если вернётся цена главы. Формула была: `premium_budget_usd = clamp(price_target × (1 − target_margin) − est_base_cogs, 0, hard_cap)`; для локального режима без прайса — дефолты под СЕЛЕКТИВНЫЙ apex ([exp08](../experiments/08-cost-model-v2.md)): ранобэ/роман ~$2 (полный apex жив), вебновелла — потолок-на-главу или явная `apex_fraction`; полный apex вебновеллы (~$82) дефолтом НЕ ставить (переполняет $30). «≤2× input-объёма» остаётся прикидочной эвристикой в доках, не контрактом кода. - Буфер +25% к расчётному COGS (ретраи, глоссарий-строительство, регенерации). Цены/имена моделей — только в конфиге с датой проверки. Для DeepSeek — планировщик внепиковых часов (надбавка ×2 в пик; сам планировщик — Фаза 3, до тех пор — просто не гнать массовые прогоны в пик). ## Р6. Хранилище и оркестрация: SQLite + durable jobs + чанк-чекпоинты. **Принято** -- Локальный режим MVP: SQLite (modernc.org/sqlite, CGO-free, без нативных расширений — см. Р3) для проектных данных; отдельный файл БД на книгу для банка памяти. Интерфейс хранилища тонкий — миграция на Postgres (pgx уже в vojo) при появлении сервера. +- Локальный режим MVP: SQLite (modernc.org/sqlite, CGO-free, без нативных расширений — см. Р3) для проектных данных; отдельный файл БД на книгу для банка памяти. Интерфейс хранилища тонкий. ⚠ **Испр. 10.09: условие «при появлении сервера» СРАБОТАЛО, но с другим исходом — миграции движкового стора НЕ будет.** Postgres получила ПЛАТФОРМА (`platform/go.mod`, `pgx/v5`; `platform/internal/pgstore/`), а движок намеренно остался на SQLite: шов между ними ПРОЦЕССНЫЙ (`D39.81`). Как было написано раньше, строка приглашала следующую сессию мигрировать движковый стор — против ратифицированного шва. Прежний текст: миграция на Postgres (pgx уже в vojo) при появлении сервера. - Оркестрация — **durable jobs в БД с resume**; джоба = глава×стадия. **Гранулярность сохранности — чанк, не глава** (правка red-team): каждый сырой ответ LLM персистится сразу после settle (ключ: request-hash), отдельно от смысловой TM; kill -9 посреди главы теряет максимум один незавершённый вызов. Из ключа resume исключаются волатильные части контекста (snapshot контекста фиксируется на джобу до её завершения) — иначе любое подтверждение термина инвалидирует TM-хиты уже переведённых чанков. `brief_hash` входит в ключ TM; изменение translation brief посреди книги — явная команда с показом стоимости пере-перевода. - Проверяется тестом: kill -9 в середине главы → рестарт → доплата только за прерванный вызов. - **Ключ TM подтверждён** (запрос бэкенд-сессии): консервативный вариант `(chunk_src_hash, lang_pair, model_id, prompt_version, brief_hash, context_snapshot_hash)`; перечанкование инвалидирует TM by design (ключ от src-чанка) — это осознанная плата за корректность. diff --git a/docs/architecture/02-mvp-plan.md b/docs/architecture/02-mvp-plan.md index c4c5628e..1db5706c 100644 --- a/docs/architecture/02-mvp-plan.md +++ b/docs/architecture/02-mvp-plan.md @@ -28,7 +28,7 @@ - ✅ ПОСТРОЕНО (адрес — шапка-таблица `09-target-architecture.md`): границы доверия памяти D16 (D39.2) · `tmctl status`+redrive (D15.3) · дешёвые гейты 04-unhappy §6/§9 (`checks.cheapgates`, v2 — D20.4) · output-санитайзер D30.3 (`checks/sanitizer.go`) · автопопуляция G1 — Go-майнером (`internal/miner`, D39.10/D39.14/D39.17), а не спот-локалью из exp06. Живой остаток: **диалог-флаггер парности (D29.1д)** — в `internal/checks` его нет; **чек-лист ja-книги — kana-написания сид-имён вносить aliases руками (D16.4)**. - Реальный прогон end-to-end **приёмочной книги 蛊真人 zh→ru (D18; ja→ru понижен до второго прогона при появлении ja-epub)**. *(этап A ✅ 10.07 — D24; этап B НЕ открыт: пере-прогон исполнен, планка не пройдена — шапка, D39.20/D39.22.)* - Режим онгоинга (`add-chapters`) — **сдвинут в Фазу 1.5**: гейтится resume-экономикой (D15.2 — content-addressed reuse чекпоинтов или селективный redrive; спека в Фазе 1, реализация Ф1.5). До этого онгоинг не продаётся. -- Экспорт txt/epub + отчёт (D29.1): ✅ ПОСТРОЕНА ассемблер-часть — `tmctl export` + `checks.ExportNormalize` + паспорт-rollup (`pipeline.ChapterPassport`). НЕ построено (греп по `backend/`, `platform/`, `frontend/` — 0 хитов): `annotations.json`, политика красных, цвет-мап+GREY, `human_override` LOCK; плюс release-state лестница D25.1 — ✅ **ЗАКРЫТА владельцем 05.09 (`D39.205`): копия БЕЗ пометки = книга БЕЗ ЕДИНОГО ПРОПУСКА, гранулярность — ЮНИТ. Открытого на владельце здесь НЕТ.** ⚠ испр. 05.09: reader-facing артефакт УЖЕ ЕСТЬ (EPUB настоящего платного перевода выдан через API 04.09, D39.194), так что лестница из «до артефакта» стала открытым вопросом владельца ЗДЕСЬ И СЕЙЧАС: какая ступень даёт читателю копию БЕЗ пометки и что печатать на титуле (ПТ-44). +- Экспорт txt/epub + отчёт (D29.1): ✅ ПОСТРОЕНО ЦЕЛИКОМ, включая выдачу ФАЙЛОМ — ⚠ **испр. 10.09: прежний текст говорил только про «ассемблер-часть» и был верен до `D39.175`.** Ассемблер: `tmctl export` + `checks.ExportNormalize` + паспорт-rollup (`pipeline.ChapterPassport`). Сборка книги в файл: пакет `backend/internal/bookfile/` (`container.go` · `epub.go` · `model.go` · `txt.go`), сшивка `backend/internal/pipeline/bookbuild.go`, глагол `backend/cmd/tmctl/main.go`, греп `case "build"` (`D39.175` — движковая половина, `D39.194` — платформенная дверь: EPUB платного перевода выдан живьём). НЕ построено (греп по `backend/`, `platform/`, `frontend/` — 0 хитов): `annotations.json`, политика красных, цвет-мап+GREY, `human_override` LOCK; плюс release-state лестница D25.1 — ✅ **ЗАКРЫТА владельцем 05.09 (`D39.205`): копия БЕЗ пометки = книга БЕЗ ЕДИНОГО ПРОПУСКА, гранулярность — ЮНИТ. Открытого на владельце здесь НЕТ.** ⚠ испр. 05.09: reader-facing артефакт УЖЕ ЕСТЬ (EPUB настоящего платного перевода выдан через API 04.09, D39.194). ⚠ **Испр. 10.09: хвост про «открытый вопрос владельца ЗДЕСЬ И СЕЙЧАС» СНЯТ — он пересказывал вопрос, который закрыт `D39.205` п.1** («ступень одна — книга без единого пропуска»), что говорит и первая половина этой же строки, и `docs/product-requirements.md` («Открытого на владельце по ПТ-44 НЕТ»). Вторая половина хвоста («что печатать на титуле») пуста по построению: титульной СТРАНИЦЫ в EPUB нет, уведомление едет в первый документ spine, а слова — ДАННЫЕ (`backend/configs/langpacks/ru/reader.txt`, ключи `notice.holes`/`notice.ghost`). **Правило 18+:** приёмочный корпус — только SFW. Маршрутизация чувствительного контента — не «канал B» фиксированным составом (состав D19.1/D22 отставлен паком-17: D39.25/D39.26–D39.28, шапка `09-target-architecture.md`), а `content_labels` × `accepts_labels`. Живые ограничения данных: DeepSeek для explicit ЗАПРЕЩЁН его ToS (D14.1); ⚠ эскалация на grok reasoning-ON НЕ снимает эхо на архаичном плотно-ханьском (D22.5); эротику судит только Grok — Gemini fail-closed `PROHIBITED_CONTENT` (D22.6); OpenAI в explicit-роутинг не включать; refusal-blacklist-гейт перед коммитом в TM обязателен (ветка `content_filter` мертва у OpenAI-совместимых — exp07; у Gemini 3.x жива составной строкой — D21.9). **L3-скрин обязателен до первой erotica-книги в проде (D22.7 + D25.4).** diff --git a/docs/architecture/03-implementation-notes.md b/docs/architecture/03-implementation-notes.md index 8e995ce7..21427cf8 100644 --- a/docs/architecture/03-implementation-notes.md +++ b/docs/architecture/03-implementation-notes.md @@ -53,7 +53,7 @@ style sheet, резюме книги/арок на момент старта д `extra_body` per-модель в models.yaml, мёрджится в JSON запроса OpenAI-совместимым адаптером (эмпирика полигона: thinking GLM-4.6 — таймауты ×3, Gemini молча жрёт max_tokens на thinking). - Стриминг в Фазе 0 НЕ реализуется, но интерфейс проектируется под него (второй метод в будущем не ломает - адаптеры); длинные генерации закрываются длинными attempt-таймаутами из профиля; провайдерский стриминг отнесён к Ф2. + адаптеры); длинные генерации закрываются дедлайном, ВЫВЕДЕННЫМ из бюджета вызова (⚠ испр. 10.09, пак строки 360: `backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline` — профиль даёт пол `attempt_s`, потолок `attempt_max_s` и скорость, а само время считается от `max_tokens` попытки; `attempt_s` стал ПОЛОМ, а не значением); провайдерский стриминг отнесён к Ф2. - DeepSeek-адаптер парсит **оба** варианта cache-полей usage (`prompt_tokens_details.cached_tokens` и `prompt_cache_hit_tokens`/`prompt_cache_miss_tokens`) — иначе эксперимент Фазы 0 по кэшу покажет ложный ноль. - Ответ транспорта читается через `LimitReader` (у донора кап есть только в gemini-native). @@ -89,7 +89,7 @@ excision_suspect / ok) 1:1, не изобретать заново. Метрик `configs/models.yaml` (модели/цены/таймауты, с датой проверки). В конфиг ядра входят: состав/порядок стадий, роль→модель, версии промптов, пороги гейтов, **режим инъекции глоссария (selective | full_prefix)** (Р5 требует обе схемы), токен-бюджеты сборки контекста (инъекция, STM, overlap), лимит регенераций до эскалации, -**именованные эскалационные цепочки** (список моделей; channel-aware фильтр применяет раннер), fan-out N для +**именованные эскалационные цепочки** (список моделей; ⚠ **испр. 10.09:** какую цепочку берёт прогон, решает ЗАГРУЗКА по `content_policy`, а не раннер — `backend/internal/config/pipeline.go:466`=`LOAD, not by the runner`; раннер лишь исполняет уже разрешённый хоп, `backend/internal/pipeline/escalation.go:125`=`st.ResolvedHop`. Слово «channel» отставлено — `backend/internal/config/pipeline.go:230`=`LegacyChannel is the RETIRED`), fan-out N для C2, cache TTL per-стадия. В models.yaml: цены (+cache write/read), профиль таймаутов/ретраев per-модель, `extra_body`. Зашито в раннер: циклы по главам/чанкам, ветвление по гейтам, механика эскалации/ретраев, семантика «эскалация → re-gate → флаг» (диаграмма pipeline.puml перегейтовку не рисует — реализую re-gate @@ -103,6 +103,8 @@ C2, cache TTL per-стадия. В models.yaml: цены (+cache write/read), п ## 5. [НУЖНО РЕШЕНИЕ] — СНЯТО, всё развязано +> ⚠ **§5 СНЯТ — а код на него ещё ссылается, поэтому вот куда он уехал** (заглушка, испр. 10.09, по образцу заглушки §2 выше). Три не-тестовых сайта продолжают адресовать эту секцию: `backend/internal/config/pipeline.go:471` (формула премиум-бюджета — см. `01-decisions.md`, там же сказано, что в коде её НЕТ и почему), `backend/internal/llm/llm.go:106` (провайдерский стриминг — отнесён к Ф2, §3 этого дока), `backend/internal/pipeline/render.go:34` (§3.4 того же дока). ⇒ читающий из кода приходит сюда и находит указатель, а не пустоту. + Пять вопросов 04.07 закрыты, карта развязок — в баннере вверху (тела D11/D14/D19/D23 — в слайсе `../archive/architecture/05-decisions-D1-D38.md`); пятый, не попавший в карту, — пункты оркестратору (фазовые пометки диаграмм, владелец задачи cache-hit), отработан арх-ресетом D39. Формулировки вопросов — `../archive/PROGRESS-2026-07-04-10.md`. ## 6. Что осталось от Фазы 0 живым долгом @@ -112,4 +114,4 @@ C2, cache TTL per-стадия. В models.yaml: цены (+cache write/read), п **Живой остаток (носители — бэклог-строка 146 `docs/BACKLOG.md`, греп `Два техдолга движка без строки трекера`):** - **Дневной потолок — пер-книжный** (ledger в файле проекта одной книги): `day_usd` сбрасывается в UTC-полночь и ограничивает burn-rate одной книги, а не «оператора за день по всем проектам»; общий потолок потребует shared spend-файла (раскладка «файл БД на книгу» из Р3/Р6). Платформенная половина — `PD-157`. - **`prices_checked` (120 д) гейтит read-only `report` и $0-resume** (F6): платных вызовов они не делают, но падают на устаревших ценах. ⚠ Приёмка D39.134: этот же гейт работает ДЕТЕКТОРОМ самолечения деплоя — снимать его бездумно нельзя. -- **`cache_ttl` ↔ `cache_write_per_m`**: цена записи в кэш в models.yaml задана под ОДИН TTL (боевой 5m). При `cache_ttl: 1h` реальная запись стоит ×2, а не ×1.25 — недоучёт; вторая цена/валидация нужны при осознанном переходе на 1h. +- **`cache_ttl` ↔ `cache_write_per_m`** ⚠ **испр. 10.09 — посылка долга на дереве не выполняется:** цена записи не «задана под 5m», а НЕ ЗАДАНА ВОВСЕ. Замер по `backend/configs/models.yaml` (371 строка): `cache_write_per_m` — 11 вхождений, ненулевых **0**; `cache_ttl` в этом файле — **0** вхождений (боевой `cache_ttl: "5m"` живёт в `backend/configs/pipeline-c1.yaml`), контроль — `attempt_s` 8 вхождений. Премия ×1.25/×2 антропиковская по определению, а провайдера этого вида в конфиге нет (`backend/configs/models.yaml`, греп `удалены вместе с провайдером anthropic`). ⇒ сработать долг не может, пока не появится провайдер с ПЛАТНОЙ записью в кэш; тогда нужны вторая цена и валидация при осознанном переходе на 1h.