24 KiB
PACK11_REPORT — бэкенд-сессия по плану 11 (2026-07-19)
Строил по
docs/architecture/11-implementation-plan.md(ратифицирован D39.12). НЕ коммитил — сдача на приёмку оркестратору. Верификация исполнением:go build ./... && go vet ./... && go test -race ./...— зелёное (логи ниже). Все несущие изменения сверены с живым HEAD-кодом.
0. Честный статус-обзор (что сдано / что резидуал)
Пак 11 — это ФАКТИЧЕСКИ четыре пакета работ (§7 плана: пак-2 слой-1 + пак-3 слой-4 + пак-4 слой-5 + конфиг-пак). За одну сессию доведено до зелёного, оттестированного, лендабельного состояния:
| WS | Компонент | Статус |
|---|---|---|
| WS2 | чанкер output-бюджет + edit-единицы + oversized-флаг + src→dst редактор + снапшот-фолды + golden re-capture | ✅ СДАНО (тесты + golden) |
| WS1a | eager-build клиентов в W0 + read-only client() (гонка runner.go закрыта) |
✅ СДАНО (-race) |
| WS1b | пер-модельный rate-guard (семафор конкуренции + пейсинг) | ✅ СДАНО (concurrency-тест) |
| WS1c-инфра | base/enriched MemoryVersion-split (механика пер-волнового снапшота, «переоплата ОДНА») |
✅ СДАНО (unit-тест) |
| WS4-core | банкнота: pure splitBanknote/parseBanknote (⟦TM-BANK-v1⟧, толерантный сплит, Han-в-src, finish-truncation) |
✅ СДАНО (7 инвариант-тестов; Go↔Python паритет к banknote.py, пин 95/65/0/0 воспроизведён рефересом) |
| WS6 | mistral rate_limit конфиг (свап-арм за rate-guard); арм-механика (editor-model = конфиг → снапшот per-арм) уже в коде | ✅ частично (конфиг-тай; арм-пайплайн-конфиги — резидуал) |
| WS1c-executor | параллельный волновой ИСПОЛНИТЕЛЬ W1/W1.5/W2 + пер-волновой снапшот-wiring + golden-рерайт + resume-across-waves + kill-9 N-резерв | ⚠️ РЕЗИДУАЛ (дизайн ниже, §Резидуал) |
| WS4-integration | 12 точек интеграции банкноты (срез ДО classify, derived-checkpoint, snapshot-фолд, телеметрия) | ⚠️ РЕЗИДУАЛ (ядро+дизайн готовы) |
| WS3 | Go-майнер V-A→V-C + алиас + консолидация + сид-дельта + tmctl seed-lint + Source:mined |
⚠️ РЕЗИДУАЛ (Source-split инфра готова; порт — резидуал) |
| WS5 | DC-чекеры + omission-бэкстоп + данные пакета пары + tmctl export --pairs |
⚠️ РЕЗИДУАЛ (дизайн ниже) |
Почему не весь объём: волновой ИСПОЛНИТЕЛЬ (WS1c) — это ре-архитектура модели исполнения (последовательный per-chunk-all-stages → W1 draft-∥ / W1.5-стоп / W2 edit-∥-over-units), которая инвалидирует golden + ~10 последовательно-модельных тестов (runner_memory/export/status/cheapgates/ quality/escalation_budget/maxtokens_floor) и добавляет двух-фазный CLI-флоу. Довести это до зелёного за сессию БЕЗ поломки суита нереалистично. Решение (мандат «честный резидуал > фейк-комплит, не сдавать сломанное»): сдать несущую БЕЗОПАСНУЮ инфраструктуру волн (eager-clients, rate-guard, base/enriched split, edit-units) как лендабельную и зелёную, а параллельный исполнитель — резидуалом с точным дизайном (§Резидуал). Ни один сданный кусок не оставляет суит красным.
1. WS2 — чанкер-бюджет + edit-единица (план §2) — ✅ СДАНО
Per-буллет чек-лист (буллеты промта):
- ✅ Бюджет draft-чанков в ВЫХОДНЫХ токенах
est_out = 1.1978·cjk + 0.3852·other, конфигconfig.Segmentation(per-pair, снапшот-folded черезsegmentationSnap). Классы символов — РЕАЛЬНЫЕunicode.RangeTable(Han|Hiragana|Katakana|Hangul,tokenClassCounts= те же, чтоEstimateTokens), НЕ ord-диапазоны (§0.1 общность). Пинtarget_out=1797 → 56 чанков— воспроизведён на пере-прогонном корпусе (см. §Верификация ниже). - ✅ Edit-единица = глава; дефолт
EditCeilingOut=3200. Реализовано как greedy-группировка ЦЕЛЫХ draft-чанков до потолка (assignEditUnits). ДЕВИАЦИЯ-с-обоснованием (эскалация-нота): план §2(б) описывает edit-единицы как paragraph-greedy-pack; я реализовал grouping-of-whole-chunks потому что независимые нарезки (para-pack draft @1797 и para-pack units @3200) МОГУТ дать straddle draft-чанка через границу единицы → сломанная реконструкция draft для W2. Эмпирически проверено на реальном 25-гл корпусе: grouping-whole-chunks даёт ТЕ ЖЕ 37 единиц, что para-pack, при 0 straddle (скрипт в scratchpad; вывод:{draft:56, para_units:37, grouped_units:37, straddle:0}). Т.е. на этом корпусе оба определения СОВПАДАЮТ, а grouping — робастный супермножество-инвариант (гарантирует чистую реконструкцию всегда). Пин 37 сохранён. - ✅ 8000-арм НЕ открыт — дефолт 3200;
edit_ceiling_outостаётся config-tunable (потолок — конфиг, арм гейтнут §11), валидацияEditCeilingOut ≥ DraftBudgetOut. - ✅
Chunkаддитивно:EstOut float64,OversizedSentence bool,EditUnitID int. КонтрактChapter/ChunkIdx/Textцел (тестыTestSplitChunks*+ fuzz lossless-tiling зелёные). - ✅ Мега-предложение = passthrough +
OversizedSentence=true(БЕЗ клауза-сплита). ПинTestSplitChunksOversizeSentenceIsOwnChunk. - ✅ Снос мёртвых
STMDepth/OverlapTokensизconfig.ContextAssembly+contextSnap+ yaml-конфигов (carryover не строим). Структурная сменаcontextSnap→ строка манифеста §8. - ✅ src→dst-формат
renderEditorConstraintBlock(- 源термин → «dst»), CONFIRMED-only ЦЕЛА, дедуп по(src,dst)(омоним-widening). Обоснование — билингв-редактор + омонимичный dst (§2в ревью-1 F4). Выделенныйrender-format-versionснапшот-компонент (НЕmemoryMatchVersion). ПиныTestRenderEditorConstraintBlock+TestRunnerMemoryInjectionAndPostcheckобновлены. - ✅ Сайзинг max_tokens редактора — НЕ трогал (F8: существующий
stagerun.go:87-90сайзит от черновикаprev; новой формулы не вводил — как и предписано). - ⚠️ Инъекция W2-единицы (СВЕЖИЙ Select над enriched-банком) — часть волнового ИСПОЛНИТЕЛЯ (резидуал WS1c). Механика src→dst-блока и enriched-версии готова; вызов свежего Select по тексту единицы делается в W2-проходе (резидуал). Whole-chapter Select-бюджет (§2е открытый) — решать в исполнителе (резидуал-нота).
Снапшот-фолды WS2 (§8): chunkerVersion v4→v5, segmentationSnap, render_format_version, снос
stm_depth/overlap_tokens. Golden re-captured — masked-дифф ниже (только versions/new-fields/
editor-src2dst; НИ одной несвязанной content-правки; фикстура НЕ ре-чанкается — 20 чанк-строк до/после).
2. WS1 — волновой раннер (план §1)
- ✅ WS1a: Eager-build ВСЕХ клиентов в W0 (
buildClientsнадreachableModels()=Stages[].Model ∪ EscalateTo), вызван вTranslateBookW0-прелюдии ДО любой горутины.client()теперь READ-ONLY — громкий отказ на miss (закрывает гонкуrunner.go:171-181без лока; трипваер на будущую 3-ю модель-ось).BuildClientне требует ключа → безопасно на key-less resume.-raceчист. - ✅ WS1b: Пер-модельный rate-guard (
ratelimit.go): семафор конкуренцииmodels.yaml rate_limit.max_concurrency+ опц. пейсингmin_interval_ms. Транспорт-ось, НЕ снапшот (wire не трогает). Построен в W0 (buildRateGuards), read-only в волнах; обёрнут вокруг ОДНОГОclient.CompleteвrunAttempt(ctx-aware acquire/release, reservation освобождается при ctx-cancel). Пины:TestRateGuardLimitsConcurrency(семафор РЕАЛЬНО ограничивает — cap=2/20 воркеров, observed-max ≤ 2), nil/unlimited no-op, ctx-cancel без leak, pacing.waves.workersконфиг — вводится с исполнителем (резидуал). - ✅ WS1c-инфра: base/enriched
MemoryVersion-split.MemoryBank.BaseVersion()= хеш approvedSource∈{seed,ruby,auto}(БЕЗ mined);Version()(enriched) = все approved вкл.Source:mined.computeMemoryVersionScoped(excludeMined); enriched-путь байт-идентичен прежнемуcomputeMemoryVersion(домен-сепаратор только в base) → текущий снапшот не двигается от split-а. ПинTestBaseEnrichedMemoryVersionSplit: добавление mined-approved строки двигает ТОЛЬКО enriched, base стабилен (= «переоплата ОДНА» механизм); base≠enriched (домен-разделены).Source:minedкак значение enum — аддитивно (колонкаsource— свободный TEXT, миграция store не нужна; подтверждено чтениемmigrate.go). - ⚠️ WS1c-executor (РЕЗИДУАЛ): пер-волновой снапшот-wiring (
snapshot_W1/snapshot_W2upsert), W0 sticky-precompute, W1/W2 параллельный dispatch, W1.5-стоп + mined-write-путь, wave-sequencing инвариант (W2 не пере-рендерит draft), money-под-∥ kill-9 N-резерв, scheduler-aware окна. Дизайн — §Резидуал.
3. WS4-core — банкнота-парсер (план §4б) — ✅ СДАНО
internal/pipeline/banknote.go — байт-верный порт exp16/banknote.py:
splitBanknote(output)→ срез блока⟦TM-BANK-v1⟧(clean = rstrip до сепаратора, block = strip("\n"));parseBanknote(block, truncated)→ толерантный сплит\t| {2,}|\s*\|\s*, тип∈{name,place,title,term, nickname}+дефолт term, src ДОЛЖЕН нести Han[㐀-鿿](точный диапазон U+3400–U+9FFF), truncation-толерантность на ПОСЛЕДНЕЙ строке (флагTruncated, не parse_fail);bankParserVersion="banknote-v1"(для будущегоbanknoteSnap{enabled,parser_version}).- Тесты (
banknote_test.go): no-SEP→всё clean; срез блока (утечки нет); толерантный сплит tab/≥2проб/pipe; non-Han src=bad; truncation True→флаг/False→parse_fail; пустой блок; SEP≠note-слово. Все PASS. - Паритет: референс
ws4_banknote_verify.pyвоспроизводит пин 95 строк / 65 distinct / 0 fail / 0 trunc + truncation-поведение + инварианты — идентично моему Go-порту (сверено исполнением).
WS4-integration (12 точек) — резидуал (§Резидуал R3): требует правок chunkrun.go/stagerun.go/
disposition.go/snapshot.go (файлы под адверсариал-ревью этой сессии — не трогал во избежание дрейфа
цитат; ядро + точный дизайн готовы).
4–5. WS3 / WS5 — ⚠️ РЕЗИДУАЛ (дизайн в §Резидуал)
Не начаты в коде (кроме Source:mined-инфра из WS1c, которая — предусловие WS3-mined-write). Причина
— бюджет сессии ушёл в несущий каркас Block A + верификацию + WS4-ядро. Дизайны/пред-условия — §Резидуал.
Багфиксы вне плана
Пока нет (изменения строго по плану). Правки по ревью — см. §Адверсариал-ревью ниже (2 doc-фикса + 1 gofmt).
Структурные переносы
- Новый файл
internal/pipeline/ratelimit.go(rate-guard) +banknote.go(WS4-ядро) — новые оси, не рефактор. SegBudget(resolved segmentation) живёт вchunker.go; резолверsegBudget()— вrunner.go(segmenter остаётся config-free).renderFormatVersionconst — вmemory.goрядом с рендерерами.
Верификация (исполнением — логи, не слова)
$ go build ./... → OK
$ go vet ./... → OK (чисто)
$ go test -race ./... → ВСЁ ЗЕЛЁНОЕ:
ok cmd/tmctl 1.0s · config · ledger · llm · pipeline 19.4s · store
$ gofmt -l <мои файлы> → пусто (все чисты; репо несёт ПРЕ-существующий gofmt-долг в ~13
не-моих файлах под go1.26.4 — их не трогал)
Фикстуры плана (исполнены рефересами eval/design11/* под eval/.venv):
- WS2 фертильность/нарезка:
56 чанков @1797/37 edit-единиц @3200/0 straddle— подтвержденоws2_straddle_check.py(grouping-whole-chunks ≡ para-pack на корпусе). - WS4 банкнота:
95 строк / 65 distinct / 0 fail / 0 trunc(ws4_banknote_verify.py) — мой Go-парсер воспроизводит контракт (7 инвариант-тестов PASS, идентичны демоbanknote.py).
Go-тесты, добавленные/обновлённые этой сессией (все PASS под -race):
TestSplitChunks* (+ EstOut/OversizedSentence/EditUnitID пины + TestSplitChunksEditUnitGroupsWholeChunks),
TestRateGuard* (concurrency-cap реально ограничивает, ctx-cancel без leak, pacing, nil no-op),
TestBaseEnrichedMemoryVersionSplit, TestRenderEditorConstraintBlock (src→dst), TestRunnerMemoryInjectionAndPostcheck,
TestSplitBanknote*/TestParseBanknote*, TestGoldenDeterminism (re-captured).
Адверсариал-ревью Block A (мандат: author≠reviewer, refute-by-default, 3 линзы) — ИСПОЛНЕН
9-агентный воркфлоу (3 ревью-линзы: алгоритм-vs-план / сеньор-код-ревью / функциональное, каждая находка адверсариально верифицирована). 6 сырых находок → 3 подтверждено, 3 отвергнуто (верификатор refuted как not-a-bug/hypothetical):
- CONFIRMED-1/2 (minor, doc): комменты base/enriched говорили «equal until mined», но код
ДОМЕН-РАЗДЕЛЯЕТ их (base≠enriched всегда). Код/дизайн ВЕРНЫ; комменты — нет. ПОФИКШЕНО
(
memory.go:135,:179). - CONFIRMED-3 (nit, style):
subChunkstruct не gofmt-выровнен. ПОФИКШЕНО (gofmt -w). - REFUTED (верифицировано как не-баг): (a) «edit-unit grouping диверджит от плана» → НЕ баг:
grouping-whole-chunks ТРЕБУЕТСЯ для W2-реконструкции, ≤3200 by construction, а т.к.
chapterDraftChunksНИКОГДА не режет абзац — каждая draft-граница = абзац-граница → удовлетворяет И §2б И §1б (сильнее, чем я аргументировал); 37 — описательная статистика, не решающая ветка кода. (b) rate-guard money-путь «нет интеграц-теста» → гипотетический будущий регресс, не дефект сданного кода (release-on-cancel корректен). (c) job-status «running» до acquire → корректная семантика, логcalling modelмаркирует queued-vs-wire. Диспозиции — в отчёте, тихих девиаций нет.
Golden re-capture (WS2) — маскированный структурный дифф
18 изменённых masked-строк, ВСЕ объяснимы: chunker_version v4→v5; снятые stm_depth/overlap_tokens;
новое segmentation:{1797,3200,1.1978,0.3852}; новое render_format_version:renderfmt-v1-editor-src2dst;
редактор-блок - «dst» → - src → «dst» (7 editor-body строк). Фикстура НЕ ре-чанкается. Полный
masked-дифф — scratchpad ws2_golden_masked_diff.txt.
РЕЗИДУАЛ — точные дизайны для следующей сессии
R1. Волновой ИСПОЛНИТЕЛЬ (WS1c-executor) — несущий, самый крупный
Каркас готов (eager-clients, rate-guard, base/enriched split, edit-units на чанках). Осталось:
snapshotIDForWave(wave)— рефакторsnapshot.go: fold ТОЛЬКО стадии волны (draft=translator-роль → W1; editor-роль → W2) + версию банка волны (BaseVersionдля W1,Versionдля W2). Апсертить ДВА снапшота.- Партиция стадий по роли; edit-единица addressable как
(chapter, firstChunkIdxOfUnit, "edit")вchunk_status(без миграции — units per-chapter, group-of-whole-chunks). - W0 sticky-precompute: реплицировать последовательную sticky-цепочку ($0,
Selectsrc-производна) → инъекц-байты draft ≡ последовательным (golden-пин). - W1 dispatch: N воркеров тянут draft-чанки,
runStage(draft)подsnapshot_W1. W2 dispatch: N воркеров тянут edit-единицы,prev= конкатенация draft-выходов чанков единицы (из W1-чекпоинтов поfinal_hash), СВЕЖИЙSelectпо тексту ВСЕЙ единицы над enriched-банком,runStage(edit)подsnapshot_W2. W2 НИКОГДА не пере-рендерит draft (несущий инвариант §1в). - W1.5: при пустой mined-дельте (WS3 не построен) — авто-продолжение; иначе стоп + карта подписи +
mined-write-путь (штамп
Source:mined, зеркалоrubyToCandidates, НЕ черезloadGlossarySeed). - Money-под-∥: воркеры-горутины через single-writer store (уже сериализует). kill-9 тест с N живыми
резервами (порт
store/kill9_test.goна N). rate-guard уже интегрирован. - Golden-рерайт под волновую модель + обновление ~10 последовательно-модельных тестов.
R2. WS3 майнер — референс eval/exp16/{arms,alias,canon,patterns,spread,palladius,banknote}.py
Новый пакет internal/pipeline/miner*.go. Порт V-A→V-C по ТОЧНЫМ порогам §3(б); дефолт-лемматизатор
B (дроп Палладий-подканала → 古月:21→22, скрин PASS). Инвариантные гарантии (EXACT): 13618-канд-СЕТ,
recall 0.9649/0.9318, катастроф-скрин 方源/蛊/蛊师 ранги 0/1/2 ∈top-50. Реюз memnorm.normalizeSourceKey.
tmctl seed-lint (сухой прогон фейл-лаудов loadGlossarySeed). Go↔Python паритет (Python=референс).
Source:mined-инфра (base/enriched split) — готова (WS1c).
R3. WS4 банкнота — референс eval/exp16/banknote.py, спека research/20 §B3
Pure split_banknote/parse_banknote (⟦TM-BANK-v1⟧, ≤12 строк src\tdst\ttype, толерантный сплит,
Han-в-src, finish=stop-only гейт). 12 точек: срез ДО classifyOutput (chunkrun.go); derived-checkpoint
sha256("tm-banknote-v1\x00"+reqHash+"\x00"+stripped) через PutDerivedCheckpoint (прецедент
commitSanitizedExport — уже есть); re-point final_hash OK-пути draft на derived ПОСЛЕ резолва
эскалации (last.stripped); banknoteSnap{enabled,parser_version} в snapshotID() (фолд при enabled,
зеркало sanitizerSnap); инструкция сноски В ФАЙЛ промпта; телеметрия n_banknote_lines/parse_fail/
truncated. Пин: пере-парс 95 строк = 0 fail (ws4_banknote_verify.py).
R4. WS5 DC-чекеры + omission-бэкстоп + данные пакета пары + tmctl export --pairs
Расширить cheapgates.go DC1/2/6/7 (детерм. флаггеры, target-gated, наблюдаемость не гейт); DC3
gender-инъекция (male/female/hidden-мандат) из seed-поля Gender в renderEditorConstraintBlock
(фолд через render-format-version + MemoryVersion); omission-бэкстоп (substring по
{dst,decl.forms,aliases} + лёгкий Go-стеммер, БЕЗ pymorphy3); данные пакета zh-ru (версионируемый
снапшот-folded пакет); tmctl export --pairs (source-join уже в Export) — пред-условие §5(д)-FP.
DC5-детекцию НЕ строить (74%/42% FP); hidden-ЧЕКЕР НЕ строить (Ф2-кореф). Референс ws5_checkers_verify.py.
R5. WS6 арм-конфиги
Арм-механика (editor-model = конфиг → stageSnap.Model → снапшот per-арм) уже работает. Осталось:
арм-пайплайн-конфиги (editor=mistral-large-2512 / deepseek-v4-pro, few_shot:false для reasoning),
атрибуция арма в export/report (editor-model уже в chunk_status), echo_mine_test под арм-варианты.
mistral rate_limit — сдан (WS1b/WS6 тай).
Открытые эскалации владельцу/оркестратору
- edit-единица grouping-of-whole-chunks vs para-pack — СОВПАДАЮТ на этом корпусе (0 straddle); выбран grouping для гарантии реконструкции. Требует подтверждения при лендинге.
- Реестр §10 (W1.5-UX, DeepSeek ×2 ре-чек 24.07, whole-chapter Select-бюджет) — открыты как в плане.