diff --git a/docs/BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md b/docs/BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md new file mode 100644 index 00000000..e892a87f --- /dev/null +++ b/docs/BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md @@ -0,0 +1,369 @@ +# Бэкенд-пак: ЗА ОДНУ ЕДИНИЦУ ПЛАТИМ ДВАЖДЫ — и по двум РАЗНЫМ причинам, которые движок не различает + +> Выдан оркестратором №23, 11.09. Зона записи — `backend/`. Пак КОДОВЫЙ, платных вызовов НЕ несёт. +> ⚠ **Редакция 2.** Первая была построена на перевёрнутой посылке («движок лечит болезнь возбудителем») и +> заказывала отключить восстановление, которое на замере работало. Опровергатель это снял; ниже — +> исправленное, и след правки виден в §1. + +## 1. Какая проблема и что решит твой результат + +На живом платном прогоне книги 11.09 **движок заплатил дважды за четыре единицы из двадцати семи**: +первая попытка вернула HTTP 200 без пригодного текста, выходные токены были выкуплены и выброшены, вторая +попытка вернула текст. Цена выброшенного — **$0.106472 = 25.4 % всей цены книги**. Из них три вызова дали +РОВНО ноль знаков (**$0.094835 = 22.6 %**), четвёртый — обрезок с текстом ($0.011637). + +⛔ **И вот что надо знать ДО того, как начнёшь чинить: лекарство работает.** Перевыпуск с удвоенным +потолком вернул текст **4 раза из 4**. Претензия к нему ДЕНЕЖНАЯ (оплачена выброшенная попытка), а не +медицинская. **Не выключай его.** Первая редакция этого промта утверждала обратное, и сессия, исполнившая +её буквально, отключила бы восстановление трёх чанков из четырёх. + +**Настоящая находка — в том, что двойных оплат ДВЕ РАЗНЫХ, а движок применяет к ним одно лекарство.** +Пере-снято мною по базе прогона (`bk_ROEHZBD46ALFI43E/project.db`, стадии `draft`/`edit`, `tm_hit=0`): + +| единица | 1-я попытка | 2-я попытка | что это значит | +|---|---|---|---| +| `draft` ch1u0 | comp **8496** · `length` · пусто · $0.012044 | **15869** · `stop` ✓ | потолка не хватило | +| `draft` ch2u1 | comp **8496** · `length` · обрезок · $0.011637 | **13004** · `stop` ✓ | потолка не хватило | +| `draft` ch3u0 | comp **8496** · `length` · пусто · $0.011782 | **14034** · `stop` ✓ | потолка не хватило | +| `edit` ch2u0 | comp **16000** · `length` · пусто · **$0.071009** | **11717** · `stop` ✓ | потолка ХВАТАЛО | + +⭐ **Контроль, и он режет популяцию без единого исключения.** Успешных черновых вызовов в прогоне ШЕСТЬ, +их размеры: `6468 · 7932 · 8243 · 13004 · 14034 · 15869`. **Все три, что уложились в 8496, прошли с первого +раза. Все три, что не уложились, с первого раза упали и прошли на удвоенном потолке. 6 из 6, исключений +ноль.** ⇒ на черновике это НЕ болезнь размышления и не случайность: **первый потолок просто меньше того, +что эта стадия реально производит на половине единиц**, и удвоение делает работу, которую должна была +сделать оценка бюджета. + +⛔ **А четвёртый случай — противоположный, и он стоит ДВУХ ТРЕТЕЙ всей потери в ОДНОМ вызове.** У `edit` +потолок 16000 был заведомо достаточен: вторая попытка уложилась в 11717. Значит первый вызов выкупил 16000 +токенов чего-то, что не было текстом. Это размышление — у DeepSeek оно считается ВНУТРИ выходных токенов +(`reasoning ⊆ completion`), и здесь удвоение потолка действительно даёт ему вдвое больше места. + +**Что решит твой результат.** Движок научится отличать «не хватило потолка» от «потолок съело размышление» +и перестанет платить за первое по цене второго. Плюс причина станет ВИДНА: сегодня спросить «что +случилось» нечем — колонка `reasoning_tokens` не несла числа ни разу за историю проекта, а цена считается +подстановкой, о которой никто не узнаёт. + +⛔ **Чего этот пак НЕ делает:** не выключает перевыпуск (он работает) · не меняет боевые значения +`reasoning` в конфигах (ждёт полигонного замера, §4.2) · не правит пин цены flash (данные, ряд 413) · не +трогает `books/` · не заводит новых стадий пайплайна. + +## 2. Зона записи и git + +Зона — **`backend/`**. Читать можно всё; писать — только в свою зону. + +⛔ **Ты НЕ коммитишь.** Коммитит оркестратор при лендинге. Готовое дерево оставляешь как есть и +сообщаешь. Не делай `git add`, `git commit`, `git stash`, `reset --hard`, `checkout` поверх грязного +дерева: в дереве параллельно работают другие сессии, и их незакоммиченные правки стираются безвозвратно. +Канон — `CLAUDE.md` §«Git-координация мультисессий», он сильнее этого абзаца. + +Свои итоги, пинги и вопросы пиши в СВОЮ секцию «Бэкенд» журнала `docs/PROGRESS.md`. Это единственное +исключение из «`docs/` — зона оркестратора». + +## 3. Карта чтения (ЗАКОН, пять позиций; дальше — только по её ссылкам) + +1. `CLAUDE.md` — ты его уже получила. +2. **`docs/experiments/00-provider-quirks.md`, пункт 3 ЦЕЛИКОМ** (подпункты 3а · 3б · 3в · 3г · 3д — + именно все пять). ⛔ Читать ДО любой правки адаптеров и вызовов; здесь их копий нет, а §3г + переписывался ТРИЖДЫ и две прежние редакции были неверны — читай действующую, не мой пересказ. +3. `backend/README.md` — механика движка. +4. `docs/architecture/12-go-style-notes.md` — норматив стиля, только больные места. +5. Ряды бэклога **414 · 415 · 422** и **413** (`docs/BACKLOG.md`) — носители замеров, на которых стоит пак. + +**Код-факты добывай САМА.** Все `file:line` ниже — отправные точки, а не истина: истина — дерево. +Если адрес уехал, это находка, и она идёт в отчёт. + +**Ратифицированное, что тебе понадобится (вложено сюда, грепать не надо):** + +* ⭐ **`D2` — основание пака, и читать надо ОБА уточнения, 2 и 3.** Решение: `finish=length` → ретрай с + бо́льшим `max_tokens` до капа, потом флаг; пустой оплаченный → skip+flag (не падение прогона). + ⛔ **п.2 ратифицировал, что `empty` ПЕРЕАТАКУЕТСЯ:** «`retry-flagged` переатакует **только** + `length`/`empty` на той же модели». Это исполнено и ЗАПИНЕНО — `internal/pipeline/disposition.go:211`=`func (r FlagReason) retryable() bool` + `func (r FlagReason) retryable() bool { return r == FlagLength || r == FlagEmpty }`, пин + `TestRetryableSubset` (`disposition_test.go:90`=`TestRetryableSubset`). ⇒ **всякое изменение ТОГО, ЧТО переатакуется, отменяет + `D2` п.2 и двигает этот пин — это пинг мне ДО кода, а не правка.** Не соблазняйся «подогнать» + `TestRetryableSubset` как заказанную смену поведения: она НЕ заказана. + ⭐ **п.3 — принцип, который ты распространяешь:** «Детектор вырождения ПЕРЕД удвоением max_tokens… + бо́льший max_tokens лишь оплачивает больше повтора… при вырождении сразу флаг, без оплаты цикла». + Он исполнен ДЛЯ ОДНОЙ причины (петля повтора, `disposition.go` рядом с `distinctRatio`) и не исполнен + для второй — «размышление съело потолок», которую тогда не знали. **Твоя работа — распространить наш + же образец, а не завести новый.** +* **`D24` п.3** — per-model флор `min_max_tokens` РАТИФИЦИРОВАН схемой (capability-слой `models.yaml`, не + комментарий); флор у `deepseek-v4-flash`/`pro` там назван **8000**. ⚠ Подъём `pro` до **16000** — НЕ из + ноты: это `backend/configs/models.yaml` (блок `deepseek-v4-pro`, `min_max_tokens: 16000`) по замеру + мини-прогона 25.07, и цена того замера — 15.5 % счёта прогона в двух выброшенных генерациях. +* **`D39.86`/`D39.87`** (02.08) — гипотеза «поднять флор» ФАЛЬСИФИЦИРОВАНА замером. ⛔ **Но узко, и + `D39.90` п.9 это прямо сузил, сняв над-клейм:** «число флора влияет на цену ретрая, а не на + проходимость» — незамеренная экстраполяция; **при `low` флор не варьировался НИ РАЗУ**. ⇒ «потолок не + рычаг» проверено на вендор-дефолтном эффорте; **на `low` не проверено никем — а черновик прогона шёл + именно на `low`.** Это прямо касается §4.1: три из четырёх двойных оплат живут в непроверенной области. +* **`D39.87` §2 — приёмочный вопрос, который держит тебя от тиражирования дефекта.** Дословно: «появится + ЧЕТВЁРТАЯ такая стадия — она унаследует ручку сама или её опять забудут?». Ответ в дереве уже + структурный: единственная деривация стадии для внутреннего вызова — `internal/config/internal_call.go`, + единственный словарь эффорта — `ValidReasoningEffort` там же, и оба стережёт статический анализатор + `internal/archguard/` («an engine-internal call must derive its stage through config.InternalCall.Stage()»), + который гоняется обычной батареей. ⇒ **новое измерение решения обязано ехать ЭТИМ швом.** Рукописных + `config.Stage{…}` в шиппинг-коде сегодня ноль — проверь сама и не заведи первый. +* **`D39.121`** — тесты и гейты не подгонять под зелень. Правка теста, вызванная ЗАКАЗАННОЙ этим паком + сменой поведения, — обслуживание, и её надо ОБЪЯВИТЬ в отчёте (что изменилось, какой тест это + описывал, куда уехала гарантия). Правка *чтобы прошло* — недопустима. + +## 4. Заказ. У каждого пункта размечена свобода + +### 4.1. Две причины — два лекарства — ЯДРО ПАКА · «реши сама и аргументируй» + +**Что уже построено и что ты ЧИТАЕШЬ, а не заводишь.** Классификация отказа есть и сохраняется: +`FlagEmpty`/`FlagLength` (`internal/pipeline/disposition.go:59`=`2xx with no usable text`), `degradedTag` (`stagerun.go:921`=`degradedTag surfaces a flag reason`), +колонка `request_log.degraded`. ⭐ **И дискриминатор «размышление внутри выходных токенов» ТОЖЕ уже +построен и живёт в ДАННЫХ:** в `backend/configs/models.yaml` у провайдера `deepseek` стоит +`reasoning: subset`, у `xai` — `additive`, у `gemini` — `additive_total`; поле объявлено в +`internal/config/models.go` (`Reasoning string`, комментарий `subset | additive | additive_total`), +резолвер — `providerReasoning()` там же, словарь — `internal/llm/provider_openai.go` +(`ReasoningSubset`/`ReasoningAdditive`/`ReasoningAdditiveTotal`). ⇒ **ось «subset против additive» +ЧИТАЕТСЯ. Второй разметки не заводить.** Найдёшь расхождение — находка, в отчёт. + +А выбор лекарства этого не спрашивает: `maxTokensForAttempt(base, escalations)` +(`internal/pipeline/disposition.go:481`=`func maxTokensForAttempt`) принимает **счётчик удвоений и больше ничего**. + +**Что построить: различение двух причин и разные ответы на них.** Приор — опровергается аргументом или +замером, и я жду именно опровержения, если оно у тебя будет: + +| причина | улика в данных | сегодня | приор | +|---|---|---|---| +| **потолок мал** — ответ не влез | 2-я попытка дала БОЛЬШЕ, чем потолок 1-й | удвоить и заплатить дважды | **чинить ОЦЕНКУ первого потолка**, а не перевыпуск: единица, которой заведомо мало, не должна покупать заведомо провальную попытку | +| **размышление съело потолок** | 2-я попытка дала МЕНЬШЕ, чем потолок 1-й (`edit`: 11717 < 16000) | удвоить — то есть дать размышлению вдвое больше места | **ступень эффорта ниже при ТОМ ЖЕ потолке** — но см. ⛔ ниже, величина не замерена | +| `length` **с текстом** | обрезок доехал | удвоить | **оставить как есть** — это ровно решение `D2` | + +⛔ **ЧЕГО В ТАБЛИЦЕ НЕТ И НЕ ДОЛЖНО БЫТЬ: «не перевыпускать».** На замеренных данных это стоило бы трёх +восстановленных чанков из четырёх и отменяло бы `D2` п.2. Вопрос — в ЦЕНЕ попытки, а не в её отмене. + +⛔ **И ВОТ ГДЕ ВЫРАСТЕТ ВЕЛОСИПЕД, ЕСЛИ НЕ СМОТРЕТЬ.** «Ступень ниже» требует знать, какие ступени есть у +ЭТОГО провайдера, — а словарь эффорта в движке **намеренно провайдер-СЛЕПОЙ**, и это объявлено известным +пределом: доккомментарий `ValidReasoningEffort` дословно — «Validating against the RESOLVED model's +capability would need a new per-model table — a new mechanism, not a use of an existing one — so it stays +a named finding, not a silent gap». ⇒ **пер-модельная таблица ступеней — НОВЫЙ механизм, молча его не +заводить.** Три законных выхода, выбирай с доводом: (1) шаг вниз по НЕЙТРАЛЬНОМУ словарю движка; (2) не +трогать эффорт вовсе и решать денежную задачу через оценку первого потолка — тогда таблица не нужна +совсем; (3) таблица — но это отдельное решение и пинг мне ДО кода. ⚠ Разница с осью `subset`/`additive` +выше именно в этом: та **построена и читается**, эта **не построена и без письма не заводится**. + +⛔ **ДВЕ ОГОВОРКИ О СИЛЕ УЛИК, обе обязательны в твоём отчёте.** +**(а) Величина среза размышления НЕ УСТАНОВЛЕНА.** Квирки §3г: параметр `low` до `pro` ДОХОДИТ (доказано +поведенческой пробой 30.08), но «ВЕЛИЧИНА среза по-прежнему не установлена… наблюдённые ×2.52 сняты +блоками, разнесёнными на 9 дней». То есть «ступень ниже» — законный ПАРАМЕТР, но не доказанное лекарство. +**(б) Весь денежный довод стоит на ЧЕТЫРЁХ одиночных розыгрышах.** Квирки §3д: «два одиночных розыгрыша +дефолтного пути `deepseek-v4-pro` разошлись в 38 раз… любая смета DeepSeek, снятая с ОДИНОЧНОГО прошлого +вызова, может ошибиться на порядок… сметы и сравнения нужно строить на ПОВТОРАХ». ⇒ у моих чисел есть ЗНАК +и нет ВЕЛИЧИНЫ. Контроль «6 из 6 без исключений» (§1) знак усиливает, но повторами не является. + +⛔ **Никаких велосипедов в остальном.** Не заводи вторую таксономию отказов, не пиши свой классификатор +ответа, не добавляй параллельный счётчик попыток. Работа — **дать существующему лекарству прочитать +существующий класс**. Если существующая классификация окажется непригодной — это находка, назови её и +аргументируй, прежде чем строить рядом. + +### 4.2. Ручка усилия у редактора — ⛔ НЕ ТРОГАЙ БОЕВОЙ КОНФИГ · «делай РОВНО так» + +Соблазн: «на `edit` стоит `reasoning: "off"`, вызов всё равно думает — поставим `low`». **В этом паке не +делай.** Причина не та, что подскажет интуиция: + +* **Ручка есть и ратифицирована.** `ReasoningNone` — OpenAI-совместимая база: `low|medium|high` уходят как + `reasoning_effort`; ничего не эмитят только `off` и пустая строка, и тогда едет вендор-дефолт `high`. + Именно `off` стоит у редактора в `backend/configs/pipeline-c1.yaml` — **это ВЫБОР конфига, а не + невозможность.** Проверь чтением `internal/llm/capability.go` сама. +* **«На pro рычага нет» больше не правда.** Вендор сменил маппинг; полигон снял поведенческую пробу 30.08 + за $0.038276 и показал, что `low` до `pro` доходит. Живая запись — квирки §3г. +* ⇒ **ось «`off` против `low`» измеряется ОТДЕЛЬНЫМ КОНФИГОМ КНИГИ. Это ДАННЫЕ**: ни правки Go, ни снятия + защиты; гейт эхо-мины требует `off` только у шиппинг-конфигов `pipeline-c1`/`c2`. +* ⛔ **Цена рычага измерена с другой стороны:** `effort: "low"` **пере-вооружает эхо-мину** — на плотном CJK + с ослабленным размышлением модель возвращает ИСХОДНИК под HTTP 200. ⚠ Честно про силу этой улики: в + квирках это **1 чистый китайский выход из 16**, то есть знак есть, а «воспроизводимо» было бы + преувеличением. Единственная чистая проба на `low` шла на АНГЛИЙСКОЙ паре и предупреждения не снимает. + +⇒ это **РАЗМЕН «дорого и пусто» против «дёшево и эхо»**, и вслепую его не разменивают. Замер закажет +оркестратор полигону, на плотном CJK. + +**Твоя работа — две вещи, обе без правки боевых значений.** (а) Построй §4.1 так, чтобы ступень усилия +была ПАРАМЕТРОМ решения, а не константой, — через шов `internal_call.go`. (б) Напиши в отчёт ОДНИМ абзацем +заказ полигону: что меряем, на каком входе, **сколько повторов** (см. §3д), какой результат означает +«меняем», какой — «не меняем». Я отдам его дословно, поэтому пиши как заказ, а не как пожелание. + +### 4.3. `reasoning_tokens` не несла числа ни разу · «реши сама и аргументируй» + +Замер оркестратора 11.09: `request_log.reasoning_tokens > 0` — **0 строк**. Контроль рядом: найдено +**448** файлов `*.db`, таблицу `request_log` несут **405**, прочитано **58 886** строк, колонка объявлена +схемой и присутствует во всех. ⇒ единственная колонка, которой доказывают «потолок съело рассуждение», +структурно молчит, и её ноль неотличим от «рассуждения не было». + +Причина известна и не баг нашего кода: DeepSeek считает рассуждение внутри `completion_tokens` и отдельным +полем usage не присылает. Но следствие наше: **прибор врёт молчанием** — а именно этим прибором отличают +две причины из §4.1 друг от друга. + +Реши сама и аргументируй: вынуть число из тела ответа, где оно есть (у DeepSeek приходит +`reasoning_content` — проверь), либо честно пометить «провайдер не сообщает» так, чтобы ноль перестал +читаться как измеренный. ⛔ Запрет один: **не оставляй ноль без различения.** + +### 4.4. Признак отказа и прибор, которым смотрят деньги · «реши сама и аргументируй» + +⚠ Формулировка ряда 414 была неверна и исправлена 11.09 — не наследуй старую. Измеренная правда по базе +прогона (33 строки): провал виден **тремя** колонками, и все три сходятся ровно на $0.106472 — `ok = 0` на +четырёх провальных и `1` на остальных 29 · `finish_reason = 'length'` на тех же четырёх · `degraded` +(`empty` ×3, `length` ×1). Из НЕСУЩИХ колонок пуста `err`. ⚠ Пуста не она одна: единственное значение по +всем 33 строкам дают также `reasoning_tokens` (предмет §4.3), `cache_creation_tokens`, `estimated`, +`est_tokens` — то есть «пуста ровно `err`» было бы отрицательным замером без контроля. + +⇒ работа НЕ в том, чтобы «добавить носитель», их три. Работа — решить и обосновать: должен ли `err` +заполняться на 2xx-без-текста (против: это не ошибка транспорта; за: рука аудитора тянется к `err` +первой), и достаточно ли того, что `ok=0` уже есть. **Возможен полностью законный исход «менять нечего, а +надо назвать»** — тогда так и напиши с доводом; это результат, а не увиливание. + +### 4.5. Цена считается по модели, которой нет в каталоге · «делай РОВНО так» в части замера, «реши сама» в части лечения + +Замер оркестратора 11.09 по всем базам машины (контроль: 448 файлов `*.db`, 405 с `request_log`, 58 886 +строк): **365 строк оплачены ПОДСТАНОВКОЙ** — провайдер вернул слаг, которого в каталоге нет. ⚠ Из них +**344 боевые** (`deepseek-v4-flash` → `deepseek-flash`, 296 строк, $2.049737; `local-qwen3-8b` → +`huihui_ai/qwen3-abliterated:8b`, 48 строк, $0) и **21 — тестовые фикстуры** (`m-req`→`m-act`, +`fake-model`), которые обязаны быть вычтены: иначе $5.51 фикстуры уедут в денежный вывод. + +**Сегодня это безвредно:** подстановка падает на ЗАПРОШЕННУЮ модель той же семьи. ⛔ Опасна не подстановка, +а её молчание. `PriceForResponse` (`internal/ledger/pricing.go:61`=`func (p *Pricer) PriceForResponse`) пробует `actual`, потом `requested`, +потом якорь — и ни одно из **двух** мест вызова (`stagerun.go:781`=`PriceForResponse(model, modelActual)`, `reprice.go:134`=`ledger.CostUSD(r.Pricer.PriceForResponse`) не различает, какая +ветвь сработала. Комментарий там же охраняет РОВНО ОДНО направление («премиальный ответ не должен биться +по дешёвому дефолту»); обратное — когда провайдер маршрутизирует ВНИЗ — не охраняет никто: запрошен `pro`, +ответила модель дешевле, счёт по пину `pro`, $3.96 за 1M выходных против $1.20 вендорских у flash = +**×3.3 из кармана читателя, молча**. + +**Делай РОВНО так:** пере-сними число сама и напечатай рядом контроль — сколько баз прибор открыл и сколько +строк прочёл, и **объяви фильтр** (боевые против фикстур). Моё число на слово не принимай: я уже публиковал +занижённый контроль, потому что мой первый прибор не дошёл до ~170 баз. + +**Реши сама и аргументируй**, как сделать подстановку наблюдаемой. Каталог — файл данных +(`backend/configs/models.yaml`), и «завести слаг, который провайдер реально возвращает» законно; но слаги +не меняются без live-фактчека вендор-доки (гардрейл `CLAUDE.md`), и «слаг живой» ≠ «модель та же». Нужен +вендор-заход, которого ты сделать не можешь, — это пинг, а не догадка. + +### 4.6. Параметр, который едет по проводу и ничего не делает · «реши сама и аргументируй» + +Вендор-дока DeepSeek, дословно (снято полигоном): *«Thinking mode does not support the `temperature`, +`top_p`, `presence_penalty`, or `frequency_penalty` parameters… setting these parameters will not trigger +an error but will also have no effect»*. А ростер шлёт `deepseek-v4-pro` на стадии `edit` +**`temperature: 0.4`** (`backend/configs/pipeline-c1.yaml`; на `draft`/flash стоит `0.3`, и оно же уезжает +на `pro` при эхо-эскалации), и размышление у `pro` включено всегда. ⇒ **параметр в теле запроса есть, +эффекта нет.** + +Само по себе денег не стоит. Стоит другое: `temperature` **входит в `RequestHash`** +(`internal/pipeline/render.go`, поле `req.Temperature`) — движок держит в ключе чекпоинта величину, которая +на этом классе моделей ни на что не влияет, и правка «настроим температуру редактора» выглядела бы +настройкой качества, а была бы громким `--resnapshot` без единого изменения в ответах. + +Реши сама: снимать параметр на таких моделях (это смена ключа — назови цену) либо оставить и объявить в +схеме модели, чтобы следующая смена не крутила мёртвую ручку. ⛔ Вендор-факт проверь сама по квиркам ПРЕЖДЕ +чем решать — я его цитирую, а цитата не улика. + +### 4.7. Чего НЕ брать + +Не выключать перевыпуск (§1) · не менять боевые `reasoning` (§4.2) · не править пин цены flash — ряд 413, +это данные и отдельное решение, и рядом висит неразрешённый вопрос, алиас ли `deepseek-flash` · не трогать +`books/` · не заводить новых стадий пайплайна. + +## 5. Самопроверка ИСПОЛНЕНИЕМ — и где у ЭТОГО пака мягко + +Мандат: после того как работа сделана, проведи **адверсариальный проход по СВОЕЙ готовой работе**. Глубину +и веер выбираешь сама. Субагенты РАЗРЕШЕНЫ явно. Артефакт — таблица «посадка → выжила/поймана → ЧЕМ +поймана», в отчёте, с выжившими поимённо. + +⚠ **Длинная сессия: раз в несколько часов работы — интервальная самоверификация субагентом против ЯВНЫХ +критериев этого промта** (`D39.121`), а не «перечитаю сама». + +⛔ **Мутация засчитывается по ТЕКСТУ падения, а не по факту красноты.** Правый вердикт по неправой причине — +дыра, а не поимка. + +⛔ **Копия под мутацию защищается ПОСТРОЕНИЕМ, а не `set -e`:** перед любой правкой в последовательности — +`test -f go.mod` и сверка `pwd` с ожидаемым путём; копия живёт ВНЕ общего скретчпада. + +**Четыре места, где мягко ИМЕННО У ЭТОГО пака:** + +1. ⛔ **Пин на «не перевыпустили» вакуумен по построению.** Утверждение «второго вызова не было» + удовлетворяется фикстурой, где второй вызов невозможен ни по какой причине. Нужна фикстура, где он + ОБЯЗАН прозвучать при старом поведении и ДОЛЖЕН молчать при новом, и обе половины исполняются. +2. ⛔ **Пин может удовлетворяться ЧУЖОЙ уликой.** Если фикстура гонит попытку через провод, признак + «ступень снижена» может прийти не от твоего решения, а из поля предыдущей попытки. Спрашивай не + «покраснело ли», а **ЧТО ИМЕННО удовлетворило утверждение**. +3. ⛔ **Денежный пин, флейковый на мутанте, измеряет пустой сценарий.** Гоняй его НЕ ОДИН раз и строй + фикстуру так, чтобы деньги были НЕИЗБЕЖНЫ, а не вероятны. +4. ⛔ **Шов между решением и вызовом тоньше самого решения** (урок предыдущего пака этой зоны, 11.09: + прибор был верен, провод от прибора к прогону не запинен ничем, и подмена аргумента прошла батарею + зелёной). Спроси у своего кода: сколько тестов зовут путь, на котором причина доезжает до выбора + лекарства, — и напечатай это число рядом со знаменателем (сколько тестовых файлов прибор прочёл). + +## 6. Предметные оси ревью + +Три, замена любой с аргументом: + +1. **Деньги** — каждая новая ветвь отвечает числом, сколько вызовов покупает и сколько экономит. +2. **Детерминизм и снапшот** — что из правки входит в `request_hash` и в `maxTokensPolicyVersion`; двинулось + ли что-нибудь, что заставит пере-оплатить книги в переводе. Ответ исполнением, не рассуждением. +3. **Общность движка** — «заработает ли пара, которой в репо ещё НЕТ, без правки Go?». Причина отказа и + таблица лекарств не ветвятся по паре или книге. ⚠ Свойство биллинга провайдера уже живёт в данных и + **читается** (§4.1) — его заводить не надо. + +## 7. Записка-план + +До первой правки кода положи в свою секцию `docs/PROGRESS.md` записку-план: что берёшь, в каком порядке, +что считаешь опасным. Перед сдачей — механическая сверка комплектности против §4, по пунктам, таблицей. + +## 8. Заявление = команда + +Каждое число и каждая категорика в отчёте — **с командой, которой получены**. Приёмка пере-ранит и сверит. +Число без команды считается неснятым. + +⛔ **Отрицательный замер обязан доказать, что спросил существующее.** Рядом с любым нулём ПЕЧАТАЕТСЯ +контрольная величина: «файлов прибор прочёл: N», «баз открыто: N». Не упоминается — печатается. + +⚠ **Дифф `^func Test` — ИСПОЛНЕНИЕМ, а не по памяти:** список добавленных и изменённых тестов сними +командой и приложи, иначе «я добавила пины» остаётся самоотчётом. + +## 9. Эхо-протокол старта + +**Первым действием, ДО работы**, вернись ко мне эхом ≤10 строк: **скоуп / инварианты / чего не делаю** — +⚠ **СВОИМИ СЛОВАМИ.** Дословный пересказ подтверждает канал, но не понимание, и повторяет ошибку промта +вместе с ним. Прямо скажи: **что поняла, что считаешь опасным, что считаешь неверным.** ⚠ Этот промт уже +один раз стоял на перевёрнутой посылке и был переписан — возражение в эхо здесь особенно уместно. + +## 10. Что НЕ удалось + +Обязательная секция отчёта. ⛔ И вторая её половина, без которой она вырождена: **«ГДЕ ПРИБОР СЛЕП И Я ЭТО +ЗНАЮ»** — что невидимо · почему не чинила · чем закрывается. Вопрос не «что не получилось», а **«что ты +знаешь и не сказала»**: в коде названо, а в отчёте нет — значит для следующей смены НЕ названо. + +## 11. Канал вопросов и право сказать «не надо» + +Конфликт промта с кодом или доками → **пинг мне, НЕ интерпретация**. Ты вправе сказать «этого делать не +надо» с аргументом — это исполнение пункта, а не отказ. ⛔ Особенно про §4.1: если существующая +классификация окажется непригодной, я хочу знать письмом, а не увидеть рядом вторую. + +⚠ **Правка промта на ходу доезжает ТОЛЬКО релеем:** я передаю её отдельным сообщением, ты подтверждаешь +эхом и называешь ОТДЕЛЬНЫМ пунктом отчёта. Изменение, о котором не было релея, исполнению не подлежит. + +## 12. Прямой канал + +Механизм — `CLAUDE.md` §«Связь между сессиями»; здесь не повторяется. Впиши свой блок в +`/tmp/textmachine-channel` первым делом, там же ищи адреса. Эхо (§9) шли по адресу оркестратора из этого +файла — оно же проверка канала. ⚠ **Нужной роли в файле нет, файла нет или имя не отвечает ⇒ КАНАЛА НЕТ, и +это НОРМАЛЬНЫЙ случай: НЕ опрашивай сессии подряд** — вопрос секцией в отчёт, работа продолжается. + +## 13. Критерий завершённости + +Работу доводишь до конца САМА. Приёмка идёт последней и вторым кругом твоей сессии не служит. + +- [ ] У каждого пункта §4 — исход: сделано · **не делаю с доводом** · пинг. +- [ ] Круги СОШЛИСЬ: последний проход не дал НОВЫХ находок; прежние закрыты таблицей + «находка → что сделано → **ЧЕМ ПРЕДЪЯВЛЕНО**». +- [ ] Таблица мутаций полная, **выжившие названы поимённо**. +- [ ] Дифф `^func Test` снят командой и приложен. +- [ ] Числа сняты ПОСЛЕ последней правки. +- [ ] Всё живое — в ДЕРЕВЕ, а не в письме. +- [ ] Явное «работа завершена, править не планирую». Без этой строки пак считается идущим. + +⚠ Перед отчётом сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии · если последний абзац отчёта — +план или обещание, сделай его сейчас · правка теста ради зелени недопустима, правка по заказанной смене +поведения — объявляется. diff --git a/docs/README.md b/docs/README.md index fb331d93..e4280db3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,6 +34,7 @@ | Бэкенд · ГЕЙТ ВМЕСТО ПРОЗЫ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 07.09** — акт **D39.225**; промт в `archive/prompts/` с баннером-исходом | | **Бэкенд · КОНСИСТЕНТНОСТЬ, КОТОРУЮ МОЖНО ПРЕДЪЯВИТЬ** | [BACKEND_CONSISTENCY_YOU_CAN_SHOW_SESSION_PROMPT.md](BACKEND_CONSISTENCY_YOU_CAN_SHOW_SESSION_PROMPT.md) | ✅ **ВЫДАН 11.09**: приоритет №1 владельца — прибор по ОТГРУЖЕННОМУ тексту (два инварианта: одна форма на термин по книге · где у банка есть форма, отгружена она) · запись выбора редакторской волны · дешёвая половина вопроса пост-проверки · совещательные правила линта сида. ⛔ Заказ стоит на замере: расхождения найдены и перечислены (⚠ **ИСПР. 11.09: их СЕМЬ, а не 18** — 11 из восемнадцати оказались ложной тревогой стеммера, ряд **419**), счётчик «промахи» даёт 0 при знаменателе 2, а пара 84/66 УЖЕ печатается — недостают ИМЕНА (держит одна строка `render.go:491`) и счёт по книге целиком. Вне пака: гейт · ряд 409 · актуатор · решение «закон ли для неподписанного» (вопрос владельца) | | Бэкенд · СТРУКТУРА ГЛАВ (ДИЗАЙН) | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ 11.09 БЕЗ СХОДИМОСТИ, правилом остановки** — акт **D39.245** (`1d2fab6`, 3 пути, $0); результат — `backend/docs/CHAPTER_STRUCTURE_DESIGN.md` (2062 строки), статус ПРЕДЛОЖЕНИЕ до пака стройки. Глава становится ИДЕНТИЧНОСТЬЮ; предметная часть устояла в четырёх кругах опровержения, механика исполнения — нет, и пак стройки обязан начать с прогона миграционной механики ОПЫТОМ. Промт был — [CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md](CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md), выдан 11.09 (актуализирован пятью врезками + 19 правок по опровергателю: мёртвые адреса, две таксономии осей, три «уже построено», собственная зона записи `backend/docs/` с дизайном-оф-рекорд `D15.2` и прибором shiftmap). ⚠ **ПРОЕКТИРОВАНИЕ, НЕ СТРОЙКА** (`D39.136` п.3): дизайн окна большой пере-нарезки — чем адресуется глава и развязка чанкера (161) · карта осей сдвига · банк-окна на chapter-ID · контент-адресуемый resume `D15.2` · IR/адаптеры/индуктор · правило заголовка и не-CJK путь (303) · гранулярность EPUB (302) и выдача (283). Снимает временный отказ интейка книге из одной главы (`D39.221`). Пак 1 «форматы и честный словарь» ОТРАБОТАН и ПРИНЯТ 06.09 — акт **D39.210**, промт в `archive/prompts/`. ⚠ **ПАК 2 (лексиконы не-CJK, корпус, замер оракулом) — НЕ НАПИСАН и ждёт файлов владельца** | + | **Бэкенд · ЗА ОДНУ ЕДИНИЦУ ПЛАТИМ ДВАЖДЫ** | [BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md](BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md) | ⏳ **НАПИСАН 11.09, редакция 2; выдаётся СВЕЖЕЙ сессии по закрытии пака консистентности актом.** Предмет — двойная оплата единицы: на платном прогоне $0.106472 = 25.4 % цены книги ушло в выброшенные первые попытки. ⛔ **Редакция 1 стояла на ПЕРЕВЁРНУТОЙ посылке** («движок лечит болезнь возбудителем») и заказала бы отключить восстановление, которое работает 4 раза из 4; опровергатель это снял — сессия, исполнившая её буквально, отключила бы восстановление трёх чанков из четырёх, отменила `D2` п.2 и сломала `TestRetryableSubset`. ⭐ Настоящая находка редакции 2: **причин ДВЕ, лекарство одно.** Черновик — потолок мал (контроль: 6 успешных вызовов, все ≤8496 прошли сразу, все >8496 упали и прошли на удвоенном; исключений 0), редактор — размышление съело достаточный потолок (2-я попытка 11717 против упавших 16000, $0.071009 = две трети потери в одном вызове). Плюс `reasoning_tokens` не несла числа ни разу за историю проекта (0 строк при 405 базах и 58 886 строках) и молчащая подстановка цены. Ряды **414** · **415** · **422**, пин flash **413** из пака ВЫНУТ | | Бэкенд · отозванный | активного НЕТ | пак КАЧЕСТВО написан и ОТОЗВАН до выдачи 05.09 (`archive/prompts/…_WITHDRAWN.md`): предмет дешевле пака — точность чекера уже измерена, пере-снять её стоит одной команды. Следующий пак — СТРУКТУРА ГЛАВ (решение владельца 05.09 «любая книга любого формата»), пишется | | Бэкенд · прежний | активного НЕТ | пак «денежный стоп» ОТРАБОТАН и ПРИНЯТ С ДОФИКСОМ 05.09 — акт **D39.206** (`81a89e9` + `616a8e4`), промт в `archive/prompts/` с баннером исхода. Следующая работа зоны — строки **291** (флаг вместо клина, `D39.204`) · **294** (форма «сколько добавить», `D39.203`) · **296** (точечная перегенерация по промаху глоссария, после 295) | | Платформа · ПРАВДА У ДВЕРИ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 11.09** — акт **D39.246** (`49f74d9`, 15 путей, $0). `PD-455` закрыт, `PD-162` наполовину у обеих денежных дверей, `PD-448` разобран и НЕ построен (четвёртый путь принят направлением, строит следующий пак вместе с минором **0.15.0**). ⛔ Приёмка нашла два дефекта ВНЕ карты автора: выживший мутант на единственной проводке `BooksDir` и тихо-зелёное в самом новом гейте. Живое — `PD-162` (эскроу), `PD-466`, `PD-139`. Промт был — [PLATFORM_TRUTH_AT_THE_DOOR_SESSION_PROMPT.md](PLATFORM_TRUTH_AT_THE_DOOR_SESSION_PROMPT.md), выдан 11.09: `PD-455` (форма обещает то, в чём дверь откажет — точка роста `Blocked.code`, не закрытый enum вердикта) · `PD-448` (двойной клик по «продолжить» — **исследуй и предложи, НЕ строй**: все три законных пути требуют контрактного минора или миграции) · `PD-162` (прогон книги с исчезнувшим каталогом висит с холдом — валидация каталога ПРИ ДОПУСКЕ). ⛔ Свип сирот и потолок аплоадов СНЯТЫ подписанным пропуском: их гейт — открытая регистрация, которой в закрытой бете нет (`PD-175` + `D39.176` п.1) | diff --git a/docs/orchestrator-norms-scratchpad.md b/docs/orchestrator-norms-scratchpad.md index 5875a73e..db3c4414 100644 --- a/docs/orchestrator-norms-scratchpad.md +++ b/docs/orchestrator-norms-scratchpad.md @@ -63,8 +63,16 @@ | полигон | `POLYGON_INSTRUMENT_REPAIR_SESSION_PROMPT.md` | — | ⛔ **готов, адресата НЕТ** — все семь живых сессий отработали, свежую поднимает владелец | | фронт | заморожен | — | разморозка по критерию `NEAR_TERM_PLAN.md` | -**В очереди, написано и ждёт слота:** `BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md` (падение, которое -видно) — выдаётся СВЕЖЕЙ бэкенд-сессии сразу, как c9 закроется актом. Опровергатель по нему запущен. +**В очереди, написано и ждёт слота:** `BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md`, **редакция 2** — +выдаётся СВЕЖЕЙ бэкенд-сессии, как c9 закроется актом. ⛔ **Опровергатель нашёл 17 находок, и первая +перевернула пак:** я построил §1 на «движок лечит болезнь возбудителем», а замер говорит обратное — +перевыпуск вернул текст 4 из 4, и в трёх случаях успешный ответ физически не влез бы в базовый потолок. +Моя таблица лекарств отключила бы восстановление трёх чанков из четырёх и отменила бы `D2` п.2 с пином +`TestRetryableSubset`. Пере-снял сам — потолок 8496 делит популяцию успешных черновиков БЕЗ ИСКЛЮЧЕНИЙ +(≤8496 прошли сразу ×3, >8496 упали и прошли на удвоенном ×3), а у редактора наоборот. ⇒ причин две, +лекарство одно — вот это и есть пак. Ещё опровергатель снял: три моих контрольных числа не сходились +(`glob` не дошёл до ~170 баз), `D24.3` и `D39.87` мис-атрибутированы, `temperature` у pro 0.4 а не 0.3, +мест вызова два а не три, «пуста ровно `err`» — пусты пять колонок. Все исправлены. ## Журнал выдач очереди №23