From 7bb08d8528278230bb06bf4c876164c914e7d13a Mon Sep 17 00:00:00 2001 From: heaven Date: Sat, 5 Sep 2026 00:24:51 +0300 Subject: [PATCH] Retire the interface money ban the owner withdrew, and correct a claim of mine that rested on a grep that never ran: the rule's text was there all along --- docs/PROGRESS.md | 4 +-- docs/architecture/05-decisions-index.md | 3 +- docs/architecture/05-decisions-log.md | 19 +++++++++- docs/architecture/14-api-contract/README.md | 35 +++++++++++++++++++ .../architecture/14-api-contract/openapi.yaml | 18 +++++++--- 5 files changed, 71 insertions(+), 8 deletions(-) diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 04f8d1bc..565f1b4d 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1,6 +1,6 @@ # Журнал прогресса -> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-04, голова D39.195 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №22** (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой передачи D39.184). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ПЕРВЫМ:** (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; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 101 (major 5), всего рядов 447 (пере-счёт `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-04, голова D39.196 — КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №22** (единственный носитель — здесь; роль передана 01.09, №21 закрыт нотой передачи D39.184). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ПЕРВЫМ:** (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; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 101 (major 5), всего рядов 447 (пере-счёт `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` и лечение новым прогоном). > ⛔⛔ **НО КНИГУ ЧЕРЕЗ API НЕЛЬЗЯ ДОВЕСТИ ДО КОНЦА — найдено тем же прогоном 04.09 и воспроизведено дважды.** Прирост потолка на прогон = `chaptersLeft × $0.03` (ставка `DefaultPerChapter`, занижена ×4.47 — D39.179 п.1), и после первой готовой главы это $0.27, тогда как движок при `waves.workers: 4` резервирует под редакторский шаг ~$0.28 ⇒ **отказ резервации, прогон встаёт, книга не двигается**. Два прогона подряд дали одно и то же: `committed` не сдвинулся, холд вернулся. ⇒ **занижение ставки — не «шкала короче», а СТОП ПРОДУКТА: с какого-то момента каждая следующая покупка не покупает ничего.** Лечить придётся согласованно двумя сторонами (ставка платформы + резерв движка под шаг с параллельными воркерами), носитель — ряд регистра зоны, вес `major` **за недостижимость продукта, а не за деньги**: сам стоп НЕ ЖЖЁТ ничего — замер платформы 04.09 показал отмену ДО ухода запроса (латентность 20–31 мс, `model_actual` пуст, токенов ноль), и вся книга сходится до микро-доллара по трём источникам (`request_log` 0.278319 · `spend.committed_usd` 0.278318752 · платформенный `committed` 278319 микро). > ⛔ **ЗАТО HALT НА ПРОГОНЕ, КОТОРЫЙ ПРОДВИНУЛСЯ, ЖЖЁТ — отдельная находка того же прогона и свежая датированная точка строки 78 («леджер = НИЖНЯЯ граница»).** Четыре редакторских вызова ушли параллельно; один дошёл ($0.063404) и своим коммитом сорвал потолок, три других отменены в тот же миг, **отработав 120756, 156469 и 156470 мс** — и легли в леджер нулём. Незаписанное ≈ **$0.05–0.19 на ОДИН halt** против $0.278 за всю книгу, то есть механизм, защищающий от перерасхода, сам создаёт неучтённый расход. ⚠ Списание провайдером НЕ доказано (биллинга у нас нет) — утверждается ровно наблюдаемое: вызовы шли 2–2.6 минуты, их цены в леджере нет. > ⇒ **Дыра НЕ в принципе, а в покрытии, и это делает её заказом, а не наблюдением** (разбор оркестратора 04.09 по коду): консервативная оценка уже построена и ратифицирована — `backend/internal/pipeline/stagerun.go`, греп `settling the reservation estimate to keep the ceiling honest` (pack-13 point-9), — но её условие требует ОТВЕТА провайдера (`resp.Usage`), а отменённый в полёте вызов ответа не получает и уходит другим путём. Направление лечения: сеттлить оценку при отмене вызова, который успел уйти; различитель — факт ухода (латентность кандидат, порог обязан быть обоснован замером, а не назначен). Половина ДВИЖКОВАЯ. ⚠ Прежняя формулировка пункта — ниже, она о состоянии ДО 04.09. Движок пишет файл с 30.08 (`tmctl build` — EPUB 3 + чистый txt, D39.175), и холодный прогон 31.08 предъявил это живьём: собранный EPUB настоящего перевода прошёл `epubcheck` 0/0/0/0 и передан владельцу. Открыто ровно одно: канон обещает `createExport`/`getExport`, а платформа их НЕ монтирует (смонтировано 15 операций из 20; нет также `getRun`, `updateBook`, `deleteBook`). Долг числится с P8 (`platform/BACKLOG.md` П-17), и НИ ОДИН пак платформы после P7 его не взял (проверка вместо ручного счёта, чтобы не гнило при каждом лендинге: `grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто). **Слово «отдаёт результат» ложно по букве ТОЛЬКО платформенной половиной** (п.4) · **(2) ~~холодный прогон движка~~ ОТРАБОТАЛ 31.08, закрыт РЕШЕНИЕМ ВЛАДЕЛЬЦА «не доводить»** (отчёт — `archive/reports/COLDRUN_V16_REPORT_2026-08-31.md`, числа относятся к `065d8ac`). Потрачено **$0.436110** из санкции $0.80. ПРЕДЪЯВЛЕНО живьём: объёмный потолок · D39.158 ОБЕИМИ половинами (стоп и не-стоп) · exit 16 · строки 154/198/239/240. ⚠ ~~**Причина большой доли редактора НЕ УСТАНОВЛЕНА**~~ — **УСТАНОВЛЕНА пробой полигона 02.09 по четырём осям: РЕДАКТОР ДУМАЕТ, А НЕ ПИШЕТ.** Размышление — **77.9%** цены редактора без ручки и 45.7% с `low`; у `glm-5` с выключенным thinking — 0.0%. Замер куплен и закончен ($6.17 из $9.50). ⚠ `research/30` §2.4 того же полигона повторяет «причина не установлена» — это про ДРУГОЙ вопрос (доля роли в прогоне против доли размышления в цене роли), и различение обязано попасть в ревью-шапку 30. Доля редактора идёт с ДВУМЯ знаменателями: **49.3%** от всего прогона ($0.214865 из $0.436110) и **81.3%** внутри порции П1 (⚠ ходившее «73%» — фантом, в отчёте его нет ни разу). Следующий шаг — контролируемая проба по ЧЕТЫРЁМ осям (роли осей и три требования, купленные ошибками этого прогона, — §14 отчёта; ось `stale` НЕДОСТИЖИМА — §10 там же). ⚠ **Цена: слово владельца 31.08 — ≈$0.60, потолок $0.80, ДЕСЯТЬ глав ПОЛНОЙ цепью включая редакторскую волну.** ⚠ **КЛЮЧИ: гейт снят словом владельца 30.08, разбор двух путей — D39.176 п.6.** Живое отсюда: конвенционного `.env` НЕТ НИ У ОДНОЙ книги корпуса (заполнены только `projects/textmachine/backend/.env` и `.../eval/.env`), поэтому прогон движка обязан передавать `--keys-file` абсолютным путём и только на `translate`; для п.3 нужен ДРУГОЙ файл — `TM_PLATFORM_ENGINE_KEYS_PATH`, тоже АБСОЛЮТНЫМ путём (`platform/internal/config/config.go`, греп `must be an absolute path`). Лечение операционное и $0 — направить переменную на уже заполненный файл; проверять ПРОГОНОМ, не чтением (гардрейл `.env`) · **(3) живой сквозной прогон через API платформы** — форма манифеста, сайдкара банка и карта причин флагов сочинены платформой САМОСТОЯТЕЛЬНО. ⚠ Уточнено 01.09: гейт СНЯТ холодным прогоном, и настоящие артефакты движка теперь ЕСТЬ (каталог `books/gu-zhenren/coldrun-v16/`), но сверка вышла ТОНКОЙ: на шов реально вышло **одно значение `flag_reason` из пятнадцати** (`sanitizer_stripped`), остальные четырнадцать по-прежнему только чтением кода, а манифест против `platform/internal/ingest/manifest.go` не сверялся вовсе · **(4) дверь выдачи** — строго ПОСЛЕ (3) (⚠ слово владельца 30.08: дверь ВСЕГДА строит файл — отказ юзеру не отдаётся, неполная книга уходит с пометкой, т.е. дверь зовёт `tmctl build --partial`; отказ по умолчанию остаётся операторской ручкой CLI; D29.1(б) «fail-closed» читать как «никогда молча» — честность несут пометки) · **(5) проводка `--max-units`** — снятие гейта, а не стройка: механизм в движке стоит (D39.170), живое основание гейта — `PD-422` · **(6) разморозка фронта** — ПОСЛЕ (4): зеркало отстало на СЕМЬ миноров (`frontend/docs/api-contract/openapi.yaml` — `0.2.3` против канона `0.9.0`, и первый из них — ЛОМАЮЩИЙ 0.3.0), 16 операций против 20; отставание ратифицировано D39.142 п.5 на время фриза и две ручки разъехались, размораживать раньше значит перегенерировать типы дважды. ⚠ **ДВА ЗАГЕЙЧЕННЫХ ТРИГГЕРА НАЖАТЫ И НЕ ОТРАБОТАНЫ — строка бэклога 237** (`go.work` · вендор-сверка эффорта `deepseek-v4-pro`; реестр `13-tech-debt-anchors.md` §Б-108). ⚠ **Состояние git — снимать КОМАНДАМИ, не чтением** (ветка `polygon` ушла вперёд, коммит здесь не цитирую): работа полигона лежит на ней и в `main` НЕ влита, а `git status -- eval/` в main-дереве пуст ПО ПОСТРОЕНИЮ и уводит в ложный вывод «работы нет»; ⚠ **«развязка git с origin» — арбитр ПЕРЕ-СНЯТ 04.09 и прежний вердикт БОЛЬШЕ НЕ ВЕРЕН:** `git merge-base --is-ancestor origin/main main` → **rc 1**, `git log main..origin/main` → `afa7700 temp` (коммит владельца 04.09, которым он передавал работу зон). Локальная линия ушла вперёд на семнадцать коммитов и содержит ВСЁ содержимое `afa7700` — оно снято `reset --soft` и заленджено раздельными зонными коммитами, — но формально ветки разошлись. Лечение — force-push рукой владельца; **не пуллить** (его же правило). ⚠ Пункт «развязка git с origin» в листе владельца ниже держится ТОЛЬКО как вопрос про force-push его рукой; утверждением о расхождении дерева он не является — арбитр этой пары именно здесь. @@ -344,7 +344,7 @@ | 273 | **ПАР-ШОВ ЛОМАЕТСЯ АСИММЕТРИЧНО: промпт-пак падает ГРОМКО, целевые ДАННЫЕ выключаются МОЛЧА — вместе с банком.** Отсутствие промптов пары — жёсткая ошибка загрузки («never silently substitute another pair's conventions»). Отсутствие ЦЕЛЕВЫХ данных — тишина в трёх местах: `lang.InjectionTextsFor` на незнакомом таргете возвращает пустое, обе банк-инъекции рендерят пустоту (`if !tx.HasData() { return "" }`), `LoadReaderWords` отдаёт значки вместо слов без ошибки. ⇒ Книга на новый таргет переводится ЦЕЛИКОМ и за ПОЛНЫЕ деньги, но БЕЗ банка-как-закона — то есть без единственного механизма, держащего термины консистентными на всю книгу, без $0-чекеров цели и с пометками из значков в выданном файле. Ни ошибки, ни варнинга, ни поля в отчёте сборки. ⚠ Строка 32 закрыта формулировкой «инертна ПО ОТСУТСТВИЮ данных» — для ЧЕКЕРОВ это хорошо, для БАНК-инъекции такого вывода не делал никто | бэкенд | скоро (гейт второй пары) | префлайт готовности таргета: банк-данные есть ⇒ работаем, нет ⇒ громко | критик полноты стратегии 04.09 | | 274 | **ИМЯ КНИГИ В ВЫДАННОМ ФАЙЛЕ — ИСХОДНОЯЗЫЧНОЕ, и исправить его пользователь не может.** `dc:title` EPUB — «spelled exactly as the book config spells it. It may be the source-language display name — the writer has no translation of it and invents none»; при пустом поле канон велит взять имя ИЗ ИМЕНИ ФАЙЛА. Ручки переименования нет: `updateBook` описан в каноне целым абзацем и не смонтирован (`grep -c 'method: "PATCH"' platform/internal/httpapi/v0.go` → 0). ⇒ Русский EPUB приезжает в читалку с китайским названием или с именем вроде `book_final2.txt`. ⚠ «Издательское качество» ломается на самом первом, что видит человек, а фикс дешёвый и лежит в уже заведённой строке `П-17` зонного бэклога — просто без причины её брать | платформа | скоро | смонтировать `updateBook` + титул на выдаче | критик полноты стратегии 04.09 | | 275 | **КАЧЕСТВО — ЦЕЛЬ №1 — НЕ МОЖЕТ СТАРТОВАТЬ ВООБЩЕ, и это не видно из трекера.** «ДОБОР ИДЕАЛА» (первый платный прогон качества) гейчен ЧЕТЫРЬМЯ строками сразу: **24** профили голоса не подписаны · **13а** веса рубрики K1–K12 не выбраны · **55** вне-претрейн чекпоинт не куплен · **149**/фаза Д выводы панели жильцов заморожены. Ни один из четырёх сегодня не закрыт. ⇒ Качество не «в очереди позади денег» — оно не может начаться, а из ленты ✅ читается как «идёт по плану». Механизмы качества почти все ПОСТРОЕНЫ; включены, наполнены данными и измерены — почти ни один. ⚠ Три из четырёх гейтов — РЕШЕНИЯ, а не стройка: они стоят слова, а не пака | полигон+владелец | скоро | закрыть четыре гейта, три из них словом | срез качества стратегического веера 04.09 | -| 276 | ⭐ **ЗАКАЗ ПЕРЕВОДА — ЭТО ОБЪЁМ; ОБЪЁМ МЕРЯЕТСЯ ЗНАКАМИ ИСХОДНИКА ПО ЮНИТУ, А «СТРАНИЦА» — ЭТО ИХ ОТОБРАЖЕНИЕ.** Согласовано оркестратором №22 и старшей моделью 04.09 по заказу владельца, ВТОРАЯ редакция: первая мерила заказ ГЛАВАМИ и пала, потому что глава надёжна в одном случае из трёх (китайский txt — да; EPUB режется по spine, что **неверно по стандарту** — `docs/research/27-chapter-detection.md`; прочий txt — ОДНА глава на всю книгу, `backend/internal/chunk/ingest.go`, греп `func splitTextChapters`). Дыру нашёл владелец. **Единица объёма — знаки ингестированного текста по ЮНИТУ, которые считает ДВИЖОК и публикует в манифесте**: есть у любой книги, ложится в целые юниты без второй арифметики, не расходится с тем, за что платим. **Три вида заказа:** `all` (по умолчанию) · `pages P` (доступен ВСЕГДА — «сколько вам нужно») · `through_chapter N` (ТОЛЬКО при доверенной структуре: «дочитать до конца тома» — интент структурный и страницами не выражается). Вердикт до клика и прогресс — всегда в объёме; главы — где они настоящие. ⚠ **Размер и имя страницы — ДАННЫЕ ЛОКАЛИ, не константа в Go**: RU/СНГ 1800 знаков с пробелами, EN ~250 слов, DE 30 строк, CN 千字 — требование интернациональности D39.176 п.4. ⚠ Округление объёма — ВВЕРХ, точное число показывается ДО клика («≈120 страниц → переведём 121.4 до конца фрагмента»); это обещание об ОБЪЁМЕ, не денежная арифметика — человека не тарифицируют за страницу, холд идёт по проекции юнитов, списание по факту. Порог «книга короче десяти глав» исчезает по построению. Строки исполнения — 277–280, замер — 281, ловушка счётчика — 282, структура выдачи — 283 | движок+платформа+фронт | скоро | решение принято; исполняется 277–283 | согласование 04.09, вторая редакция после находки владельца | +| 276 | ⭐ **ФОРМА ЗАКАЗА ПЕРЕВОДА — решение владельца 05.09, ратифицировано D39.196.** На аккаунте **баланс в ДЕНЬГАХ** и он виден. Человек стартует перевод; хочет задать потолок — тянет ползунок **в деньгах**; рядом ответ на его настоящий вопрос: **хватает на полную книгу или нет**. Хочет задать объёмом — второй ползунок: **ГЛАВЫ когда книга распозналась**, **ЗНАКИ когда нет** (о нераспознавании говорим ЯВНО). Старт = **холд с баланса**. Отдельная кнопка **«перевести книгу целиком»** — за целую книгу оценка точнее, чем за произвольный кусок. ⚠ **Две ручки связаны проекцией:** объём — ЗАКАЗ (главная), деньги — ЛИМИТ (вторая); тронутая последней ограничивает, другая пересчитывается и показывается производной («5 глав ≈ $0.55, зарезервируем до $0.8»); минимум денежного ползунка = неделимый шаг движка, чтобы ползунком нельзя было выразить невозможный заказ. Не тронута ни одна ⇒ заказ = вся книга. ⚠ **Цифра на экране — ПАРА, а не точка:** «ожидаемо ≈ $1.1, зарезервируем до $1.6, спишем по факту», и закрытие фактом после прогона — проекция имеет дисперсию (редакторские вызовы 25–90 % worst-case), точечное число превратится в «сказали 1.14, списали 1.31». Главный ответ — фраза, цифра рядом и мельче. ⚠ **Запрет «денег в UI» ОТОЗВАН владельцем** (D39.196 п.2): виден баланс, потолок, холд; цены моделей и стадий — нет. «Страница» из контракта убирается совсем — владелец назвал ЗНАКИ, форматирование «≈67 стр.» это локаль фронта. Исполнение — 277–283 | движок+платформа+фронт | скоро | решение принято и ратифицировано D39.196; исполняется 277–283 | решение владельца 05.09 + сведение с внешним рецензентом | | 277 | **ШАГ 1, ГЕЙТ ДЛЯ ВСЕГО ОСТАЛЬНОГО: отказ резервации = «не начинать новое», а не авария книги.** Сегодня отказ ОДНОГО из N параллельных резервов зовёт `cancel()` производного контекста и гасит уже допущенные вызовы (`waverun.go`, общий путь ошибки; `stagerun.go`, ветка `ReserveDeniedBook`). Чинится: защёлка на уровне кормильца (перестать раздавать индексы по первому отказу), доработка допущенного, штатное завершение с леджером; при непустом полёте — ждать settle, потому что settle ≤ оценки и запас растёт; поздняя отмена сеттлится ОЦЕНКОЙ по уже ратифицированному пути. ⚠ Без этого ЛЮБОЙ заказ упирается в ту же стену, поэтому строка идёт ПЕРВОЙ. Прецедент внутри пакета: объёмный потолок уже построен так («THE STOP IS A COMPLETION, NOT A HALT», `backend/internal/pipeline/volume.go`), и три места уже деградируют по `errReserveCeiling` вместо аборта. Закрывает `PD-441`. Пять рисков реализации названы зоной — в промт дословно | бэкенд | скоро | защёлка + отказ гасить допущенное + посадка «под потолком волна погасила допущенный вызов» красной | разбор зоны движка + старшей модели 04.09 | | 278 | **ШАГ 2: движок публикует ПРОЕКЦИЮ ЦЕНЫ и говорит, сколько не хватило.** В `manifest --json` и `status --json` — `book_once_usd` (книжное слагаемое, терминология: расход КНИГИ, а не юнита — иначе короткая книга получит заниженную оценку там, где ей больнее), по главам `expected_usd`, и `step_max_usd` (самый крупный неделимый резерв). Считается из СВОИХ цен и фертильности, БЕЗ константы. Плюс `next_step_micro_usd` в событие `ceiling` — сегодня «сколько не хватило» умирает в stderr транзиентного юнита, хотя движок это число печатает. Плюс денежный леджер в `finished` по образцу объёмного. ⚠ Всё аддитивно, версию манифеста не двигает. Даёт форму строке **166**. ⚠ **ВТОРАЯ РЕДАКЦИЯ (04.09): манифест получает по ЮНИТУ `source_chars` (руны ингестированного текста, ВКЛЮЧАЯ пробелы — так, чтобы человек мог пересчитать в редакторе) плюс раздельно ПЛОТНЫЕ/РАЗРЕЖЕННЫЕ знаки** (движок уже умеет: `backend/internal/text/runes.go`, Han/кана/хангыль ≈ 1 токен, прочее ≈ ⅓), **и `structure: detected|declared|none`** — признак доверия к нарезке, без которого вид заказа `through_chapter` предлагать нельзя. Всё аддитивно, версию манифеста не двигает | бэкенд | скоро | три аддитивных поля + событие | согласование 04.09; требование «отказ обязан называть недостающую сумму» — зона платформы | | 279 | **ШАГ 3, КОНТРАКТ (минор, полоса ломающих правок мажора 0 законна): заказ объёмом.** `ceiling_chapters` → `chapters` со смыслом ОБЪЁМА (`null` = вся книга) · `CeilingBounds{min,max,default}` → `OrderOptions{chapters_left, affordable_chapters, verdict: covers_all|covers_part|covers_none, blocked}` · `PausedReason` += `run_limit_reached` (и `credit_exhausted` СТАНОВИТСЯ правдой — сегодня лжёт при 34% баланса, наблюдение H14) · `Run` += `ordered_chapters`/`delivered_chapters`. ⚠ Фразы — ДАННЫМИ по коду причины и локали, не литералом. ⚠ Наружу по-прежнему НЕ уходят: суммы в любом виде, имена моделей и стадий, слова «резервация»/«оценка»/«worst case». Ремедиа-таблица `resumeRun` упрощается: «Продолжить» — новый прогон ТОГО ЖЕ заказа | контракт | скоро | минор контракта одним актом с платформенной половиной | согласование 04.09 | diff --git a/docs/architecture/05-decisions-index.md b/docs/architecture/05-decisions-index.md index a0375305..b1d79a53 100644 --- a/docs/architecture/05-decisions-index.md +++ b/docs/architecture/05-decisions-index.md @@ -1,4 +1,4 @@ -# Реестр D-нот — карта актуальности v2 (D1–D39.195; титул — носитель головы, бампать при каждом аппенде) +# Реестр D-нот — карта актуальности v2 (D1–D39.196; титул — носитель головы, бампать при каждом аппенде) > ⚠ **Колонку «тело» `counts.py --check` НЕ сторожит по устройству:** он сверяет полноту НОМЕРОВ, а не > место тела, поэтому колонка держится дисциплиной лендинга. Не нашёл тело по колонке — иди в слайсы, @@ -254,3 +254,4 @@ | D39.193 | 04.09 | **Три ратификации по оси рода, выданные каналом и НЕ внесённые в журнал** (долг оркестратора, предъявлен приёмкой: греп `KeyTrusted` по D-логу → 0). Производитель рода — КЛАССИФИКАТОР (ноль новых вызовов, закрытый словарь парсера; следствие: банковый контур в c1 — предпосылка) · замок расщеплён: род гейтится МАТЧЕМ (`KeyTrusted`), а не статусом, в ОБОИХ блоках · ⚠ дедуп закон-блока по голому `src` РОНЯЛ ПОДПИСЬ — ратификация оркестратора была неверна, принята редакция зоны: уступает только неподписанная | жив | ЖИВОЕ: ратификация без проверки ключа уникальности предмета вносит дефект; каждая посадка предъявляется КРАСНОЙ | род ратификация дедуп | | D39.194 | 04.09 | **Платформенный пак «закрыть цикл» ПРИНЯТ и ЗАЛЕНДЖЕН `adf5e53`** (39 файлов; цикл замкнут живьём — платный EPUB через API, `epubcheck` 0/0/0/0). Строка бэклога 246 закрыта, её якорь мёртв ОТ ЛЕЧЕНИЯ. **Контрактный минор 0.10.0** описывает построенную дверь: `failure_code` — enum из четырёх кодов, `Export.url` — `uri-reference` с честным механизмом вместо несуществующей капабилити, третий адрес получает `downloadExport`, шестнадцатая причина — `wrong_language`. Ошибка ратификации «вхождений два» исправлена ДО правки: второе — `about:blank` по RFC 9457. Отзыв причины смертей стенда (не хост, а группа процессов сессии) и вышедшая из него `PD-447`: рецепт глушил коллизию портов ложной зеленью. **Три класса ложной зелени** — упавшая сборка · обрезанный вывод · ЭКВИВАЛЕНТНАЯ ПОСАДКА (класс оркестратора). Гейт чисел получил два носителя: титул реестра нот и живой счёт регистра. `classifierVersion` НЕ двигать | жив | ЖИВОЕ: SURVIVED не находка, пока посадка не показала, что меняет поведение; бамп версии должен смене, способной пере-вердиктить СУЩЕСТВУЮЩИЙ снапшот | лендинг контракт минор ложная-зелень мутации гейт | | D39.195 | 04.09 | **Движковый вайр-батч ПРИНЯТ и закрыт** (`b8154cd`·`c77c3ba`·`ecf063e`·`2e13cd1`; каталог мутаций 120→141, 136 красных, 0 переживших, 4 протухшие записи найдены новым пер-записным вердиктом). Строки заказа **210·113·46·134·140·184 ЗАКРЫТЫ**, 232 остаётся (чужой пак). ⚠ **Петля СВЁРНУТА решением владельца, а не замкнута доказательством** — цена названа: дважды подряд дефект вносил ФИКС предыдущего круга, круг 3 добавил ~2000 строк и не ревьюил из них ни одной (строка 266). Заведено 13 строк (256–268) формулировками зоны. `classifierVersion` не двигаем. **Три класса ложной зелени** (упавшая сборка · обрезанный вывод · ЭКВИВАЛЕНТНАЯ ПОСАДКА) и **граница инструмента с замером**: каталог доказывает поведение и молчит о том, что код утверждает о себе — 4 сверх-утверждения круга 3, ни одного не нашла посадка | жив | ЖИВОЕ: SURVIVED не находка, пока посадка не показала, что меняет поведение; ложный ПИН дороже пропущенной посадки; вопрос к комментарию — «что сломалось бы, если это правда» | приёмка движок петля мутации каталог | +| D39.196 | 05.09 | **Форма заказа перевода — как у обычного харнесса (решение владельца).** Баланс аккаунта — ДЕНЬГАМИ на экране; ползунок потолка — деньгами; рядом ответ «хватает на полную книгу или нет»; второй ползунок объёмом — ГЛАВЫ когда книга распозналась, ЗНАКИ когда нет (о нераспознавании говорим ЯВНО); старт = холд с баланса. ⛔ **Правило «денежных полей в UI нет вовсе» ВЛАДЕЛЬЦУ НЕ ПРИНАДЛЕЖАЛО** («я такого правила не ставил») — текста в журнале НЕТ, живёт только в производных; удаляется как ложная атрибуция, не как снятие запрета. Область: баланс, потолок, холд — да; цены моделей и структура расходов — нет | жив | ЖИВОЕ: процент остатка честен как СИГНАЛ ТРЕВОГИ и бесполезен как ответ на вопрос «хватит ли на заказ» | заказ форма деньги-в-UI баланс холд | diff --git a/docs/architecture/05-decisions-log.md b/docs/architecture/05-decisions-log.md index 540190d5..46b5ee0a 100644 --- a/docs/architecture/05-decisions-log.md +++ b/docs/architecture/05-decisions-log.md @@ -1,4 +1,4 @@ -# Журнал решений оркестратора — контракт D1–D39.195 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`) +# Журнал решений оркестратора — контракт D1–D39.196 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`) > **⟶ КАРТА АКТУАЛЬНОСТИ (ревизия D31, продлена до D38.2 [12.07]; исторические записи ниже НЕ переписываются — дисциплина D23.3).** Работая с контрактом (греп номера: живой файл → слайсы, целиком НЕ читать — D39.125), держи под рукой, что чем перекрыто: > ⚠ **Эррата 09.08 (D39.125):** D39.111 п.1 предписывал промту S3 «максимум = баланс МИНУС открытые холды» — формула ОШИБОЧНА (вычитание дважды), исправлена D39.115 п.2(а): максимум = Balance КАК ЕСТЬ; тело D39.111 живёт ниже в этом файле (голова D39.106+). @@ -2178,3 +2178,20 @@ CJK-цели · закон-блок редактора единый, марке зоной: не «правда ли то, что здесь написано», а **«что должно было бы сломаться, если это правда, и ломается ли оно»**. ⚠ И ложный ПИН дороже пропущенной посадки: `WB36` гарантировала одно, рапортовала другое, и следующий пошёл бы чинить не-дефект. + +## D39.196 — ФОРМА ЗАКАЗА ПЕРЕВОДА: баланс и потолок — ДЕНЬГАМИ на экране; запрет «сумм в UI нет» СНЯТ (решение владельца 05.09). ✅ + +**1. Экран старта перевода — как у обычного харнесса, дословно по владельцу.** На аккаунте — **баланс в деньгах**. Человек стартует перевод и, если хочет задать потолок, тянет **ползунок в ДЕНЬГАХ**; рядом мы отвечаем на его настоящий вопрос — **хватает на полную книгу или нет**. Если он предпочитает задавать не деньгами, а объёмом, есть второй ползунок: **в ГЛАВАХ, когда книга распозналась**, и **в ЗНАКАХ, когда не распозналась** (о факте нераспознавания говорим ЯВНО). Старт — **холд с баланса**. + +**2. ⛔ ЗАПРЕТ «ДЕНЕЖНЫХ ПОЛЕЙ В UI НЕТ ВОВСЕ» ОТОЗВАН ВЛАДЕЛЬЦЕМ 05.09.** Дословно: **«я такого правила не ставил, надо от него избавляться»**. +⚠ **ЭРРАТА К ЭТОМУ ЖЕ ПУНКТУ, вписана оркестратором сразу: первая редакция утверждала «текста правила в журнале НЕТ» — это ЛОЖНОЕ ОТРИЦАНИЕ, выведенное из СЛОМАННОЙ КОМАНДЫ.** Греп шёл с `|` без `-E`, то есть в basic-regex вертикальная черта — литерал, и находил только собственную цитату паттерна. Правильный `grep -E` даёт два хита. **Текст ЕСТЬ и записан как СЛОВО ВЛАДЕЛЬЦА** — тело D39.84, перечень «(1) Восемь решений владельца»: «оплат в MVP нет, денежных полей в UI нет вовсе (расход остаётся телеметрией владельца; потолки/rebill-согласие движка канон §5 не трогает)». ⇒ Верная форма — **владелец ОТОЗВАЛ собственное решение** (суперсид п.(1) D39.84 в части денег), а не «оркестратор приписал ему чужое». ⚠ Оговорка, которую из текста снять нельзя: нота пришла РЕЛЕЕМ отчёта фронт-сессии, и что там было прямой речью владельца, а что пересказом релея — по тексту не различимо; ровно та же неразличимость, что D39.112 п.3 пометил у СОСЕДНЕЙ ноты. +⚠ **Класс ошибки — мой, и он тот самый, который я весь день правил у других: отрицательное утверждение, выведенное не из проверки, а из команды, которая не сработала.** Канон это прямо запрещает («отрицательное утверждение из отсутствия упоминания НЕ выводится»), и я нарушил его в НОТЕ. Поймал внешний рецензент, пере-запустив мою же команду. **Норма, купленная этим: команда, на которой стоит отрицание, приводится В ТЕКСТЕ и обязана быть воспроизведена другим исполнителем.** +**Носителей запрета БОЛЬШЕ ДВУХ — все обязаны быть закрыты паком формы заказа:** `05-decisions-log.md:222` (тело D39.84) · `05-decisions-log.md:246` (D39.100 К-8: «запрет денег на экране D39.84 в силе и НЕ superseded») · `docs/product-requirements.md` ПТ-35 · `docs/architecture/15-money-path.md` §«Запрет денег на экране» · `14-api-contract/openapi.yaml` §Transport и §Credit · зеркало фронта `frontend/docs/api-contract/openapi.yaml` · `frontend/docs/API_CONTRACT_INPUT.md` §4.8 · `platform/docs/ENGINEERING_STANDARDS.md` (зонный, чинит зона). + +**2а. Что теперь верно.** На аккаунте — **баланс в ДЕНЬГАХ**, и он виден. Потолок заказа — деньгами. Наружу выходят баланс, потолок и холд. НЕ выходят: цены моделей, стоимость стадий, структура наших расходов (ПТ-33 цел — это отдельная норма и она владельцем не отменялась). + +**3. Почему прежняя форма была абсурдной, и это надо записать, чтобы не вернулись.** Баланс хранится в деньгах (микродоллары), а наружу отдавался ПРОЦЕНТОМ неистраченного от начисленного (`RemainingPercent`, «a percentage, never an amount»). Человек, задавший объём заказа, из «осталось 70 %» не может понять ничего: семьдесят процентов от суммы, которую он вносил когда-то и не помнит, соотнести с ценой, которой он не видит. **Процент честен ровно как СИГНАЛ ТРЕВОГИ** («мало», «пусто») — там он и остаётся. + +**4. Что это меняет в согласованной схеме (строки 276–283).** Схема «заказ = объём, вердикт ДО клика, движку два потолка» устояла и не переоткрывается; ползунок денег — ТРЕТИЙ способ выразить тот же заказ, и вердикт «хватает на полную книгу» обязателен при любом из трёх. Развилка по разбору остаётся: главы — когда структура распознана, знаки — когда нет, и о нераспознавании говорим прямо. ⚠ «Страница» как отображение знаков — вопрос подписи, не механизма; размер страницы у разных рынков разный (D39.176 п.4), поэтому в контракт едут ЗНАКИ. + +**5. Не отменяется:** структура глав для ВЫДАЧИ (строка 283) — читатель не-CJK txt получает книгу одним полотном при любой единице заказа, гейт беты; ложная подпись «Знаков» у счётчика байтов файла (строка 282) — она же вход пути «не распозналось»; движковая половина (строка 277) первой — без неё в стену упирается любой путь. diff --git a/docs/architecture/14-api-contract/README.md b/docs/architecture/14-api-contract/README.md index ff5c6629..5e865b38 100644 --- a/docs/architecture/14-api-contract/README.md +++ b/docs/architecture/14-api-contract/README.md @@ -946,6 +946,41 @@ first built format»), и условие наступило. Набор — то **Шестнадцатая причина замечания.** `off_target_lang` → `wrong_language`; разбор и ⚠ о ранге — в Приложении А. +### 2.24. ⛔ ЗАПРЕТ «ДЕНЕГ В ИНТЕРФЕЙСЕ НЕТ» ОТОЗВАН ВЛАДЕЛЬЦЕМ (05.09, прозой — не минор) + +**Что случилось.** Норма «оплат в MVP нет, денежных полей в UI нет вовсе» месяц определяла форму +продукта. Владелец 05.09: **«я такого правила не ставил, надо от него избавляться»**. Форма — +**отзыв собственного решения**: текст записан в теле D39.84 (`05-decisions-log.md:222`) в перечне +«(1) Восемь решений владельца». ⚠ Нота пришла РЕЛЕЕМ отчёта фронт-сессии, и что в ней прямая речь, а +что пересказ релея, по тексту не различимо — та же неразличимость, которую D39.112 п.3 пометил у +соседней ноты. + +⚠ **Первая редакция этого раздела утверждала «текста правила в журнале НЕТ» — ложное отрицание, +выведенное из СЛОМАННОЙ команды** (`grep` с `|` без `-E`: в basic-regex черта — литерал, находилась +только собственная цитата паттерна). Поймал внешний рецензент, пере-запустив ту же команду. Норма, +купленная этим: **команда, на которой стоит отрицание, приводится в тексте и обязана быть +воспроизведена другим исполнителем.** + +**Что теперь верно.** Баланс аккаунта — деньги и показывается деньгами; потолок заказа и холд — тоже. +Остаётся запрещённым и НЕ отменялось: цена модели, стоимость стадии, стоимость вызова, структура +НАШИХ расходов (ПТ-33). Линия: **деньги, которыми владеет аккаунт, — его; деньги, которые тратим мы, +— наши.** + +**Форма показа — пара, а не точка.** Проекция имеет дисперсию (редакторские вызовы 25–90 % от +worst-case, хвост ретраев 1.5–23 % между прогонами, D39.165 §1), поэтому точечное «$1.14» через прогон +превращается в «вы сказали 1.14, списали 1.31». Верная форма: «ожидаемо ≈ $1.1, зарезервируем до +$1.6, спишем по факту», и закрытие после прогона фактом. Главный ответ — фраза «хватит на всю книгу»; +цифра рядом и мельче. + +**Почему прозой, а не минором.** Схема `Credit` НЕ тронута: поле суммы приезжает с паком, который +строит форму заказа. Двигать канон впереди кода — дефект, за который в тот же день написана эррата +04.09-в. Проза перестаёт лгать сегодня, поле появляется со своим кодом. + +**Носители запрета, которые обязан закрыть тот же пак:** `05-decisions-log.md:222` · `:246` (D39.100 +К-8 «запрет в силе и НЕ superseded») · `product-requirements.md` ПТ-35 · `15-money-path.md` §запрета · +`openapi.yaml` §Transport и §Credit · зеркало фронта · `API_CONTRACT_INPUT.md` §4.8 · +`platform/docs/ENGINEERING_STANDARDS.md` (зонный — чинит зона). + ## 3. Зависимости: чтение → источник → строка бэклога **Правило, введённое 0.3.0 (Б-21): предупреждение о недостроенном ОБЯЗАНО нести номер строки diff --git a/docs/architecture/14-api-contract/openapi.yaml b/docs/architecture/14-api-contract/openapi.yaml index c5f67b85..e1835bde 100644 --- a/docs/architecture/14-api-contract/openapi.yaml +++ b/docs/architecture/14-api-contract/openapi.yaml @@ -15,7 +15,10 @@ info: The client reads the platform read-model only; no path below addresses the translation service. **Nothing about HOW a book is translated crosses this boundary** — no model names, no phase or - stage names, no internal vocabularies, no money sums. The projection is an allowlist: a field not + stage names, no internal vocabularies, and no cost of any model, stage or call. ⚠ **Money the + ACCOUNT owns is not in that set** (owner, 05.09): the balance, an order's ceiling and its hold are + the user's own figures and are shown as amounts. The earlier blanket «no money sums» was the owner's own + rule of 02.08 and he RETIRED it on 05.09; what stays banned is the structure of OUR spend. The projection is an allowlist: a field not named here never reaches the client. The rule binds the PROSE too — every description here is compiled into the generated client's source. @@ -816,9 +819,13 @@ paths: summary: State of the credit balance. description: | Credits are a BALANCE, not a subscription with windows: no period, no `resets_at`, no - "resets in" — the screen shows what is LEFT, as a share of the account's grants. + "resets in" — the screen shows what is LEFT. - Money SUMS never cross this boundary in any form: a percentage, never an amount. + ⚠ **«A percentage, never an amount» is RETIRED (owner, 05.09).** The account's balance is money + and is shown as money; a percentage answers «is it running out», never «will it cover this + order», and the order screen asks the second question. The AMOUNT field lands with the pack + that builds the order form — this schema is not changed ahead of it, so today the percentage is + still all this endpoint returns. What remains banned is the cost of models, stages and calls. responses: '200': description: Balance state. @@ -2540,7 +2547,10 @@ components: type: integer minimum: 0 maximum: 100 - description: Share of the account's grants still available. A percentage, never an amount. + description: | + Share of the account's grants still available. ⚠ Honest as an ALARM («low», «exhausted») and + useless as an answer to «will my balance cover this order» — the amount field that answers it + lands with the order-form pack (owner, 05.09). halt_reason: oneOf: - $ref: '#/components/schemas/AccountHaltReason'