Ratify D39.96-97: standards pack and archguard dofix accepted and landed, rows 107 112 117 closed, snapshot calibration and tools module decisions recorded

This commit is contained in:
heaven 2026-08-04 01:55:00 +03:00
parent 0e64efed7e
commit 062ad27d14
10 changed files with 760 additions and 14 deletions

View file

@ -43,6 +43,7 @@
## Методология (продолжай как предшественники — она себя оплатила)
- **Двухступенчатая верификация:** промты сессий несут мандат самопроверки ИСПОЛНЕНИЕМ (код+запросы+результаты); твоя пост-хок адверсариальная верификация при лендинге — второй рубеж, author≠reviewer, воркфлоу-инструментом, по СЫРЬЮ с пере-выводом чисел. Эра №6: рубеж-2 поймал CRITICAL-артефакт (окно судьи HEAD_CHARS) и спас контракт от трёх ложных заголовков — не ослабляй.
- **Анти-паттерны приёмки:** приёмка проверяет НАПРАВЛЕНИЕ и решения пака, не только клеймы отчёта. Шесть ловушек: след отчёта (проверяешь лишь названные автором места) · связность вместо истинности (форма отчёта убеждает сама) · соглашательство с чёткой позицией (распространенная ошибка ллм моделей склонных соглашаться с человеком) · рационализация задним числом · слепой участок общих моделей обученных на одинх и тех же данных (выводы одной модели усиливают уверенность в правильности другой) · экономия усилия на неудобном. Механика-минимум: ≥1 верификатор выносит мнение по промту+диффу ДО чтения отчёта · ≥1 ищет дефекты ВНЕ карты отчёта · экспериментальный клейм без ре-рана (хотя бы редуцированного) в D-ноте помечается «со слов сессии», не «исполнением» · «решение владельца», известное только со слов сессии, НЕ ратифицируется — список confirm/deny владельцу ДО лендинга, если решение несущее · к утверждениям самой приёмки та же дисциплина «заявление=команда» (догадку фактом не подавать) · оценочные эпитеты в ноты не писать.
- **Пре-рег дисциплина полигона:** фриз коммитом ДО платных вызовов (единственный коммит сессии); изменения после = новый experiment-ID; девиации — явно в отчёте; стоп-гейты по бюджету легитимны; «если не влезает — стоп и пинг, не резать молча».
- **Лендинг:** микро-дефекты доков чинишь сам с пометкой «испр. оркестратором»; отчёты получают ревью-шапку; код не правишь — находки в фикс-лист; коммиты скоуп-раздельные, стейджинг пофайловый; `git status` перед каждым коммитом (в дереве бывают ≥2 живые сессии) + **`git diff --cached` перед `commit`** — коммит уносит ВЕСЬ индекс: чужой staged `git mv` уедет в твой коммит (инцидент 77dd9b8); `add` общего файла сметает чужую секцию — diff-контент/`add -p`. **Лендинг, двигающий файлы или состав активных промтов, обновляет `docs/README.md` тем же коммитом** (норма D39.80; урок 02.08: README объявлял заленденный пак «можно запускать» с мёртвой ссылкой — поймал не процесс, а новая сессия). Статус/голову README не несёт вовсе — единственный носитель состояния = PROGRESS CURRENT-STATE. **Перед коммитом ратификации — механический чек головы:** `grep "голова D" docs/PROGRESS.md` обязан показать номер ТОЛЬКО ЧТО ратифицированной ноты (голова отставала дважды: D39.81 и D39.83 — оба раза «Текущее» обновлено, число головы забыто).
- **Findings-ledger процесс (концерн 5):** каждая находка — явная диспозиция до закрытия; вопросы «к бэкенду» из ресёрчей маршрутизируются обратно; completeness-critic на границах фаз.

File diff suppressed because one or more lines are too long

View file

@ -15,7 +15,7 @@
- `experiments/` — эмпирика полигона: [00-provider-quirks.md](experiments/00-provider-quirks.md) — **читать перед любым вызовом провайдера**; [08-cost-model-v2.md](experiments/08-cost-model-v2.md) — денежная модель; [09-pilot-protocol.md](experiments/09-pilot-protocol.md) — пилот Ф2.5; остальные 0116 — отчёты закрытых экспериментов (судьба — в баннерах/D-логе).
- `research/` — фактура ресёрчей 0122; у принятых — ревью-шапки, часть тел под ⚠ superseded: **читай баннер прежде содержимого**. Ключевые для навигации: 15 голос · 16 ридер-IDE · 17 внешняя критика · 18 рычаги качества · 19 нарезка · 20 банк-майнинг · 21 обзор транспорта · 22 доменные харнессы · 23 шов движок↔платформа (читать перед любым кодом стыка).
- [PROGRESS.md](PROGRESS.md) — журнал: CURRENT-STATE + **ЕДИНЫЙ БЭКЛОГ** (единственный трекер) + живой хвост хроники. НЕ источник решений.
- Активные хендофф-промты сессий (состав обновляется при каждом лендинге — норма D39.80): [ORCHESTRATOR_SESSION_PROMPT.md](ORCHESTRATOR_SESSION_PROMPT.md) (роль/нормы; состояния не дублирует) · [POLYGON_PACKAGE4_SESSION_PROMPT.md](POLYGON_PACKAGE4_SESSION_PROMPT.md) (полигон, отложен). [BACKEND_REPO_STANDARDS_SESSION_PROMPT.md](BACKEND_REPO_STANDARDS_SESSION_PROMPT.md) (**текущий бэкендный**, D39.92: линтер/CI/Makefile · бамп зависимостей с живой пробой провода · гарды→analyzer с паритетом посадок; этап 0 = пять шиппинг-конфигов на `low`, строка 112; предшественник — ручка эффорта — исполнен, ПРИНЯТ D39.91 и заархивирован) · [BANK_ARBITRATION_RESEARCH_SESSION_PROMPT.md](BANK_ARBITRATION_RESEARCH_SESSION_PROMPT.md) (**ресёрч+полигон**, D39.89; ⚠ заход 02.08 ПРЕРВАН владельцем, находки в дерево не попали — перед пере-выдачей промт ПОДРЕЗАТЬ по D39.90 п.5: §3-A+B1+B3, B2 → под-проба, §5-C отложен, потолок $1.20; вопрос владельцу о находках прерванной сессии открыт, D39.94 п.4). Фронт-промт — в чужой зоне `frontend/docs/`.
- Активные хендофф-промты сессий (состав обновляется при каждом лендинге — норма D39.80): [ORCHESTRATOR_SESSION_PROMPT.md](ORCHESTRATOR_SESSION_PROMPT.md) (роль/нормы; состояния не дублирует) · [POLYGON_PACKAGE4_SESSION_PROMPT.md](POLYGON_PACKAGE4_SESSION_PROMPT.md) (полигон, отложен). **Бэкендного активного промта НЕТ** — пак стандартов + дофикс archguard исполнены, приняты и залендены (D39.96/97, 04.08; промты в `archive/prompts/`); следующие бэкендные: пак экрана 46 (промт ждёт выдачи по D39.94 п.3) и малый пак tools/go.mod (строка 118, D39.97 п.4) · [BANK_ARBITRATION_RESEARCH_SESSION_PROMPT.md](BANK_ARBITRATION_RESEARCH_SESSION_PROMPT.md) (**ресёрч+полигон**, D39.89; ⚠ заход 02.08 ПРЕРВАН владельцем, находки в дерево не попали — перед пере-выдачей промт ПОДРЕЗАТЬ по D39.90 п.5: §3-A+B1+B3, B2 → под-проба, §5-C отложен, потолок $1.20; вопрос владельцу о находках прерванной сессии открыт, D39.94 п.4). Фронт-промт — в чужой зоне `frontend/docs/`.
- Зоны фронта (чужие, читать при касании стыка; каждая ведёт СВОЙ зонный бэклог — единый бэклог их строк не принимает, D39.84): [../frontend/](../frontend/) — веб-интерфейс: промт фронт-сессий S0S7 + [STACK_DECISIONS.md](../frontend/docs/STACK_DECISIONS.md) (пины версий точными числами и ловушки, сверены с вебом 02.08) + [BACKLOG.md](../frontend/docs/BACKLOG.md) · [../platform/](../platform/) — SaaS control plane: README-заглушка + [BACKLOG.md](../platform/BACKLOG.md).
- `archive/` — история ([правила архива](archive/README.md)): закрытые промты (`prompts/`) · отчёты с ревью-шапками (`reports/` — на них ссылаются приёмки) · исполненные арх-доки (`architecture/`) · слайсы хроники `PROGRESS-*.md`. Инструкции оттуда не исполнять.
- Диаграммы: [../backend/docs/components.puml](../backend/docs/components.puml) · [../backend/docs/pipeline.puml](../backend/docs/pipeline.puml) — дом рядом с кодом (D39.80), правятся бэкендом одним коммитом с кодом; вручную НЕ рендерить (владелец смотрит PlantUML-расширением VS Code).

View file

@ -1,4 +1,4 @@
# Журнал решений оркестратора — контракт D1D39.95 (развязки 04.07 · пакеты 0910.07 · приёмка/качество-первым/пивот/эмпирика 1112.07 · арх-ресет+стройка пере-прогонного стека 1319.07)
# Журнал решений оркестратора — контракт D1D39.97 (развязки 04.07 · пакеты 0910.07 · приёмка/качество-первым/пивот/эмпирика 1112.07 · арх-ресет+стройка пере-прогонного стека 1319.07)
> **КАРТА АКТУАЛЬНОСТИ (ревизия D31, продлена до D38.2 [12.07]; исторические записи ниже НЕ переписываются — дисциплина D23.3).** Читая контракт целиком, держи под рукой, что чем перекрыто:
> ⚠ **Навигация (актуализация 01.08):** карта ниже детально покрывает D1D39.28; решения D39.29+ живут хронологически в теле файла, **свежая голова — С ХВОСТА** (новые ноты аппендятся вниз). Сводка текущей головы и очередь — CURRENT-STATE в `../PROGRESS.md`.
@ -1388,3 +1388,37 @@ API-529-долг закрыт: 8-осевой refute-by-default воркфлоу
## D39.95 — Пост-клоуз гигиена по речеку владельца: онбординг-хвосты добиты (02.08, ночь). ✅
Речек владельца «перечитай онбординг» вскрыл хвосты, пропущенные лендингами №11: (1) **строка 74 ЗАКРЫТА** — её собственная буква «закрывается приёмкой этого пака» исполнилась в D39.91, а строка осталась в таблице; развилка решена (б) ещё D39.87, обе половины замера исполнены (флор-фальсификация · low-оживление), остатки давно живут в именованных строках: конфиги → 112 · экран → 46 · холодный прогон → 16 · число флора → 44; очередь после закрытия — **96 строк** (счёт D39.94 «97» устарел этой нотой). (2) Промт `BACKEND_EFFORT_HANDLE` заархивирован с баннером приёмки, карта промтов README обновлена (текущий бэкендный — `BACKEND_REPO_STANDARDS`, банк-ресёрч помечен прерванным с диспозицией подрезки). (3) Буллеты CURRENT-STATE «Курс»/«Горизонт»/«Стек» освежены: ре-проба 74 — исполнена, не будущее; стоячее ограничение перед платными прогонами теперь — строка 112 (шиппинг-конфиги до лендинга покупают пустые ответы); «editor v4-pro вендором НЕ тронут» дополнено анонсом смены маппинга (вахта 108). (4) Шапка-диапазон этого файла и счётчик ролей в промте оркестратора доведены до D39.95. Урок в копилку №12: закрытие строки обязано триггерить греп по её НОМЕРУ во всех живых доках — буквы «закрывается X» в чужих строках и буллетах не закрываются сами.
## D39.96 — ПРИЁМКА пака «стандарты репо» (строки 107 · 112 · Д7, оркестратор №12), ДВА ПАССА: пак принят по качеству кода УСЛОВНО, лендинг ГЕЙЧЕН тремя решениями владельца; найдена регрессия egress-паритета, запрещающая снос байт-гарда по букве промта (03.08). ⏳
**Мандат — D39.92 п.23, задача №1 хендоффа D39.94. Отчёт: `archive/reports/REPO_STANDARDS_2026-08-03.md` (ревью-шапка).** Приёмка шла ДВУМЯ пассами: пасс-1 (шесть верификаторов по клеймам отчёта) и — после вмешательства владельца, вскрывшего «след отчёта» как метод-дефект — пасс-2 из пяти АНТИ-СЛЕДОВЫХ линз (мнение до чтения отчёта · поиск дефектов вне его карты · реконструкция снесённых гардов из HEAD · аудит исключений линтера по коду · независимая репликация экспериментальных клеймов). Всё ниже — итог обоих пассов; расхождения пасс-1 ↔ пасс-2 названы явно как урок.
**1. Ядро подтверждено исполнением (оба пасса, $0).** Батарея: `make tools-check` + `make battery` EXIT=0 (real 1m25s на тёплом кэше) · golangci-lint 2.12.2 **0 issues** (сырой uncapped errcheck воспроизведён точно: 413; ⚠ методологическая грабля — без `--max-issues-per-linter=0 --max-same-issues=0` no-config-режим молча режет к 50) · vet оба тег-набора с `-vettool` · 15 пакетов ok `-race` · скип ровно один. Конфиги: ровно 5 флипов черновиков; находка-1 (адрес c2:39 из промта = judge на glm-5) подтверждена кодом `models.yaml:191194` + `capability.go:155168`; редакторские/судейские `off` целы. Бамп: версии и lockstep-пины сверены; голден-дифф пере-выведен независимо — двигаются ТОЛЬКО хеш-поля, атрибуция установлена форензикой пасса-2: **голден сдвинут исключительно пином x/text (`norm.go:69`), не флипом конфигов и не x/net** (голден живёт на fake-model). Код-дифф: все 9 дефект-фиксов §2 подтверждены, посторонних правок нет, `//nolint` в дереве 0, зеленения-хаков нет.
**2. Экспериментальные клеймы — РЕПЛИЦИРОВАНЫ независимо (пасс-2; пасс-1 принял их со слов отчёта — это была ошибка приёмки, вскрытая владельцем).** (а) Эквивалентность x/text 0.38↔0.40 ПОДТВЕРЖДЕНА с нуля и шире спецификации: все скаляры Unicode × {NFC, NFKC, width.Fold} + битый UTF-8 + меж-рунные пары/хангыль + энкодеры GB18030/GBK/UTF-16 (наш `ingest.go`) — три независимых SHA-256 идентичны под обеими версиями. (б) h2-фингерпринт: появление `SETTINGS_MAX_FRAME_SIZE=16384` ПОДТВЕРЖДЕНО на байтах фреймов (свой TLS+ALPN стенд, копия `keepAliveHTTPClient`); набор/значения заголовков и тело байт-идентичны. ⚠ **Поправка принятого клейма: «порядок HPACK-заголовков изменился» — МИСХАРАКТЕРИЗАЦИЯ** (отчёта И пасса-1): порядок обычных заголовков рандомен per-request под ОБЕИМИ версиями (map-итерация в net/http), стабильного «до/после» не существует — опираться на него не мог никто; стабильны псевдо-заголовки и хвост. Дельта фингерпринта = только SETTINGS-раскладка. Непроверенным осталось (честная граница редукции): прокси-туннель CONNECT, CONTINUATION/PING, TLS-слой — закрывается живой пробой (п.5).
**3. Посадки приёмки в гарды: пасс-1 — 7 своих (6/7 CAUGHT); пасс-2 добавил реконструкцию старых гардов из HEAD и НОВЫЕ посадки вне карты отчёта. Итог по осям:** (а) **Stage-сеам — прирост без регрессий:** двойная цепь алиасов: старый гард ловил только ПЕРВОЕ звено (одна точка отказа), новый — все три узла; конверсия из 17-полевого близнеца ловится; негативные контроли чисты. (б) **Request-сеам — дыра ОБЩАЯ с предшественником, подтверждено исполнением на реконструкции:** `var r pipeline.Request`+присваивание и `new(pipeline.Request)` проходят ОБА поколения (контроль чувствительности: hoisted-литерал старый гард ловит) — клейм пасса-1 «не регрессия» оказался верен, но был записан ДО проверки, это второй урок; дыра нигде не задокументирована на Request-стороне (`runRequestSeam` — один узел CompositeLit, `archguard.go:197`, против четырёх у Stage) — носитель 121. (в) ⚠ **Egress-сеам — РЕГРЕССИЯ против снесённого байт-гарда, две исполнимые формы (посадки пасса-2, обе exit=0 у tmvet):** пакетная функция `llm.Complete(...)` (у qualified identifier `Selections[sel]` = nil → анализатор молча выходит, `archguard.go:240-242`; пакет llm исключён целиком) и потребительский интерфейс, прячущий llm-типы ВНУТРИ композита (`Complete(ctx, []llm.LLMRequest) ([]*llm.LLMResponse, error)``touchesPkg` не рекурсивен, `archguard.go:79-82`). Старый регекс `.Complete(` ловил ОБЕ формы. Несущий коммент «that is what cannot be hidden» (`archguard.go:250-253`) и клейм фикстуры ФАЛЬСИФИЦИРОВАНЫ. Сегодня таких форм в дереве нет (латентно), но **промт разрешал снос гардов «ТОЛЬКО после доказанного паритета» — по egress-оси паритет неполон ⇒ снос байт-гарда в текущем виде букве промта НЕ соответствует.** Развилка владельцу — п.7.
**4. Направление и решения (линза пасса-2, мнение сформировано ДО чтения отчёта).** Состав линтера обоснован числами и анти-хак-правило соблюдено буквально; аудит исключений по коду: все классы подтверждены для СЕГОДНЯШНЕГО дерева (единственный контрпример фразе «все 33 прод-Close — читатели»: `chunktest/epub.go:51`, файл на запись в тест-фикстуре; потерю данных не прячет — zip.Writer.Close проверяется). Точностные хвосты → 122: Fprint-исключение на весь модуль вместо `cmd/` · `w\.Write`-исключение ключуется именем receiver-а вопреки собственному принципу конфига · miningstop-исключение глушит текстом весь файл · комменты-обоснования конфига — снимки настоящего времени, ничто не пере-проверяет их посылку · дрейф provenance-чисел (293/31/93 против записанных 294/33/91) · `.skips.log` не в gitignore · GO_MIN_VERSION без патч-уровня · коммент c1 «echo mine unarmed» противоречит quirks §4 («low ПЕРЕ-ВООРУЖАЕТ эхо-мину», 1/16) — сам флип ратифицирован, вреда нет, коммент поправить. Батарея против «не тормозить»: двойной прогон сьюта ради имён скипов ≈6.4s из 1m25s (~8%) — приемлемо; главный расход — архитектурные инварианты исполняются трижды (vet ×2 тег-сета + archguard-тест 17.7s LoadAllSyntax под -race) — осознанно, резать при росте модуля здесь.
**5. Живая проба провода — единственный пункт промта, не закрытый никем; после п.2(б) её предмет сузился до реального: примут ли WAF/фронты провайдеров новую SETTINGS-раскладку и 31 минорку семантики ретраев.** Сессия пробу не провела (довод «не было санкции» неверен — потолок $0.50 ратифицирован D39.92 п.2), приёмке запуск заблокирован пермишен-слоем на `. ./.env`. Линза направления независимо назвала это главным долгом пака: «осторожный, затем полный» требовал осторожную половину именно здесь. Команда владельцу — строка 117. До пробы (или явного слова владельца) транспортный бамп НЕ лендится.
**6. Пять mid-flight решений владельца 03.08 — НЕ ратифицированы (пасс-1 ошибочно ратифицировал «с вето-окном» — снято): известны только со слов сессии, владельцу выдан список confirm/deny.** (i) CI-воркфлоу не писать (Forgejo 10.0.3, раннер не подтверждён) → жилец 108. (ii) Охват eval/ анализатором снять (замер: три программы, ни одна не строит Stage/Request). (iii) **Бамп `@latest` вместо минимального — НЕСУЩЕЕ:** при confirm лендится текущий go.mod (x/net 0.57 + sqlite 1.55 + пере-захват голдена), при deny — минимальный бамп (x/tools@v0.45 → x/net 0.54, x/text НЕ двигается, голден БЕЗ пере-захвата; жизнеспособность доказана ревьюером сессии) и состав bump-коммита другой. (iv) Пере-захват голдена — следствие (iii). (v) Адверсариальное ревью §9 по требованию владельца — информационно.
**7. ВЕРДИКТ И РАЗВИЛКА ЭТАПА E.** Пак **ПРИНЯТ по качеству кода** (ядро п.1, репликации п.2, аудиты п.4 — уровень высокий, самопроверка сессии реальная: 18 своих дефектов починены до сдачи). **Лендинг ГЕЙЧЕН тремя решениями владельца:** (1) проба 117 — или явная санкция лендить до пробы; (2) confirm/deny пяти решений п.6 (несущее — @latest); (3) **этап E: рекомендация оркестратора — МИКРО-ДОФИКС бэкенд-сессией ДО лендинга сноса** (~30 строк + посадки: requestseam ValueSpec/new-симметрия [п.3б] · egressseam fallback на Uses при nil-Selections [п.3в-1] · рекурсивный touchesPkg [п.3в-2] · честные комменты вместо «cannot be hidden»); альтернатива — лендить как есть с ЗАДОКУМЕНТИРОВАННОЙ регрессией по двум egress-формам и строкой 121 «скоро». **План лендинга — семь pathspec-коммитов с само-атрибуцией** (рекомендация линзы направления, форензика голдена бесплатна в истории): конфиги 112 → дефект-фиксы §2 (поведенческие, именованные) → механические линт-фиксы → бамп+пины+голден (гейт 117 в сообщении) → анализаторы+снос гардов+README → .golangci.yml+Makefile → док-лендинг. Строки 107/112 закрываются док-коммитом лендинга; носители 117123 и жильцы 108 подготовлены в PROGRESS тем же касанием.
**8. УРОК ПРИЁМКИ (слово владельца 03.08, оплачено этой нотой; норма — в промте оркестратора).** Пасс-1 попался на шести ловушках: след отчёта (план верификации структурирован его секциями) · связность вместо истинности (два несущих клейма приняты со слов с записью «исполнением») · соглашательство (пять «решений владельца» ратифицированы со слов сессии; оценочные эпитеты в ноте) · догадка фактом («не регрессия» до проверки) · слепой участок одной модели · экономия усилия на неудобном (направление/решения не ревьюились). Пасс-2 (анти-следовые линзы) на тех же токенах нашёл: регрессию egress-паритета, запрещающую снос по букве промта; мисхарактеризацию принятого клейма; двух исполнимых обходов вне карты отчёта. Механика-минимум закреплена нормой в `ORCHESTRATOR_SESSION_PROMPT.md` (03.08).
## D39.97 — ПРИЁМКА дофикса archguard + живой пробы (оркестратор №12): ПРИНЯТ, весь пак стандартов ЗАЛЕНДЕН семью pathspec-коммитами, строки 107/112/117 ЗАКРЫТЫ; решение по tools/go.mod — строить; вопрос владельца о снапшоте разобран (04.08). ✅
**Мандат — D39.96 п.7 (дофикс до сноса — выбор владельца) + промт `BACKEND_ARCHGUARD_DOFIX`. Отчёт: `archive/reports/ARCHGUARD_DOFIX_2026-08-03.md` (ревью-шапка ПРИНЯТ).**
**1. Приёмка исполнением (короткая, по пропорциональности; свои посадки — вне карты отчёта).** Батарея на реальном дереве EXIT=0 (1m29s, скип один). Деньги вторым путём: usage×цены `models.yaml` ≡ заявленным по всем трём вызовам и сумме, дельта $0.000000 ($0.005269 ≈ заявленным $0.0053). Свои посадки в свежей копии: значение метода `f := c.Complete` CAUGHT (клейм таблицы подтверждён независимо) · **method expression `llm.LLMClient.Complete` CAUGHT — формы нет ни в таблице отчёта, ни в списке пропусков** (SHAPE-половина берёт её по типам аргументов — гард сильнее собственной документации) · негативный контроль шва чист. Вход приёмки на месте: **CONFIRM владельца по `@latest` записан отчётом** (D39.96 п.6-iii закрыт) — проба шла на текущем go.mod.
**2. Что принято.** Этап 1: шесть дыр закрыто (3 из промта + 3 сверх, включая блокер «вызов через параметр типа» — невидимый ОБОИМ прежним поколениям; ось имени `llm.Do`; ложное срабатывание `var s *config.Stage` снято); оба сеама на общем обходчике `seamRule`; исключения переведены с path-суффиксов на import-path (`//line`-идентичность отрезана `PositionFor(…, false)`); 9/9 мутаций ловятся, мутация M7 нашла мёртвую ветку TypeParam в самом фиксе — образец, зачем мутационная проверка обязательна; `assertGuardVocabularyIsLive` против молчаливого гниения строковых ключей; 38 want-посадок + 8 негативных контролей; ноль диагностик на боевом дереве. Этап 2: проба PASS 3/3 (`finish=stop`, новых отказов/фреймингов нет); **прямой клейм по проводу: `reasoning_content` 965 симв., `reasoning_tokens:234` ⊂ `completion:280` ⇒ `ReasoningSubset` подтверждён на проводе; механика 112 подтверждена телом запроса** (`reasoning_effort` отсутствует на `ReasoningNone`); h2-ALPN подтверждён на всех трёх эндпоинтах за $0 ⇒ новая SETTINGS-раскладка не отвергнута. Граница честная (три эндпоинта · по вызову · одна сеть) — принята. Семь осознанных пропусков — фикстурами без want (покраснеют в день, когда начнут ловиться) — носитель 121(д). Оговорка сессии о 20/43 умерших ревью-агентах — компенсирована её пере-проверкой и моими посадками. Остаток: инвариант-коммент `httpllm.go:77` («zero wire bytes» — верен для полей пинга, неверен для бампа, который их привёз) дофикс не тронул → хвост в 122.
**3. ЛЕНДИНГ: весь пак стандартов + дофикс — семь pathspec-коммитов с само-атрибуцией** (план D39.96 п.7): `e8d4ac0` конфиги 112 · `5a97020` девять поведенческих дефект-фиксов · `b155da3` механика линта · `cb368c0` бамп+пины+голден (движутся только хеш-поля) · `6356471` анализаторы+снос старых гардов · `5a81bff` .golangci.yml+Makefile+README · `0e64efe` opt-in лога тел в live-пробе. Фронт-сессия закоммитила своё (`ece94ef`) ПАРАЛЛЕЛЬНО лендингу — pathspec-дисциплина коллизию исключила by construction. **Строки 107, 112, 117 ЗАКРЫТЫ.** Оба промта архивированы. Оставшиеся не-несущие mid-flight решения 03.08 (CI-воркфлоу не писать · охват eval/ снят) ратифицированы этой нотой — порог нормы «confirm только для несущих» соблюдён, триггеры реопена в реестре 108.
**4. Решение по tools/go.mod (владелец 04.08: «вам решать») — СТРОИТЬ, отдельным малым паком, НЕ гейт ничему.** Форма: отдельный модуль (например `backend/tools/` со своим go.mod), куда переезжают `cmd/tmvet` + `internal/archguard` — ⚠ `tool`-директива Go 1.24 НЕ решает задачу (тулы в главном go.mod участвуют в MVS так же). Следствия, которые пак обязан удержать: `make battery` гоняет тесты ОБОИХ модулей (инвариант «падать на обычной батарее» переезжает в Makefile — он и так единственная точка входа); `shippingtree_test` живёт в tools-модуле и грузит главный. Выигрыш: бамп инструментов ФИЗИЧЕСКИ не может двинуть x/net/x/text главного модуля ⇒ класс «линтер тронул денежную ось» закрывается по построению. Туда же — решение по sqlite: оставить 1.55 (уже заленден, прогонов не было, откат = отдельное движение без выгоды). Строка 118 переформулирована под это решение.
**5. Вопрос владельца «не слишком ли агрессивен снапшот, можно ли безопасно ослабить» — разбор.** Короткий ответ: механизм НЕ слишком агрессивен — он неточно НАЦЕЛЕН, причём в обе стороны сразу, и лечится калибровкой осей + дешёвой перекупкой, а не ослаблением. Три факта: (а) **есть недо-чувствительность** (строка 119: `SanitizerVersion` НЕ сворачивает `width.Fold` — смена x/text перевернула бы вердикт БЕЗ движения снапшота; это аргумент ПРОТИВ ослабления вслепую); (б) **есть пере-чувствительность по охвату**: `EmbeddedVersion` фолдится безусловно по ВСЕМ файлам всех пар (D39.90 п.3) — добавление ja-данных перекупает zh-книги; лечится пер-парным хешом (только файлы своей пары) — заодно шаг к мандату общности (родня 115/120); (в) **версия-строка ≠ поведение**: бамп x/text доказан поведенчески пустым на 17M+ входов, но снапшот сдвинулся, потому что фолдится СТРОКА версии. Направление фикса (в): не «фолдить меньше», а **сделать перекупку дешёвой, когда поведение не менялось**`tmctl resnapshot --dry-run`, который по чекпойнтам сверяет БАЙТЫ тел (`msgsContentHash` уже в голдене): байт-идентичные юниты пере-привязываются к новому снапшоту без пере-вызова, платятся только реально изменившиеся; детерминизм цел (каждая пере-привязка аудируема), деньги видимы (канон §5), «тихих перепокупок» не появляется — наоборот, исчезают громкие ПУСТЫЕ перекупки. Опция «фолдить поведенческий отпечаток вместо строки версии» рассмотрена и отложена: probe-корпус = пол, не доказательство; риск тихого изменения поведения хуже цены ложной перекупки. **Заведена строка 124** (дизайн-вход: (а)+(б)+(в) одним паком, приоритет — перед МАСШТАБОМ, когда перекупки начнут стоить реальных денег; сегодня боль теоретическая — книг в работе нет).

View file

@ -0,0 +1,48 @@
# Промт бэкенд-сессии: дофикс паритета анализаторов + живая проба провода (строки 121-п.0 · 117; хвост пака стандартов)
> ✅ **ИСПОЛНЕН И ПРИНЯТ (D39.97, 04.08): шесть дыр закрыто (3 промта + 3 сверх, вкл. блокер type-param), 9/9 мутаций, проба PASS 3/3 с прямым клеймом `reasoning_content` на проводе, $0.0053 из $0.50. Заленден в составе пака стандартов.** Отчёт: `../reports/ARCHGUARD_DOFIX_2026-08-03.md`. Инструкции отсюда не исполнять.
**Мандат:** приёмка пака стандартов D39.96 (03.08): пак принят по качеству кода, но пасс-2 приёмки нашёл **регрессию egress-паритета против снесённого байт-гарда** — снос гардов в текущем виде нарушает условие исходного промта «только после доказанного паритета». Владелец выбрал дофикс ДО лендинга. Твой предшественник сделал качественный пак — ты закрываешь три конкретные дыры, найденные посадками приёмки, и добиваешь единственный неисполненный пункт (живую пробу провода).
**Онбординг:** CLAUDE.md → `docs/README.md` → CURRENT-STATE в `docs/PROGRESS.md` → D39.96 в `05-decisions-log.md` (вся фактура приёмки там) → отчёт `archive/reports/REPO_STANDARDS_2026-08-03.md` (ревью-шапка = сводка находок). Код-факты бери из кода — file:line ниже это отправные точки, код первичен.
## §0. Скоуп и правила
- Зона записи: `backend/` ТОЛЬКО. НЕ коммитить — лендит оркестратор. `.env` НЕ ЧИТАТЬ (для этапа 2 сорсить шеллом — ключи в контекст/отчёт не тащить).
- Деньги: этап 1 = **$0**. Этап 2 = живая проба, потолок **$0.50** (санкция D39.92 п.2), каждый цент в отчёт.
- **Эхо-протокол:** первый деливерабл — ≤10 строк «что понял: скоуп / инварианты / не-делать» ДО работы.
- **Канал вопросов:** непонятно / конфликт промта с кодом или доками → пинг оркестратору через владельца, НЕ интерпретация.
- Дерево содержит незакоммиченный пак стандартов — это ТВОЯ база, поверх неё и работаешь. Чужого в дереве быть не должно, но `git status` перед началом сверь.
## Этап 1 ($0): три дыры анализаторов — фикс + посадки
Все три доказаны исполнением при приёмке (посадки exit=0 там, где обязан быть флаг). Форма фикса — «реши сам»; приоры ниже опровергаются аргументом.
**A. egressseam пропускает пакетную функцию `llm.Complete(...)`.** `archguard.go:240-242`: у qualified identifier (функция пакета, не метод) `pass.TypesInfo.Selections[sel]` = nil → анализатор молча выходит. Пакет `internal/llm` исключён целиком, так что хелпер `func Complete(...)` в llm + вызов откуда угодно = второй egress, невидимый ничем. Приор фикса: fallback на `pass.TypesInfo.Uses[sel.Sel]``*types.Func` с `Pkg()` = llm.
**B. egressseam пропускает интерфейс, прячущий llm-типы ВНУТРИ композита.** `touchesPkg` (`archguard.go:76-92`) разворачивает один уровень указателя и не смотрит внутрь элементов: `Complete(ctx context.Context, reqs []llm.LLMRequest) ([]*llm.LLMResponse, error)` проходит молча. Приор: рекурсия по Slice/Map/Chan/Array/Signature (+указатель уже есть).
**C. requestseam ловит только литералы.** `runRequestSeam` обходит один узел `*ast.CompositeLit` (`archguard.go:197`) против четырёх у stage-сеама (`:140-165`): `var r pipeline.Request` + пополевое присваивание и `new(pipeline.Request)` из чужого пакета проходят молча (дыра общая со старым гардом — не регрессия, но и не задокументирована). Приор: симметрия ValueSpec/CallExpr со stage-сеамом.
**D. Честные комменты.** «that is what cannot be hidden» (`archguard.go:250-253`) и клейм фикстуры (`testdata/.../egresssites/egresssites.go:33`) фальсифицированы посадками — переписать по фактическому охвату. Если что-то из AC решишь осознанно НЕ ловить — это легитимно, но пропуск ОБЯЗАН быть назван в doc-комменте пакета и негативной фикстурой (молчащий пропуск = как раз то, что приёмка вскрыла).
**E. Посадки.** Каждая закрытая форма — кейс `analysistest` в testdata (формы из A/B/C выше) + негативные контроли (легитимный вызов через шов; `Complete` чужого пакета с не-llm сигнатурой; `var` в exempt-местах). Плюс мутационная самопроверка: сломай каждый фикс — соответствующий кейс обязан упасть.
**F. Батарея.** `make battery` EXIT=0; четыре поведенческих теста pipeline (`BankProbe`/`ReasoningIsPart`/`RepairStage`/`InternalCallDecidesEveryStageField`) живы.
## Этап 2: живая проба провода (строка 117 — гейт платных прогонов)
⚠ **ГЕЙТ: слово владельца по бампу `@latest` (D39.96 п.6-iii) — спроси ДО этапа 2, если он не передал с промтом.**
- **Confirm @latest** → проба на текущем дереве.
- **Deny** → сначала минимальный пере-бамп (приор, доказан ревьюером исходного пака; опровергается замером): `go get golang.org/x/tools@v0.45.0` (тянет x/net v0.54.0, **x/text НЕ двигается**) + откат `modernc.org/sqlite` на v1.53.0 + откат x/text-пинов (`norm.go:69` → v0.38.0, зеркала `enclosure_test.go` и `memory_f1_test.go`) + голден обязан пройти БЕЗ пере-захвата (вернётся к HEAD-версии). Затем проба.
Команда (ключи грузит шелл; вывод фильтруй от ключей):
```
cd backend && set -a && . ./.env && set +a && TM_LIVE=1 go test -tags live -run TestLiveAdapterConformance -v ./internal/pipeline/
```
Кроет deepseek/zai/kimi (grok закомментирован — clean-prod гигиена, НЕ включать). Сверять против `docs/experiments/00-provider-quirks.md`: `finish=stop` · `reasoning_content` непустой где ожидается · нет новых отказов/фреймингов. Контекст риска: после бампа x/net у клиента новый `SETTINGS_MAX_FRAME_SIZE=16384` (семантически инертен, но меняет h2-фингерпринт для WAF). **Любое расхождение = СТОП-пинг с сырьём, откатов без слова владельца не делать.**
## Отчёт
`docs/archive/reports/ARCHGUARD_DOFIX_<дата>.md`, компактный: эхо-шапка · дифф этапа 1 с таблицей «форма × ловится» · итог мутационной самопроверки · результат пробы по провайдерам (finish/usage/цена; тел с ключами НЕ вставлять) · найдено-не-починено. Каждое число — с командой. **СТОП — приёмка оркестратора.**

View file

@ -1,5 +1,7 @@
# Промт бэкенд-сессии: индустриальные стандарты репозитория — линтер · CI · бамп зависимостей · гарды-в-analyzer (строки 107 · 112 · Д7)
> ✅ **ИСПОЛНЕН, ПРИНЯТ (D39.96 двумя пассами) И ЗАЛЕНДЕН (D39.97, 04.08; семь pathspec-коммитов `e8d4ac0`…`0e64efe`). Строки 107/112/117 закрыты.** Регрессия egress-паритета, найденная приёмкой, закрыта дофикс-паком (`BACKEND_ARCHGUARD_DOFIX_SESSION_PROMPT.md` рядом); живая проба провода исполнена там же (PASS 3/3). Отчёты: `../reports/REPO_STANDARDS_2026-08-03.md` · `../reports/ARCHGUARD_DOFIX_2026-08-03.md`. ⚠ Эрратум промта: адрес `pipeline-c2.yaml:39` был ошибкой трекера (judge на glm-5, не черновик) — флипов ровно 5. Инструкции отсюда не исполнять.
**Мандат:** решение владельца 02.08 (D39.92): «делаем по стандартам индустрии: CI, линтер, бамп x/net и всех библиотек; осторожный, затем полный подход; спилить самописные AST-тест-гарды в пользу штатной формы». Ограничение владельца, равное по силе самому мандату: **стандарты не должны мешать разрабатывать, тормозить и приводить модели к хакам.**
**Онбординг:** CLAUDE.md → `docs/README.md` → CURRENT-STATE в `docs/PROGRESS.md` → голова D39.9192 в `05-decisions-log.md``backend/README.md` + `12-go-style-notes.md`. Контекст предмета: отчёт `archive/reports/EFFORT_HANDLE_2026-08-02.md` §1.3(2), §5.5, Д7; D39.90 п.7; строка 107 трекера.

View file

@ -0,0 +1,210 @@
# Дофикс анализаторов 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 агентов умерли на лимите сессии, почти все на фазе
опровержения** — то есть находки вернулись неопровергнутыми, а не подтверждёнными. Всё, что попало в
этот отчёт, перепроверено исполнением отдельно; список неперепроверенного — раздел «найдено, не
починено».
**СТОП — приёмка оркестратора.**

View file

@ -317,7 +317,7 @@ sourceScriptShare > 0.15 (disposition.go:224,275)
Альтернативы проверены запуском и **не проходят наше ограничение «падать на обычной батарее»**: `go test -vet=<свой анализатор>`*«-vet argument must be a supported analyzer»*; `GOFLAGS=-vettool` при `go test` игнорируется; штатный `composites` вообще не входит в набор `go test`. `golangci-lint` — 38.7 МБ бинарь и первый в проекте `.golangci.yml`. `x/tools` в тесте тянет `x/net` 0.26.0 → 0.54.0, а `x/net/http2` — наш транспорт к провайдерам.
`exhaustruct` — инструмент ровно под исходный класс бага, но на нашем коде даёт 16 находок, из которых 15 в тестах и одна — сам конструктор.
`exhaustruct` — инструмент ровно под исходный класс бага, но на нашем коде даёт 16 находок, из которых 15 в тестах и одна — сам конструктор. *(⚠ эрратум приёмки D39.96, испр. оркестратором: «16» — число, СУЖЕННОЕ до типов `config.Stage`/`pipeline.Request` (фактически 17, семнадцатый — сам шов `InternalCall.Stage()`); ГЛОБАЛЬНЫЙ exhaustruct по репо = **988** — замер пака стандартов, отчёт `REPO_STANDARDS_2026-08-03.md` §1 находка-2.)*
---

View file

@ -0,0 +1,436 @@
# Отчёт бэкенд-сессии: индустриальные стандарты репозитория (строки 107 · 112 · Д7)
> ⏳ **ПРИНЯТ ПО КАЧЕСТВУ КОДА приёмкой оркестратора №12 в ДВА ПАССА (03.08, D39.96); ЛЕНДИНГ ГЕЙЧЕН тремя решениями владельца (проба провода · confirm пяти mid-flight санкций, несущее — `@latest` · развилка этапа E).** Пасс-1 — шесть верификаторов по клеймам отчёта: батарея EXIT=0 (lint 0 issues, скип ровно один), 5 флипов и находка-1 подтверждены кодом (`models.yaml:191194`, `capability.go:155168`), голден-дифф пере-выведен независимо (только хеши; атрибуция: двигает ИСКЛЮЧИТЕЛЬНО пин x/text, не конфиги и не x/net), 9 дефект-фиксов §2 подтверждены, 7 своих посадок в анализаторы (6/7 CAUGHT). Пасс-2 — пять АНТИ-СЛЕДОВЫХ линз (мнение до чтения отчёта · поиск вне его карты · реконструкция снесённых гардов из HEAD · аудит исключений линтера по коду · независимая репликация клеймов).
> **Итоги пасса-2, меняющие картину отчёта:** (1) ⚠ **РЕГРЕССИЯ egress-паритета против снесённого байт-гарда, две исполнимые формы:** пакетная `llm.Complete(...)` (nil-`Selections` → молчаливый выход, `archguard.go:240-242`) и интерфейс с llm-типами внутри композита (`touchesPkg` не рекурсивен, `:79-82`) — старый регекс `.Complete(` ловил обе; коммент «cannot be hidden» (`:250-253`) и клейм фикстуры фальсифицированы; **снос байт-гарда в текущем виде нарушает условие промта «только после доказанного паритета»** — развилка у владельца (D39.96 п.7), рекомендация: микро-дофикс ~30 строк до сноса. (2) ⚠ **Находка-4(б) «порядок HPACK-заголовков изменился» — мисхарактеризация:** порядок обычных заголовков рандомен per-request под ОБЕИМИ версиями x/net (map-итерация), стабильного «до/после» не существует; реальная дельта фингерпринта — только SETTINGS (`MAX_FRAME_SIZE=16384` подтверждён репликацией на байтах фреймов). (3) Клейм эквивалентности x/text 0.38↔0.40 РЕПЛИЦИРОВАН независимо и шире заявленного (скаляры+битый UTF-8+пары/хангыль+энкодеры GB18030/GBK/UTF-16; три SHA-256 идентичны). (4) Посадка P3: `var r pipeline.Request`/`new()` проходят ОБА поколения гарда — «не регрессия» подтверждена ИСПОЛНЕНИЕМ на реконструкции HEAD-гарда; дыра на Request-стороне нигде не задокументирована → строка 121. (5) Аудит исключений errcheck по коду: все классы подтверждены для сегодняшнего дерева; контрпример фразе «все прод-Close — читатели» — `chunktest/epub.go:51`; точностные хвосты (Fprint-скоуп, `w\.Write`-ключ по имени, дрейф provenance-чисел 293/31/93, `.skips.log` не в gitignore, коммент c1 против quirks §4) → строка 122.
> **Мелкие поправки отчёта:** адреса c1 съехали на 2 (флип фактически `c1:53`, редакторский `c1:81`); счёт §4 «396→143» — фактически 395→141; строка §2 про `dotenv.go` устарела (финал — §9 п.14: warns, never fails); дифф go.mod несёт также indirect `x/mod`/`x/sync`. **Живая проба провода НЕ добита никем** (приёмке запуск заблокирован пермишен-слоем на `.env`) — строка 117 = гейт; довод отчёта «не было санкции» неверен (потолок $0.50 ратифицирован D39.92 п.2). Пять mid-flight решений владельца 03.08 НЕ ратифицированы — выданы владельцу на confirm/deny (D39.96 п.6). Носители — строки 117123 + реестр 108.
**Промт:** `docs/BACKEND_REPO_STANDARDS_SESSION_PROMPT.md` (D39.92). **Дата:** 03.08.2026. **Зона:** `backend/` (+ этот отчёт).
**Правило отчёта:** каждое число ниже — с командой, которой оно снято. Ничего не «зеленено» обходом.
**Эхо-шапка (что понято как задача):** привести репо к штатным для Go формам — линтер с конфигом, одна команда батареи, бамп зависимостей, архитектурные гарды на `x/tools/go/analysis` вместо самописных обходов; плюс санкционированная правка пяти шиппинг-конфигов. Ограничение владельца (стандарты не должны мешать разрабатывать и толкать модель к хакам) держится выше полноты покрытия.
**Состояние:** этапы 0, A, B, C, E исполнены. Этап D исполнен в части бампа и $0-верификации; **живая проба провода НЕ проводилась** — см. §5.
---
## §0. Строка 112 — пять шиппинг-конфигов
Флип `stages[draft].reasoning: "off" → "low"` в пяти файлах:
| файл | строка | стадия | модель |
|---|---|---|---|
| `pipeline-c1.yaml` | 55 | draft | deepseek-v4-flash |
| `pipeline-c2.yaml` | 28 | draft | deepseek-v4-flash |
| `pipeline-arm-mistral.yaml` | 32 | draft | deepseek-v4-flash |
| `pipeline-arm-glm.yaml` | 36 | draft | deepseek-v4-flash |
| `pipeline-arm-deepseek-pro.yaml` | 36 | draft | deepseek-v4-flash |
**Проверено исполнением** (одноразовый пробник по `LoadPipeline` + `Capability.applyToBody`, удалён после снятия): все пять драфтов дают тело `{max_tokens, reasoning_effort:low, temperature}`, `ThinksOnWire=true`.
### ⚠ НАХОДКА-1: адрес `pipeline-c2.yaml:39` в промте и в строке 112 ОШИБОЧЕН
Оба документа адресуют его как «черновую стадию». Это стадия **`select`, роль `judge`, модель `glm-5`**. У glm-5 `capabilities.reasoning.control = extra_body_disable`, то есть `"off"` здесь — живой выключатель, а не отсутствие ключа. Контрфакт снят тем же пробником:
```
glm-5 off → {max_tokens, temperature, thinking:{type:disabled}} ThinksOnWire=false
glm-5 low → {max_tokens, temperature} ThinksOnWire=true
```
`on_extra_body` у glm-5 не задан, поэтому любой `low|medium|high` (`llm/capability.go` `applyToBody`, ветка `ReasoningExtraBodyDisable`) не шлёт ничего и отдаёт thinking вендор-дефолту = ON ⇒ таймауты ×3 (эмпирика полигона, `pipeline-arm-glm.yaml:10`). **Флип дал бы ровно обратное тому, ради чего заведена строка 112.** Не тронуто; в файл вписано предупреждение с причиной. **Строку 112 трекера нужно поправить — это не моя зона.**
### Решения, принятые явно (как требовал промт)
- **`pipeline-arm-mistral.yaml:44` (editor, mistral-large-2512) — оставлено `"off"`.** Модель несёт `ReasoningNone` и не является reasoning-моделью (`models.yaml` note) ⇒ ключ не шлётся, вендор-дефолт без размышления, стена «reasoning ест max_tokens» неприменима. `"low"` здесь = купить размышление, которого не было, без замера. Триггер пере-решения вписан в файл.
- **Редакторские `off` не тронуты:** `c1:83`, `c2:56`, `arm-glm:45`, `arm-deepseek-pro:45`.
- **Хоп эскалации наследует ручку** (`stagerun.go` `attemptRequest` берёт `st.Reasoning` и для hop-модели). На `deepseek-v4-pro` вендор маппит `low→high` (quirks §3а(ii)) ⇒ фактический эффорт хопа не меняется, меняется только тело запроса.
- **`tmctl status --config` на конфигах не гонялось** — промт предписывает это, но команда принимает **book.yaml** и открывает БД (`main.go:200` `NewReadOnlyRunner`), а не pipeline-конфиг. Верификация — существующие тесты, которые грузят все пять файлов: `config/echo_mine_test.go`, `prompt_pack_test.go`, `content_labels_test.go`.
---
## §1. Этап A — замер ($0, ничего не чинилось)
**Инструмент:** golangci-lint **2.12.2** (пиновано; бинарь в `~/.local/bin`, вне репо; sha256 тарбола сверен с опубликованным `golangci-lint-2.12.2-checksums.txt`). Дерево: **205** `.go`-файлов, из них **115** тестовых.
```
golangci-lint run --default=standard --enable=misspell --enable=nolintlint \
--timeout=15m --max-issues-per-linter=0 --max-same-issues=0 ./...
```
**435 находок.** СТОП-порог промта (>1000) не достигнут.
| линтер | всего | prod | test |
|---|---|---|---|
| errcheck | 413 | 124 | 289 |
| staticcheck | 17 | 5 | 12 |
| misspell | 4 | 1 | 3 |
| unused | 1 | 1 | 0 |
| govet · ineffassign · nolintlint | 0 | 0 | 0 |
`govet 0` подтверждает базу «`go vet` чист». `nolintlint 0` — потому что `//nolint`-директив в дереве **ноль** (`grep -rn "//nolint" --include=*.go . | wc -l` → 0).
**errcheck по классам:** `Close` **294** · `fmt.Fprint*` **91** · прочее **28** (8 prod / 20 test). То есть 93% errcheck — два идиоматических класса.
**staticcheck по правилам:** QF1001 6 · SA1024 5 · ST1018 2 · QF1003 1 · S1021 1 · S1002 1 · QF1004 1.
**Дополнительные замеры (кандидаты для этапа B):**
```
exhaustruct 988 bodyclose 0 copyloopvar 0 durationcheck 0 makezero 0
errorlint 3 nilerr 3 rowserrcheck 2 sqlclosecheck 1
```
### ⚠ НАХОДКА-2: число `exhaustruct` в §5.5 отчёта EFFORT_HANDLE — это СУЖЕННОЕ число, не глобальное
Глобально **988**, а не 16. Суженный до двух типов, ради которых он предлагался, — **17** (`include: config\.Stage`, `pipeline\.Request`), что сходится с «16, из них 15 в тестах». Из 17 **шестнадцать — тестовые литералы, а семнадцатый — САМ ШОВ** (`config/internal_call.go:53`, `InternalCall.Stage()`), единственное разрешённое место постройки. Вывод промта («глобально не включать») подтверждён, но обоснование должно опираться на 988, а не на 16.
---
## §2. Этап B — состав `.golangci.yml` (каждое правило обосновано числом)
Файл: `backend/.golangci.yml` (первый в проекте). `golangci-lint config verify` → чисто.
**ВКЛ:** `errcheck` 413→6 после исключений · `govet` 0 · `ineffassign` 0 · `staticcheck` 17→11 · `unused` 1 · `errorlint` 3 · `misspell` 4→0 · `nilerr` 3 · `rowserrcheck` 2 · `sqlclosecheck` 1 · `nolintlint` 0 · `bodyclose`/`copyloopvar`/`durationcheck`/`makezero` по 0 (бесплатны сегодня и не дают регрессировать; `bodyclose` сторожит провод к провайдерам).
**ВЫКЛ с причиной (анти-хак-правило владельца: спорное правило гасится в КОНФИГЕ, а не обходом в коде):**
- **`exhaustruct`** — 988 глобально; суженный флагует сам шов, а различать шов и обход умеет только анализатор §6.
- **`QF1001`** (6, «применить закон де Моргана») — текущая форма условий повторяет прозу над ними; переписывание ради линтера читается хуже правила, которое кодирует. Вкусовое, не класс дефектов.
- **`.Close`/`.Flush`** (294) — все 33 продовых места перечислены и проверены поштучно: это читатели и хендлы БД. Единственный в репо писатель с flush-on-Close (`chunktest/epub.go`, `zip.Writer`) свой `Close` **проверяет**, поэтому исключение не может спрятать потерянную запись.
- **`fmt.Fprint*`** (91) — рендер CLI в stdout; сломанный пайп на `tmctl status | head` не то, на что CLI может отреагировать.
- **`tx.Rollback`** (5) — идиома «откатить, если не закоммичено»; на счастливом пути ошибка = `sql.ErrTxDone` по построению.
- **`h.Write`** (2) — `hash.Hash.Write` документирован как никогда не возвращающий ошибку.
- **`initals`** — намеренная опечатка-фикстура: тесты лангпака пишут её в файл, чтобы доказать, что парсер ругается на неизвестный ключ. Исправление удалило бы тест.
- **`misspell.locale` НЕ выставлен.** Замерено: `locale: US` даёт **309** находок вместо 4 — он срабатывает на русских комментариях.
**Режим внедрения: big-bang.** Критерий промта («всё с ≤20 находками») выполнен с запасом: после конфига остаётся **27** находок в **19** файлах, что ниже СТОП-порога «>30 файлов разом». `--new-from-rev` не понадобился.
### Что из 27 оказалось настоящими дефектами, а не косметикой
| место | что | почему это дефект |
|---|---|---|
| `pipeline/banknote.go:303` | `applyBanknote` мёртв | все вызовы (2 прод + 5 тест) идут в `applyBanknoteWithEntries`; удалён, док слит с живой функцией |
| `cmd/tmctl/backup.go:57` | `os.Stat` → любая ошибка = «свежая книга» | отказ по правам молча пропускал бэкап-гард перед платным прогоном. Стало `errors.Is(err, fs.ErrNotExist)` |
| `pipeline/mining.go:384` | то же на авто-банке | нечитаемый файл молча ронял намайненные строки |
| `cmd/tmctl/dotenv.go:55` | `os.Setenv` глотал ошибку | битая строка `.env` = ключ не загрузился, всплывало позже как 401 без причины. `loadDotEnv` теперь возвращает ошибку |
| `store/voice_test.go:41,44` | `raw.Query` с результатом в `_` | течёт `*sql.Rows` |
| `store/glossary.go:147` | три ручных `rows.Close()` | вынесено в `priorRenderings``defer` стал корректен, потому что жизнь rows теперь ограничена функцией и закрывается ДО `DELETE`-ов на том же tx |
| `lang/langpack.go:338` | `%v` вместо `%w`, и `serr` мог быть nil | разделено на два случая: нечитаемо (`%w`) и не каталог |
| `chunk/ingest.go:829` | `z.Err() != io.EOF` | обёрнутая ошибка прошла бы мимо |
| `miner/miner_alias.go:161` | `var find func(...)` + присваивание | замыкание НЕ рекурсивно (итеративное сжатие путей), так что staticcheck прав |
**Одно место закрыто `//nolint` с причиной, а не правкой:** `synthetic_stage_seam_test.go``nilerr` на пропуске нечитаемого файла. Этот код удалён этапом E вместе с обходом.
**Итог:** `golangci-lint run`**0 issues**.
---
## §3. Этап C — `make battery`
`backend/Makefile`, цели `build · vet · fmt · lint · test · battery · battery-stand · tools-check`.
- `battery` = герметичная часть: `build``vet` (оба тег-набора, с `-vettool`) → `gofmt``lint``test -race -count=1`, и в конце **НАЗЫВАЕТ пропущенные тесты** — молчаливый скип читается как покрытие.
- `battery-stand` = то же + `TM_MINER_PARITY=1 TM_CHECKER_LABELS=1`, где отсутствие данных **падает**, а не скипается.
- `fmt` проверяет ПУСТОТУ вывода `gofmt -l`: сама команда выходит с 0, даже когда перечисляет файлы.
- `tools-check` требует ровно golangci-lint 2.12.2 и не ставит его сам: находки версионно-зависимы, а гейт, меняющийся под тобой, не гейт.
**CI-воркфлоу НЕ написан — решение владельца 03.08.** Установлено: удалёнка `ssh://git@git.vojo.chat:2222/heaven/textmachine.git`**Forgejo 10.0.3** (`GET /api/v1/version``{"version":"10.0.3+gitea-1.22.0"}`), каталогов `.forgejo/` и `.github/` в репо нет, раннер не подтверждён. Файл лежал бы вне зоны `backend/`. Точка входа до появления раннера — `make battery`.
---
## §4. Этап E — гарды на `x/tools/go/analysis`
**Новое:** `backend/internal/archguard/` (три анализатора) + `backend/cmd/tmvet/` (multichecker). Запуск — `go vet -vettool=bin/tmvet ./...` из `make battery`, оба тег-набора.
| анализатор | инвариант |
|---|---|
| `stageseam` | `config.Stage` строится только через `config.InternalCall.Stage()` |
| `requestseam` | `pipeline.Request` собирается только в `Runner.attemptRequest` |
| `egressseam` | к провайдеру ходит одно место — `stagerun.go runAttempt` (Д7) |
### Паритет посадками (`analysistest`, `internal/archguard/testdata/`)
| # | посадка | чем била предшественника |
|---|---|---|
| 1 | `config.Stage{…}` | — (исходные три дефектных сайта) |
| 2 | `&config.Stage{…}` | байтовый скан |
| 3 | `[]config.Stage{{…}}` | элидированный тип элемента: `CompositeLit.Type == nil` |
| 4 | `map[string]config.Stage{k:{…}}` | то же |
| 5 | `var s config.Stage` + присваивание полей | литерала нет вовсе |
| 6 | `new(config.Stage)` | то же |
| 7 | алиас импорта `cfg "…/config"` | матч по ИМЕНИ пакета |
| 8 | дот-импорт → `Stage{…}` | нет квалификатора |
| 9 | `type myStage = config.Stage` + `myStage{…}` | алиас типа |
| 10 | `type derivedStage config.Stage` | определённый тип поверх Stage |
| 11 | hoisted `req := pipeline.Request{…}` | скан по `RequestHash(Request{…})` |
| 12 | `[]pipeline.Request{{…}}` | элидированный элемент |
| 13 | алиас импорта пакета pipeline | матч по имени |
| 14 | `c.Complete(…)` через интерфейс | — |
| 15 | `h.Complete(…)` через конкретный адаптер | — |
**Негативные контроли в тех же фикстурах** (гард, который флагует всё, бесполезен так же, как молчащий): вызов через шов `InternalCall{…}.Stage()` · `[]config.Stage` как ТИП поля/параметра · пустой `[]config.Stage{}` · `pipeline.Request` как параметр · **чужой метод с именем `Complete`** — байтовый скан ловил его, типовой не ловит.
```
go test ./internal/archguard/ -count=1 → ok
go vet -vettool=bin/tmvet ./... → (пусто)
```
**Что реально выиграно.** Не «алиасы закрываются по построению» — предшественник резолвил импорты по ПУТИ и алиас уже ловил. Выиграно два конкретных свойства: (а) `TypesInfo.TypeOf` даёт тип **элидированного** литерала, поэтому `[]T{{…}}` ловится без ручного разворачивания обёрток; (б) egress-инвариант различает НАШ `Complete` от чужого одноимённого метода — байтовый скан не мог и вынужден был исключать собственный исходник из совпадения с собственным же паттерном.
### ⚠ Слепая зона `go/packages` — решение принято ЯВНО
Предшественники ходили по ВСЕМУ репозиторию, доставая `eval/` — Go-файлы вне модуля, где жил исторический четвёртый дефектный сайт. Анализатор так не может: ему нужен собираемый пакет.
**Замерено перед отказом:** в `eval/` три Go-программы — `softsign-decl/main.go`, `pkg7/termharness/main.go`, `pkg7/minerharness/main.go`. Одна (`termharness`) импортирует `internal/config` и `internal/pipeline`, но **ни одна не строит `config.Stage` или `pipeline.Request`**, и ни одна не собирается (`go.mod` в `eval/` нет: `go build``cannot find main module`). То есть охват сторожил ничто.
**Решение (владелец 03.08):** охват `eval/` снят. Полигон — по CLAUDE.md чернорабочая зона; гейтить его не стоит ограничения на форму гарда. Если харнесс там когда-нибудь потребует покрытия, честная починка — `go.mod` в `eval/`, а не синтаксический обход здесь. Записано в доккомменте пакета.
### Снесено / оставлено
**Снесено** (после 15/15 у анализатора, последним шагом):
- `synthetic_stage_seam_test.go`: `TestSyntheticStageSeamIsSingle`, `TestRequestIdentitySeamIsSingle`, `walkGoFiles`, `fileImports`, `namedType`, `namedTypeExact`, `namedTypeLocal`, `walkedFileFloor`, `configPkgPath`, `pipelinePkgPath`**396 → 143 строки**;
- `content_routing_test.go`: `TestProviderEgressSeamIsSingle` (байтовый гард Д7) — **47 строк**.
**Оставлено осознанно:**
- `TestInternalCallDecidesEveryStageField` — рефлексионный, не про обход дерева (промт прямо запрещает трогать);
- `TestBankProbeAndAttemptAddressOneCheckpoint`, `TestReasoningIsPartOfTheRequestIdentity`, `TestRepairStageTakesItsEffortFromItsOwnGate` — поведенческая половина: анализатор не отвечает на вопрос, доносит ли шов ручку до денежного пути.
**Пол «я реально видел файлы»** (`walkedFileFloor = 50`) эквивалента не получил и не нуждается: `go vet` перечисляет пакеты сам и падает, если пакет не грузится, — обход не может тихо разобрать ноль файлов.
---
## §5. Этап D — бамп зависимостей
```
go get golang.org/x/net@latest golang.org/x/text@latest gopkg.in/yaml.v3@latest modernc.org/sqlite@latest
golang.org/x/net v0.26.0 => v0.57.0
golang.org/x/text v0.38.0 => v0.40.0 ← MVS тянет принудительно
golang.org/x/sys v0.44.0 => v0.47.0
modernc.org/libc v1.73.4 => v1.74.1
modernc.org/sqlite v1.53.0 => v1.55.0
+ golang.org/x/tools v0.48.0 (под этап E)
```
### ⚠ НАХОДКА-3: бамп сдвинул денежную ось — и это был ВЫБОР, а не принуждение
**ПОПРАВКА по адверсариальному ревью (03.08).** Изначально здесь стояло «`x/net` нельзя взять отдельно — MVS тянет `x/text` ПРИНУДИТЕЛЬНО». Это верно только для взятого мной `@latest`. Замерено ревьюером на чистой копии HEAD: `x/tools@v0.45.0` тянет `x/net v0.54.0`, чей `go.mod` требует `x/text v0.37.0`**ниже нашего v0.38.0, то есть `x/text` НЕ двигается**, и весь стек анализатора на этой комбинации собирается и проходит. Значит перекупку снапшота вызвал мой выбор `@latest`, а не потребность этапа E. Владелец санкционировал бамп до фронтира отдельно и осознанно (03.08, книг в работе нет ⇒ цена нулевая), но формулировка «принудительно» была неверна и снята. Индустриальный способ навсегда развязать инструментальные зависимости от денежной оси — отдельный `tools/go.mod`; носитель заведён в §7.
Второй ревьюер независимо **доказал, что бамп не меняет поведения**: NFC/NFKC байт-идентичны между x/text v0.38.0 и v0.40.0 на всех 1 112 064 скалярных значениях Unicode и на 400 000 случайных последовательностей 16 рун (одинаковый SHA-256 под обеими версиями). То есть сдвиг снапшота — чистая смена версионной метки.
`x/net` при этом действительно тянет `x/text`. А `x/text` вшит в `memoryNormAlgoVersion` (`internal/text/norm.go:70`) специально ради этого случая. Сработал собственный тревожный тест проекта:
```
xTextVersion = "v0.38.0" but go.mod pins golang.org/x/text "v0.40.0" — bump the constant IN LOCKSTEP
```
Константа обновлена, следом обновлены два её зеркала: `text/enclosure_test.go:34` (полная строка версии) и `membank/memory_f1_test.go` (два хеша `ComputeVersionScoped`).
**Заявление трекера про `x/tools` подтверждено исполнением.** Проверено на КОПИИ реального модуля: добавление `x/tools/go/analysis` поднимает `x/net 0.26.0 → 0.54.0`. (Чистый пробный модуль этого не показывает — там `x/tools` приходит один; связка возникает именно на нашем графе.) То есть этапы D и E действительно сцеплены.
### Голден: что именно сдвинулось
Голден пере-захвачен (`TM_UPDATE_GOLDEN=1`) — санкция владельца 03.08. **Дифф разобран механически, не глазами:**
```
изменённых строк: 90
изменившихся по причине, ОТЛИЧНОЙ от hex-хеша: 0
content_hash идентичен n=32 disp идентичен n=68
flag идентичен n=84 cost идентичен n=102
finish идентичен n=86 degraded идентичен n=52
tokens идентичен n=52 detail идентичен n=72
escalated идентичен n=68
```
`content_hash` — это `msgsContentHash`, подпись отрендеренных сообщений. Её неизменность означает, что **байты, уходящие модели, те же**. Сдвинулись только версионные идентификаторы: `memory_version``snapshot_*``RequestHash` (в него входит `SnapshotID`, `render.go:309`) → `final_hash`. **Таблицы Unicode в x/text 0.40 не изменили ни одного поведения на этой фикстуре.**
### ⚠ НАХОДКА-4 (ревью): бамп `x/net` РЕАЛЬНО меняет байты на проводе
Ревьюер поднял h2-слушателя с TLS+ALPN и прогнал через него дословную копию `keepAliveHTTPClient`
(`internal/llm/httpllm.go:79-99`) под обеими версиями. Два воспроизводимых расхождения:
**(а) SETTINGS-фрейм клиента вырос 18 → 24 байта** — появился `SETTINGS_MAX_FRAME_SIZE` (id `0x0005`) = 16384:
```
v0.26.0 000200000000 000400400000 000600a00000
v0.57.0 000200000000 000400400000 000500004000 000600a00000
```
16384 — и так протокольный дефолт, то есть семантически инертно, но это НОВАЯ байтовая последовательность.
**(б) Изменился ПОРЯДОК заголовков запроса** (HPACK, декодировано):
```
v0.26.0: :authority :method :path :scheme authorization accept x-title content-type content-length
v0.57.0: :authority :method :path :scheme accept x-title content-type authorization content-length
```
Набор и значения те же, регистр в обеих строчный (h2 это предписывает) — **смены регистра нет, изменился только порядок**.
**Почему это важно.** Порядок заголовков и раскладка SETTINGS — ровно те входы, по которым фронты
бот-детекции (класса Akamai/Cloudflare) строят HTTP/2-фингерпринт клиента. Для провайдера за таким WAF
бамп меняет наш отпечаток. **Отреагирует ли конкретный провайдер — БЕЗ живого вызова не установить.**
Это не дефект, это неразрешённый вопрос — и ровно тот, ради которого промт требовал живую пробу.
Заодно из 78 http2-коммитов в диапазоне: «do not retry RoundTrip after peer sends a stream protocol
error» · «don't PING a responsive server when resetting a stream» · «detect hung client connections by
confirming stream resets» — семантика ретраев и разрыва изменилась ПОД нашим собственным `retryLoop`.
RFC 9218 priority у нас НЕ активен: `client_priority_go126.go` возвращает `clientPriorityDisabled()=true`
для `!go1.27` именно чтобы не менять поведение при импорте.
⚠ Это противоречит инварианту, записанному в `internal/llm/httpllm.go:75-78` («провод … не меняется, ноль
байт») — он верен для полей пинга, но не для бампа, который их привёз. Строку надо поправить.
### ⚠ НАХОДКА-5 (ревью): вердикт-несущее использование x/text НЕ завёрнуто в версию
Бамп x/text оказался громким **по случайности**. Единственная сворачивающая его версия —
`memoryNormAlgoVersion`, и она доезжает до снапшота только потому, что `membank.ComputeVersionScopedIn`
(`internal/membank/memory.go:390`) пишет `text.NormVersion()` безусловно. Всё остальное сворачивает
только stdlib-овский `unicode.Version` либо ничего:
| Версия | Сворачивает | Какое x/text-поведение покрывает |
|---|---|---|
| `checks.SanitizerVersion` (`sanitizer.go:119`) | **ничего** | `width.Fold` (`:687`), `norm.NFC` (`:734`) |
| `checks.CheapGateVersion` (`cheapgates.go:99`) | `unicode.Version` | NFC-свёртку (по собственному комментарию `:59`) |
| `pipeline.chunkerVersion` (`render.go:53`) | `unicode.Version` | `text.NormalizeSource``norm.NFC` (`source.go:29`) |
`detectCJKLeak` сворачивает ПЕРЕД решением (`sanitizer.go:211`), поэтому смена `width.Fold`
**переворачивает вердикт** → флаг → ремонт/эскалация → живые деньги, а `SanitizerVersion` стоит на месте.
И сама `SanitizerVersion` сворачивается только при ВКЛЮЧЁННОМ гейте (`snapshot.go:97-103`).
Резче: `ExportNormalize` исполняется на КАЖДОМ отгружаемом финале, зависит от `width.Fold` + `norm.NFC`,
и при этом объявлена «НЕ входом снапшота/вердикта» (`sanitizer.go:709`). Смена x/text там поменяла бы
экспортируемые байты, не сдвинув ни одного хеша. Латентно, не сработало — но носитель нужен.
### ⚠ НАХОДКА-6 (ревью): `xTextVersion` пинит строку go.mod, а НЕ скомпилированные таблицы
`golang.org/x/text/unicode/norm` возит ДВА набора таблиц, выбираемых ТУЛЧЕЙНОМ:
`tables15.0.0.go //go:build !go1.27` и `tables17.0.0.go //go:build go1.27`. Сегодня
`go list -f '{{.GoFiles}}'` подтверждает: под go1.26.5 компилируется `tables15.0.0.go`.
**Тулчейн Go 1.27 переключит x/text на таблицы Unicode 17.0.0, не тронув `xTextVersion`.**
Комментарий `norm.go:62-68` утверждает, что константа делает собственную Unicode-редакцию x/text громкой —
она этого не делает. Вероятно спасает смежное: stdlib-овский `unicode.Version` на том же тулчейне тоже
сдвинется, а он свёрнут. Но это совпадение, а не механизм; стоит превратить в явную проверку.
### Дифференциал нормализации: РАЗРЕШЕНО, поведение не изменилось
Исходники `unicode/norm` действительно правились (`forminfo.go`, `iter.go`, `normalize.go`: путь
невалидного UTF-8 переписан с `sz==0 → sz=1` на явный сентинел `Properties{flags:0x80}` + маску `& 0x3f`).
Таблицы `norm` и весь пакет `width` — байт-идентичны. Прогон нормализаторов репо и всех примитивов x/text,
которые дёргает `internal/checks`, под обеими версиями:
| корпус | входов | результат |
|---|---|---|
| каждое скалярное значение Unicode, отдельно + после 5 баз + перед 6 комб. знаками + 46 фикстур | **13 344 816** | идентично |
| невалидный/усечённый UTF-8 (все 1-байтовые, все 2/3-байтовые лиды, усечения в 7 префиксах × 5 суффиксах, оверлонги, суррогаты, вне диапазона, в CJK/кириллица/хангыль-контексте) | **263 560** | идентично |
| `~/books/gu-zhenren/labels/raw/corpus.jsonl` — значения, строки, предложения, слова, руны, пары и тройки рун | **3 561 250** входов / 12 133 355 рун / **2 568 различных кодпойнтов** | идентично |
**Ноль расходящихся кодпойнтов.** Это несравнимо сильнее моего собственного доказательства по голдену.
### Голден: моя проверка смотрела не на то поле
Мой масочный дифф воспроизводится, но `content_hash/disp/flag/cost/...` — не тот набор. Поле, способное
спрятать смысловое изменение под видом хеша, это **`final_hash`**: для двух санитайзерных строк он равен
`sha256("tm-sanitized-v1\0" + reqHash + "\0" + stripped)` (`stagerun.go:355`), то есть сворачивает
ФАКТИЧЕСКИЙ экспортный текст. Изменившийся текст и смена версии в нём неразличимы.
Вопрос закрывает то, что голден пишет и сырой текст: `final_text=%q` (`golden_test.go:217`),
`stage_text=%q` (`:223`) и 18 тел запросов (`:280`) — **эти 68 строк байт-идентичны**, проверено прямо.
Голден НЕ записывает (`golden_test.go:174-176`) латентность, таймстемпы, пути, URL — и, не будучи
перечисленным, **заголовки запроса и фрейминг транспорта**. Поэтому находка-4 ему невидима by construction.
### ⚠ Живая проба провода НЕ проводилась
Промт требует после бампа `x/net` мини-вызов на КАЖДОГО провайдера (потолок $0.50) со сверкой тел, ключей, `reasoning_content`, `finish_reason` и отсутствия новых заголовков против `00-provider-quirks.md`. **Не сделано:** отдельной санкции на трату не было, а ключи провайдеров лежат в `backend/.env`, который читать запрещено.
Чем закрыто вместо неё ($0): полная батарея под `-race` зелёная · голден разобран построчно, включая сырой текст · нормализация доказана идентичной на 17 млн входов (выше) · `sqlite` 1.55 проверен по changelog: новые DSN-сокращения нас не касаются, репо использует только канонические `_pragma(...)`/`_txlock`.
**После находки-4 живая проба нужна СИЛЬНЕЕ, чем считал промт.** httptest говорит HTTP/1.1 без ALPN, поэтому путь h2 в тестах не исполняется вовсе, а на h2 отпечаток клиента ИЗМЕНИЛСЯ (порядок заголовков + SETTINGS). Установить реакцию провайдеров можно только вызовом. **Носитель обязателен.**
---
## §6. Батарея
```
make battery-stand → EXIT=0
```
- `golangci-lint` — **0 issues**
- `go vet` + `go vet -tags live`, оба с `-vettool=bin/tmvet` — чисто
- `gofmt -l` — пусто
- `go test ./... -race -count=1` — 14 пакетов ok, 0 FAIL, 0 DATA RACE
**Стенд-данные на машине присутствуют**, поэтому корпусные тесты реально исполнились. Замороженные числа держатся:
```
PARITY: n=13618 catastrophe{方源:0 蛊:1 蛊师:2 古月:22} recall@proposed=0.9655 (56/58 GT)
K6 BASELINE: tp=1 fp=14 fn=0 tn=243 disp=2
K6 AFTER (stemmer + #10): tp=1 fp=6 fn=0 tn=251 disp=2
k2 92 34/1/6/51 · k4_inverse 75 3/0/1/71 · k4a 348 0/0/0/348 · k4b 146 10/1/36/99
```
**Артефакт контраста восстановлен.** `eval/exp16/data/jieba_dict_general_zh.txt` отсутствовал → `TestMinerFullBookParity` скипался. Пере-собран из `jieba==0.42.1`; **полный sha256 `7197c3211ddd98962b036cdf40324d1ea2bfaa12bd028e68faa70111a88e12a8`** совпал с урезанным пином `7197c321…e12a8` в `docs/experiments/16-bank-mining.md:183`. Файл остаётся вне git (`eval/.gitignore:4`).
В `make battery` (без стенда) скип теперь ровно один — `TestHelperKillLoop` (подпроцессный хелпер, by design).
---
## §7. Найдено, не починено — нужны носители
| # | находка | почему не мной |
|---|---|---|
| 1 | **Строка 112 трекера несёт ошибочный адрес `c2:39`** (§0) | `docs/` — зона оркестратора |
| 2 | **Живая проба провода после бампа `x/net`** (§5) | нужна санкция на трату + ключи |
| 3 | **Урезанный sha256 в `16-bank-mining.md:183`** — полный записан в §6 | зона оркестратора |
| 4 | **`jieba==0.42.1` не в `eval/requirements.txt`** — это build-time зависимость артефакта, не runtime (ни один скрипт полигона `jieba` не импортирует) | зона полигона |
| 5 | **Число `exhaustruct` в §5.5 EFFORT_HANDLE читается как глобальное** (§1) | зона оркестратора |
| 6 | **`TestCheckerLabelsCandidates` не содержит ни одного ассерта** — не может упасть; сегодня это дизайн-REPL в одежде теста | отдельная задача, не в скоупе промта |
| 7 | **`TestMemoryVersionPinsTheUnverifiedRenderRevision` не различает** «изменился рендер» и «изменилась версия нормализации» — оба двигают его литералы | пин работает, но его сообщение вводит в заблуждение |
| 8 | **`buildCheckers` жёстко берёт `lang.Load(root,"zh","ru")`**, а бейзлайны ключуются по классу, не по паре | приезд ja-ru потребует правки Go — против мандата общности |
| 9 | **`backend/README.md:3`** заявляет, что границу импортов «проверяет `go list -deps`» — не автоматизировано ничем | кандидат в четвёртый анализатор `archguard` |
| 10 | **Инструментальные зависимости в главном модуле.** `x/tools` нужен только для `cmd/tmvet`, но живёт в `backend/go.mod` и потому участвует в MVS вместе с `x/net`/`x/text`. Штатное лекарство — отдельный `tools/go.mod` (или `tool`-директива Go 1.24 в отдельном модуле): тогда линтинг физически не может двинуть снапшот | требует решения по структуре модулей |
| 11 | **`modernc.org/sqlite 1.53.0 → 1.55.0` не требовался ничем** — приехал вместе с `@latest`. Это драйвер под денежным леджером и чекпойнтами | входит в то же решение по бампу |
| 12 | **Анализатор не ловит четыре формы**, дающие тот же дефект D39.87: встраивание структуры, `make([]config.Stage, 1)` + присваивание полей, дженерик-ноль, — предшественники их тоже не ловили (не регрессия). Класс закрывается НЕ длинным switch, а типом: сделать нулевое значение `config.Stage` непригодным вне своего пакета | отдельная задача |
| 13 | **`go vet` покрывает только теги `default` и `live`.** Файл за любым другим build-тегом невидим (старый обход парсил исходники независимо от тегов). Сегодня других тегов в репо нет | политика, не свойство |
---
## §8. Что осталось от промта неисполненным
- **Живая мини-проба провода этапа D** (§5) — единственный пункт промта, не закрытый ни исполнением, ни обоснованным отказом. Нужна санкция.
- **CI-воркфлоу** — не написан по решению владельца 03.08 (§3), не по недосмотру.
- **`tmctl status --config` на пяти конфигах** — команда для этого не предназначена (§0); заменено существующими тестами загрузки.
---
## §9. Адверсариальное ревью пака (03.08, по требованию владельца)
Четыре независимых ревьюера по диффу: корректность правок · паритет анализатора · денежная ось и детерминизм · индустриальность. Каждый проверял ИСПОЛНЕНИЕМ; двое реконструировали снесённые гарды из истории и стравили их с новым на одних подсадках.
**Найдено и починено (всё — в моём коде):**
| # | дефект | статус |
|---|---|---|
| 1 | `egressseam` обходился локально объявленным интерфейсом с тем же методом: `Obj().Pkg()` становился пакетом потребителя. Байтовый гард это ловил — прямая регрессия | починено сканом СИГНАТУРЫ (подставимый интерфейс обязан упоминать типы `llm`), посадка 17 |
| 2 | Исключения потеряли сегмент `/backend/` — вложенный `internal/` и второй модуль `platform/` наследовали их | восстановлен полный путь |
| 3 | Внешние тестовые пакеты (`…/config_test`) не покрывались пропуском пакета ⇒ ложные срабатывания | `inPackage()` со срезом `_test` |
| 4 | Конверсия `config.Stage(twin{…})` не ловилась — ветка `CallExpr` смотрела только на `new` | добавлена, посадка 16 |
| 5 | `new` опознавался по ИМЕНИ идентификатора, хотя репо шадовит встроенные имена в 22 местах | сверка с `types.Universe.Lookup("new")` |
| 6 | **Инвариант ушёл из `go test`** — держался только на `make vet` | `shippingtree_test.go`: те же `Analyzers()` через `checker.Analyze` по всему модулю; доказано подсадкой |
| 7 | Пол «я реально видел файлы» не был восстановлен: пустой `Analyzers()` оставлял всё зелёным | `packageFloor` + ассерт длины `Analyzers()` + проверка ошибок загрузки пакетов |
| 8 | `go vet -vettool` ЗАМЕЩАЕТ штатный набор vet, а комментарий утверждал обратное | `make vet` гоняет оба прохода; комментарий исправлен |
| 9 | Тест-исключение errcheck из-за необязательной группы в regex глушило безымянный класс во всех 115 тестах | сужено; вскрылись 12 скрытых находок — все `runToSignatureStop`, исключены точечно с причиной |
| 10 | `$(GO)fmt` — склейка строк: `make GO=go1.26.5 fmt` звал `go1.26.5fmt` | `gofmt` напрямую |
| 11 | `GO_MIN_VERSION` объявлен и не использован, шапка заявляла «тулчейн пиноват» | добавлена проверка версии Go в `tools-check` |
| 12 | Сбор скипов глушил stderr и терял свой код возврата: падение теста БЕЗ `-race` прошло бы незамеченным | падение пробрасывается |
| 13 | `loadDotEnv`: `os.Open` глотал ЛЮБУЮ ошибку (файл под 0000 = ключи молча не загружены) | различает `fs.ErrNotExist`, остальное — предупреждение |
| 14 | **Моя же регрессия:** возврат ошибки из `loadDotEnv` ронял `status`/`report`/`export` на кривой строке `.env` — против D20.4 («аудит-поверхность не требует ключей»), и последующие валидные строки не применялись | предупреждает, не падает; остальные строки грузятся |
| 15 | Обоснование `misspell.locale` было фактически неверным (не русские комментарии, а en-GB↔en-US в английской прозе; 0 против 311, а не 4 против 309) | переписано по замеру |
| 16 | Исключения `h.Write`/`tx.Rollback` ключевались на ИМЕНИ переменной — `hasher.Write` дал бы находку и толкнул к переименованию ради линтера | ключ по методу |
| 17 | Числа в комментариях конфига поданы настоящим временем, хотя всё названное уже починено | помечены как провенанс |
| 18 | Оборванный комментарий-хвост от снесённого гарда | убран |
**Подтверждено чистым:** `analysistest` честен в обе стороны (сломанный анализатор роняет тест мутацией `isNamed`; негативные контроли ловят лишнее срабатывание) · `make battery` анализатор действительно несёт (проверено подсадкой) · ложных срабатываний на `[]config.Stage` как типе нет, и новый анализатор ЧИНИТ старое ложное срабатывание на `[]config.Stage{}` · обе правки строковых литералов байт-эквивалентны (дубль ASCII-пробела в cutset-МНОЖЕСТВЕ; `\x1f` = тот же байт) · `priorRenderings` поведенчески идентичен, пустая карта не nil, rows закрываются до `DELETE` · `preflightBackup`/`loadAutoBank` — новые ошибки возникают до любой траты · `miner_alias.go` замыкание действительно не рекурсивно · зона соблюдена, генеративность не нарушена.
**Замеры пропущенных линтеров** (report-only, на этом дереве): gosec 250 (шум: G104 115 — дубль errcheck, G304 56, G306 53) · revive 80 · perfsprint 32 · exhaustive 11 · noctx 11 · prealloc 10 · gocritic 7 · errname 5 · **wastedassign 2** · **unconvert 1** · usestdlibvars/predeclared/nakedret 0. Ревьюер справедливо отмечает: `unconvert` (1) и `wastedassign` (2) дешевле уже включённых `errorlint` (3) и `nilerr` (3) — по собственному правилу конфига их стоит включить. Носитель — эта строка.

View file

@ -180,8 +180,8 @@ A1A3+абл ($0) → алиас ($0) → A6/A6b (стенд $0) → cold-star
- Код `miner-v1` (SHA256): exp16_common `92bc737a` · detectors `89059e53` · patterns `7ac8240c` ·
spread `5422415f` · palladius `a071295e` · arms `a5ed2ae2` · run_arms `c4e3e17d` · banknote `069ad8fe` ·
coldstart_gen `70c8d9c1` · alias `354172fa` · canon `e947ebf3` · reaudit_14b `8d95ad58` · length_sweep `59a60b15`.
- Словарь-артефакт (контраст): `data/jieba_dict_general_zh.txt` **sha256 `7197c321e12a8`** (jieba 0.42.1
`dict.txt`, общий zh word-freq; вне git — воспроизводим из jieba 0.42.1, SHA пиннится здесь).
- Словарь-артефакт (контраст): `data/jieba_dict_general_zh.txt` **sha256 `7197c3211ddd98962b036cdf40324d1ea2bfaa12bd028e68faa70111a88e12a8`** (jieba 0.42.1
`dict.txt`, общий zh word-freq; вне git — воспроизводим из jieba 0.42.1, SHA пиннится здесь; полный хеш вместо урезанного — испр. оркестратором 03.08 по D39.96: пере-сборка бэкенд-сессией из jieba==0.42.1 сошлась с пином).
- Сид `175a67ad…` · records `3de49eaf…` · translator.md `35d3ad6e…` · бэкенд HEAD `b9e62a7`.
### 1.11. Решения владельца, вшитые (1718.07, НЕ пересматривать)