textmachine/docs/archive/reports/PACK11_CONT_REPORT_2026-07-19.md

28 KiB
Raw Blame History

PACK11_CONT_REPORT — бэкенд-сессия ПРОДОЛЖЕНИЯ, резидуал R1R5 (2026-07-19)

Продолжение PACK11_REPORT.md (Block A, 85f5b9e). Строил по docs/architecture/11-implementation-plan.md (ратифицирован D39.12) + PACK11_REPORT.md §Резидуал (пошаговые дизайны R1R5). НЕ коммитил — сдача на приёмку. Верификация исполнением: go build ./... && go vet ./... && go test -race ./... — ВСЁ ЗЕЛЁНОЕ на каждом чекпойнте (логи в §Верификация). Block A НЕ трогал (под тестами).

0. Санкционированный порядок и итог

Развилка порядка эскалирована владельцу/оркестратору в начале сессии (R1 = крупный каскад, меняющий ГРАНУЛЯРНОСТЬ вывода → ~весь e2e-суит + read-models + golden; риск красного дерева vs жёсткое правило «суит зелёный на каждом чекпойнте»). Оркестратор санкционировал вариант 1: R2→R3→R4→R5 сначала (все самодостаточны и зелёно-достижимы независимо от R1), R1 — капстоуном на остатке бюджета, с условиями: чекпойнт-ревью после R2+R3 и после R4; R3 на текущем последовательном раннере; каждый снапшот-двигающий лендинг — свой golden re-capture с masked-диффом, СТРУКТУРНЫЙ рерайт — один раз в R1; R1 сначала аддитивные под-шаги без каскада, переключение драйвера — финальный атомарный шаг; не дотягивает до зелёного → дерево зелёное, R1 точным резидуалом (запланированный исход).

WS / шаг Статус Верификация
R2 · WS3 Go-майнер СДАНО Go↔Python паритет EXACT: 13618 канд-СЕТ, катастроф {方源:0,蛊:1,蛊师:2,古月:22}, recall@proposed 0.9649; alias-ярус-1 (5 R4-блоков+компози-гард); сид-дельта+MinedDeltaYAML; Source:mined-путь; tmctl seed-lint.
R3 · WS4 банкнота 12 точек СДАНО (на посл. раннере) срез-до-classify (fresh+resume); tm-banknote-v1: derived + re-point после эскалации; banknoteSnap (omitempty); v9-телеметрия; finish=stop-only гейт. 5 e2e-пинов.
R4a · tmctl export --pairs СДАНО source-столбец (пред-условие §5(д)-FP DC1/DC2).
R4b · чекеры DC1/DC2/DC6 + zh-ru пакет СДАНО эмпир-позитивы пойманы; DC2 байт-верно референсу (ok_re-supress + case-sensitive — фикс ревью); cheapGateVersion v3; golden re-capture masked-чист.
R4c · DC3 gender-инъекция СДАНО memoryEntry.gender→editor-констрейнт; renderFormatVersion v2; golden re-capture masked-чист.
R5 · WS6 арм-конфиги СДАНО pipeline-arm-mistral.yaml/pipeline-arm-deepseek-pro.yaml; echo_mine_test под варианты; rate-guard связка.
R1 · волновой ИСПОЛНИТЕЛЬ ⚠️ АДДИТИВ СДАН · драйвер-свитч РЕЗИДУАЛ snapshotIDForWave (пер-волновой снапшот, «переоплата ОДНА» пиннута) + W0 sticky-precompute (байт-идентично, golden-доказано) — СДАНЫ, зелёные. Параллельный dispatch W1/W1.5/W2 — резидуал (§R1-residual, дизайн ниже).
Чекпойнт-ревью ×3 R2+R3 (4 minor/nit, фикс) · R4 (2 DC2-parity, фикс) · финал (см. §Ревью). Адверсариал-воркфлоу, author≠reviewer, refute-by-default.

R2 — WS3 Go-майнер (план §3) — СДАНО

Новый пакет internal/pipeline/miner*.go (substrate/detect/patterns/palladius/alias/emit) — байт-верный порт frozen exp16 (exp16_common/detectors/patterns/arms/alias/palladius/emit_owner_sheets.py), дефолт-лемматизатор B. Per-буллет (§R2):

  • Порт V-A→V-C по ТОЧНЫМ порогам §3(б): c-value g(L)=log₂(L+1) (не log₂L — пин TestMinerLengthMultNonDegenerate/CValueSingleCharNested); subsumption α=0.80 (TestMinerSubsumption); weirdness p_book/max(word_rel,char_indep_rel,1e-9), N_book = полная пре-флор масса; n-граммы Han 16 overlapping, freq-floor 3; паттерн-каналы (surname/百家姓−凝, title-suffix/bare, ordinal_title, rank_grade, topo, авто-формант over_rep≥15∧partners≥3 — TestMinerFormantDetection); arm A3 λ=0, bonus·pw, subfloor bonus+40·f·pw. Детерминизм: все сорты (-score,-freq,src), map-итерация только ПЕРЕД тотал-сортом.
  • Дефолт B — дроп Палладий-подканала V-C (плюс dst_variants, is_name_lemma, spread λ, alias-R3, canon-dst-recovery — все pymorphy3-зависимые; dst доставляет банкнота WS4). is_palladius_token порт как детерминированный артефакт (Палладий-таблица, без pymorphy3) + тестируем (TestMinerPalladiusToken). Палладий-ЗАВИСИМЫЙ ранг 古月:21→22 — как предписано §10-14/§3(д); катастроф-скрин ∈top-50 PASS.
  • Палладий-ИНВАРИАНТНЫЕ гарантии — EXACT (TestMinerFullBookParity, data-gated TM_MINER_PARITY=1 на стенд-данных: jieba-контраст + rerun/records.json + guzhenren-seed-v2.yaml): n=13618 (SET-членство), recall@proposed 0.9649 (55/57 GT), катастроф {方源:0,蛊:1,蛊师:2,古月:22}. Скипается при отсутствии данных (как TM_LIVE). Python = референс: byte-verified, расхождений 0 по инвариантам.
  • Алиас-ярус-1 (R1R4, R4 hard-blocks ПЕРВЫМИ): TestMinerAliasRules (5 сабтестов): R1-extension (方源⊂古月方源), компози-гард (古月+族长=фраза-не-алиас, но 古月+方源=fullname-алиас), R4-iii/v (族长≠四代族长 по разному approved-dst), R4-ii (разный пол блок, hidden не блокирует), R4-i/R2 (одна фамилия+разные имена→family). R3 (shared-ru-rendering) ОМИТ в дефолте B (нужен dst_lemmas/pymorphy3) — задокументировано в коде.
  • Эмиссия сид-дельты §C2-7: MineBankMinedTerm с фильтрами build_precision30 (тип∈{name,place,title}, freq≥5, ∉subsumed, len≥2, ∉фрагмент по PARTICLE) — НЕ сырой 13618-дамп; alias-кластеризация (репрезентант = топ-ранг; кластер с seed-поверхностью = алиас-of-existing, не эмитится). MinedDeltaYAML → seedTerm-схема БЕЗ новых полей. TestMineBankDeterministicAndNonSeed/TestMinerEmissionFilters.
  • Source:"mined" mined-write-путь: minedToCandidates (штамп Source:mined, status:auto, no-dst, зеркало rubyToCandidates, НЕ через loadGlossarySeed). TestMinedToCandidatesStampsMinedSource + TestMinedDeltaStaysBaseBankStable (Source:mined НЕ двигает BaseVersion — «переоплата ОДНА» через реальный путь).
  • tmctl seed-lint: SeedLint(path) = сухой прогон РЕАЛЬНОГО loadGlossarySeed+approvedSharedKeyCollisions; CLI seed-lint --seed <yaml> (без --config). seedlint_test.go: чистый→OK; дефекты (approved-без-dst / unknown-status / duplicate / shared-key) → фейл-лауд; эмитированная дельта линтует чисто (TestSeedLintEmittedMinedDelta).

Артефакт-зависимость: прод-майнер грузит jieba-контраст (5MB, SHA 7197c321…, ВНЕ git) через LoadContrast(io.Reader) — путь конфигурируется вызывающим. Тесты — синтетический малый контраст; паритет — реальный jieba со стенда.

Не-каскадная зона R2: MineBank/minedToCandidates/MinedDeltaYAML — библиотека пак-3; ВЫЗОВ на W1.5-границе (реseed через ReplaceGlossary) — часть волнового W1.5-стопа (R1-residual). Сдано как самодостаточное, тестируемое ядро (санкция оркестратора: R2 самодостаточен, R1 вайрит).


R3 — WS4 банкнота: 12 интеграционных точек (план §4) — СДАНО (на посл. раннере)

Ядро banknote.go (Block A) + интеграция. Все точки пер-стадийные → в волновую модель переезжают КАК ЕСТЬ (применяются к W1/draft-стадии; условие оркестратора). Per-точка:

  1. Санитайзер: банкнота-срез — ПЕРВЫЙ рубеж (applyBanknote до classifyOutput); trailing-note НЕ реюзим (ревью-1 F3). Финал-бэкстоп = телеметрия п.10.
  2. cjk_leak: src-колонка Han по построению → срез ДО санитайзера снимает ложь.
  3. classify/echo (п.3, ЖЁСТКИЙ порядок): applyBanknote режет блок на ОБОИХ путях runAttempt (fresh-call И checkpoint-hit/resume) ДО classifyOutput → echo/coverage/length видят ОЧИЩЕННЫЙ перевод, не Han-сноску. Пин TestBanknoteClassifyOverCleaned (клин RU + Han-тяжёлый блок → OK, не cjk_artifact).
  4. Coverage: числитель по очищенному (срез раньше coverageCheck).
  5. max_tokens += bankTokenBudget (≤12×12) для translator при enabled (перевод первым, блок хвостом → трункация бьёт блок). Пин: draft max_tokens=656 (512+144) в e2e.
  6. request_hash/снапшот: banknoteSnap{enabled,parser_version} в snapshotID(). ДЕВИАЦИЯ-с-обоснованием: omitempty-указатель (nil при off) вместо безусловного {enabled:false} зеркала sanitizerSnap — включение канала И bump парсера ВСЁ РАВНО громкий сдвиг (несущая loudness §4а-п6 сохранена), но ДОБАВЛЕНИЕ фичи НЕ инвалидирует banknote-OFF книги (в т.ч. golden-фикстуру) → R3 golden БАЙТ-СТАБИЛЕН, re-capture не нужен. Пин TestBanknoteSnapshotFold.
  7. Чекпоинт/резюм: derived content-addressed → resume re-derives id бесплатно; сырой чекпоинт хранит RAW-ответ, resume пере-режет.
  8. Re-point (главная правка): OK-путь draft final_hashtm-banknote-v1: derived-чекпоинт с ОЧИЩЕННЫМ черновиком (commitBanknoteExport, формула §4б sha256("tm-banknote-v1\x00"+reqHash+"\x00"+stripped)); стоит ПОСЛЕ резолва эскалации, потребляет last.bankStripped (эскалированный черновик тоже очищен). Пины TestBanknoteSliceReTelemetryResume (final_hash=derived, derived=clean, resume $0) + TestBanknoteEscalatedDraftStripped.
  9. Пост-чек глоссария: не затронут.
  10. Телеметрия (лауд): store v9 (n_banknote_lines/banknote_parse_fail/banknote_truncated) в retrieval_state; тред draft→translateChunk→persistRetrievalState.
  11. DeepSeek thinking-ON нетронут (echoMineViolation).
  12. Экспорт: final без сноски автоматически (срез раньше).

§4б: толерантный парсер (Block A) + finish=stop-only гейт — кандидаты ПАРСЯТСЯ только при finish=="stop"; срез идёт РЕГАРДЛЕСС (echo/length classify всегда видят clean). Пин TestBanknoteFinishStopOnlyGate.


R4 — WS5 чекеры + данные пакета + export --pairs (план §5) — ЯДРО СДАНО

  • R4a tmctl export --pairs: Export(pairs bool) + ChunkExport.Source (omitempty, $0 из манифеста); дефолт-экспорт target-only (обратно-совместим). CLI --pairs. TestExportPairsIncludesSource. Пред-условие §5(д)-FP DC1/DC2 (src↔target). Read-only проекция — снапшот/wire/деньги не трогает.
  • R4b DC1/DC2/DC6 + zh-ru пакет (checkers_zh_ru.go, порт ws5_checkers_verify.py): наблюдаемость (НЕ диспозиция), в runCheapGates→retrieval_state; самогейтятся на контенте (0 на не-zh/чистом). DC2 — байт-верно референсу (после ревью-фикса): ok_re-suppression гард (数十万 не фаерит при верном «сотни тысяч»)
    • case-SENSITIVE inner (референс без re.I). TestDC1/DC2/DC6 (эмпир-позитивы + non-firing + parity-кейсы).
  • R4c DC3 gender-инъекция (§5(б), «фикс Бай Нинбин»): memoryEntry.genderrow.Gender; genderConstraintNote в renderEditorConstraintBlock (male/female жёсткие формы; hidden — мандат безродовых); ДАННЫЕ пола уже в MemoryVersion, РЕНДЕР — в renderFormatVersion v2. TestDC3GenderInjection.
  • Пакет данных = код-консты, версия cheapGateVersion (та же дисциплина, что существующие cheap-данные): правка данных = громкий resnapshot. Конфиг-загружаемый per-pair пакет — чище, резидуал (§Резидуал).

Golden re-capture ×2 (masked-чист, §Golden): (1) cheapGateVersion v2→v3 — единственная payload-правка = style_check_version, wire/вердикты байт-идентичны после hash-маскинга; (2) renderFormatVersion v1→v2 — payload-версия + 7 editor-wire-тел получают gender-note (gender:m терм фикстуры), НИ ОДНОЙ вердикт/final-правки.


R5 — WS6 арм-конфиги (план §6) — СДАНО

  • Армы = КОНФИГ: configs/pipeline-arm-mistral.yaml (editor=mistral-large-2512) / pipeline-arm-deepseek-pro.yaml (editor=deepseek-v4-pro, few_shot:false). Оба — копии боевого C1 с ЕДИНСТВЕННОЙ правкой editor-model → другой stageSnap.Model → другой снапшот (арм-механика Block A). draft/ translator неизменны.
  • echo_mine_test под варианты (TestSwapArmConfigs): каждый арм грузится; editor=арм-модель; НИ ОДИН не вооружает эхо-мину (deepseek-v4-pro → ReasoningNone, thinking ON, echoMineViolation пуст на echo-prone провайдере); few_shot (deepseek off / mistral nil=ON); mistral-large-2512 несёт rate_limit (WS6 пред-условие — свап-арм за rate-guard WS1b).
  • Атрибуция арма: editor-model уже в chunk_status/checkpoint model_actual → export/report несут её БЕЗ новой кодовой правки (план: «уже в chunk_status»). Пере-прогон армов = отдельные джобы (разные снапшоты).

R1 — волновой ИСПОЛНИТЕЛЬ: аддитив СДАН, драйвер-свитч РЕЗИДУАЛ

Санкция оркестратора: сначала аддитивные под-шаги без каскада; драйвер-свитч — финальный атомарный шаг; не дотягивает → зелёное дерево + точный резидуал (запланированный исход). Сделано:

  • Пер-волновой снапшот (snapshotIDForWave, §1в — несущее): рефактор snapshotID()buildSnapshotID(stages, memVersion) (байт-идентичен для all-stages+enriched → golden/последовательный драйвер НЕ двигаются). snapshotIDForWave(W1) = translator-стадии + BASE-версия (excl mined); (W2) = editor-стадии + ENRICHED. waveStages (партиция по роли), baseMemoryVersion. Пин TestSnapshotIDForWave: W1≠W2; mined-approved добавка двигает ТОЛЬКО W2, W1 стабилен («переоплата ОДНА» на снапшот-уровне) + TestWaveStagesPartition.
  • W0 sticky-precompute (wave.go precomputeSticky, §1б): sticky-цепочка — кросс-чанковая зависимость (не пере-считать внутри параллельной волны) → предвычислена в W0 одним проходом (пер-глава reset, Select, advance). Последовательный драйвер ТЕПЕРЬ ЕЁ КОНСУМИТ (bookrun.go W0-precompute; translateChunk принимает готовый memSel, снят inline-Select + activeIDs-return). БАЙТ-ИДЕНТИЧНОTestGolden зелёный без правок (несущий байт-паритет-пруф). Это ТОЧНЫЙ precompute, который консумит волновой исполнитель.

РЕЗИДУАЛ R1 — драйвер-свитч (точный дизайн, PACK11_REPORT.md §R1 + план §1):

  1. Партиция стадий по волне — ГОТОВА (waveStages); пер-волновой снапшот-апсерт (два UpsertSnapshot) — рефактор bookrun.go W0-прелюдии (сейчас один snapshotID; заменить на snapshotIDForWave(W1/W2), пинить стадии волны к своему через EnsureJob(snapshot_Wx)).
  2. W1-dispatch: N воркеров тянут draft-чанки, runStage(draft) под snapshot_W1, memSel=precompute[i] — ГОТОВЫ: precompute, rate-guard (Block A), single-writer деньги (Block A), eager-clients (Block A).
  3. W1.5-стоп: пустая mined-дельта → авто-продолжение; иначе MineBank→карта-подписи→подпись→minedToCandidatesReplaceGlossary(reseed)→snapshot_W2компоненты ГОТОВЫ (R2), нужна оркестровка стопа/резюма.
  4. W2-dispatch: N воркеров тянут edit-ЕДИНИЦЫ (EditUnitID, Block A), prev=конкатенация draft-чекпоинтов единицы по final_hash, СВЕЖИЙ Select по тексту ВСЕЙ единицы над enriched-банком, runStage(edit) под snapshot_W2. W2 НИКОГДА не пере-рендерит draft (несущий инвариант — редактор читает по final_hash).
  5. Каскад (почему не в этой сессии): edit=единица меняет ГРАНУЛЯРНОСТЬ вывода (edit per-UNIT, адресуется (chapter, firstChunkIdx, "edit")) → ChunkOutcome-форма + export/status/quality read-models + ~весь e2e-суит (asserts per-chunk 2-стадии + call-counts) + СТРУКТУРНЫЙ golden-рерайт. Оркестратор явно санкционировал структурный рерайт «один раз в R1»; аддитив-пруф (golden байт-стабилен) даёт уверенность, что все несущие R2R5 компоненты на месте для атомарного свитча в отдельной сессии/окне.
  6. kill-9 с N резервами: порт store/kill9_test.go на N воркеров (money-инвариант Block A уже single-writer-safe).

Открытые эскалации / решения (флаг оркестратору при лендинге)

  1. Дефолт-B майнер — ратифицированный §10-14 дроп pymorphy3-подканалов (spread λ / dst_variants / is_name_lemma / Палладий-конфирм / alias-R3 / canon-dst). 古月:22 (не 21). Все Палладий-ИНВАРИАНТНЫЕ гарантии воспроизведены EXACT. dst доставляет банкнота. Faithful-A (полный pymorphy3-порт) — только при готовой поддерживаемой Go-морфологии (не строил).
  2. banknoteSnap omitempty (не безусловный sanitizerSnap-зеркало) — де­виация-с-обоснованием (§R3-п6): loudness сохранена, off-книги не re-bill-ятся, R3 golden байт-стабилен. Требует подтверждения при лендинге.
  3. DC-пакет данных = код-консты (версия cheapGateVersion), не конфиг-загружаемый per-pair файл — конфиг-форма резидуал (§Резидуал). Дисциплина resnapshot сохранена.
  4. R4 резидуал (честный, бюджет→R1): DC7 grade/role-чекер (нужен promote 甲乙丙丁/四代族长 approved — owner-only, §5-A2 UNVERIFIED); DC3 male/female-ЧЕКЕР (нужна coref-lite, §5(д)-FP-гейт); omission-бэкстоп (лёгкий Go-ru-стеммер без pymorphy3 + лемма-матч по {dst,decl.forms,aliases}); DC2 word↔word дроби (q4a rule_b1/b2). Дизайны — план §5(б).
  5. R1 драйвер-свитч резидуал (запланированный исход, санкция оркестратора) — дизайн §R1 выше.
  6. §5(д)-FP-замер DC1/2/3/6 — платный пере-прогон (не в этой сессии); export --pairs — пред-условие СДАНО. ⚠ FP-замер прогонять через Go-код (не ws5_checkers_verify.py), т.к. чекеры теперь байт-верны референсу, но лендинг-решение должно отражать что реально шипается.

Структурные переносы

  • Новые файлы internal/pipeline/miner_*.go (9 шт — новый детектор-пакет, не рефактор), checkers_zh_ru.go, wave.go (W0-precompute). Все — новые оси.
  • snapshotID() → тонкая обёртка над извлечённым buildSnapshotID(stages, memVersion) (байт-идентично).
  • translateChunk сигнатура: stickyPrev map[...]memSel memorySelection (готовый из W0); снят activeIDs-return (precompute владеет sticky-advance). Внутренний вызов, вне-скоуп поведение не затронуто.
  • Export()Export(pairs bool) (6 тест-вызовов обновлены на Export(false)).

Багфиксы вне плана

  • Нет вне-плановых. Правки по ревью: R2+R3 (4): derived-namespace banknote-v1tm-banknote-v1 (§4б EXACT-формула, отдельная const от parser_version), мёртвая rank-карта в MineBank, doc-коммент, error-wrap commitBanknoteExport. R4 (2): DC2 ok_re-suppression-гард + case-sensitive inner (байт-паритет референсу). Все верифицированы адверсариально, пофикшены, суит зелёный.

Golden re-capture (masked-структурный дифф — обязан быть объясним)

  • R4b (cheapGateVersion v2→v3): masked-дифф = 4 строки, ЕДИНСТВЕННАя payload-правка style_check_version; wire-тела + вердикт-строки БАЙТ-ИДЕНТИЧНЫ после hash-маскинга (verified скриптом).
  • R4c (renderFormatVersion v1→v2): masked-дифф = 18 строк: 4 версия-payload + 7×(old/new) editor-wire-тел с gender-note (gender:m терм). НИ ОДНОЙ вердикт/disposition/final-text правки (Counter: version/gender-note/wire только).
  • R3/R5/R1-аддитив: golden НЕ двигается (banknote omitempty off; армы — отдельные файлы; snapshot-рефактор байт-идентичен). СТРУКТУРНЫЙ golden-рерайт — событие драйвер-свитча (R1-residual), не этой сессии.

Верификация (исполнением — логи, не слова)

$ go build ./...          → OK
$ go vet ./...            → OK (чисто)
$ go test -race ./...     → ВСЁ ЗЕЛЁНОЕ:
  ok cmd/tmctl · config · ledger · llm · pipeline (~28s -race) · store
$ gofmt -l <мои файлы>    → пусто (репо несёт ПРЕ-существующий gofmt-долг в не-моих файлах — не трогал)
$ TM_MINER_PARITY=1 go test -run TestMinerFullBookParity
  → n=13618 · катастроф {方源:0,蛊:1,蛊师:2,古月:22} · recall@proposed=0.9649 (55/57 GT) — PASS

Фикстуры плана исполнены: miner-паритет (Палладий-инвариант EXACT, 古月:22 дефолт-B) · банкнота 12 точек (5 e2e-пинов) · seed-lint 0 фейл-лаудов · DC-позитивы пойманы · пер-волновой снапшот «переоплата ОДНА» · W0 sticky байт-паритет (golden) · draft request_hash стабилен (golden).

Чекпойнт-ревью (адверсариал-воркфлоу, author≠reviewer, refute-by-default, 3 линзы)

  • R2+R3: 5 сырых → 4 confirmed (все minor/nit, 1 refuted): tm-banknote-v1 namespace / мёртвая rank / doc / error-wrap. НИ ОДНОГО major correctness/wire/money. Все пофикшены.
  • R4: 2 confirmed (оба DC2 reference-divergence): ok_re-supress-гард (MAJOR — реальный FP-дивердж на ch2/chunk0) + case-sensitivity (MINOR). Оба пофикшены — DC2 байт-верно референсу.
  • Финал (R5 + R1-аддитив + кросс-каттинг инвариантов, 2-мерный воркфлоу + verify): 0 находок — ЧИСТЫЙ вердикт. Обе линзы (R1-аддитив байт-паритет/пер-волновой снапшот/precompute-эквивалентность; R5-армы + кросс-каттинг: деньги/снапшот-дисциплина/детерминизм/store-v9) вернули пустой список. Ни одного invariant- break, un-folded wire/verdict-change, byte-parity-break или map-order-в-выводе. go test -race ./... зелёный, golden байт-стабилен, arm/wave-тесты проходят — clean bill.