362 lines
44 KiB
Markdown
362 lines
44 KiB
Markdown
# Пак бэкенда: «ВЫЗОВ, КОТОРЫЙ ОБОРВАЛИ МЫ»
|
||
|
||
## 1. Какая проблема и что решит твой результат
|
||
|
||
Движок отправляет запрос провайдеру, тот принимает его и генерирует — а мы по своему же дедлайну
|
||
перестаём ждать и рвём соединение. Провайдер об этом не знает и работу не отменяет. Дальше движок
|
||
**выбрасывает единственную улику**: `internal/llm/httpllm.go:409` — `data, _ := io.ReadAll(...)`, ошибка
|
||
чтения уходит в `_`, а обрыв распознаётся по РАЗМЕРУ. Тело, оборванное нашим дедлайном, маленькое ⇒ не
|
||
отличимо от целого ⇒ не разбирается как JSON ⇒ объявляется «оплаченным 2xx с нечитаемым телом» и
|
||
**ретраится** (`:433` терминален только для >16 МиБ; `:365` даёт один повтор).
|
||
|
||
**Цена — один замер и одно наблюдение, и разница между ними важна.** `docs/experiments/23-editor-tier.md` §5: латентности ровно `2×attempt_s`,
|
||
**$0.475893 = 23.8 %** цены руки — это САМИ четыре строки; юниты целиком, вместе с их попытками 0, —
|
||
**$0.689215 = 34.5 %**. ⛔ Источник пишет дословно «две законные цифры, и путать их нельзя» и регистрирует
|
||
эту путаницу СВОИМ дефектом №11; не повторяй её, называя одно число двумя долями. Живой прогон 08.09: два редакторских вызова,
|
||
`unexpected end of JSON input`, **$0.407606 из $1.259217 = 32 % бюджета**. ⚠ **Что это ИМЕННО таймауты —
|
||
не измерено, а выведено по совпадению подписи** с замером выше: сырого леджера того прогона в дереве нет
|
||
(`.db` новее 07.09 — 0 при 163). Не опирайся на второе число как на улику класса.
|
||
|
||
**Корень — НЕ транспорт, а дедлайн против оси регенерации.** DeepSeek держит 75–76 tok/s (n=139). Грант
|
||
редактора 16 000 ≈ 213 с; реген удваивает его до 32 000 ≈ **427 с** и НЕ трогает время, а `attempt_s`
|
||
равен 240. Потолок по времени 17–21 тыс. токенов; максимум, когда-либо доставленный регеном, — **19 354 =
|
||
60 % гранта**, и **13 доставок из 17 уложились бы в исходные 16 000**. ⇒ ось эскалации сегодня фиктивна:
|
||
резервация бронирует недостижимое и съедает headroom под книжным потолком (строка **369**).
|
||
|
||
**Что покупает твой результат.** (1) Мы перестаём покупать одну генерацию дважды. (2) Оператор и читатель
|
||
видят, какие деньги — оценка, а не факт. (3) Эскалация становится настоящей: правильный дедлайн делает
|
||
выданный бюджет достижимым.
|
||
|
||
## 2. Зона и git
|
||
|
||
**Зона записи — `backend/`** плюс своя секция в `docs/PROGRESS.md` (это ЕДИНСТВЕННОЕ исключение из
|
||
«`docs/` — зона оркестратора»). Чужие зоны (`platform/`, `frontend/`, `eval/`, `books/`) не трогаешь ни
|
||
строкой; расхождения — пингом. **Ты НЕ коммитишь**: дерево готовишь и передаёшь на лендинг. Никаких
|
||
`git add -A`, `reset --hard`, переписывания истории. `.env` не читать никогда.
|
||
|
||
## 3. Карта чтения — ЗАКОН, дальше только по её ссылкам
|
||
|
||
1. `docs/BACKLOG.md` — строки (⚠ «строка N» здесь — **ID в первой колонке, а не номер строки файла**: `sed -n '360p'` даст пустоту, греп `^| 360 |` — нужный ряд) **360** (несёт СОШЕДШУЮСЯ конструкцию лечения целиком), **369** (ось
|
||
эскалации), **78** (движковая половина `PD-441`: публикация оценочных строк). Греп `^| 360 |`.
|
||
2. `docs/experiments/00-provider-quirks.md` — **канон велит читать ПЕРЕД правкой адаптеров и вызовов.**
|
||
⚠ Класса, который ты чинишь, там НЕТ (замер: `early-200`/«empty lines»/«10 minutes» — 0 хитов) — это
|
||
пробел, и он пингом полигону, не твоя правка.
|
||
3. `docs/research/21-llm-transport-survey.md` — §Q6 и **ЭРРАТА ОРКЕСТРАТОРА 08.09 в шапке**: обзор неверен
|
||
дважды, и обе половины вместе полтора месяца держали неверный дедлайн. Читай эррату ПЕРЕД §Q6.
|
||
4. `docs/architecture/15-money-path.md` — денежный путь; леджер есть НИЖНЯЯ граница.
|
||
5. `backend/cmd/tmmutate/main.go` — шапка целиком (контракт мутационного инструмента) + `backend/Makefile`,
|
||
цели `battery` и `mutations`.
|
||
|
||
Ратифицированное, нужное по существу, вложено сюда и похода в журнал решений НЕ требует:
|
||
- ⛔ **`D39.230` п.1 — СЛОВО ВЛАДЕЛЬЦА, и у него есть УСЛОВИЕ.** Оценка за оборванный нами вызов
|
||
списывается с читателя. **Но пометка — часть решения, а не украшение:** строка обязана нести свой класс
|
||
и флаг `estimated=1`, а счёт и сумма оценочных строк — публиковаться машинно-читаемо рядом с
|
||
`committed_usd`. Списать с человека непроверенную оценку без видимой пометки — НЕ то, на что дано слово.
|
||
- **`D39.230` п.2** — платная проба вендорского баланса ОТКЛОНЕНА владельцем. ⇒ вопрос «биллит ли вендор
|
||
оборванное» осознанно ОТКРЫТ; оценка есть верхняя граница под гипотезой «вендор досчитывает». Проб не
|
||
проектировать и не предлагать.
|
||
- **`D39.230` п.3** — ожидание провайдера до ~20 минут РАЗРЕШЕНО. Сверху не убьёт ничто: `RuntimeMaxSec`/
|
||
`WatchdogSec` — 0 хитов при 16 файлах с systemd-свойствами, у движка дедлайна нет.
|
||
- **`D39.216`** — где форма не тянет, её развязывают или переписывают; «дешевле подпереть» доводом НЕ
|
||
является; второй носитель одного знания не заводится (п.3б).
|
||
- **`D39.183`** — запрет править тесты «чтобы прошло» про МОТИВ; правка от ЗАКАЗАННОЙ смены поведения —
|
||
обслуживание, но объявляется отдельным пунктом отчёта.
|
||
- **`D39.202`** — прибор, честно отвечающий на СВОЙ вопрос, читается как ответ на другой. Ноль и «не
|
||
смотрели» неразличимы без контрольной величины.
|
||
|
||
## 4. Состав и разметка свободы
|
||
|
||
### 4.1 Признак: граница денег — ДОСТАВКА запроса, а не статус-строка — **делай РОВНО так**
|
||
|
||
⛔ **Доктрина «2xx ⇒ провайдер уже списал» (`httpllm.go:428`, `stagerun.go:610`) ЛОЖНА для нашего боевого
|
||
провайдера.** DeepSeek документирует ранний `200` с пустыми строками, пока запрос ждёт планирования, и
|
||
закрытие соединения, если инференс не начался за 10 минут; в наших логах пустое тело при `200` видно буквально
|
||
(`response="\n"`, **8 строк из 617** «llm exchange» под `books/gu-zhenren/` — счёт мой). ⚠ **Но НЕ бери эту строку как
|
||
улику self-cut: она пришла на 91-й секунде при дедлайне 240 — ушли не мы.** Настоящие self-cut в том же
|
||
файле лежат ровно на **240.0 с** — четыре трейса в `probe-4axes/logs/p7-off.log` ⚠ (счёт опровергателя
|
||
приёмки, оркестратором НЕ пере-снят; пере-сними сама, если будешь на них опираться). Разница и есть предмет
|
||
пака: одна подпись, два разных класса, и различает их только признак §4.1. ⇒ `200` значит «принят», не «сгенерирован», и ветвь `No 2xx ever arrived`
|
||
на DeepSeek недостижима по построению — все наши обрывы прятались под `decode_error`.
|
||
|
||
**Единственный факт, общий для всех этих случаев: запрос ДОСТАВЛЕН, и ушли МЫ.** Он фиксируется без байта
|
||
на проводе — `httptrace.ClientTrace{WroteRequest, GotFirstResponseByte}` в `attempt()`. Различение
|
||
проверено исполнением на клиенте той же сборки:
|
||
|
||
| случай | `WroteRequest` | `GotFirstResponseByte` |
|
||
|---|---|---|
|
||
| доставлен, заголовки держат, наш дедлайн | `true` | `false` |
|
||
| TLS-рукопожатие висит — НЕ доставлен | `false` | `false` |
|
||
| ранний `200` + `"\n"`, наш дедлайн | `true` | `true` |
|
||
|
||
**Заказ:** (а) ошибка `ReadAll` больше не выбрасывается; (б) заводится типизированная
|
||
`AttemptTimeoutError{Delivered, AfterHeaders, BytesRead, WhitespaceOnly, Elapsed}`; (в) исходы —
|
||
**перечислены ниже поимённо, свой набор не собирай**. ⛔ Обёртывание обязано СОХРАНИТЬ
|
||
`errors.Is(err, context.Canceled)` для кодов выхода — иначе стоп прогона получит чужой exit-код.
|
||
|
||
**ПОСЛЕ 2xx — пять исходов:**
|
||
|
||
| исход | признак | вызовов | деньги | чанк |
|
||
|---|---|---|---|---|
|
||
| целое, JSON ок | `readErr == nil` | 1 | usage | как есть |
|
||
| целое, JSON битый | `readErr == nil`, разбор упал | **2** (кап 1, как сегодня) | оценка | флаг |
|
||
| **self-cut** | `readErr != nil`, дедлайн `attemptCtx`, родитель ЖИВ | 1 | `settleUSDForCutCall` | флаг; резюм — **0** вызовов |
|
||
| **отмена РОДИТЕЛЕМ** (стоп, Ctrl-C) | `readErr != nil`, `ctx.Err() != nil` | 1 | `settleUSDForCutCall` | ⛔ своя причина `cancelled`, НЕ общий флаг «нужен человек»; резюм — 1 вызов на ТОМ ЖЕ бюджете |
|
||
| разрыв соединения | `readErr != nil` иначе (`ErrUnexpectedEOF`, h2 stream) | 2 | `settleUSDForCutCall` | на исчерпании — **инфра-пауза, НЕ флаг** |
|
||
|
||
**ДО заголовков — четыре:** self-cut доставленный (1 вызов, флаг, резюм 0) · отмена родителем
|
||
доставленного (1, деньги те же, резюм 1 на том же бюджете) · разрыв после доставки (2, деньги те же,
|
||
инфра-пауза, резюм 1) · **НЕ доставлен** (до `max_attempts`, **$0**, освобождение резерва, резюм 1 — это
|
||
первая покупка, а не перекупка).
|
||
⚠ **`TestBilledDecodeErrorReBillCap` не меняется** — у него `readErr == nil`, это второй исход таблицы.
|
||
|
||
### 4.2 Диспозиция: не ретраить то, что оборвали мы — **делай РОВНО так**
|
||
|
||
- **Доставлен + наш дедлайн** (с заголовками и без) — класс `attempt_timeout`, **чекпойнт + флаг, БЕЗ
|
||
ретрая**, сумма — через `settleUSDForCutCall`.
|
||
- **НЕ доставлен** (дедлайн до `WroteRequest`) — единственный случай, где «ничего не куплено» верно по
|
||
построению: остаётся как сегодня (retryable, на исчерпании освобождение резерва, инфра-пауза).
|
||
- ⛔ **ОТМЕНА РОДИТЕЛЕМ доставленного вызова** (стоп прогона, Ctrl-C) — **это тот самый случай, с которого
|
||
начался заказ (строка 331), и диспозиция у него ДРУГАЯ.** Деньги — та же `settleUSDForCutCall` (ровно то, что просит
|
||
`PD-441` — по-русски: «сеттлить оценку и при отмене вызова, который успел уйти, различая по ФАКТУ ухода»), но чанк получает **СВОЮ причину `cancelled`, а не общий флаг «нужен человек»**: вызов был здоров, остановил его ЧЕЛОВЕК. ⛔ **Это НЕ «флага нет».** Дыры без пометки не должно быть ни в один момент: пока прогон не резюмирован, диспозиция чанка не `ok`, и экспорт показывает `flagged(cancelled)` — читается как «остановлено, резюм пере-сделает», а не как «нужен человек». Флаг, врущий о ПРИЧИНЕ, запрещён отдельно (`D39.93` п.2); невидимая дыра запрещена тем более. ⛔ **И резюм обязан пере-делать
|
||
его на ТОМ ЖЕ `max_tokens`** — `maxTokensForAttempt` удваивает бюджет по оси попыток
|
||
(`disposition.go:393-401`, `base << uint(attempt)`; петля берёт его из НОМЕРА попытки,
|
||
`stagerun.go:147`), и здесь это ловушка: удвоение купит вдвое больший вызов за здоровье, которого никто
|
||
не терял.
|
||
⛔⛔ **КАК это построить — задано, потому что наивный путь НЕВОЗМОЖЕН, и это проверено.** В сторе ровно
|
||
три функции (`Reserve`, `Release`, `SettleWithCheckpoint`), деньги нельзя записать БЕЗ чекпойнта, а
|
||
чекпойнт — ключ резюма (`ON CONFLICT (request_hash) DO NOTHING` ⇒ «Duplicate settle: book no new
|
||
spend»). ⇒ «заплатить и пере-сделать на том же ключе» несовместимо по построению.
|
||
**Развязка — в ПЕТЛЕ, а не в сторе:** индекс попытки и счётчик УДВОЕНИЙ разводятся.
|
||
`maxTokens = base << escalations`, где `escalations` растёт только на `length`/`empty`; кап регенерации
|
||
считает УДВОЕНИЯ, а не индекс. Тогда попытка N+1 берёт тот же бюджет, но НОВЫЙ хеш (`Attempt` —
|
||
самостоятельное измерение) ⇒ свежие `reserve → call → settle`, `ON CONFLICT` недостижим, инвариант
|
||
`committed == sum(checkpoints)` цел, стор не тронут. ⛔ **Стор и его инварианты не трогать** — упрёшься,
|
||
это пинг.
|
||
⚠ И проверь побочное: `priceprojection.go` считает проекцию через `maxTokensForAttempt` («RETRIES ARE IN
|
||
IT, AND THAT IS THE POINT») — смена оси двигает СМЕТУ. Двинулась — назови в отчёте числом. ⚠ **Видимое следствие, назови его в отчёте:** стоп прогона
|
||
теперь ПЛАТИТ оценку за каждый летящий вызов (в редакторской волне это до восьми × ~$0.13). Слово
|
||
владельца это покрывает («оборванный НАМИ» — мы рвём по его команде), но молча вводить это нельзя.
|
||
- **Разрыв соединения ПОСЛЕ доставки** — один ретрай (кап ключуется на «доставлен», а не на «2xx»), на
|
||
исчерпании **инфра-пауза, не флаг**: серия таких — мёртвый провайдер, и довод нулевой ветви про
|
||
флаг-шторм здесь верен. ⛔ **Но деньги назвать обязательно, иначе граница течёт через эту дверь:**
|
||
сегодняшний инфра-путь зовёт `releaseReservation` и даёт **$0**, а запрос был ДОСТАВЛЕН ⇒ идёт
|
||
`settleUSDForCutCall` + класс `connection_lost`, и ТОЛЬКО потом пауза; резюм пере-делает на том же
|
||
бюджете. Для post-header разрыва это сегодняшнее число, для pre-header — новое, и объявляется
|
||
заказанной сменой поведения.
|
||
⚠ Довод против «оставить ретрай, он же 3 раза из 7 спасал»: правильный дедлайн даёт то же самое без
|
||
монетки по цене полного счёта. Клапан на будущее — ДАННЫЕ (`tok_s_floor`), не код.
|
||
|
||
### 4.3 ⛔ ТРЕТЬЯ УТЕЧКА, которой нет даже в строке 331 — **делай РОВНО так**
|
||
|
||
Сегодняшняя нулевая ветвь (`stagerun.go:634`) зовёт `releaseReservation` и **чекпойнта НЕ пишет**, ставя
|
||
стадию `failed` ⇒ **резюм ПОКУПАЕТ ВЫЗОВ ЗАНОВО**. Убрать ретрай и оставить это значит закрыть класс
|
||
наполовину: тот же платный вызов уходит другой дверью.
|
||
**Заказ:** доставленный self-cut получает чекпойнт, и резюм его находит. **Предъявление именно этим:** тест
|
||
утверждает, что после self-cut резюм на том же снапшоте **не делает НИ ОДНОГО HTTP-вызова**. Мутация:
|
||
убрать запись чекпойнта — тест обязан покраснеть текстом про свежий вызов, а не про деньги.
|
||
|
||
### 4.4 Дедлайн считается от бюджета — **делай РОВНО так по форме, числа реши сам и обоснуй**
|
||
|
||
`timeouts` получает `tok_s_floor`, `queue_slack_s`, `attempt_max_s`; дедлайн вызова =
|
||
`clamp(queue_slack_s + max_tokens/tok_s_floor, attempt_s, attempt_max_s)`, считается в `attempt()` из
|
||
`maxTokens` запроса. **`attempt_s` становится ПОЛОМ** ⇒ ни один существующий конфиг не теряет времени.
|
||
Дефолт пола — **35.5 tok/s** (128 000/час, единственное опубликованное вендором число).
|
||
⭐ **Почему этот дефолт правильный, и это же твой тест:** он ПРОИЗВОДНЫЙ, и на черновике (8 496 токенов)
|
||
даёт **238.95 с** — сегодняшние 240 с точностью до секунды. То есть нынешнее значение верно ровно для
|
||
черновика и было перенесено на редактора слепо. ⛔ **Тест-тождество обязан пинить ФОРМУЛУ, а не число, и вот операционная форма — иначе его исполнят
|
||
формально.** `assert derive(8496) == 238.95` негоден: это литерал, произведённый формулой, и через полгода
|
||
его «починят» под круглые 240. Требуется: ожидание **вычисляется в тесте** из двух вендорских констант
|
||
(час, 128 000) и гранта; **литералов `238`/`239`/`240`/`900` в тест-файле НЕТ** — предъяви грепом с
|
||
контрольной величиной; не меньше ДВУХ грантов (8 496 и 32 000); отношение удвоения
|
||
`derive(2N) − slack == 2·(derive(N) − slack)`; края `clamp`; пустое и нулевое поле дают ДЕФОЛТ, а не
|
||
деление на ноль и не нулевой дедлайн.
|
||
**Твоё — ЧИСЛА. Моё — ПРАВИЛО их вывода, и оно РОВНО так:**
|
||
- `tok_s_floor` — НИЖЕ p10 наблюдаемой скорости модели по ≥100 строкам `request_log`, округление вниз
|
||
объявлено (DeepSeek: p10 ≈ 67 ⇒ 50);
|
||
- `queue_slack_s` — документированное ВЕНДОРОМ максимальное ожидание до инференса (DeepSeek — 600).
|
||
⛔ Меньше — только с доводом и ПИНГОМ: владелец сказал «готов ждать» именно про ~20 минут, и меньший слак
|
||
меняет принятый им размен молча, в другую сторону;
|
||
- `attempt_max_s` — слак + максимальный грант С УДВОЕНИЕМ / пол, округлённый вверх (DeepSeek:
|
||
600 + 32000/50 = 1240).
|
||
⛔ **И два механизма, без которых «новый провайдер без правки Go» не работает:** WARN на старте для
|
||
провайдера БЕЗ `tok_s_floor`, чей стадийный грант с учётом удвоения выше ~8.5k («дефолт 35.5, дедлайн для
|
||
32 000 = 900 с, задай после первого прогона»), и валидационный тест `models.yaml`, ТРЕБУЮЩИЙ поле для таких
|
||
провайдеров.
|
||
⚠ **Снапшот не двигается, и проверено это ПРЯМО, а не соседством:** `buildSnapshotID`
|
||
(`internal/pipeline/snapshot.go`) перечисляет входы поимённо, и таймаутов среди них нет — читай сам список,
|
||
а не грепай близость слов. ⛔ Прежняя редакция этого пака писала «`AttemptS` — 0 хитов из 120 файлов со
|
||
`snapshot`»: замер был неверен в ЧИСЛИТЕЛЕ (хит 1 — `config/models.go`) и в ЗНАМЕНАТЕЛЕ (файлов 145), а
|
||
вывод верен — правый вердикт по неправой причине, `D39.202`. Если твоя правка список входов меняет — СТОП
|
||
и пинг: перекупка книг словом владельца не покрыта.
|
||
|
||
### 4.5 Пометка — УСЛОВИЕ решения владельца — **делай РОВНО так**
|
||
|
||
Класс `attempt_timeout` и `estimated=1` едут в чекпойнт и `request_log`; `classify()` узнаёт класс на
|
||
резюме; `paidtail` включает его в LostUSD. **И сверх того — то, ради чего дано слово:** счёт и сумма
|
||
оценочных строк публикуются **машинно-читаемо рядом с `committed_usd`** (`D39.230` п.1; это же движковая
|
||
половина `PD-441`, строка **78**). ⚠ **Поверхность назови точно: это тот JSON, который ЧИТАЕТ платформа
|
||
(`tmctl status --json`), а не новый файл.** Формат — пингом платформе, не решением за неё. ⚠ Сегодня флаг `estimated` виден только человеку в `tmctl render.go`;
|
||
платформа его не читает — ей нужно число, а не строка экрана.
|
||
|
||
### 4.6 Оператор не должен принимать ожидание за висяк — **реши сам, бери если по пути**
|
||
|
||
С разрешённым ожиданием до ~20 минут INFO-лог говорит «calling model» и потом молчит. Дешёвая строка «жду
|
||
провайдера: прошло X из Y» раз в N минут снимает это. ⚠ И назови в отчёте, что `waitForSettle` теперь ждёт
|
||
до самого длинного летящего вызова — это принятая цена решения владельца, но она должна быть названной.
|
||
|
||
### 4.6-бис Мандат чистоты — **делай РОВНО так**
|
||
|
||
Пак ПРОЕКТИРУЕТ: новый тип ошибки, три новых поля конфига, новая денежная семантика. ⇒ `D39.216` действует
|
||
буквой: где нынешняя форма не выдерживает заказ, её **развязывают или переписывают**, а не обкладывают
|
||
заплатой; «дешевле подпереть» доводом НЕ является; второй носитель одного знания не заводится (п.3б).
|
||
Отступление законно, но объявляется С ЦЕНОЙ: почему заплата дешевле развязки и что именно она откладывает.
|
||
⚠ Нота требует этой строки в КАЖДОМ паке с проектированием и называет её отсутствие дефектом промта — она
|
||
здесь не для полноты.
|
||
|
||
### 4.6-трис ⚠ **ИСПР. 10.09 ПО ИТОГУ ПРИЁМКИ — ДВА МЕСТА ЭТОГО ПАКА ОКАЗАЛИСЬ НЕВЕРНЫ, И ОБА МОИ**
|
||
|
||
Приёмка предъявила два расхождения между буквой пака и тем, что ратифицировано. Исправляю здесь, а не в
|
||
письме, чтобы читатель пака не мерился ложной буквой.
|
||
|
||
1. ⛔ **§4.2 писал: «доставлен + наш дедлайн (с заголовками И БЕЗ) ⇒ сумма через `settleUSDForCutCall`».**
|
||
Это отменено МОИМ ЖЕ поздним заказом — разведением предикатов: деньги считаются по
|
||
`answered && afterHeaders`, а до заголовков доставленный обрыв книжится **нулём**. Довод был замером и
|
||
он в силе: 3 из 25 отменённых в полёте сеттлили оценку за запрос, которого не видел ни один хендлер, —
|
||
переплата ЧИТАТЕЛЯ, единственное запрещённое `D39.196` п.2а направление. ⇒ **буква §4.2 про «и без»
|
||
недействительна; действует разведение предикатов.** Остаток (доставленный обрыв до заголовков уходит в
|
||
ноль) — строка бэклога, не дефект исполнения.
|
||
2. ⛔ **§4.7 писал: «Anthropic-адаптер — ВНЕ пака ЦЕЛИКОМ».** Отменено моей же ратификацией `D39.231` п.2:
|
||
одна строка `CheckRedirect` внесена, потому что через редирект уходил `x-api-key`, а `net/http`
|
||
кастомный заголовок через смену хоста не срезает. Приёмка права, что это выход за объявленное сужение —
|
||
и права также в том, что цена (тест и мутация) ровно та, которой сужение и мотивировалось. ⇒ **сужение
|
||
§4.7 действует на всё, КРОМЕ утечки учётных данных.**
|
||
|
||
### 4.7 Чего в паке НЕТ — **не делай**, и это ОБЪЯВЛЕННЫЕ сужения
|
||
|
||
- **Стриминг** — не спас бы ни одно из 19 замеренных событий; цена: `stream:true` в теле ⇒ `RequestHash` ⇒
|
||
перекупка книги, непроверенная байт-идентичность склейки, новый парсер, потеря usage-fidelity. Trip-wire
|
||
обзора переписан: доля self-cut в спенде редактора ПОСЛЕ починки дедлайна.
|
||
- **Платная проба вендорского баланса** — ОТКЛОНЕНА владельцем (`D39.230` п.2). Не проектировать.
|
||
- **Адаптивный пол из леджера на лету** — прячет число от оператора, тащит состояние прогона в дедлайн.
|
||
- **`max_attempts` и бэкофф** — другая ось, не трогать.
|
||
- **Порог по латентности** («≈2×attempt_s») — совпадение; протухнет молча при смене `attempt_s`.
|
||
- **Idle-timeout в non-streaming** — схлопывается в общий бюджет (§Q6 прав).
|
||
- **Разбор обрывков тела.**
|
||
- **Оценка по времени `elapsed × rate`** — догадка на догадке; оправдана только исходом пробы, которой не будет.
|
||
- **Денежная сумма как YAML-ручка** — политику нельзя переключать молча конфигом; она за одной функцией.
|
||
- **Строка 368 (`est_tokens` занижена в 5 раз)** — отдельный носитель, другой предмет. Наткнёшься —
|
||
наблюдением в отчёт, не чини: два денежных класса в одном лендинге мы сегодня уже разводили.
|
||
- **Anthropic-адаптер** (`provider_anthropic.go`) — ВНЕ пака целиком: поверхность deprecated (из стека
|
||
убрана 04.07), боевой цены ноль, а тесты и мутации она бы добавила. Уходит строкой бэклога.
|
||
|
||
## 5. Мандат самопроверки ИСПОЛНЕНИЕМ — «перечитал сам» не считается
|
||
|
||
1. **Каждая гарантия предъявляется ИСПОЛНЕНИЕМ на фейк-сервере**, а не чтением. Пять исходов после 2xx и
|
||
четыре до заголовков — **ДЕВЯТЬ фикстур**, и у каждой утверждается ЧИСЛО HTTP-вызовов, а не только исход.
|
||
⛔ **Счёт вызовов берётся у ФЕЙК-СЕРВЕРА** (сколько запросов он принял), а не счётчиком клиента: клиент
|
||
считает свои намерения, сервер — факты.
|
||
⛔⛔ **И к КАЖДОЙ доставленной-и-потерянной фикстуре — ПАРНОЕ утверждение о РЕЗЮМЕ**, иначе конструкция
|
||
вырождается в противоположную сторону: сессия может «пере-закрыть» — не пере-делывать НИЧЕГО
|
||
доставленного — и остаться зелёной. Ожидания: self-cut → резюм **0** вызовов; отмена родителем и
|
||
исчерпанный разрыв → **ровно 1 вызов на ТОМ ЖЕ `max_tokens`**; не доставлен → 1.
|
||
⛔ **Поля утверждаются ДВУСТОРОННЕ, иначе они декоративны:** `WhitespaceOnly` — `true` на теле `"\n"` и
|
||
`false` на обрывке JSON; `Delivered` — обе стороны. И денежное утверждение на каждой строке:
|
||
`estimated=1`, `cost_usd == estimate`, класс на месте; у «не доставлен» — `reserved_usd` вернулся в 0.
|
||
2. **Мутации — на копии, рецепт УЖЕ в цели `mutations`** (`backend/Makefile`); своего однострочника не пиши.
|
||
Краснота читается ПО ТЕКСТУ падения: говорит ли он про сломанное тобой.
|
||
3. **Мутируй и СВОИ новые фикстуры.** ⛔ Особая осторожность: у тебя условные сообщения (пометка
|
||
`estimated`, WARN про пол провайдера) — на каждое нужна фикстура, где оно ОБЯЗАНО прозвучать, И вторая,
|
||
где обязано молчать. Тест, утверждающий только молчание, вакуумен.
|
||
4. **Субагенты РАЗРЕШЕНЫ ЯВНО** — и на охват, и на адверсариальный проход по ГОТОВОЙ работе.
|
||
⛔ **Направление — что уязвимо ИМЕННО в этом паке** (норма куплена замером 08.09: направленный проход
|
||
нашёл шесть дефектов пака на ЗЕЛЁНОЙ батарее): **(1)** фикстуры, где «доставлен» и «не доставлен»
|
||
случайно совпали — весь пак стоит на этом различении; **(2)** путь резюма: чекпойнт написан, но
|
||
`classify()` его не узнаёт ⇒ перекупка возвращается тихо; **(3)** `errors.Is(context.Canceled)` после
|
||
твоего обёртывания — коды выхода ломаются молча, И одновременно `errors.As` обязан НАХОДИТЬ твой тип:
|
||
два утверждения в одной фикстуре, не в разных; **(4)** исход «отмена родителем» — его легко потерять,
|
||
потому что он единственный, где деньги списываются, а флага НЕТ.
|
||
5. **Проверяемый артефакт** — таблица «находка → что сделано → ЧЕМ ПРЕДЪЯВЛЕНО» плюс столбец
|
||
**«закрыт в N носителях из M»**, где M посчитан командой.
|
||
6. **BASELINE НА ВХОДЕ сними САМ** и сверь с моим: `make battery` → 19 `ok` · 0 FAIL · 4 «no test files» ·
|
||
4 скипа; `make mutations` → **65 записей · 0 выживших · свип `0 of 276`**; `python3
|
||
docs/scripts/counts.py --check` → «Литералы сходятся». ⛔ **Печатай счёт СКИПОВ рядом с числом `ok`** —
|
||
норма куплена 08.09: «20 пакетов ok» и «20 ok при 431 скипе» неразличимы, и приёмка так приняла механизм,
|
||
ни разу его не коснувшись. Красное на входе — находка и пинг, а не твоя работа.
|
||
|
||
## 6. Оси ревью — характер «Код», плюс деньги и шов (ось вправе заменить с аргументом)
|
||
|
||
Тест утверждает не то, что думает (`D39.171`): часы как способ синхронизации там, где предмет НЕ время, и
|
||
ассерт по подстроке в общем буфере. Обе формы дают и ложное красное, и **тихо-зелёное** — оно дороже.
|
||
**Деньги:** любое число из СЫРОГО леджера, а не из агрегата; леджер — нижняя граница.
|
||
**Шов:** платформа биллит пользователю `committed_usd` и просила у движка публикацию оценочных строк
|
||
(`PD-441`). Инвентарь её каналов — чтением ЧУЖОГО кода, не по памяти; правок в `platform/` НЕ делать.
|
||
**Индустриальный первоисточник прежде велосипеда:** `httptrace` — stdlib; если решишь иначе, отвергни с доводом.
|
||
|
||
## 7. Записка-план ДО работы
|
||
|
||
До первой правки — короткая записка в свою секцию журнала: что берёшь, в каком порядке, что считаешь
|
||
рискованным. После работы — сверка комплектности против §4 МЕХАНИЧЕСКИ (таблицей), не перечитыванием.
|
||
|
||
## 8. Заявление = команда
|
||
|
||
⛔ **Эрудиция НЕ улика:** утверждение о коде, провайдере или чужой зоне грунтуется `file:line`, цитатой или
|
||
замером — либо несёт пометку **«мнение»**. Каждое число и категорика — с командой, которой получены. Отрицательный замер печатается ВМЕСТЕ с
|
||
контрольной величиной. Сверх этого: **перед отчётом сверь каждый клейм с результатом инструмента ЭТОЙ
|
||
сессии**, а не с памятью и не с этим промтом — адреса §4 сняты 08.09 и могли уехать. **Последний абзац
|
||
отчёта оказался планом или обещанием? сделай это СЕЙЧАС.** ⛔ Править тест/гейт, чтобы он прошёл, —
|
||
НЕДОПУСТИМО; исключение — правка от ЗАКАЗАННОЙ смены поведения, и она объявляется отдельным пунктом.
|
||
Сессия длинная — поставь себе интервальную самоверификацию субагентом против ЯВНЫХ критериев §13.
|
||
|
||
## 9. Эхо-протокол старта
|
||
|
||
ДО работы — ≤10 строк **СВОИМИ словами**: скоуп · инварианты · что НЕ делаешь. Дословный пересказ промта
|
||
подтверждает канал, но не понимание, и ошибку промта повторяет вместе с ним. Эхо — оркестратору по адресу
|
||
из прямого канала (§12); канала нет — эхо первой секцией отчёта, работа продолжается.
|
||
|
||
## 10. Что НЕ удалось — обязательная секция отчёта
|
||
|
||
Что не вышло, что не проверено, где сомневаешься. «Не проверено» и «опровергнуто» — РАЗНЫЕ исходы.
|
||
|
||
## 11. Канал вопросов и право сказать «этого делать не надо»
|
||
|
||
Конфликт промта с кодом или доками — **пинг, а не интерпретация**: конструкция этого пака сошлась в
|
||
консилиуме двух моделей, но её проверяли чтением, а не твоим прогоном. Считаешь пункт неверным — скажи с
|
||
доводом, довод сильнее моей подписи. Решение владельца требуется, если работа упрётся в перекупку книг, в
|
||
скоуп или в продукт.
|
||
|
||
## 12. Прямой канал
|
||
|
||
Механизм — `CLAUDE.md` §«Связь между сессиями». Впиши СВОЙ блок в `/tmp/textmachine-channel` первым делом,
|
||
чужие блоки не трогай. ⚠ Адрес из файла не доказывает, что сессия жива: сверься с `ListAgents`. Нужной роли
|
||
нет или имя не отвечает ⇒ **канала нет, и это НОРМАЛЬНЫЙ случай: НЕ опрашивай сессии подряд** — вопрос
|
||
секцией в отчёт, работа продолжается.
|
||
|
||
## 13. Критерий завершённости — по нему тебя примут
|
||
|
||
Работу доводишь до конца САМ; приёмка идёт последней и вторым кругом твоей самопроверки не служит.
|
||
Готово — это когда:
|
||
- у КАЖДОГО пункта §4 есть исход: сделано · не делаю с доводом · пинг;
|
||
- круги СОШЛИСЬ: последний проход не дал НОВЫХ находок, прежние закрыты таблицей;
|
||
- **каждый класс закрыт со ЗНАМЕНАТЕЛЕМ «N из M»**, и M посчитан командой;
|
||
- ДЕВЯТЬ фикстур §5.1 существуют, утверждают ЧИСЛО вызовов у СЕРВЕРА и несут ПАРНОЕ утверждение о резюме;
|
||
пин резюма (§4.3) предъявлен мутацией; поля утверждены двусторонне;
|
||
- тест-тождество §4.4 пинит формулу, а не константу;
|
||
- числа сняты ПОСЛЕ последней правки, со счётом скипов рядом с числом `ok`;
|
||
- **таблица мутаций ПОЛНАЯ, и выжившие названы поимённо** (выжившая мутация — находка, а не шум);
|
||
- **дифф `^func Test` снят ИСПОЛНЕНИЕМ, а не памятью**, и назван прибор, которым считан;
|
||
- всё живое — в ДЕРЕВЕ, а не в письме;
|
||
- сказано явно: **«работа завершена, править не планирую»**. Без этой фразы пак считается идущим.
|
||
|
||
## Деньги
|
||
|
||
**Пак $0.** Платных вызовов провайдерам НОЛЬ: все девять исходов предъявляются на `httptest`-сервере,
|
||
формула дедлайна — арифметикой, резюм — фикстурой. ⛔ Платная проба вендорского баланса ОТКЛОНЕНА
|
||
владельцем (`D39.230` п.2) — не проектировать и не предлагать. Кажется, что нужен платный прогон, — это
|
||
стоп и пинг, а не вызов.
|
||
|
||
## Отчёт
|
||
|
||
Своя секция в `docs/PROGRESS.md`: исход по каждому пункту §4 · таблица «находка → что сделано → чем
|
||
предъявлено» · столбец «закрыт в N из M» · числа со счётом скипов · заказанные смены поведения отдельными
|
||
пунктами · пинги (квирк-реестр — полигону; публикация оценочных строк — платформе) · §10 «что не удалось» ·
|
||
вопросы оркестратору.
|