textmachine/docs/archive/reports/R1_DRIVER_SWITCH_REPORT_2026-07-19.md
2026-07-19 20:11:26 +03:00

16 KiB
Raw Blame History

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) → runDraftChunkrunStageSequence. memSel = W0-precompute (base-банк). Деньги single-writer (Reserve/Settle), rate-guard, eager-clients — Block A, реюз.
  • Банк-майнинг-стоп (mining.go): пустая дельта → авто-продолжение; непустая → MineBankMinedDeltaYAML→signature-map файл→СТОП (WaveSignatureStop, CLI-сентинел). Гейт: r.pack != nil && ContrastPath != "" (иначе no-op). Не конфигурировано → авто-продолжение (все текущие фикстуры).
  • Редактура-dispatch (∥): runEditUnit per 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.Load fail-loud; пара БЕЗ → nil-and-run.
  • pack.Version() фолд в buildSnapshotID (langpack_version, omitempty при nil → книги-без-пака байт-стабильны).
  • mined-write путь: loadMinedDelta (book mined_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-редакцию независимо от stdlib unicode.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 для не-лидеров); --pairs source = 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-изменений — ВСЕ объяснимы:

  1. Хедер: snapshot_idsnapshot_draft+snapshot_edit+base_memory_version.
  2. ch1: 9 чанков → 8 единиц (ch1/0+ch1/1 → 1 единица); ch1/1 edit chunk_status/request_log/wire удалены.
  3. edit-skip detail переформулирован («upstream stage» → «member draft chunk of this edit unit»).
  4. 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).

Открытые вопросы / решения оркестратору

  1. escMu держится через LLM-вызов fresh-хопа → эскалации сериализуются (не параллелятся). Осознанный трейд: корректность soft-cap > параллелизм редких эскалаций. Флаг.
  2. Ш-2 расширен на chunker/estimator (сверх названных memnorm/classifier/style) — тот же silent-drift класс, но не буквальная директива. Подтвердить.
  3. pack.Version() фолд — майнер offline (proposals inert до approve), фолд консервативен (pack-edit=resnapshot даже без mining-прогона); omitempty → книги-без-пака не задеты. Подтвердить.
  4. quality per-chunk KPI-строки разрежены на мульти-чанк-единицах (не-лидеры 0 KPI) — наблюдаемость, агрегаты верны. Приемлемо?
  5. 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-путь — юнит-логика.
  6. 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/waveW2waveDraft/waveEdit, runW15runBankMiningStop, локали/логи/комменты по всему пакету (директива владельца).