16 KiB
R1 драйвер-свитч — бэкенд-сессия, отчёт (2026-07-19)
Строил по BACKEND_PLAN11_SESSION_PROMPT.md + PACK11_CONT_REPORT §R1 + план §1/§2. НЕ коммитил — сдача на приёмку. $0 к провайдерам (вся верификация build/test/golden/фикстуры). Верификация исполнением: go build ./... && go vet ./... && go test -race ./... — ВСЁ ЗЕЛЁНОЕ (логи ниже).
Итог по задачам захода
| # | Задача | Статус |
|---|---|---|
| 1 | R1 драйвер-свитч (волновой исполнитель черновик(∥)→банк-майнинг-стоп→редактура(∥) + партиция стадий + dispatch + стоп/подпись/mined-write + kill-9) | ✅ СДАНО |
| 2 | Wiring майнера в живой банк-майнинг-стоп (грузить lang.Pack + фолд pack.Version() + mined-write Source:mined) | ✅ СДАНО |
| 3 | Ш-2 (unicode.Version в memoryNormVersion + classifier/style + chunker/estimator) | ✅ СДАНО |
| 4 | Ш-1 (masked-diff хелпер при golden re-capture) | ✅ СДАНО |
| 5 | Структурный golden-рерайт + обновление последовательно-модельных тестов | ✅ СДАНО (38 объяснимых wire/verdict-изменений) |
| + | Именование (директива владельца: без аббревиатур W0/W1/W1.5/W2 в коде/логах) | ✅ СДЕЛАНО (весь пакет) |
Чек-лист исполнения (per-буллет)
R1 — волновой ИСПОЛНИТЕЛЬ (waverun.go — новый; bookrun.go; escalation.go)
- ✅
translateBookWavesзаменил последовательный цикл вTranslateBook. Партиция стадий по роли:waveStagesIndexed(waveDraft/waveEdit)(translator vs non-translator). - ✅ Пер-волновой снапшот-апсерт+пин: черновик под
snapshotIDForWave(waveDraft)(base-банк), редактура подsnapshotIDForWave(waveEdit)(enriched); каждый апсертится +EnsureJobпинит джобы своей волны. «Переоплата ОДНА» — пиннутаsnapshot_wave_test/memory_wave_test(не регрессировали) + golden (base_memory_version отдельно от memory_version). - ✅ Черновик-dispatch (∥):
runWave(N-воркер пул, dependency-free) →runDraftChunk→runStageSequence.memSel= W0-precompute (base-банк). Деньги single-writer (Reserve/Settle), rate-guard, eager-clients — Block A, реюз. - ✅ Банк-майнинг-стоп (mining.go): пустая дельта → авто-продолжение; непустая →
MineBank→MinedDeltaYAML→signature-map файл→СТОП (WaveSignatureStop, CLI-сентинел). Гейт:r.pack != nil && ContrastPath != ""(иначе no-op). Не конфигурировано → авто-продолжение (все текущие фикстуры). - ✅ Редактура-dispatch (∥):
runEditUnitper edit-UNIT.prev= конкатенация draft-выводов членов (unitJoinSeparator="\n\n"); СВЕЖИЙSelectпо тексту ВСЕЙ единицы над ENRICHED-банком, sticky=nil;renderEditorConstraintBlock(editSel). НИКОГДА не пере-рендерит draft (несущий инвариант — golden доказывает: ch2-8 edit-wire байт-идентичны, только ch1 concat меняется; черновик-wire все байт-идентичны). - ✅ Flag+skip: член-черновик флагнут → единица флагнута, редактура пропущена (
recordSkippedStages), деньги хороших членов committed. ПинTestWaveEditUnitFlaggedMemberDraft. - ✅ BookResult = per edit-UNIT (
ChunkOutcome{ChunkIdx=firstChunkIdx, Stages=[member drafts..., edit]}). Форма struct не менялась → renderTranslate/большинство тестов выжили. - ✅ Эскалация под ∥:
escMuсериализует budget-admission fresh-хопа через settle (soft-cap точен: overshoot ≤ 1 хоп, не N-1). $0-replay lock-free. ПинTestWaveEscalationBudgetSerializedUnderParallelism. - ✅ kill-9 / деньги: атомарность settle+checkpoint — store/kill9_test (SIGKILL, не менялся, model-уровень single-writer). N-воркер сохранность денег —
TestWaveParallelWorkersMoneyConserved(committed==run-total, reserved==0). -race чист.
Wiring майнера (runner.go; mining.go; seeding.go; snapshot.go; config)
- ✅
loadLangPackв openRunner (write+read пути — read-модели репродуцируют снапшот): пара С каталогом (configs/langpacks/<src>-<tgt>/) →lang.Loadfail-loud; пара БЕЗ → nil-and-run. - ✅
pack.Version()фолд вbuildSnapshotID(langpack_version, omitempty при nil → книги-без-пака байт-стабильны). - ✅ mined-write путь:
loadMinedDelta(bookmined_delta) грузит черезloadGlossarySeed+ пере-штампуетSource:mined(НЕ seed) → enriched двигается, base стабилен.seedGlossaryаппендит.
Ш-2 (memnorm.go; disposition.go; cheapgates.go; render.go)
- ✅
unicode.Versionвплетён вmemoryNormAlgoVersion,classifierVersion,cheapGateVersion(директива) + расширено наchunkerVersion,estimatorVersion(тот же silent-drift класс — tokenClassCounts/EstimateTokens классифицируют unicode-таблицами → задают чанк-границы+max_tokens → wire; обосновано в коммент-note). ТРИПВАЕР: не бампать тулчейн до Ш-2 в снапшоте (на 1.26.4/Unicode 15.0.0). - Резидуал:
x/text/normнесёт СВОЮ Unicode-редакцию независимо от stdlibunicode.Version— standalone x/text-бамп = deliberate --resnapshot (как тулчейн-бамп); отмечено коммент-note (директива былаunicode.Version).
Ш-1 (golden_test.go)
- ✅
goldenMaskedDiff— маскирует hex-хеши (≥12) + snapshot-payload-строки + wire-body call-индекс (нормализует reorder волн), считает «N wire/verdict-изменений / M version-only» мультисетом (переживает вставки/удаления строк), печатает ДО записи вTM_UPDATE_GOLDEN-пути.
Read-модели (export.go / status.go / quality.go) — волно-осведомлённые
- export: итерирует
outputUnits(edit-единицы для edit-пайплайна, чанки для draft-only); одна строка на единицу (не фантом-pending для не-лидеров);--pairssource = joined member-source (байт-выровнено сrunEditUnit). Drift — ОБЕ волны (партиция строк по стадии-волне, сравнение с per-wave проекцией). - status: per-unit (expected = members·|draft|+|edit|); miss/style лукап по лидеру; флагнутая edit-единица = 1 в вердикте (не per-member); SnapshotDrift = >1 ВНУТРИ волны (2 волны — норма); ConfigDrift/Redrive-guard per-wave (иначе спурьёзный abort).
- quality: TotalChunks=units (bookChunks); агрегаты (KPI/misses/style/echo) корректны per-unit; per-chunk KPI-строки не-лидеров разрежены (draft-сигналы, 0 KPI) — наблюдаемость, не гейт.
Golden re-capture (masked-дифф — объясним)
Фикстура: 8 глав → 8 единиц (ch1 = 2 черновик-чанка → 1 edit-единица), 18 вызовов (было 19: ch1 два edit → один concat). Masked-дифф: 38 wire/verdict-изменений — ВСЕ объяснимы:
- Хедер:
snapshot_id→snapshot_draft+snapshot_edit+base_memory_version. - ch1: 9 чанков → 8 единиц (ch1/0+ch1/1 → 1 единица); ch1/1 edit chunk_status/request_log/wire удалены.
- edit-skip detail переформулирован («upstream stage» → «member draft chunk of this edit unit»).
- ch1 edit-wire: 2 per-chunk edit → 1 concat edit (
ЧЕРНОВИК\n\nЧЕРНОВИК).
- 200 version-only (Ш-2 версия-churn + per-wave/langpack хеши). НИ ОДНОГО неожиданного verdict-изменения; черновик-wire + ch2-8 edit-wire байт-идентичны (несущий инвариант).
Чекпойнт-ревью после R1-ядра (адверсариал, author≠reviewer, refute-by-default, 4 линзы + verify)
4 линзы (алгоритм-vs-план · конкуренция/деньги · resume/снапшот · read-модели) → verify-пас (refute-by-default). 4 raw / 4 confirmed / 0 refuted. Все находки ВПИТАНЫ + пофикшены + пиннуты:
- MAJOR (3 линзы независимо — CONFIRMED): draft-инъекция считалась над ENRICHED-банком (вкл Source:mined), а снапшот черновика пинился к BASE-версии (excl mined). База/enriched-сплит был реализован ТОЛЬКО на уровне версия-хеша (
computeMemoryVersionScoped), НЕ на уровне выборки-инъекции —precomputeSticky(chunks, r.memory)селектил над полным банком. Сценарий: владелец подписывает mined-терм → на ре-прогоне терм входит в draft-msgs →content_hash/request_hashменяются → draft resume ПРОМАХ → тихий re-bill всей mined-касающейся части черновика БЕЗ громкого --resnapshot (base-снапшот не двигался). Нарушало инвариант 2 («переоплата ОДНА») + 6 (resume-$0) на прод-пути майнинга. ФИКС:r.baseMemory— base-scoped банк (Source != mined), материализуется вseedGlossary(при отсутствии mined = ТОТ ЖЕ объект чтоr.memory— без двойной материализации, golden байт-стабилен). Черновик-волна селектит надr.baseMemory(инъекция байт-идентична через enrichment); редактор — надr.memory(enriched). ПИН:TestWaveMinedSignDoesNotRebillDraft— подпись mined-терма оставляет draft chunk_status {SnapshotID,ContentHash,FinalHash} байт-стабильным, редактор-снапшот двигается (единственная gated-переоплата). Верифицирован: падает без фикса («draft injection CHANGED»), проходит с фиксом. - NIT (CONFIRMED):
QualityReportне дропал ghost-строки против манифеста (ProcessedChunks мог превысить TotalChunks при усечении источника). ФИКС: inManifest-гард в обеих петлях (паритет export/status).
Багфиксы вне плана
- Нет вне-плановых. Одна ратифицированная СЕМАНТИЧЕСКАЯ смена (не баг): пер-волновой --resnapshot СУРГИЧЕН (не all-or-nothing) — смена только draft-конфига двигает только
snapshot_draft; редактура резюмится, если её wire/снапшот не тронуты (иначе каскадит через content-hash).TestRunnerSnapshotPinningобновлён 4→3 с объяснением. Флаг оркестратору: это улучшение (меньше лишней переоплаты), прямое следствие пер-волнового снапшота плана §1(в); в реале смена draft-промпта меняет черновик → каскад в редактуру (calls=4).
Открытые вопросы / решения оркестратору
- escMu держится через LLM-вызов fresh-хопа → эскалации сериализуются (не параллелятся). Осознанный трейд: корректность soft-cap > параллелизм редких эскалаций. Флаг.
- Ш-2 расширен на chunker/estimator (сверх названных memnorm/classifier/style) — тот же silent-drift класс, но не буквальная директива. Подтвердить.
- pack.Version() фолд — майнер offline (proposals inert до approve), фолд консервативен (pack-edit=resnapshot даже без mining-прогона); omitempty → книги-без-пака не задеты. Подтвердить.
- quality per-chunk KPI-строки разрежены на мульти-чанк-единицах (не-лидеры 0 KPI) — наблюдаемость, агрегаты верны. Приемлемо?
- W1.5-UX/mined round-trip (§10-1) — реализован seam (стоп→signature-file→owner curates
mined_delta→resume); полный live-прогон stand-only ($0, нет contrast в git). Тестируется gate + auto-continue; stop-путь — юнит-логика. - kill-9 N-воркер SIGKILL-тест — store/kill9_test покрывает атомарность (model-уровень, worker-count-независимо) +
TestWaveParallelWorkersMoneyConservedпокрывает N-воркер сохранность. Отдельный wave-SIGKILL subprocess-тест НЕ добавлял (маргинальная ценность).
Верификация (логи)
go build ./... → OK
go vet ./... → OK
go test -race ./... → ВСЁ ЗЕЛЁНОЕ: cmd/tmctl · config · lang · ledger · llm · pipeline (~31s -race) · store
gofmt -l <мои файлы> → пусто (пре-существующий gofmt-долг в не-моих файлах — llm.go/memory_*_test.go — не трогал)
golden fresh↔resume → байт-стабилен; masked-дифф 38 объяснимых wire/verdict + 200 version-only
Новые пины (waverun_test.go): TestWaveMultiChunkEditUnit (несущий: 2 чанка→1 единица, edit над concat, export/status per-unit, resume $0), TestWaveParallelWorkersMoneyConserved (workers=4, committed==run-total), TestWaveEditUnitFlaggedMemberDraft, TestWaveMiningUnconfiguredAutoContinues, TestWaveEscalationBudgetSerializedUnderParallelism (escMu), TestWaveMinedSignDoesNotRebillDraft (переоплата-ОДНА на injection-уровне — регресс-пин ревью-фикса, падает без фикса). Обновлены: TestRunnerSnapshotPinning (4→3, per-wave surgical), TestExportDetectsConfigDrift (both-wave drift).
Структурные переносы
- Новые:
waverun.go(исполнитель),mining.go(стоп),waverun_test.go(пины). Расширены:wave.go(buildEditUnits/editUnit). TranslateBookтело →translateBookWaves;translateChunk(последовательный per-chunk-all-stages) УДАЛЁН (логика вrunStageSequence/runDraftChunk/runEditUnit).BookResult.Chunksсемантика: per-chunk → per-edit-unit (struct-форма цела).- Именование: enum
waveW1/waveW2→waveDraft/waveEdit,runW15→runBankMiningStop, локали/логи/комменты по всему пакету (директива владельца).