11 KiB
Правила общности движка + Go-ответы по больным местам (12)
Короткий норматив для бэкенд-сессий (24.07.2026). Только неочевидное/больное — общий Go-стиль сессии и так соблюдают, его тут нет. Источники Go-части: Google Go Style Guide (guide/decisions/best-practices) + Effective Go + Go Wiki CodeReviewComments.
§0. Общность движка (директива владельца 24.07 — жёсткая)
- Движок language/book-agnostic. Go-ЛОГИКА не ветвится по паре языков или книге. Языко-специфичное — ДАННЫМИ в
internal/lang+configs/langpacks/, и КЛЮЧ каталога называет ПРЕДМЕТ данных:<src>/— факты исходного языка ·<src>-<tgt>/— факты ПАРЫ (транслитерация, DC-чекеры, заголовки) ·<tgt>/— то, что принадлежит ЦЕЛЕВОМУ языку независимо от источника (появился 30.08 сreader.txtписателя книги, D39.175). ⚠ Два режима, и путать их дорого: данные, которые видит МОДЕЛЬ или гейт, читаетlang.Load— их байты фолдятся вpack.Version()→ снапшот, и правка = громкий--resnapshot; данные ЧИСТО РЕНДЕРА (читательские слова файла книги) читает отдельный загрузчик и вVersion()НЕ фолдятся — иначе добавление слова в словарь перекупало бы каждую книгу. Отказ калибруется объявленностью: объявленный файл канала отсутствует ⇒ ГРОМКИЙ отказ; опциональный ⇒ фича инертна молча и версия байт-стабильна. Книго-специфичное — данными КНИГИ: сид-глоссарий / brief / book.yaml. Книжный канон в общем пар-слое = утечка (прецедент: 古月 в surnames-compound.txt). - Пар-скоупнутые модули (checkers_zh_ru и т.п.) легитимны как код за pair-гейтом, но их ДАННЫЕ (словники, руны, таблицы) подлежат выносу в langpack.
- Тест-данные пары легитимны: zh/ru-строки в
*_test.go/golden упражняют общую логику. Не путать с логикой. - Промпты резолвятся КОНВЕНЦИЕЙ ПУТИ, а не ключом конфига (форма пака-15): пара-пак —
configs/pairs/<пара>.yaml, роль —prompts/<пара>/<роль>.md, и пара без пакета падает ГРОМКО на загрузке с именем пути, который искали (internal/config/pair.go; пиныconfig.TestPromptResolvesByPairAndRoleConventionиconfig.TestPromptMissingPairPackFailsLoud). ⚠ Прежний ключ не просто снят — он ОТВЕРГАЕТСЯ загрузкой (config.TestRetiredPromptKeysAreRejected), то есть конфиг, написанный по старой редакции этого норматива, не поднимется вовсе. Конвенции пары (меры, стихи, дискурс) живут в теле промпта СВОЕЙ пары — это данные. Дальний план — извлечение в структурированный пакет конвенций пары (10-prompt-architecture, гейтится формой пакета). - Ревью-вопрос по умолчанию: «заработает ли это на паре, которой в репо ещё НЕТ, без правки Go?» Если нет — данные наружу.
- Контент-свойства — тем же правилом (владелец 25.07, D39.25): движок НЕ ветвится по типу контента (18+/архаика/жанр). Механизм generic: content-label книги/глав = данные книги · capability провайдера = данные models.yaml · сопоставление и политика цепочки = движок/конфиг. «adult» — значение данных, не Go-идентификатор. Refusal-обработка причино-агностична (D12: детектим ФАКТ отказа; причина — операторская диагностика). Ревью-вопрос: «добавится ли второй лейбл (archaic-register) без правки Go?»
§1. Go-ответы (только то, о чём реально спорим)
⚠ ИСПР. 05.09: пример ПРОТУХ и охранял несуществующее. Прежняя редакция называла glossaryBlockHeader и маркер ⟨проверить⟩ живыми wire-строками, которые «остаются и не трогаются». Символа нет (grep -rn glossaryBlockHeader backend --include=*.go → 0), а маркер СНЯТ С ПРОВОДА в обоих блоках (backend/internal/membank/memory.go, греп leaves the wire in BOTH blocks; D39.104 п.2, строка 134 закрыта). Норматив, охраняющий снятое, отправляет сессию возвращать провод назад — а это сдвиг RequestHash и перекупка. Живой пример той же формы брать из кода, а не отсюда.
⚠ ТРЕТЬЯ КОРЗИНА, которой правило не знало (внесена 04.09): ЧИТАТЕЛЬСКИЕ слова целевого языка живут ДАННЫМИ и вне снапшота — configs/langpacks/<цель>/reader.txt (D39.175), и туда же относятся фразы пользователя ВСЕХ зон, которые адресуются кодом причины и локалью, а не строкой в Go (D39.176 п.4, строка бэклога 189). То есть корзин не две, а три, и различает их не язык, а то, КТО читает: провод модели · данные пары · слова читателя и пользователя.
- Имена без аббревиатур этапов (владелец 19.07): ни
W0/W1/W1.5/W2в идентификаторах, логах и комментариях — пишемwaveDraft/waveEdit, «draft wave started». Аббревиатуры волн попали в глоссарий как ИСТОРИЯ и оттуда не возвращаются. - Комментарий режется по ВОДЕ, а не по длине (владелец 26.07, формулировка уточнена им же 21.08). ⚠ Счёт строк («пиши одну-две») — негодный гейт и был снят: комментарий на три строки может быть нужен, на одну — достаточен, длиной это не решается. Режем то, что не помогает читателю КОДА: пересказ решений («владелец решил… и поехали»), провенанс, сага о том, как шли, изложение исследования вместо краткой ссылки на него. Остаётся столько, сколько нужно, всё, что из одной функции НЕ выводится: порядок блокировок, инварианты между таблицами, цена забывания, вендор-квирк. Проверочный вопрос — «поможет ли это тому, кто через полгода правит именно эту функцию», а не «сколько тут строк».
- PlantUML: две ловушки activity-синтаксиса, каждая уже стоила реального бага (25.07). Точка с запятой ВНУТРИ многострочной метки обрывает её; строка метки, начинающаяся с
/ | < > ] }, читается парсером как спец-терминатор. ⚠ Диаграммы НЕ рендерить в svg/png — владелец смотрит расширением VS Code (гардрейлCLAUDE.md); дом диаграмм —backend/docs/, правятся одним коммитом с кодом. - Код группируется логическими папками по назначению, а не свалкой
archive/(владелец, про КОД). Архив-метафора законна для доков и промтов; для кода каталог обязан называть предмет. _test.goрядом с кодом — не бардак, а требование тулчейна:go testсобирает тесты только из каталога пакета; отдельныхtests/-деревьев в Go не существует.- Число/размер файлов — НЕ критерий сплита (Google: «maintainers can move code between files without affecting callers»; эталон — stdlib
bytes). Легитимный триггер — концептуально отдельная подсистема со своим узким API. Этот тест прогнан против фактуры (пак-14, прил. A: связность измерена) — его проходят miner/checks/membank/chunk → сплит РАТИФИЦИРОВАН (D39.23, пак-15): сначала хойст общего субстрата (internal/text+ value-типы), затем подсистемы; драйвер остаётся композит-корнемinternal/pipeline. Дробление «ради размера» по-прежнему запрещено (util-пакеты, течь деталей в API); crush-масштаб (~71 пакет) не эмулируем — сплит только по измеренному узкому API. - Никаких пакетов
util/common/helpers/types— имя пакета обязано нести домен (Decisions/CodeReviewComments). - «Least mechanism» (Guide): сперва slice/map/struct, потом stdlib, только потом своё/зависимость; это же — мера сдержанности к дженерикам и абстракциям «на вырост».
- Интерфейс объявляет потребитель, не реализация; не заводить интерфейсы заранее «для моков» (CodeReviewComments) — наш
BuildClient/адаптеры уже так устроены, держать линию.