textmachine/docs/archive/PROGRESS-2026-08-backend.md

89 KiB
Raw Blame History

АРХИВ: секция «Бэкенд» журнала прогресса, эра 2428.08.2026

ИСТОРИЧЕСКОЕ. Инструкции отсюда НЕ ИСПОЛНЯЮТСЯ. Это вынесенная 28.08 прожитая проза зонной секции docs/PROGRESS.md — пять итогов бэкенд-сессий и их записки-планы. ВСЕ описанные здесь паки ЗАЛЕНДЖЕНЫ, и каждый «дерево готово, НЕ закоммичено» относится к состоянию, которого больше нет.

Куда что уехало: входная дверь шва (2427.08, четыре сессии) — D39.158, лендинг d1eb8a9; пак «тихая порча» (28.08) — D39.164, лендинг 7d0c6f2.

Остатки, которые здесь названы, живут СТРОКАМИ единого бэклога, а не этим файлом: 228 (алиас возвращает отклонённую поверхность), 229 (снапшот не фолдит модель внутренних гейтов), 230 (размен сходимости против поучения), 231 (смета пере-прохода), 141 (остаток детектора сносок), 131 (кап инъекции не считает маркер). Вендор-вердикт по Gemini живёт ПИНГОМ ПОЛИГОНУ в живом журнале — здесь его копия, не носитель.

Вынесено аудитом документации 28.08 (D39.167) по слову владельца «пройденную прозу можно сносить».

⟶ Состояние бэкенда — CURRENT-STATE (один носитель, D39.80); здесь только пинги живых сессий.

ИТОГ БЭКЕНД-СЕССИИ 28.08 — ПАК «ТИХАЯ ПОРЧА» (docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md), все три пункта. Дерево готово, НЕ закоммичено.

§6 КОМПЛЕКТНОСТЬ — пункт → что сделано → каким ИСПОЛНЕНИЕМ подтверждено.

Заказ Что сделано Чем подтверждено ИСПОЛНЕНИЕМ
§3.1 инъекция банка доезжает на каждом провайдере либо громкий отказ Новая ось llm.Capability.SystemMessages (multi|single, capability.go:87-127,158-165); под single toOpenAIMessages СКЛЕИВАЕТ ведущий системный ряд в одно сообщение (httpllm.go:513-559) и падает громко на системном посреди диалога — зеркально Anthropic; YAML-ось capabilities.system_messages с валидацией enum (config/models.go:101-106,566-578,646-650); квирк объявлен у провайдера gemini с вендор-ссылкой и датой (configs/models.yaml:120-137) 6 тестов internal/llm/systemmessages_test.go + сквозной на ОБОИХ волнах internal/pipeline/systemmessages_wave_test.go. Мутация «ось игнорируется» → падает TestSingleSystemEndpointCarriesTheBankOnBothWaves обеими под-ветками и …CarriesTheInjection с текстом got 2 (roles [system system user]). Мутация «ось теряется по дороге из YAML» → тот же тест. Каталог: SH1, SH2
§3.2 дыра выдачи видна читателю файла Заказ пере-формулирован по находке (разрешено оркестратором 28.08): маркер уже существовал и ВРАЛ. ChunkOutcome/ChunkExport несут DroppedMembers (bookrun.go:69-75, export.go:45-51, waverun.go:488, export.go:227-230); cmd/tmctl/render.go печатает маркер В ПОТОКЕ ТЕКСТА (gapMarker, :26-63) в обоих рендерах и честные счётчики withheld/incomplete Сквозной прогон БОЕВОГО бинаря cmd/tmctl/exportgap_cli_test.go: tmctl translatetmctl export --plaintext, текст ДО и ПОСЛЕ на ОДНОМ состоянии (ниже). Матрица семи состояний. Мутации SH3 (маркер пуст) и SH4 (счёт выпавших не ведётся)
§3.3 повтор принятого документа сходится refuseSeedConflicts больше не срабатывает, когда отказ УЖЕ на записи (membank/decisions.go:365-393, новый rejectsHoldSurface :690-700); порядок записи в writeDecisionFiles изменён на REJECTS-FIRST с обоснованием (bankdecisions.go:427-441,458-489) 5 тестов membank/decisions_converge_test.go + 3 дверных pipeline/bankdecisions_converge_test.go (реальные файлы, реальные классы отказа). Мутации SH5 (сужение снято) и SH6 (порядок возвращён)

РЕШЕНИЯ ПО ПРОПУСКАМ И РОДНЕ — строкой каждое, как просил оркестратор.

  • Четвёртая дыра (строка 141) — ВЗЯТА БЕСПЛАТНО, работы не потребовала. Проверено исполнением (проба по internal/checks, файл удалён после замера): сноска [1] 蛊真人 — название романа. мимо детектора примечаний идёт, но поднимает класс CJKLeak, а он CosmeticOnly (checks/sanitizer.go:161-163) ⇒ StripCosmetic вырезает ханьцзы, юнит уходит sanitizer_stripped, и баннер «leak cleaned, verify» для НЕГО правдив. Механизм этого же заказа накрывает класс без правки детектора — граница промта соблюдена буквально. ⚠ Смежное, НЕ взятое и названное строкой: сноска БЕЗ ханьцзы ([1] Название романа.) не поднимает ни одного класса (замер: total=0), едет как есть — но текста при этом НЕ теряет, это лишняя добавка, а не дыра. Маркер на ней дезинформировал бы ( анти-скоуп промта). Ловля таких сносок — работа В ДЕТЕКТОРЕ, промт её прямо запретил.
  • Д1 — НЕ ВЗЯТ, отдельным предметом. Право §9. Канон определяет decline ПОВЕРХНОСТНО («declining a surface removes EVERY window of that surface», 14-api-contract/openapi.yaml:2051), и расширять его до энтити значило бы менять ратифицированный контракт из бэкенд-сессии. Вторая половина Д1 («память глушит стоп») — уже НАЗВАННЫЙ и принятый двусторонний остаток в комментарии mining.go:308-313, а не необнаруженный дефект. ⚠ Но одна траектория Д1 реальна и требует ЧУЖОГО решения: эмиссия майнера энтити-широка (miner_emit.go clusterTouches), а фильтр авто-банка ключуется только по собственному src строки (mining.go:662), поэтому ОТКЛОНЁННАЯ поверхность может вернуться алиасом уцелевшей строки. Это противоречит канону уже сегодня. Не чиню в этом паке (правильная форма — снять алиас со строки, а не снести строку; это предмет банковой онтологии, а не тихой порчи) — но это заказ, который стоит выдать.
  • Д3 — НЕ ВЗЯТ, вне моей зоны. LiftBankStop в backend/ не существует ни в каком написании; символ живёт в platform/internal/runs/spawn.go и reconcile.go, то есть в зоне, запертой паком P9. Движковая половина при этом здорова: порядок «карта → память» ратифицирован и исполнен (mining.go:191 пишет карту, :274 метит память), отказ памяти оценён в комментарии :275-276 как «one extra stop, nothing lost». ⚠ Поправка к моему же раннему пингу: я сказал «гарда не существует» — точнее «не существует В ДВИЖКЕ».
  • Сосед 131 — НАЗВАН, не чиню. Кап инъекции считает только src → dst (membank/memory.go:517-521 glossaryLineTokens), а рендер дописывает к КАЖДОЙ строке tx.UnverifiedMarker (:706) либо genderConstraintNote(...) (:711), а редакторский блок ещё и префикс - с кавычками-ёлочками (:821). То есть отгружаемая строка строго длиннее той, за которую платит бюджет; на коротких именных строках перерасход велик. Тема денег и качества инъекции — не тихий брак в тексте.

НАХОДКИ, МЕНЯЮЩИЕ ПРОМТ (все три подписаны, ни одна не абсорбирована молча).

  1. §3.2: маркер существовал и ВРАЛ. Ветка case ce.FinalText != "" печатала «(leak cleaned, verify)» ЛЮБОМУ флагнутому юниту с текстом, а c-lite member-drop (export.go ставит DispFlagged при непустом тексте) попадал ровно в неё. Существующий тест не ловил: покрывал ветку только причиной sanitizer_stripped (export_cli_test.go:66). Предмет заказа изменён с «завести маркер» на «перестать врать и покрыть непокрытое» — разрешено оркестратором 28.08 при закрытом гейте.
  2. §3.2: подсказка промта про место правки была опасна. Промт указывал export.go:237 рядом с ApplyHeading. Туда нельзя: ChunkExport.FinalText — поле артефактного контракта, а провод судит translated/withheld предикатом «текст непуст» (waverun.go:203-204 и events.go:538-554, runevents.go:130 Shipped). Маркер в пустом юните перевернул бы withheldtranslated — ровно (а) промта. ApplyHeading этого не делает лишь потому, что на пустом тексте он no-op. Маркер живёт в РЕНДЕР-слое. Проверено: FlagReason-константы до и после ИДЕНТИЧНЫ, exportVersion не двигался, dropped_members аддитивно и omitempty.
  3. §3.1: посылка «Gemini принимает ровно одно» отозвана автором (оркестратор, 28.08) — вендор- вердикт со ссылками и датами отправлен ему отдельно, для 00-provider-quirks.md. Кратко: структурная посылка ПОДТВЕРЖДЕНА (systemInstruction — один object (Content), Content.role ∈ {user, model}), поведение шима вендором НЕ документировано нигде, наш замер даёт «не склеиваются» и «инструкция первого не исполнена»; «выживает одно или ноль» остаётся ОТКРЫТЫМ и для решения безразлично.

ДЕФЕКТ, КОТОРЫЙ Я ВНЁС САМ И ЗАКРЫЛ (называю, потому что он показателен). Первая редакция ветки маркера срабатывала по DroppedMembers > 0 БЕЗ проверки непустого текста. Юнит, у которого выпали ВСЕ члены, ship-ит пустоту — и получил бы баннер «(INCOMPLETE)» с фразой «фрагмент отсутствует в тексте НИЖЕ», указывающей на несуществующий текст. То есть я едва не заменил одну ложь читателю на другую. Найдено собственным разбором ветвлений до опровергателей, закрыто гардом на обоих сайтах вызова, покрыто состоянием «выпали все» в матрице и запинено мутацией (снятие гарда → тест падает).

§5 ОСИ РЕВЮ.

  1. Читатель книги. Все четыре класса проверены на ОТГРУЖЕННОМ ТЕКСТЕ, не на структурах: (а) c-lite — был помечен ложно, теперь помечен верно и в потоке текста; (б) юнит целиком withheld и (в) glossary_miss — помечены верно и раньше, работы не требовали, теперь ещё и не попадают под маркер по ошибке; (г) сноска с ханьцзы — накрыта существующим верным маркером.
  2. Пара, которой в репо нет. Правка не ветвится по паре ни в одном месте: ось system_messages — свойство ЭНДПОЙНТА, маркер — английский операторский словарь той же поверхности, что и соседние баннеры, поэтому у книги с любым target_lang и БЕЗ langpack вывод байт-идентичен. Специально НЕ стал класть маркер в langpack: InjectionTextsFor для незнакомой цели возвращает нулевое значение молча (lang/embedded.go:411-423) — маркер бы исчез, и дыра снова стала бы тихой ровно для той пары, ради которой мандат общности и написан.
  3. Детерминизм. Голден зелёный (go test ./internal/pipeline/ -run TestGolden -count=1), и это сильная улика: голден пинит ПОЛНОЕ тело запроса с обоими системными сообщениями (testdata/golden/capture.golden:110), то есть глобальная свёртка сделала бы его красным, а квирк-свёртка не трогает никого, кто квирк не объявил. omitempty на новом поле запинен тестом (TestCapabilityWithoutTheAxisMarshalsUnchanged сверяет БАЙТЫ). Снапшот фолдит Capability только праймари и escalate_to (snapshot.go:316-340), а в pipeline-c1.yaml это deepseek-v4-pro (:60) — gemini там лишь третий в цепочке при budget_usd: 0 и SINGLE-HOP раннере ⇒ снапшот боевого конфига не двигается, перекупки нет.

§8 OBSTACLE — что НЕ удалось и что НЕ проверено.

  • Живого вызова к Gemini НЕ было (пак платных вызовов не требует, и я их не делал). Что склейка ПОМОГАЕТ, доказано структурно вендор-докой и логикой «при любом чтении, кроме склеивания, два системных теряют текст» — но НЕ замерено на проводе. Решающая проба стоит денег и принадлежит полигону: маркер на ПОСЛЕДНЕЕ системное + user-only контроль закрыли бы открытый вопрос «выживает одно или ноль». Также: проба полигона гонялась на gemini-3.1-flash-lite, а в цепочке стоит gemini-3.1-pro-preview — слой тот же, слаг другой, перенос не замерен.
  • Достижимость §3.1 сегодня НУЛЕВАЯ, и это не смягчение дефекта, а его честная рамка. Раннер исполняет SINGLE-HOP, escalate_to боевого конфига — deepseek-v4-pro, budget_usd: 0. Gemini не достигается ни одним шиппинг-конфигом; дефект ждал первого конфига, который назовёт его в escalate_to или в chain[0] лейбла. Кривая пост-чек-промахов на эскалационном хопе, которую §4.2 просил как второй замер, СНЯТА БЫТЬ НЕ МОЖЕТ по той же причине — мерить нечего.
  • Ловушка, которую я НЕ чинил и обязан назвать: capabilities у провайдера с kind: anthropic принимается валидацией и молча игнорируетсяclients.go не передаёт Cap в Anthropic-клиент вовсе. Это ПРЕД-СУЩЕСТВУЮЩИЙ класс (так ведут себя ВСЕ оси: budget_field, temperature, reasoning, min_max_tokens), моя ось лишь наследует его. Чинить правильно — тащить kind в validateCapabilities, что трогает каждую ось; в этом паке это чужой предмет. Практический риск сегодня нулевой: провайдера anthropic в models.yaml нет.
  • Изменён СУЩЕСТВУЮЩИЙ тест — заявляю прямо, не прячу в дифе. internal/pipeline/bankreport_test.go TestHalfLandedIsNamedPerFile кодировал ПОРЯДОК переименований в своей фикстуре (занимал путь rejects, чтобы упал второй rename). Порядок изменён заказом §3.3, и фикстура пере-нацелена на путь delta. Утверждение НЕ ослаблено: по-прежнему требуется, что ровно одна половина легла, что это ПРОВЕРЕННЫЕ байты и что отчёт называет какая. Сверх того добавлен TestTheRenameOrderIsWhatMakesADeclineRecover, который пинит САМ порядок вместе с его причиной, чтобы его нельзя было вернуть как «уборку». Если оркестратор считает это подгонкой — я готов откатить и вынести §3.3-половину с полусостоянием отдельным заказом.
  • Каталог мутаций: запись AJ-seed-alias-decline пере-заякорена. Моя правка сдвинула строку, на которую она указывала, и инструмент падал жёсткой ошибкой «the source moved under the catalogue» — это был бы хард-блокер для следующей сессии. Атакуемое свойство сохранено дословно (снять дельта-оговорку, оставить остальное); плюс добавлены шесть СВОИХ посадок SH1SH6, по две на пункт, чтобы фикс был постоянным гейтом, а не разовым замером.
  • Не проверено: поведение под реальным параллелизмом двух bank-apply (лок это исключает, но я не гонял); поведение склейки на провайдере, который принимает одно системное И имеет префиксный кеш (у gemini кеш иной) — довод «склейка кеш не портит» опирается на комментарий самого кода и на то, что префикс сохраняет байты и позицию, живьём не мерен.

§4 ОПРОВЕРГАТЕЛЬ — ЗАКАЗ ИСПОЛНЕН, И ОН ОКУПИЛСЯ. Шесть линз, мандат «опровергни». Веер шесть, а не «24» из §4: численный потолок снят словом владельца 28.08, соразмеряю предмету. Линзы: Anthropic-путь и кеш · читатель книги · провод withheld/translated · безопасность банковых решений · пара, которой в репо нет + детерминизм · вырожденность собственных тестов. Пять из шести вернули вердикт REFUTED или PARTIAL, и это правильный исход: они нашли ШЕСТЬ дефектов, из них ЧЕТЫРЕ я внёс сам этим паком. Все воспроизведены ИСПОЛНЕНИЕМ на копиях дерева, не чтением.

Находка Кто/чем доказано Что сделано
MAJOR: маркер называл ЧУЖУЮ причину. Юнит, где редактура флагнулась САМА (косм. чистка) И выпал член, печатал причину ЧИСТКИ как причину дыры: «фрагмент не переведён: sanitizer_stripped». Та же ложь, что убирал пак, перенесённая в поток текста 2 линзы независимо, боевой бинарь ИСПРАВЛЕНО: заведены DroppedReason на ChunkOutcome/ChunkExport, маркер несёт причину ЧЛЕНА, баннер — вердикт ЮНИТА. Пин SH7, состояние в матрице обоих рендеров
MAJOR: анти-скоуп. В черновично-однофазном конвейере юнит — одиночный чанк, и его СОБСТВЕННЫЙ флаг читался как «выпавший член» ⇒ косметически почищенный чанк, у которого ВЕСЬ текст на месте, объявлялся неполным боевой бинарь, tmctl translate и tmctl export расходились на одном юните ИСПРАВЛЕНО: учёт выпадений только когда финальная волна — редакторская (export.go). Тест TestADraftOnlyStrippedChunkIsNotCalledIncomplete, пин SH8
MAJOR: перестановка порядка записи купила сходимость ЦЕНОЙ второго тихого вреда. Rejects-first оставляет строку дельты на диске, а seedGlossary дельту отказами НЕ фильтровал ⇒ в окне между падением и ре-сендом ОПЛАЧЕННЫЙ прогон вливал в глоссарий редактора терм, который владелец отклонил 2 линзы, исполнение на реальных файлах ИСПРАВЛЕНО: loadMinedDelta отбрасывает поверхность, стоящую в списке отказов, и говорит об этом WARN'ом. Тест на реальном полусостоянии, пин SH12. Заодно исправлено утверждение в шапке writeDecisionFiles, которое после перестановки стало ложным
MAJOR: сходимость останавливалась на ОДНОЙ из двух дверей. refuseInertDeclines я решил не трогать — и ошибся: траектория из ОДНИХ ПРИНЯТЫХ вызовов (decline терма-со-своей-строкой → approve терма, который несёт его алиасом) делала повтор стоячего реестра решений отказным навсегда, а всё-или-ничего уносило с ним законный approve исполнение, полная трасса ИСПРАВЛЕНО: та же оговорка на второй двери (по in.Rejects, не res.Rejects — фолд уже отработал). Тест TestTheStandingLedgerConvergesThroughBothDoors + направление-гард на живом алиасе, пин SH13
MAJOR: у боевого объявления в configs/models.yaml не было ГЕЙТА вовсе. Две строки — единственное, что доводит фикс до реального эндпойнта, а удаление их оставляло ВЕСЬ модуль зелёным опровергатель посадил свою мутацию и получил SURVIVED ИСПРАВЛЕНО: TestShippedGeminiDeclaresOneSystemMessage над боевым конфигом, пин SH9 (проверено: удаление строк → тест краснеет)
MINOR: гарды в renderTranslate не были запинены (близнецы в renderExport были), case "multi" не покрыт, счётчики читались как разбиение целого исполнение ИСПРАВЛЕНО: матрица шести состояний для renderTranslate, тест TestExplicitMultiCostsNothing, счётчик переписан как exported=N (of them incomplete=M). Пины SH10, SH11

ЧТО ОПРОВЕРГАТЕЛИ АТАКОВАЛИ И НЕ СЛОМАЛИ (чтобы следующий читатель знал, что уже покрыто): Anthropic-путь (склейка до него не доходит структурно — Cap в него не передаётся; два блока и живой cache_control на месте) · префиксный кеш (стабильный префикс сохраняет байты и позицию) · провод translated/withheld (UnitDone.Shipped считается по пустоте текста; ни DroppedMembers, ни маркер её не трогают) · отсутствие новых причин флага (константы FlagReason до и после ИДЕНТИЧНЫ) · exportVersion не двигался · снапшоты боевых конфигов не двигались · арм-конфиги грузятся.

ЧТО НАЙДЕНО, НО СОЗНАТЕЛЬНО НЕ ЧИНИТСЯ — с аргументом.

  1. Ловушка capabilities на kind: anthropic — оказалась чинибельной дёшево, и я её ВЗЯЛ: ось теперь отказывается на этом виде по образцу cache_ttl (models.go), с тестом. Пред-существующий класс для ОСТАЛЬНЫХ осей (budget_field, temperature, reasoning, min_max_tokens) остаётся — его правильная форма тащит kind в validateCapabilities и трогает каждую ось; это чужой предмет.
  2. Снапшот не покрывает модель ГЕЙТА ТЕРМИНОЛОГИИ (config/pipeline.go — не фолдится сознательно, ратифицированно), а терминолог шлёт ДВА системных. ⇒ флип оси у провайдера, которым пользуется только этот гейт, меняет байты под НЕИЗМЕННЫМ RequestHash, и чекпойнты ложно попадают. Дыра НЕ моей оси — по ней ездят все оси capabilities с D3.1 — но моя первая, чей смысл именно в смене этих байтов. Утверждение в комментарии я ослабил до правды и дыру НАЗВАЛ прямо в коде (capability.go); закрытие — фолд модели гейта, как это делает repairSnapshot, и это правка снапшот-контракта, принадлежащая владельцу решения «не фолдить», а не провайдерскому квирку.
  3. Сузив отказ по поверхности, я потерял поучение в одном углу: decline, который И записан, И по-прежнему инертен против подписанного сид-терма, теперь отвечает already_applied вместо единственной работающей инструкции «убери из glossary_seed». Сходимость сочтена более тяжёлой обязанностью (у вызывающего, который следует опубликованному ретраю, другого хода нет), но размен реальный: закрывается полем отчёта, несущим стоячий факт, и это форма шва.

ХАРД-БЛОКЕР, НАЙДЕННЫЙ И СНЯТЫЙ (требование владельца «после тебя не осталось хард-блокеров»). go test ./cmd/tmctl/ строил боевой бинарь в os.MkdirTemp и НИКОГДА не убирал каталог — по 20 МБ за прогон. За этот пак накопилось 249 брошенных каталогов tmctl-bin*, ~5 ГБ, и /tmp заполнился до 99%: батарея под -race начала падать с link: mapping output file failed: no space left on device, что читается как сломанная сборка, а не как кончившееся место. Я на это и купился на один прогон — сообщать «батарея красная» было бы ложью. Причина найдена, место освобождено, харнесс починен: добавлен TestMain, снимающий каталог (cmd/tmctl/exitcontract_test.go); проверено исполнением — два прогона подряд, прирост каталогов 0. Дефект пред-существующий, пак его лишь довёл до отказа.

ЧИСЛА, С КОМАНДАМИ (§4.1).

  • go build ./... · gofmt -l . (пусто) · go vet ./... · go vet -tags live ./...все 0.
  • go test ./... -count=1 -timeout=20mEXIT 0.
  • go test ./... -race -count=1 -timeout=20m (= строка make test) → EXIT 0, real 2m09s.
  • golangci-lint run --timeout=15m ./... (= make lint) → 0 issues.
  • go test ./internal/pipeline/ -run TestGolden -count=1ok (голден пинит ПОЛНОЕ тело запроса).
  • go run ./cmd/tmmutate -root <КОПИЯ>73 посадки, 0 неожиданных исходов, EXIT 0 (72 RED + один каталогизированный выживший G-byte-gate; все 13 моих красны); каталог вырос 60 → 73 (13 новых посадок: SH1SH13), у каждой из трёх заказанных тем ≥2 своих. ⚠ Каталог гонять ТОЛЬКО на копии — сам инструмент это требует, а я один раз запустил его на рабочем дереве и словил таймаут; дерево спас обработчик сигнала самого инструмента (проверено: мутационного текста не осталось, батарея зелена). Записываю как урок, а не как мелочь.

ФАЙЛЫ (19 правленых + 5 новых; всё в backend/, плюс эта запись в журнале). cmd/tmctl/render.go · cmd/tmctl/exitcontract_test.go · cmd/tmmutate/mutations.json · configs/models.yaml · internal/config/models.go · internal/config/models_catalog_test.go · internal/llm/capability.go · internal/llm/httpllm.go · internal/llm/provider_openai.go · internal/llm/provider_local.go · internal/membank/decisions.go · internal/membank/decisions_fuzz_test.go · internal/pipeline/bankdecisions.go · internal/pipeline/bankreport_test.go · internal/pipeline/bookrun.go · internal/pipeline/export.go · internal/pipeline/mining.go · internal/pipeline/runner_test.go · internal/pipeline/waverun.go. Новые: cmd/tmctl/exportgap_cli_test.go · internal/llm/systemmessages_test.go · internal/membank/decisions_converge_test.go · internal/pipeline/bankdecisions_converge_test.go · internal/pipeline/systemmessages_wave_test.go.

ВОПРОСЫ И ПИНГИ ОРКЕСТРАТОРУ — четыре, каждый требует ЧУЖОГО решения.

  1. Изменён СУЩЕСТВУЮЩИЙ тест, и я это заявляю, а не прячу. bankreport_test.go TestHalfLandedIsNamedPerFile кодировал в фикстуре ПОРЯДОК переименований, который заказ §3.3 изменил; фикстура пере-нацелена на второй rename, утверждение сохранено дословно и усилено новым TestTheRenameOrderIsWhatMakesADeclineRecover, пинящим сам порядок с его причиной. Считаешь подгонкой — откачу, и тогда половину §3.3 с полусостоянием надо выносить отдельным заказом.
  2. Вендор-вердикт по Gemini — прислан отдельным сообщением, с URL и датами; три поправки к 00-provider-quirks.md:157 (структурная посылка ≠ поведенческий вывод · Content.parts[] повторяемо · «форум Google» — это два не-сотрудника из 2025). Пишешь ты, зона твоя.
  3. Дыра снапшота у модели гейта терминологии (см. выше, п.2 «не чинится»). Закрытие — правка снапшот-контракта; принадлежит владельцу решения «гейт не фолдится», не мне.
  4. Д1 достоин собственного заказа. Не «расширить decline до энтити» (это против канона), а узко: эмиссия майнера энтити-широка (miner_emit.go clusterTouches), а фильтр авто-банка ключуется только по собственному src строки (mining.go:662), поэтому ОТКЛОНЁННАЯ поверхность возвращается алиасом уцелевшей строки. Канон уже сегодня обещает «declining a surface removes EVERY window of that surface» — то есть движок не держит обещание. Правильная форма — снять алиас со строки, а не снести строку; это банковая онтология, и в «тихую порчу» её тянуть не следует.

ПРАВО §9 ПРИМЕНЕНО ТРИЖДЫ, каждый раз с грунтом: Д3 отклонён (символ живёт в platform/, зона заперта; движковая половина здорова и порядок «карта → память» исполнен) · Д1 отклонён как отдельный предмет (выше) · подсказка промта про место маркера отклонена (она перевернула бы withheldtranslated). ⚠ И один раз право §9 я применил ОШИБОЧНО: решил не трогать вторую дверь refuseInertDeclines, рассудив, что её случай — «устаревший документ, а не ретрай». Опровергатель показал траекторию из ОДНИХ ПРИНЯТЫХ вызовов. Рассуждение было неверным, исполнение это доказало, дверь починена.

⚠ ЗАПИСКА НИЖЕ — ПЛАН ДО ПРАВОК (§6), она СОХРАНЕНА как след дисциплины, но её выводы местами ОПРОВЕРГНУТЫ итогом выше. Не наследовать из неё: «сломан только класс (а)» (сломан ещё и стык «редактура флагнулась сама + выпал член»), «фиксация порядка записи безопасна» (она купила второй тихий вред, закрытый фильтром дельты) и «Д3 не существует» (существует, в platform/).

📝 ЗАПИСКА-ПЛАН БЭКЕНД-СЕССИИ 28.08 — ПАК «ТИХАЯ ПОРЧА» (docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md), §6. Правок кода на момент записи НЕТ. Три заказа: §3.1 инъекция банка не теряется · §3.2 дыра выдачи видна читателю файла · §3.3 повтор принятого документа сходится. Ниже — что установлено ЧТЕНИЕМ до правок и как из этого следует план. ⚠ Всё, что помечено «проверить исполнением», в план входит как обязательство, а не как факт.

Находка 1 (меняет §3.2). Маркер уже есть — и он ВРЁТ. cmd/tmctl/render.go:499 печатает для флагнутого юнита с НЕПУСТЫМ текстом баннер «(leak cleaned, verify)», а :38 — «[FLAG … — leak cleaned, exported cleaned, verify]». Ветка выбирается по ce.FinalText != "", и c-lite member-drop (export.go:226-230 ставит DispFlagged при непустом тексте) попадает ровно в неё. То есть юнит, у которого ВЫПАЛ ЦЕЛЫЙ ЧЛЕН, объявляется читателю косметической чисткой санитайзера, которой не было. Существующий тест это не ловит: cmd/tmctl/export_cli_test.go:66 покрывает ветку только причиной sanitizer_stripped. ⇒ Предмет §3.2 не «завести маркер», а «перестать врать и покрыть непокрытое» (пере-формулировка разрешена оркестратором 28.08 при закрытом гейте «что читает человек»).

Гейт §3.2 закрыт чтением: движок НЕ ПИШЕТ ФАЙЛОВ ВООБЩЕ. Все команды рендерят в stdout (cmd/tmctl/main.go:227-228, :338-349). Читаемых человеком поверхностей у движка две, обе текстовые и обе уже несут пер-юнитный баннер: tmctl translate (render.go:27-72) и tmctl export --plaintext (render.go:478-511). Дефолт export — JSON (export_cli_test.go:24-28), и он же артефакт шва к платформе (research/23 строка 76 — «артефактный контракт (export-JSON · 101 · 100)»). Читательский файл (epub и пр.) собирает ПЛАТФОРМА по POST /books/{bookId}/exports (14-api-contract/openapi.yaml:824-870) — чужая зона. ⇒ Адресат моего маркера — человек, читающий plaintext-выгрузку движка; платформенная половина остаётся за платформой, и провод различение несёт (14-api-contract/README.md:300).

Следствие для места правки, и оно расходится с подсказкой промта. Промт указывал на export.go:237 (прецедент ApplyHeading). Туда маркер класть НЕЛЬЗЯ: ChunkExport.FinalText — это поле final_text артефактного контракта, а waverun.go:203-204 кормит провод предикатом oc.FinalText != "" (shipped). Любой маркер, вклеенный в текст ПУСТОГО юнита, перевернул бы withheldtranslated — ровно (а) промта. ApplyHeading этого не делает только потому, что на пустом тексте он no-op (chunk/chunker.go:186-191). Различение: титул — часть КНИГИ и обязан ехать на провод; маркер дыры — метаданные О тексте, и в поле, по которому провод судит, ему нельзя. ⇒ маркер живёт в РЕНДЕР-слое (cmd/tmctl/render.go), на слой дальше проекции. Это ответ на требование промта «либо тем же механизмом, либо объясни, почему другое».

Классы дыр — ревизия по коду. (а) c-lite member-drop — помечен, но ЛОЖНО (находка 1); (б) юнит целиком withheld (waverun.go:496-509) и (в) gate-override glossary_miss (export.go:319-323) дают пустой текст и попадают в ветку render.go:502-504 «(not translated, flagged for a human)» — помечены ВЕРНО, работы не требуют; (г) четвёртая, сноска «[1] …» мимо детектора примечаний: детектор требует заголовочного слова (lang/data/target-ru.txt, sanitizer_trailing_note), но ханьцзы в сноске поднимают класс CJKLeak, а он CosmeticOnly (checks/sanitizer.go:161-163) ⇒ юнит уходит как sanitizer_stripped, и баннер «leak cleaned» для НЕГО правдив. То есть класс (г) накрыт существующим механизмом БЕСПЛАТНО и работы в детекторе не требует — граница промта соблюдена. Проверить исполнением обязательно: что сноска с ханьцзы действительно даёт CosmeticOnly, а не подтягивает субстантивный класс.

§3.1 — план и его цена. Свёртки в toOpenAIMessages нет (llm/httpllm.go:513-521, копия один-в-один); её комментарий :511-513 говорит, что CacheBoundary на этом пути бессмысленна ⇒ свёртка кеш не портит. У Anthropic путь другой и трогать его нельзя (llm/provider_anthropic.go:169-179: ведущий system-префикс → блоки, CacheBoundary → реальный cache_control). Ось лечения — llm.Capability + блок capabilities: (провайдерный ИЛИ модельный, config/models.go:85,:146,:442-450), прецедент безопасного расширения — MinMaxTokens с json:",omitempty". Детерминизм: снапшот фолдит РАЗРЕШЁННУЮ Capability только для праймари и единственного escalate_to (pipeline/snapshot.go:316-340,:479,:495); в pipeline-c1.yaml escalate_to: deepseek-v4-pro (:60), а gemini стоит лишь третьим в цепочке default (:129) при budget_usd: 0 (:136) и раннере SINGLE-HOP ⇒ объявление квирка у gemini снапшот боевого конфига НЕ двигает. Проверить исполнением (голдены + снапшот-тесты). ⚠ Честная граница достижимости: при SINGLE-HOP и текущих конфигах gemini не достигается ВООБЩЕ; дефект латентный и ждёт конфига, который назовёт gemini в escalate_to или в chain[0] лейбла. ⚠ Развилка, которую выношу на опровергателя (§4 заказ): дефолт оси. «Квирк объявляет тот, у кого он есть» (нулевое значение = сегодняшнее поведение) не двигает ничего, но НОВЫЙ провайдер с той же ловушкой снова теряет инъекцию молча — тот же класс тихого вреда. «Свёртка по умолчанию, исключение объявляет проверенный» безопасна для незнакомого провайдера, но меняет провод боевому переводчику без замера (запрет «эмпирика решает») и двигает снапшот. Решение — после опровергателя, с обоснованием. ⚠ Посылка промта «Gemini принимает ровно одно системное» ОТОЗВАНА автором (оркестратор, 28.08): это было рассуждение; собственный замер полигона (00-provider-quirks.md:157) даёт 535 токенов ≈ не выжило НИ ОДНО. Заказу это безразлично, тесту — нет: утверждается пост-фиксный инвариант «системное РОВНО ОДНО и инъекция ВНУТРИ него», он верен при любом чтении. Вендор-вердикт со ссылкой уходит оркестратору для 00-provider-quirks.md (его зона).

§3.3 — трасса контрактной сессии ПОДТВЕРЖДЕНА моим чтением, и подтверждено то, что она сама не читала. Отказ живёт в membank/decisions.go:374-377, бежит ДО фолда (:296-300), и всё-или-ничего вытирает документ целиком (:302-307). Порядок записи я прочитал сам: bankdecisions.go:458-473 коммитит ДЕЛЬТУ ПЕРВОЙ, а собственный комментарий функции :436-438 говорит дословно: «delta-first is safe for an approve and NOT for a decline … convergence is what actually carries both». ⇒ окно застревания реально, и код сам называет допущение, которое ложно. Идемпотентная ветка уже есть (already_applied, :221-224,:269-272) — вводить понятие не надо. План: сузить условие отказа так, чтобы оно не срабатывало, когда отказ УЖЕ записан в rejects, плюс направление-гард (настояще-инертный decline обязан отказывать по-прежнему тем же текстом). Идентичность документа НЕ вводить — сходимость по СОСТОЯНИЮ.

Д1 и Д3 — беру право §9 и НЕ чиню, оба с грунтом. · Д3 ОТКЛОНЁН как несуществующий: гард LiftBankStop не найден в коде НИ В КАКОМ написании (греп по всему backend/); реальный носитель — mining.go:266-277, и он уже реализует ратифицированный порядок «карта → память»: writeFileAtomic карты на :191, MarkStopPresented на :274, с комментарием :270-273 «strictly after the map … the reverse order can mark surfaces presented whose map never reached disk» и с явной оценкой отказа памяти как «one extra stop, nothing lost» (:275-276). То есть механизм написан ВМЕСТЕ с памятью v16, а не до неё. · Д1 ОТКЛОНЁН как ратифицированное поведение + названный остаток: канон определяет decline ПОВЕРХНОСТНО («declining a surface removes EVERY window of that surface» — 14-api-contract/openapi.yaml:2051), и расширение до энтити противоречило бы контракту; а «память глушит стоп» — уже НАЗВАННЫЙ и принятый двусторонний остаток в комментарии mining.go:308-313. Оба — строка с причиной, не правка.

Сосед 131 (называю, не чиню, как велит §3.4): кап инъекции считает только «src → dst» и не считает маркер ⟨проверить⟩ и родовые пометки (lang/data/injection.txt несёт их как отдельные значения) ⇒ реальная инъекция шире бюджета. Проверю точный носитель при работе в этом коде и назову file:line в итоге; чинить не буду — тема денег и качества инъекции, а не тихого брака в тексте.

Порядок работы: §3.1 → §3.2 → §3.3, каждый с тестом на ПОСТ-фиксный инвариант; батарея под -race + линтер; посадки мутаций ≥1 на пункт с вердиктом по ДЕЛЬТЕ и ТОПИЧНОСТИ; опровергатель на выбор места §3.1 (заказ §4). Веер агентов соразмеряю предмету: численный потолок «24» из §4 промта снят словом владельца 28.08.

ИТОГ БЭКЕНД-СЕССИИ 27.08 (2) — ФИКС3 ПО ФИКС-ЛИСТУ ПРИЁМКИ (docs/BACKEND_SEAM_FIX3_SESSION_PROMPT.md), все 6 пунктов. Дерево готово, НЕ закоммичено; продолжает дерево трёх паков. Четыре пина, один снос, ноль новых механизмов: пин порядка «карта → память» (мутация приёмки воспроизведена в трёх пакетах ДО пина; занятый путь карты → ошибка прогона + пустая память) · пин «отчёт класса 15 мерит подпись по ДИСКУ, не по намерению» (утверждение панели воспроизведено) · оба носителя старого пола полосы приведены к двух-ярусной букве · отменённая формула «стоп гаснет, когда решён каждый терм» зачищена ГРЕПОМ — восемь носителей, включая два файла самого пака · снесена машинная таблица .bank-stop.json вместе с писателем и четырьмя тестами (читателя нет ни в одной зоне — свой греп; человеческая .bank-stop.txt не тронута) · банк-экспорт получил якорь свежести as_of+run_id (аддитивно на tm-bank-v1; «может быть устаревшим» больше не живёт только в логе). Доказательная база — FINDINGS §46; каталог мутаций 58 → 60 (FX23, FX24).

Пинги:

  1. Платформе/оркестратору: .bank-stop.json СНЕСЁН (мёртвая публикация упразднённого D39.144 пер-термного экрана) — платформенные журналы (platform-PROGRESS.md, STACK_DECISIONS.md) называют его атомарно-читаемым сайдкаром, упоминания устареют при лендинге.
  2. Потребителям банк-экспорта: документ несёт as_of (граница прогона) и run_id (тот же id, что стрим events.jsonl); аддитивно, старые читатели не ломаются; пустой run_id = «идентичности нет», не «текущий».

ИТОГ БЭКЕНД-СЕССИИ 27.08 — ДОФИКС-2 «ФЛАЖОК СТАНОВИТСЯ ФЛАЖКОМ» (docs/BACKEND_SEAM_FIX2_SESSION_PROMPT.md). Дерево готово, НЕ закоммичено; продолжает дерево паков 2425.08. Все 13 пунктов §4 закрыты: движок сам реализует модель D39.144 — стоп банка срабатывает только на кластер, которого ни один прежний стоп не предъявлял (персистентная память bank_stop_presented, схема v16); авто-провод неподписанных строк работает и при ПОДНЯТОМ флаге; отчёт двери собирается ОДНИМ конструктором по явному исходу (проекция · отказ · no-op · прерывание · записано · запись не удалась), версия отчёта → v2 (written_delta/written_rejects, mode refused/write_incomplete); полоса отказов получила два яруса и класс write_incomplete (exit 15); оба файла решений готовятся целиком до первого rename + fsync каталога; ключи mined_delta/mined_rejects СНЯТЫ (пути только конвенцией — МЕЖКНИЖНАЯ гонка недостижима, -пинг прошлого пака закрыт; воркфлоу-ревью нашло остаток той же оси: второй конфиг ТОЙ ЖЕ книги с другим объявленным project_db пишет те же файлы под другим локом, мимо flock живого прогона — пинг ниже, решение не паку); карта подписи пишется атомарно и несёт конверт map_version+map_id; шесть слепых пятен гейта закрыты пинами, проверенными посадкой. Поверх — воркфлоу-ревью (шесть полос × три скептика, FINDINGS §45): его две кодовые находки закрыты правками с пинами — --dry-run теперь отказ для всех глаголов без проекции (раньше translate --dry-run МОЛЧА запускал платный прогон), а повтор после отказа синка каталога держит класс 15, пока долговечность не пере-доказана. Доказательная база — backend/docs/SEAM_PACK_FINDINGS.md §§36§45; каталог мутаций 36 → 58 записей, 0 неожиданных исходов. Здесь — только требующее действия.

ПОРЯДОК ДЕПЛОЯ/ЛЕНДИНГА — зависимость НАПИСАНА, а не выведена (заказ §4.12):

  1. Движок первым, платформа второй. Сброс флага --verify-bank платформой (её персистентная колонка + особый случай в спавне, platform/internal/runs/spawn.go) может умереть ТОЛЬКО ПОСЛЕ лендинга этого пака: в обратном порядке каждая книга с поднятым флажком в день лендинга перестаёт писать авто-банк и пере-сидироваться ровно в режиме, ради которого флажок существует.
  2. tmctl migrate до прогонов (голова схемы v15 → v16): OpenReadOnly отказывает классом 13 всем непере-мигрированным книгам — ратифицированный порядок D39.134 п.3 это покрывает, но назван явно.
  3. Миграционная нота: после апгрейда память пуста → ОДИН доброкачественный стоп на первом verify-прогоне каждой книги.

ПИНГИ ЧУЖИМ ЗОНАМ (правок не делал):

  1. Платформе: у неё умирает сброс флага при resume (после лендинга движка — п.1 выше) · отчёт двери теперь v2 (tm-bank-decisions-report-v2: mode refused/stopped/write_incomplete, пофайловые written_*) — её readEngine выбрасывает stdout при ненулевых кодах, прежний пинг об этом стал острее: класс 15 БЕЗ отчёта неотличим от «ничего не легло» · новый exit 15 в полосе 1019 читается её ридером как refused по построению полосы — ратификация значения на оркестраторе при лендинге.
  2. Полигону: eval/bank_autonomy/parse_common.py парсит mined-signature.yaml — карта теперь несёт верхние ключи map_version/map_id (лежащие артефакты coldrun не тронуты; новые прогоны будут в новом жанре).
  3. Фронту: экран подписи, читающий signature.undecided из отчёта двери, обязан перестать трактовать его как «стоп не погаснет» — поле информационное (D39.144), контракт поля пере-описан в коде.
  4. Майнерский хвост за потолком структурно недостижим (§4.4, НЕ чинилось по заказу): потолок эмиссии (200) режет ранжированный пул ДО фильтров пригодности/сида/отказов (miner_emit.go, ranked[:emitRankCap] до emissionEligible). Пере-замерено своей командой (числа прежнего сплошного читателя в дерево не попали): 2354 ранжированных · 209 годных во всём алфавите · 11 в окне потолка · 198 невидимы постоянно; отклонение всех годных окна не подняло ни одного из хвоста (FINDINGS §38). Первоисточник замера — постоянный якорь-пин internal/miner/emissioncap_test.go: он утверждает ТЕКУЩЕЕ поведение и покраснеет в день сдвига потолка или порядка фильтров, приведя двигающего к этому пингу. Двигать потолок — трогать фрозен-паритет; отдельное решение.
  5. Гипотеза «подписание промахивается мимо чекпоинтов терминолога и пере-оплачивает роль» механизмом НЕ подтверждена (§4.13): память предъявленного входов чекпоинта не трогает; если перерасход существует — он существовал и до пака, отдельный предмет.
  6. Остаток оси арбитра (нашло воркфлоу-ревью, воспроизведено живьём): второй конфиг ТОЙ ЖЕ книги (один каталог, один book_id, другой объявленный project_db) пишет ту же пару файлов решений под ДРУГИМ локом — в т.ч. мимо flock живого прогона. Межкнижная версия снята конвенцией; эта — второй конфиг одной книги, т.е. рукотворная конфигурация оператора. Выбор лечения (второй арбитр на идентичности файлов решений или отказ при загрузке) трогает закон п.2 — решение не паку, пинг. Довод в коде сужен честно (bankdecisions.go, THE ARBITER).

ИТОГ БЭКЕНД-СЕССИИ 25.08 — ДОФИКС ТОЙ ЖЕ ДВЕРИ по приёмке (docs/BACKEND_SEAM_FIX_SESSION_PROMPT.md). Дерево готово, НЕ закоммичено; продолжает дерево пака 24.08, а не заменяет его. Все 16 пунктов §4 закрыты: блокер формата (дверь писала байты, которых её же парсер не читает, и любой следующий вызов по книге — включая проекцию — падал) · пред-существующие поломки чинятся по одной · два ключа на один путь · гарды флагов на ПРИСУТСТВИЕ (--decisions= уходил на ПЛАТНЫЙ путь) · кап документа решений · конверт путей на manifest --json · алиасы судятся по НАБОРУ · перевёрнутое окно глав · честность отчёта о стопе · SIGTERM · десять слепых пятен гейта · цепь целиком в репозитории. Доказательная база, замеры, попунктный чек и обе итерации ревью — backend/docs/SEAM_PACK_FINDINGS.md §§1430; здесь только то, что требует ДЕЙСТВИЯ.

-ЗАКАЗ ПРИЁМКЕ ИЗ ПАКА СНЯТ. Цепь «стоп → bank-apply → резюм → стоп ГАСНЕТ» теперь живёт в репозитории как $0-тест на фейковом провайдере (internal/pipeline/bankchain_test.go), на ОДНОМ проекте от стопа до погасшего стопа. Довод пака «своими силами невозможно, надо дойти оплаченной черновой волной» был шире правды: харнесс для этого уже был в репозитории. Мина №1 строки 199 (ливлок на status: auto) закрыта исполнением, а не рассуждением.

⚠ РАТИФИКАЦИЯ КЛАССА 14 — прежнее требование в силе, объём немного шире. Класс decisions_rejected теперь несёт и отказ по КАПУ (документ больше, чем движок применяет за один акт): это факт о ЗАПРОСЕ, а не о деплое, поэтому 10 был бы неверен. Носители полосы поднимает оркестратор при лендинге, как и планировалось.

ПИНГИ ДОФИКСА (правок не делал):

  1. exit 2 при OOM платформа читает как успех — общедвижковый класс; кап удерживает bank-apply от того, чтобы стать первым, кто до него доедет, но самого класса не снимает.
  2. Каталог книги без права записи даёт exit 1 (вне полосы). Класс движка корректен и одинаков у всех глаголов — чинить надо чтение кода 1 на платформе, которая списывает проблему прав ХОСТА в бюджет попыток КНИГИ.
  3. Проекция отвечает «что применится», а не «почём». Рекомендация — НЕ строить: глагол $0 по построению, поле «$0» было бы шумом. Решать оркестратору.
  4. Карта подписи не удаляется после погасшего стопа — пред-существующее; зафиксировано тестом как текущее поведение. Честный ответ на «есть ли что подписать» — новое поле отчёта signature.undecided.
  5. Цена глагола Θ(N²) по построению — каждый пер-решенческий помощник сканирует весь документ. Именно поэтому кап на КОЛИЧЕСТВЕ стоит на 5 000. Поднимать его без индексации этих сканов нельзя.
  6. Апстрим-дефект gopkg.in/yaml.v3 v3.0.1 (эмиттер пишет блочный скаляр, который его же парсер не читает) вылечивается по-настоящему только скаляр-типом схемы с явным стилем или переездом на go.yaml.in/yaml/v4 (релиз-кандидат, смена module path в ДВУХ зонах). Мой обход несёт ссылку на исходник библиотеки и характеризационный тест, который покраснеет в день починки апстрима.
  7. Фолд поверхностей Go-резидентный: internal/text/norm.go держит trad2simp и катакана→хирагана в Go, а не в langpack'е. Пара, которой в репо нет, деградирует до NFKC+lowercase — не блокирована, но это ровно тот класс, о котором ревью-вопрос общности. Шире двери, поэтому пингом.

⚠ ЧТО В ЗАПИСИ ПАКА 24.08 (ниже) УСТАРЕЛО — читать её через эту поправку.

  1. Пинг 2, «таймаут: 1.6с на 5 000 термов, 9.1с на 20 000» — ЧИСЛА НЕВЕРНЫ, и это ровно тот датум, которым платформенный пак будет мерить свой таймаут. Мой замер той же командой: 5 000 approve5.2 с, 20 000 → 50 с; на минимальных decline (самая дешёвая форма, и потому та, которой байтовый кап пускает больше всего) 5 000 → 11.3 с, 12 000 → 58 с, 28 900 → 308 с. Брать надо FINDINGS §18 и §29 п.1, не эти.
  2. Пинг 6 «однострочник при лендинге: документ решений не капирован» — СДЕЛАН. Капов теперь два (1 МиБ на чтение + 5 000 решений на применение), потому что байтовый не ограничивает время.
  3. Пинг 3 про флейки пере-снят сильнее: изолированный A/B против дерева HEAD даёт побайтно одинаковое падение с обеих сторон — FINDINGS §31.

ПИНГИ, КОТОРЫЕ ЖИЛИ ТОЛЬКО В FINDINGS И НЕ ДОЕЗЖАЛИ ДО ЖУРНАЛА (найдено проверкой согласованности): карта подписи пишется обычным os.WriteFile мимо атомарной конвенции сайдкаров того же пакета (пред-существующее) · цена доказывающего рендера ≈5 % времени, плюс notCanonical теперь тоже разбирает документ · опубликованные пути двух файлов решений не несут версионного конверта (пинг пака §10.5) · схема сида без omitempty (пинг пака §10.6).

⚠ Два пред-существующих -race-флейка в internal/store не тронуты (TestKillMinus9LosesAtMostOneCall, TestTheSeamIsNotChargedToTheStoreOperationBudget). База ДО правок, снятая на нетронутом дереве пака, уже была красной по первому — это моя собственная атрибуция, а не наследованная.

ПИНГ, КОТОРЫЙ ТРОГАЕТ РАТИФИЦИРОВАННЫЙ ЗАКОН (п.2) — решать не паку. Арбитр двери — flock на <project_db>.lock, а пишутся mined_delta/mined_rejects, которые book.yaml может направить куда угодно. Две книги с РАЗНЫМИ project_db и ОДНИМ delta друг друга не исключают: воркфлоу-ревью продемонстрировало гонку на 1500 одобрений с каждой стороны — одна сторона потеряла всё, exit 0, отчёт «применено». Внутрикнижную версию этого §4.3 уже запрещает; межкнижная законна сегодня. Выбор — второй арбитр или отказ при загрузке на путь вне каталога книги. В корпусе нарушителей нет. В коде стоит явное «арбитр покрывает проект, а не пути», а не молчаливое допущение.

🔧 В репозиторий добавлен инструмент: backend/cmd/tmmutate — харнесс мутаций с каталогом из 36 посадок, у каждой названо СВОЙСТВО, за которое она отвечает, и записан ожидаемый исход. Три сессии подряд писали такой инструмент в скретчпад и теряли его; приёмка при этом нашла восемь мест, где гейт был слеп. ⚠ Он дважды подводил и оба раза был починен по следу: сначала подсадил мутацию и прошёл собственную sha256-самопроверку (эталон второй правки снимался с уже пропатченного файла), затем считал КРАСНЫМ провал СБОРКИ — из-за чего две записи каталога, сломанные рефакторингом, целую итерацию числились пойманными тестами, которые не могли отработать. Теперь: одно чтение и запись на файл, отпечаток всего каталога перед каждой посадкой, зелёная базовая линия пакета ДО первой посадки и третий вердикт NOTHING, роняющий гейт. FINDINGS §25 и §34.

ИТОГ БЭКЕНД-СЕССИИ 24.08 — ПАК «ВХОДНАЯ ДВЕРЬ ШВА», движковая половина D39.156. Дерево готово, НЕ закоммичено, передаётся на лендинг. Заказ §4.1§4.6 выполнен: конвенционные дефолты mined_delta/mined_rejects (ключей не объявляет и файлов заранее не создаёт никто) · глагол tmctl bank-apply --config <book.yaml> --decisions <doc.json> [--dry-run] ($0, идемпотентен побайтово, всё-или-ничего, занятый flock — ровно 12 неблокирующе, ответ несёт версию и явное depth: edit_wave) · --keys-file у translate (строка 211) · строгость сид-загрузчика (212) · status_version + artifacts{} с абсолютными путями (213) · дефолт стендового корпуса маркерным резолвом (218, критерий оркестратора выполнен буквально). Вся доказательная база, замеры и попунктный чек промта — backend/docs/SEAM_PACK_FINDINGS.md; здесь только то, что требует ДЕЙСТВИЯ.

⚠ ТРЕБУЕТ РАТИФИКАЦИИ: заведён класс полосы отказов 14 decisions_rejected. Отвергнутый по существу набор решений ≠ сломанный деплой: platform/internal/ingest/exit.go DeploymentFault относит 10 к «условию, общему для всех книг хоста», и по 10 набор решений одного пользователя списался бы на оператора. Старый читатель деградирует безопасно (Refused(14)OutcomeRefused). 10 сохранён для документа, не прочитавшегося КАК документ решений. Гейт тотальности таблицы добавлен.

ЗАКАЗ ПРИЁМКЕ — единственный незакрытый замер (согласован со сторонним архитектором). Цепь целиком не гонялась ни разу: стоп красный → bank-apply → резюм → стоп гаснет. Части доказаны порознь, но мина №1 строки 199 — livelock уровня ЦЕПИ, которого частичные тесты по построению не видят. Снять не могу, и это проверено: банк-стоп на машине не воспроизводится (contrast_path → отсутствующий eval/exp16/data/jieba_dict_general_zh.txt, строка 123), а до стопа надо дойти оплаченной черновой волной. Команда: на КОПИИ книги на exit 3 скормить bank-apply документ на всю карту подписи, затем translate --verify-bank. ДЕФЕКТ — стоп снова с ТЕМИ ЖЕ поверхностями; не дефект — свежая пачка ≤200 термов реверс-секции (мина №3 строки 199). До этого прогона вердикт по циклу — PLAUSIBLE.

ПИНГИ ЗОНАМ И СЛЕДУЮЩИМ ПАКАМ:

  1. rubyToCandidates мёртв в проде — единственный вызов из теста (memseed_test.go:204); seeding.go:36-38 строит manualSrcs и никуда не передаёт (Go молчит: запись в map = использование). Строк Source:"ruby" в банке не возникает, а платформа держит для них ветку словаря (ingest/bank.go termOriginOriginAnnotated).
  2. Платформенному паку: первый контакт с непере-мигрированной книгой даёт exit 13 — воркер решений обязан планировать 13 → migrate → повтор · проекция берёт тот же лок, поэтому правка термина при живом прогоне даёт 12 даже на превью, на всю длину прогона · таймаут: 1.6с на 5 000 термов банка, 9.1с на 20 000, бюджет платформы на вызов движка — 60с (runs/reconcile.go:280) · bank-apply создаёт <project_db>.lock при первом касании никогда не запускавшегося проекта.
  3. ⚠ БАТАРЕЯ НЕ ДЕТЕРМИНИРОВАННО ЗЕЛЕНА, и это НЕ пак — прочесть ДО пере-рана. Два пред-существующих -race-флейка в internal/store срабатывают примерно раз на 24 полных прогона (go test ./... -race -count=1 ×4 → 1 чист, 2-й красный). Красное именно в этих двух тестах ожидаемо; любое другое красное — регрессия. Тесты НЕ трогал (D39.121). TestTheSeamIsNotChargedToTheStoreOperationBudget, TestKillMinus9LosesAtMostOneCall. Чередующийся A/B на git worktree HEAD: оба падают на HEAD не реже (kill9 2/30 против 0/30; seam 2/30 против 1/30; без -race 0/30). Покрытие: LockProject/Release этими тестами не исполняются вовсе. ⚠ 0/30 против 2/30 НЕ значит «пак улучшает» — на таком числе это шум (Фишер p≈0.5); достаточно того, что флейк живёт на дереве, которого пак не касался.
  4. Гейт TestDispatchCommandsCoversTheSwitch слеп к case с константой (считает только *ast.BasicLit) — команда, объявленная константой, невидима гейту, чей смысл в дефекте строки 176. Я вернулся к литералу (конвенция файла), дыра не эксплуатируется.
  5. redrive — платный путь без канала ключей аргументом. Промт: «флаг только у translate», сделано ровно так; канал у него есть (конвенционный .env), платформа его не зовёт. Развилка следующему паку.
  6. Однострочник при лендинге (не блокер): документ решений читается неограниченным os.ReadFile, тогда как платформа свои чтения от движка капает. Документ на 10 000 решений ≈2МБ ⇒ кап 116МБ.

⚠ ПРАВИЛА, ВЫНЕСЕННЫЕ ИНЦИДЕНТАМИ — для следующих сессий, не про этот пак. Прогон харнесса мутаций был убит таймаутом, finally не отработал, и посадка просидела в дереве ~40 минут, выглядя продакшн-кодом; сторонний ревьюер снял с него замеры и доложил несуществующий дефект. Поймал посадку собственный пин пака. Отсюда три правила: харнесс мутаций обязан фингерпринтить цели (починено) · снимай отпечаток дерева перед сборкой бинаря замера (на этом наступили обе стороны) · одиночный прогон — не замер (атрибуцию флейков я дважды вывел неверно из одиночных прогонов).

Что НЕ доказано: рантайм-вердикт по платному пути — PLAUSIBLE. Доказано, что ключ из --keys-file доезжает до заголовка и что без флага прогон отказывает конфигом; не доказано, что реальный провайдер примет ключ и что SaaS положит файл по этому пути — платформенная половина строки 211, чужая зона. Живой translate не гонялся: санкции на платные вызовы у пака нет. (24.08)

ПИНГ ОРКЕСТРАТОРА №19 — 23.08, ЗОНЕ БЭКЕНДА. Четыре движковых дефекта, все сверены моими командами.

  1. ОТВЕТ НА ПИНГ ПОЛИГОНА: дефект ПОДТВЕРЖДЁН, он ваш. Эскалация на Gemini теряет инъекцию банка памяти (строка 208). Механизм пере-проверен: pipeline/render.go:256-258=Role: "system", Content: injection, CacheBoundary: false штатно строит ВТОРОЕ системное сообщение, а llm/httpllm.go (до-паковые строки 517-523, якорь ИСТОРИЧЕСКИЙ — сигнатура сменилась лендингом D39.164: функция приняла ось mode SystemMessagesMode и под квирком СКЛЕИВАЕТ системный ряд, так что описанного здесь «один-в-один» больше нет) копирует сообщения дословно — склейки нет нигде. ⚠ Лекарство того же класса в проекте УЖЕ написано и работает для другого провайдера: llm/provider_anthropic.go:169-179=inSystemPrefix := true сворачивает ведущий system-префикс в одно поле. Не обобщено на Gemini — это и есть работа. Полигон ждёт ответа с 22.08 и починил у себя по вендор-доке; их находка верна.

  2. Род не производится авто-путём вовсе (строка 210, дополнена 23.08): замка ДВА и они независимы, а поле Gender в схеме сида УЖЕ есть — ось мертва в ПИСАТЕЛЯХ, не в формате.

  3. Выпавший член юнита исчезает из отгружаемых байт БЕЗ внутритекстового маркераpipeline/waverun.go:487-490=the flagged member is DROPPED from the edit. Родня строки 193; для читателя это молчаливая дыра в тексте.

  4. Загрузчик сида НЕстрогий (строка 212): голый разбор YAML без проверки имён ключей, и seed-lint этого не видит. Опечатка в имени поля молча теряется — в файлах, которые владелец правит руками чаще всего.

⚠ Плюс строки 211 (движок на SaaS не получает провайдерских ключей — блокер живого прогона) и 213 (платформа дублирует вашу конвенцию пути): обе половинные, движковая часть ваша.

ПИНГ ОРКЕСТРАТОРА №20 — 22.08, ЗОНЕ БЭКЕНДА: три ваших дока разошлись с деревом (аудит доков, сверено кодом; рукой не лезу — ваша зона).

  1. backend/README.md, карта пакетов объявлена сверенной по коду, а в ней НЕТ internal/runevents — эмиттера журнала прогона, то есть ВСЕГО шва движок↔платформа (D39.131), — и нет второго бинаря cmd/tmvet. Греп: runevents и tmvet в файле по нулю вхождений, при том что оба каталога в дереве.
  2. backend/README.md, «Известный техдолг» держит живыми Cyrillic-хардкоды DC-чекеров, которые сам код называет ПОГАШЕННЫМ долгом (фаза 2 общности, D39.64: isRuTarget удалён, TargetActive/TargetScriptNonLatin), и оба его file:line-якоря указывают мимо.
  3. (дописано 24.08) backend/README.md:47=/home/ubuntu/books/gu-zhenren/ зовёт книгу-стенд по МЁРТВОМУ пути /home/ubuntu/books/gu-zhenren/ — каталога нет ни там, ни в $HOME: книги переехали в <репозиторий>/books и версионируются своим git (D39.157 п.2). Там же фраза «тексты и производные ВНЕ git» стала неточной: вне ЭТОГО git. Тем же касанием — три тест-хелпера, резолвящие корпус от $HOME/books (строка 218); они уже в заказе движкового пака шва §4.6, так что дублировать правку не надо, но док обязан сойтись с ними.
  4. backend/docs/components.puml — диаграмма, которую карта доков продаёт как правимую «одним коммитом с кодом», отстала примерно на месяц: старый exit-контракт (без 4/5 и полосы 1019), неполный список глаголов CLI, удалённый паком-17 failover.go и ни следа эмиттера. ⚠ Рендерить её в svg/png НЕ надо (гардрейл) — править исходник.

(Пак «честные числа и статусы» — ИСПОЛНЕН, ПРИНЯТ С ФИКС-ЛИСТОМ и ЗАЛЕНДЕН f840867 (17.08; D39.149, ФЧ-5 — D39.150). Отчёт, две волны адверсариального ревью, мутационная приёмка и тело фикс-листа ФЧ-1…ФЧ-8 — в archive/PROGRESS-2026-08-17.md; живой остаток — строка бэклога 197.)

(Пинги закрытых паков — фикс-пак банка D39.118, блокеры контракта D39.122 — в archive/PROGRESS-2026-08-04-09.md, D39.125.)

(Закрытые бэкенд-записи 0102.08 — общность фаза 2 · качество банка (оба этапа) · фикс-пак банка · finding-1 · пакет-чекеров · мелкая пачка · coldrun-b · ручка эффорта — в archive/PROGRESS-2026-08-01-02.md.)

(Закрытые бэкенд-записи 1415.08 — эмиттер шва (D39.131) · tmctl migrate (D39.134) · пере-пин цен DeepSeek (D39.137) — в archive/PROGRESS-2026-08-14-15.md; вынесено D39.139.)

(Закрытая хроника пакетов №0№4 (0410.07) перенесена в archive/PROGRESS-2026-07-04-10.md — ревизия D31.)

(Хроника пакетов 1112.07 (пакет-5, D15.2-этап-А, санитайзер-фикс D33) — в archive/PROGRESS-2026-07-10-13.md.) (Трек-A пак-1/1.5 (D39.2/D39.5) — см. записи оркестратора №6 выше.)

(Хроника бэкенд-паков 1116 (1925.07, вкл. отчёт-запись пака-15) — в archive/PROGRESS-2026-07-13-25.md; отчёты паков — archive/reports/.)

(Закрытые записи 2531.07 — пак-18 · мини-прогон · фикс-пак · сквозная проверка · хендофф 26.07 · ресёрч общности фаза 1 — в archive/PROGRESS-2026-07-25-31.md.)