# Дофикс анализаторов archguard + живая проба провода (бэкенд, 03.08.2026) > ✅ **ПРИНЯТ приёмкой оркестратора №12 (04.08, D39.97) и заленден в составе пака стандартов (семь pathspec-коммитов).** Приёмка исполнением: батарея EXIT=0 · деньги вторым путём usage×цены — дельта $0.000000 по всем вызовам и сумме · две СОБСТВЕННЫЕ посадки вне карты отчёта: значение метода CAUGHT (клейм подтверждён независимо), **method expression `llm.LLMClient.Complete` CAUGHT — формы нет ни в таблице, ни в списке пропусков: гард сильнее собственной документации** · негативный контроль шва чист. CONFIRM владельца по `@latest` принят входом. Семь осознанных пропусков → строка 121(д); нетронутый инвариант-коммент `httpllm.go:77` → строка 122. Оговорка о 20/43 умерших ревью-агентах компенсирована пере-проверкой сессии + посадками приёмки. **Мандат:** `BACKEND_ARCHGUARD_DOFIX_SESSION_PROMPT.md` — хвост пака стандартов D39.96. **Итог:** оба этапа закрыты. `make battery` EXIT=0, проба PASS 3/3, потрачено $0.0053 из потолка $0.50. Не коммитил — лендит оркестратор. ## Вход приёмки | | | |---|---| | Слово владельца по `@latest` (D39.96 п.6-iii) | **CONFIRM** (03.08) ⇒ проба на текущем дереве; минимальный пере-бамп не выполнялся | | go.mod пробы | `go 1.26.4`; x/net **v0.57.0** · x/text **v0.40.0** · x/tools **v0.48.0** · yaml.v3 v3.0.1 · sqlite **v1.55.0** | | Тулчейн | `go1.26.5 linux/amd64` | | Пин x/text | `internal/text/norm.go:69` = `v0.40.0`, зеркала в `enclosure_test.go` и `memory_f1_test.go` | --- ## Этап 1 ($0): дыры анализаторов Три дыры из промта закрыты, плюс три найдены и закрыты сверх него. Все шесть доказаны посадкой: до фикса `go vet -vettool` даёт exit 0, после — exit 1. **A. Пакетная `llm.Complete(...)`** — `Selections` не заполняется для qualified identifier. Резолв переведён на `Uses` по идентификатору вызова (`archguard.go:454`); попутно закрылся dot-import. **B. Композиты в сигнатуре** — плоский скан разворачивал один указатель и вставал, поэтому `Complete(ctx, []llm.LLMRequest) ([]*llm.LLMResponse, error)` проходила. Заменено на `typeReaches` (`:156`) — рекурсия по конструкторам типа с защитой от цикла. **C. Симметрия сеамов** — `requestseam` смотрел один вид узла против четырёх у `stageseam`. Оба переведены на общий обходчик `seamRule` (`:249`): разъезд по видам узлов и был причиной дефекта. **D. Вызов через параметр типа (блокер, найден ревью).** `runEgressSeam` требовал `*types.Signature`; у параметра типа `Underlying()` — интерфейс-ограничение, поэтому анализатор выходил до всякой проверки. Реалистичная форма — Ф2-судья через дженерик — была невидима обоим драйверам. SHAPE-половина читает теперь **место вызова** (типы аргументов + тип узла вызова), которое go/types резолвит в любом написании; сигнатура нужна только provenance и может быть nil. **E. Именованный результат.** `func f() (s config.Stage)` — то же объявление нулевого значения, что `var s config.Stage`. Добавлен `*ast.FuncType`, только результаты (параметр даёт вызывающий). **F. Исключения не были привязаны к корню модуля.** `HasSuffix` по абсолютному пути ошибался в обе стороны: вложенная директория с тем же хвостом **наследовала** исключение, а на чекауте, где каталог модуля назван не `backend`, целевые файлы его **теряли**. Ключ переведён на import-path + базовое имя, позиция берётся `PositionFor(…, false)` — иначе `//line` позволяет присвоить чужую идентичность. Сверх этого закрыта ось **имени**: оба прежних поколения гарда ключевались на слове `Complete`, так что `llm.Do(ctx, req)` был свободен. Гард стреляет по дизъюнкции **SHAPE** (несёт пару `LLMRequest`→`LLMResponse`, имя не важно) ∨ **PROVENANCE** (зовётся `Complete` и принадлежит `internal/llm` — половина паритета с байт-сканом). Снято ложное срабатывание: `var s *config.Stage` репортился, хотя ничего не конструирует. Разворот указателя оставлен **только** литеральной ветви — замер показал, что для `[]*T{{…}}` он несущий. ### Форма × ловится `✗` = проходила молча · `✓` = репортится · `—` = формы у поколения не было. | форма | байт-гард | 1-й анализатор | сейчас | |---|---|---|---| | **egress** | | | | | через интерфейс / адаптер / локальный интерфейс | ✓ | ✓ | ✓ | | пакетная `llm.Complete` | ✓ | ✗ | ✓ | | пара в композите `[]llm.LLMRequest` | ✓ | ✗ | ✓ | | другое имя `llm.Do` | ✗ | ✗ | ✓ | | значение метода `f := c.Complete` | ✗ | ✗ | ✓ | | **вызов через параметр типа** | ✗ | ✗ | ✓ | | запрос как параметр типа (через ограничение) | ✗ | ✗ | ✓ | | **request** | | | | | литерал / срез / мапа / алиас- и dot-импорт | ✓ | ✓ | ✓ | | `var r pipeline.Request` + присваивание | ✗ | ✗ | ✓ | | `new(pipeline.Request)` | ✗ | ✗ | ✓ | | алиас / производный тип / конверсия | ✗ | ✗ | ✓ | | **оба сеама** | | | | | именованный результат `func f() (s T)` | ✗ | ✗ | ✓ | | `var p *T`, `(*T)(x)` — ложное срабатывание | — | ✗ (репортил) | ✓ молчит | ### Мутационная самопроверка Девять мутаций, каждая ломает один фикс; все девять пойманы, после восстановления сьюта зелёная. | | ломает | упавшие кейсы | |---|---|---| | M1 | резолв через `Selections` | egress:41 | | M2 | `typeReaches` стоп на срезе | egress:52,57 | | M3 | `requestseam` только литерал | request:36,44,48,54,62,66 | | M4 | SHAPE-половина off | egress:57,64,72,85,103 | | M5 | PROVENANCE-половина off | egress:32,41 | | M6 | egress требует сигнатуру | egress:85 | | M7 | `typeReaches` без `TypeParam` | egress:103 | | M8 | сеам не видит `FuncType` | stage:78, request:66 | | M9 | литерал не разворачивает указатель | stage:98 | Мутация нашла дефект в самом фиксе: первая редакция ветки `TypeParam` была **мёртвой** — `Constraint()` возвращает именованный тип, а обход на именованном по правилу встаёт. Исправлено на `Constraint().Underlying()` (единственное место, где обход сознательно проходит сквозь именованный тип; оговорка стоит в коде). ### Закрыто молчаливое гниение Гарды ключуются на строках — переименование не роняет ни сборку, ни тест, а SHAPE-половина тихо становится декорацией. `assertGuardVocabularyIsLive` требует, чтобы каждое исключение указывало на существующий файл, а оба имени типов резолвились в `internal/llm`. Обе проверки проверены мутацией. Снято мёртвое исключение `render.go`: замер показал, что дерево чисто и без него, а исключение, не покрывающее существующего кода, молча пред-одобряет следующий литерал. ### Батарея ``` make battery # EXIT=0, 1m44s golangci-lint # 0 issues go vet (×2 тег-набора, с -vettool и без) # чисто go test ./... -race # 15 пакетов ok, скип один (TestHelperKillLoop, прежний) ``` Посадок `// want`: **38** (15 stage · 13 request · 10 egress) + 5 фикстур-пропусков + 8 негативных контролей. `TestInvariantsHoldInTheShippingTree` гоняет анализаторы по реальному модулю — **0 диагностик**, то есть ни одна из новых веток не дала ложного срабатывания на боевом дереве. Четыре поведенческих теста живы: `BankProbeAndAttemptAddressOneCheckpoint`, `ReasoningIsPartOfTheRequestIdentity`, `RepairStageTakesItsEffortFromItsOwnGate` (`synthetic_stage_seam_test.go`), `InternalCallDecidesEveryStageField` (`internal_call_test.go`). --- ## Этап 2: живая проба провода ``` set -a; . ./.env; set +a TM_LIVE=1 go test -tags live -run TestLiveAdapterConformance -v ./internal/pipeline/ ``` PASS 3/3, 45s. grok закомментирован, не включал. | модель | finish | in/out | share исходника | вердикт | цена | |---|---|---|---|---|---| | `deepseek-v4-flash` | `stop` | 138/380 | 0.00 | ok | $0.000126 | | `glm-5` | `stop` | 69/21 | 0.00 | ok | $0.000136 | | `kimi-k2.6` | `stop` | 75/1214 | 0.00 | ok | $0.004927 | Новых finish_reason, отказов и фреймингов нет. Эхо-мина не выстрелила даже на фрагменте Лу Синя 祝福, но это одно наблюдение: quirks:59 фиксируют, что эхо на 0731 стохастично по вызову. ### Прямой клейм по `reasoning_content` Нейтральный `LLMResponse` полей thinking не несёт, поэтому взведён штатный лог сырых тел (`LOG_LLM_BODIES=1` + `LOG_LEVEL=debug`, зеркало `tmctl`; `obs.LogLLMExchange` пишет только тела, никогда URL и заголовки). Один повторный вызов deepseek, $0.000080: - `choices[0].message` = `['content', 'reasoning_content', 'role']`, **`reasoning_content` 965 символов**; - `reasoning_tokens: 234` при `completion_tokens: 280` ⇒ **`ReasoningSubset` подтверждён на проводе**: думание внутри completion, и наш нулевой `ReasoningTokens` — защита от двойного счёта, а не слепота; - тело запроса — `['max_tokens','messages','model','stream','temperature']`, **`reasoning_effort` отсутствует** ⇒ механика строки 112 подтверждена на проводе: `reasoning:"off"` на `ReasoningNone`-модели не кладёт ничего и вызов едет на вендорском дефолте. ### h2-фингерпринт (главный вопрос D39.96 §5) Клиент действительно ездит на x/net/http2 (`httpllm.go:17` + `http2.ConfigureTransports` поверх клона `DefaultTransport`), и все три эндпоинта договариваются на h2 — снято сырым TLS-хендшейком за $0: ``` api.deepseek.com:443 alpn="h2" tls=0x0304 api.z.ai:443 alpn="h2" tls=0x0304 api.moonshot.ai:443 alpn="h2" tls=0x0304 ``` ⇒ вызовы шли с новой раскладкой SETTINGS, и ни один фронт её не отверг. Долг строки 117 закрыт. **Граница:** три эндпоинта, по одному вызову, из одной сети, в один момент. Это «отказа нет сейчас отсюда», не «отказа не будет». CONNECT-туннель, CONTINUATION и поведение под нагрузкой не проверены. --- ## Найдено, не починено Осознанные пропуски по разрешённому промтом пути §D — назван в doc-комменте + посажен фикстурой без ожидающего комментария (analysistest покраснеет в день, когда пропуск начнёт ловиться). **Приняты оркестратором 03.08, носитель — строка 121(д), форма закрытия — типовой шов 121(б).** 1. Помощник, который зовётся не `Complete` И отмывает пару типов через свои (`llm.Ask`-класс). 2. Пара внутри named-обёртки: `typeReaches` по решению встаёт на именованном типе. 3. Ось видов узла: встраивание, `make([]T,1)`+присваивание, generic-zero. 4. `internal/llm` исключён целиком — второй провод внутри пакета адаптеров ловится только ревью. 5. Внешние тест-пакеты (`llm_test`, `config_test`) исключены — следствие `inPackage`, названо в коде. 6. Рефлексия (`reflect.Value.Call`) — статическим анализом не закрывается. 7. Файлы за третьим сборочным тегом и в директориях, которые пропускает загрузчик. Реальная потеря охвата против байт-скана; лечится строкой в Makefile на каждый новый тег. ## Правки комментариев по факту | было | стало | |---|---| | `cmd/tmvet`: «runs the invariants **alongside** the standard vet passes» | `-vettool` их **замещает** — измерено; Makefile гоняет оба прохода | | `inPackage`: «matching the path alone let `config_test` build Stages freely» | **инверсия**: без `TrimSuffix` посаженный `config_test` репортится (exit 1), с ним молчит (exit 0) — то есть `TrimSuffix` исключение расширяет. Поведение оставлено, цена названа | | `content_routing_test.go`: «resolving the SELECTION» | selections больше не резолвятся; переписано по фактической механике | | `archguard.go`: «builtins carry no signature» | встроенные с неконстантным результатом сигнатуру получают (`new(T)` → `func(T) *T`); переписано | | `pipeline-c1.yaml`: «echo mine unarmed» | quirks §4: `low` **пере-вооружает** эхо-мину (1 из 16); флип — осознанный размен, гейт ловит хвост | ## Оговорка о достоверности ревью Адверсариальное ревью диффа: 6 линз, 43 агента. **20 агентов умерли на лимите сессии, почти все на фазе опровержения** — то есть находки вернулись неопровергнутыми, а не подтверждёнными. Всё, что попало в этот отчёт, перепроверено исполнением отдельно; список неперепроверенного — раздел «найдено, не починено». **СТОП — приёмка оркестратора.**