diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 3a58f65c..fac5417a 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -3,7 +3,7 @@ > **Единственный трекер проекта.** Здесь живут строки, на которые доки, промты и D-ноты ссылаются словами «строка N» / «строка бэклога N»: **ID строки стабилен навсегда**, не перенумеровывается и не переиспользуется (D39.80). Правки — только через оркестратора; каждая петля обязана иметь диспозицию: решено / отложено-с-записью / отклонено. > ⚠ **Это НЕ бэклог зоны `docs/`, а бэклог ПРОЕКТА.** У зон свои, с другими неймспейсами ID, и единый их строк не принимает (D39.84): платформа — [../platform/BACKLOG.md](../platform/BACKLOG.md) (`П-N`) и её регистр дефектов `platform/docs/DEFECT_REGISTER.md` (`PD-N`); фронт — [../frontend/docs/BACKLOG.md](../frontend/docs/BACKLOG.md) (`Ф-N`). > ⚠ **Состояние проекта — не здесь.** Очередь, курс, CURRENT-STATE, состояние паков и живая хроника — шапка [PROGRESS.md](PROGRESS.md). Здесь только долг и его диспозиции. -> - **СЧЁТ ОЧЕРЕДИ на 17.09 (скриптом по таблице — `python3 docs/scripts/counts.py`; обновлять при каждом лендинге):** всего **374** строк · зона бэкенд **200** строго / **253** широко (⚠ колонки счётчик читает С КОНЦА — испр. D39.116) · **блокеров очереди 0** (236 закрыта D39.175; **371** — актом D39.233, **360** — актом D39.232, обе 10.09), платные прогоны разблокированы · «скоро» **201** (перечень — грепом по таблице, рукописный список снят D39.126) · гейт-строка эксп-22: 153 (плюс 150 — руки владельца; ⚠ 55 из гейтов СНЯТА — D39.198 п.5); строка 5 — остаток гейчен ре-пробой 74, носитель события теперь строка 188 (аудит D39.140); остальное «когда-нибудь». ⚠ Счёт — НИЖНЯЯ граница долга, не потолок (разбор — легенда таблицы ниже). +> - **СЧЁТ ОЧЕРЕДИ на 17.09 (скриптом по таблице — `python3 docs/scripts/counts.py`; обновлять при каждом лендинге):** всего **375** строк · зона бэкенд **201** строго / **254** широко (⚠ колонки счётчик читает С КОНЦА — испр. D39.116) · **блокеров очереди 0** (236 закрыта D39.175; **371** — актом D39.233, **360** — актом D39.232, обе 10.09), платные прогоны разблокированы · «скоро» **202** (перечень — грепом по таблице, рукописный список снят D39.126) · гейт-строка эксп-22: 153 (плюс 150 — руки владельца; ⚠ 55 из гейтов СНЯТА — D39.198 п.5); строка 5 — остаток гейчен ре-пробой 74, носитель события теперь строка 188 (аудит D39.140); остальное «когда-нибудь». ⚠ Счёт — НИЖНЯЯ граница долга, не потолок (разбор — легенда таблицы ниже). > - ⚠ **Переезд 06.09 (D39.218):** таблица вынесена из `PROGRESS.md` ДОСЛОВНО и ни один ID не сдвинут — **с единственным исключением: две ячейки (строки 220 и 306) тронуты РОВНО в номере якоря `docs/scripts/counts.py:566`→`:569` и `:592`→`:595`**, потому что цель сдвинул этот же коммит; текст ячеек не менялся, длины равны. В `PROGRESS.md` на прежнем месте оставлен заголовок-указатель, поэтому ссылки «секция „Бэклог“» доезжают в один хоп. ## Бэклог (ЕДИНЫЙ, собран 26.07, актуализация 04.08 D39.99/D39.101; правки — только через оркестратора) @@ -410,3 +410,4 @@ | 499 | ⛔ **ИНГЕСТ EPUB ЧИТАЕТ СТРОКУ СТИХА КАК АБЗАЦ ПРОЗЫ.** ⚠ **ИСПР. 18.09 — ПРЕЖНИЙ ЗАГОЛОВОК НАЗЫВАЛ НЕ ТОТ ПРЕДИКАТ, и опроверг его ИСПОЛНЕНИЕМ полигон (`textmachine-b5`), а не рассуждение.** Было: «вложенность блочной цитаты умножает переносы». Неверно: `backend/internal/chunk/chunker.go:470`=`func splitParagraphs(chapter string) []string` делит по `\n\n` и **отбрасывает пустое** ⇒ избыток переносов безвреден, вложенность в десять уровней роли не играет. **Настоящий механизм виден в коде:** `blockTags` эмитит `"\n\n"` и на открытии (`backend/internal/chunk/ingest.go:1157`=`body.WriteString("\n\n")`), и на закрытии (`:1185`), тогда как `
` даёт одиночный `\n` (`:1152`=`body.WriteByte('\n')`) ⇒ **вложенный `

` внутри `

` — разметка издателя для СТРОКИ стиха — получает АБЗАЦНЫЙ разрыв**, и эпиграф из четырёх строк приезжает переводчику четырьмя независимыми абзацами прозы. ⭐ **Лечение по роду (формулировка полигона):** не «меньше переносов», а КЛАСС цитатно-стиховых контейнеров, внутри которых вложенный `

` даёт строчный перенос, а сам контейнер — абзацный; стих приезжает одним абзацем из строк, состав класса — данные. ⚠ **Побочный эффект назван автором находки ДО приёмки:** многоабзацная цитата ПРОЗЫ тоже склеится в один абзац с переносами — меньшее зло, чем разорванный стих, но решение владельца предмета. ⛔ **Альтернатива «абзац не кончается знаком конца предложения ⇒ это строка» ОТВЕРГНУТА обеими сторонами:** знаки препинания у пар разные, это ветвление по языку, которого инвариант не допускает. ⚠ Дефект живёт только на EPUB-парах: китайская боевая пара идёт из txt и потому не проявляет его — он прожил незамеченным именно поэтому. ⚠ Адрес `ingest.go:614` из пинга полигона относится к ДРУГОМУ срезу (основной чекаут против воркри); в этом ряду адреса мои. `backend/internal/chunk/ingest.go:978`=`"h5": true, "h6": true, "li": true, "blockquote": true` — каждый блочный тег отдаёт перенос, и набор существует НАМЕРЕННО: соседний докблок (`:975`=`survives extraction; without them a whole chapter collapses into one paragraph`) объясняет, что без него глава схлопывается в один абзац. ⛔ Но у книги, где эпиграф набран ВЛОЖЕННЫМИ `blockquote` — по одному на типографскую строку, — переносы умножаются на глубину. **Замер пере-снят оркестратором своим прибором и сошёлся с зонным знак в знак: 382 тега `blockquote` в 12 документах из 126, максимальная вложенность 10.** Следствие на входе модели (замер сессии по всем 71 главам движка): непустых абзацев 11 203, из них **726 не кончаются знаком конца предложения** — 6.5 % абзацев и 1.7 % рун, то есть обрывки фразы; прогонов из 8 подряд переносов — 101, из 16 — 66, встречается до 24. Обычная проза при этом ЦЕЛА (контроль: 2532 абзаца в крупнейшей главе) ⇒ предмет именно вложенность, а не блочность. ⛔ **Цена — качество перевода:** модель получает стих как набор обрывков, разделённых восемью пустыми строками, и сшивает их по своему усмотрению. ⚠ **Сегодня это обойдено ПРАВИЛОМ В ПРОМТЕ английской пары («собери такой блок в целую фразу»), и сессия сама назвала это КОСТЫЛЁМ вслух** — обход прячет цену, пока нет механизма. Лечение по роду: вложенная цитата есть ОДИН блок, а не N; условие снятия костыля — пин, краснеющий на вложенности ≥2 | бэкенд | скоро | вложенный `blockquote` даёт один перенос, а не по одному на уровень; пин на книге с вложенностью ≥2; правило-костыль снято из промта и названо в отчёте | замер `textmachine-main-c5` 17.09, пере-снят оркестратором (382/12/10 — сошлось) | | 500 | ⚠ **ПОДОЗРЕНИЕ, А НЕ ЗАМЕР: ИНГЕСТ EPUB, ПОХОЖЕ, ТЕРЯЕТ КУРСИВ ЦЕЛИКОМ — и носитель курсива тут НЕ инлайн-тег.** `backend/internal/chunk/ingest.go:1085`=`func extractXHTML(data []byte) (string, []RubyReading, error)` пишет текстовые токены; разметка следа не оставляет. Для художественного перевода курсив несёт эмфазу, мысль героя, чужеязычное слово и эпиграф — авторское средство, и восстановить его данными нельзя: его нет на входе. ⛔ **Разметка честная: сквозного замера «курсив не доезжает до чанка» НЕ делал никто** — ни сессия, ни оркестратор; сделано только чтение кода и счёт спанов. ⚠ **И поправка к МЕХАНИЗМУ, снятая оркестратором: `` — 0, `` — 0.** Курсив в этой книге несёт **CSS-класс**, и счёт спанов расходится по популяциям: у оркестратора 4842 (`class="…ital…"`), у советчика сессии 4863. ⇒ формулировка «инлайн-теги не оставляют следа» описывает не тот носитель, и починка «читать ``/``» не сработала бы вовсе: читать надо класс. Прежде чем строить — сквозной замер на одном документе: доезжает ли хоть один курсивный спан до текста чанка | бэкенд | скоро (сперва ЗАМЕР, потом решение) | предъявлен сквозной замер «курсив на входе → курсив в чанке» с названным знаменателем; по его итогу — ряд на починку либо закрытие с доводом | подозрение сессии `textmachine-main-c5` 17.09; механизм поправлен оркестратором (класс, а не тег) | | 501 | ⚠ **АНГЛИЙСКАЯ ОСЬ ПОЛИГОНА СНЯТА НА СПЛОШНОМ БЛОКЕ: ИСКАЖЁН АБСОЛЮТНЫЙ УРОВЕНЬ, КОНТРАСТЫ АРМОВ ЦЕЛЫ.** ⛔ **Разметка: это утверждение ПОЛИГОНА (`textmachine-b5`) о собственной работе, оркестратором НЕ пере-снятое**; носителем оно стало потому, что на этих числах стоят промты боевой английской пары — то есть предмет ратификаций, а не внутреннее дело зоны. Английский экстрактор полигона (`pair_en.raw_text`) сносил из epub теги регексом вместе с `

`: `eval/dovodka/podacha.py:19`=`11 447 абзацев, медиана 62 знака` — модель получала сплошной блок вместо одиннадцати тысяч абзацев. **Затронуты ВСЕ 273 английские клетки**, а не отдельные армы: восстановленную подачу получает единственный арм `RA` (`eval/dovodka/rol.py:162`=`RESTORE_ARMS = ("RA",)`), и он НЕ КУПЛЕН — 0 клеток ⇒ **цена дефекта не измерена ни разу**. ⭐ **Но контрасты держатся, и дефект был обойдён ОСОЗНАННО, а не пропущен:** вход у всех армов одинаково плоский, и довод автора стоит в самом коде (`eval/dovodka/rol.py:126`=`RA / RN — эффект ПОДАЧИ`; парная строка выше даёт `RN / ZP` как эффект ПРОМТА при одинаковом входе) ⇒ сравнение армов между собой чисто, искажён общий УРОВЕНЬ, на котором стоят все. **Уцелевает:** контрасты промтов и топологий (`RB > RC > RN`), вендорские сравнения, основание выбора формулировки владельца. **НЕ уцелевает:** любое утверждение об АБСОЛЮТНОМ качестве английского текста, о вёрстке, об абзацном строе и длине абзаца — и любой перенос английского числа на пару, где исходник абзацы не терял. **Два числа боевых промтов en-ru стоят на этом и помечены в доке зоны в месте цитирования:** вес ролевого промта 1.50/1.13 и «редактор на английском покупает» 1.22 против 2.78 ошибок на единицу — оба судейские, на английской панели, при разрешении прибора `eval/dovodka/KONSILIUM-22-08.md:159`=`разрешение 1.63 места на китайском` против 2.15 на английском. ⛔ **Починка экстрактора ретроактивно обнуляет адресацию купленного:** uid единицы есть `sha1(source.strip())`, любая правка пробелов переименовывает ВСЕ единицы — 289 оплаченных клеток на $2.64 теряют адресата вместе с судейскими ответами; поэтому диспозиция зоны (`eval/dovodka/KONSILIUM-22-08.md:141`=`Починка экстрактора абзацев`) велит чинить НА ПОДАЧЕ (`eval/dovodka/podacha.py:203`=`def restore_paragraphs`), сохраняя uid. ⚠ **Не путать с рядом 499:** там дефект ДВИЖКА (`

` внутри `

` читается как абзац прозы), здесь — дефект ПОЛИГОННОГО экстрактора (регексп снёс `

`); одной правкой не чинятся и в один ряд не сливаются. ⚠ **Третья оговорка автора, без которой ряд придётся переписывать:** после починки ингеста движка старые полигонные числа станут НЕСОПОСТАВИМЫ с новыми движковыми — они сняты на разном тексте, и следующая смена иначе сравнит несравнимое. Китайская пара не затронута вовсе: исходник txt, абзацы не терялись, арма `RA` у неё «нет и быть не может» | полигон (носитель — оркестратор) | скоро (гейт: перед любым ПЕРЕНОСОМ английского числа полигона в ратификацию) | ⚠ **КРИТЕРИЙ ПЕРЕ-РЕШЁН 18.09 ПО ВОЗРАЖЕНИЮ АВТОРА НАХОДКИ, и прежний был слабее:** я ставил закрытием «носить пометку», то есть документировал предмет бессрочно вместо того, чтобы его закрыть. Пометка не несёт МАСШТАБА — из неё нельзя узнать, искажение это два процента или полтора места, — и потому обесценивает чистые числа ровно настолько же, насколько защищает грязные. **Закрывается:** куплен арм `RA` (16 единиц, промт и жилец как у `RN`, отличие ТОЛЬКО в подаче) и контраст `RA/RN` даёт число цены подачи. ⛔ **Ограничение покупки, без которого она бесполезна:** `RA` берётся на ТОМ ЖЕ жильце и с ТЕМ ЖЕ уровнем усилия, что `RN` (`eval/dovodka/rol.py:623`=`dspro: вендор-дефолт high` — оба на вендор-дефолте), иначе контраст смешает подачу с усилием и родит второй такой же ряд вместо закрытия первого. **До покупки** каждое английское число полигона в носителях несёт пометку «абсолютный уровень искажён, контраст чист, **величина искажения НЕ измерена**»; пометка ВРЕМЕННАЯ и снимается или уточняется величиной по итогу `RA/RN`. ⚠ **Цена — вход владельцу, а не санкция:** ≈$0.45–0.51 (сумма купленных клеток `RB` $0.5089 и `RN` $0.4454 — пере-снято оркестратором, сошлось до цента; ⚠ знаменатель расходится по провенансу: 16 ЕДИНИЦ против 32 ФАЙЛОВ, у каждой единицы есть спутник `.ERROR.json`, и медиана по файлам вчетверо ниже медианы по единицам). ⚠ **История оси несёт 402, но единиц НЕ теряла — пере-снято оркестратором 18.09 по артефактам:** английских записей об ошибке 52, ненулевых по цене среди них 0, по роду — 44 × `APIStatusError 402 Insufficient` (упор в баланс провайдера), 5 × `RateLimitError 429`, 2 × `APIConnectionError`, 1 × `BadRequestError 400`. ⛔ **Но у КАЖДОЙ ошибочной единицы есть та же единица с содержимым** (пересечение uid: `RB` 16/16, `RC` 16/16, `RN` 14/14, `RGT` 5/5), потерянных единиц НОЛЬ, и у всех тринадцати армов ровно по 16 единиц с содержимым ⇒ риг ретраил и добирал состав полностью. ⇒ **опасение «покупка потребует второго захода» замером НЕ поддержано**, а требование считать контраст по пересечению непустых остаётся верным по другой причине: у `RN` одна из 16 единиц несёт ПУСТОЕ содержимое (класс «доставленная пустота», ряды 414–415). ⚠ Провенанс счёта: «273 английские клетки» есть ФАЙЛЫ (включая спутники `.ERROR.json`), единиц с содержимым по 16 на арм. Зона полигона выведена из скоупа словом владельца 11.09 ⇒ покупку решает ОН | утверждение `textmachine-b5` 18.09; все пять адресов пере-проверены оркестратором | +| 502 | ⛔ **УПОР В БАЛАНС ПРОВАЙДЕРА НЕ ИМЕЕТ СОБСТВЕННОГО ИСХОДА: ПРОВОД КЛАССИФИЦИРУЕТ ЕГО ВЕРНО, А ОПЕРАТОР ПОЛУЧАЕТ ОБЫЧНОЕ ТЕРМИНАЛЬНОЕ ПАДЕНИЕ СТАДИИ.** ⚠ Разметка честная: **снято ЧТЕНИЕМ КОДА, живого 402 не исполнял** — движок в этом состоянии не гонялся. **Что построено и работает:** `backend/internal/llm/httpllm.go:691`=`func retryableStatus(status int, body []byte) bool` есть ЕДИНСТВЕННЫЙ источник раздела «повторять/терминально» и стережётся дрейф-гардом; 402 терминален по умолчанию (`:698`=`terminal 4xx (incl. 402 Payment Required)`), а 429 с телом об исчерпанных кредитах тоже терминален — узкий набор маркеров `:710`=`insufficient_quota` / `quota_exceeded`, и довод стоит рядом: повтор под удержанной резервацией жжёт только время, потому что «the operator must top up». ⇒ **денег на пустые ретраи мы не тратим, и это важно сказать первым.** ⛔ **Дыра выше провода:** типизированный `HTTPStatusError` (`backend/internal/llm/httpllm.go:717`) не покидает пакет `llm` — 7 упоминаний, ВСЕ в `internal/llm`, в конвейере ноль; ошибка едет наверх обёрнутым текстом (`%w`), а перечислимых причин в `backend/internal` **43**, и **ни одна** не про кредиты провайдера (контроль: строк со словом `reason` — 943; единственный хит шаблона «причина + кредиты» — посторонний комментарий `backend/internal/pipeline/rebill.go:269`). ⇒ «у провайдера кончились деньги» и «стадия упала терминально» на уровне ИСХОДА неразличимы; тело ответа провайдера доезжает до человека только внутри текста обёрнутой ошибки. ⛔ **Почему это гейт готовности, а не гигиена:** предстоят ДВЕ книги по 20 глав ПАРАЛЛЕЛЬНО, и прогон, встретивший 402 на середине, останавливается наполовину купленным; оператору нужно за секунды отличить «пополни счёт» от «движок сломался», а сегодня он это читает из текста ошибки. ⚠ **Событие не гипотетическое:** на английской оси полигона 44 записи об ошибке из 52 — ровно `APIStatusError 402 Insufficient` (ряд **501**), то есть провайдер уже упирался в баланс, и заметили это постфактум, разбирая артефакты. | бэкенд | **скоро (кандидат в блокеры ратификации готовности к эксперименту двух переводов)** | у исчерпания кредитов провайдера есть НАЗВАННЫЙ исход, отличимый от обычного терминального падения стадии, и он виден оператору прогона; пин краснеет, когда тело с 402 превращается в родовую причину | замер оркестратора 18.09 чтением кода; повод — находка `textmachine-b5` о 44 отказах по балансу на английской оси |