textmachine/eval/softsign-decl/README.md

15 KiB
Raw Blame History

softsign-decl — разрыв мягкого знака в декл-пост-чеке банка (fix-pack §а, finding-1)

Замер $0 к backend-бэклог-строке 91 (finding-1): чем закрывать разрыв, когда мягкознаковое имя («Фан Юань», «Винный червь») склоняется («Фан Юаня», «Винного червя»), а стеммер §3 «ь» не стрипает, и потому декл-форму НЕ принимает. Наивное «добавить «ь» в decl_suffix» отвергнуто в fix-pack (глобальная коллизия «Синь/Линь»→«синий/линия»). Вопрос: движковый anchor-gated матч ЛУЧШЕ ли сид-перечисления — и безопасен ли он на реальном корпусе, а не на бумаге.

Метод (конвенция полигона)

  • Харнесс main.go — БОЕВОЙ стеммер lang.NewTargetStemmer(lang.TargetChecksFor("ru")); build.sh материализует go.mod+replace ВНЕ репо, пин бэкенда на коммит (git archive) — прецедент pkg7/minerharness. Ноль сети, ноль моделей, $0. Пин прогона: 78b7b34 (HEAD на момент замера — «ь» уже реверсирован, D39.75).
  • Корпус: банк ~/books/gu-zhenren/coldrun-a/BANK-FULL.tsv (147 уник. dst) × боевой 25-глав-выход ~/books/gu-zhenren/acceptance/guzhenren-25ch-ru.txt (37 480 слов, editor=glm-5, приёмка Ф1).
  • Сырьё/выход — durable ~/books/gu-zhenren/softsign-decl/ (правило 9): softsign_probe_out.txt (отчёт харнесса), adversarial-review.txt (author≠reviewer прогон).
  • Правило 10 (вскрытие единиц + 2-й путь счёта): каждый агрегат ниже сверен независимым grep -oE по боевому выходу; A0-реимплементация харнесса сверена с реальным (unexported) dstFormPresent in-package пробой — 0/29 расхождений — до порта на экспортные символы.

Четыре правила матчинга декл-окна (база-цельнословная не меняется — она уже ловит номинатив):

  • A0 — боевое: declinedFormPresent (mempostcheck.go:169-191), «ь» НЕ стрипается.
  • A1 — наивное: «ь» в реестр, на КАЖДЫЙ компонент, БЕЗ якоря (отвергнутое; для контроля вреда).
  • A2 — anchor-gated: мягкий стрип разрешён, если ДРУГОЙ компонент совпал ТОЧНО.
  • A3 — anchor-gated: мягкий стрип разрешён, если ДРУГОЙ компонент совпал по НОРМАЛЬНОМУ стему (⊇ A2).

Цифры

Разрыв реален, но КОНЦЕНТРИРОВАН и много-словный

29 из 147 dst несут мягкознаковое слово. Захват окон над A0 (occurrence-level):

dst слов soft→stem A0 A3 A3A0 статус
Фан Юань 2 юань→юан 324 508 +184 TRUE (склонение хвоста «Юаня/Юаню/Юанем/Юане», якорь «фан» точный)
Винный червь 2 червь→черв 46 113 +67 TRUE (оба слова склонны: «Винного червя»; A2 промахивает ВСЕ 67)
ветвь Чи 2 ветвь→ветв 0 3 +3 TRUE (soft в ГОЛОВЕ, якорь «чи» точный)
ветвь Фан 2 ветвь→ветв 1 3 +2 TRUE
демонический путь 2 путь→пут 1 2 +1 TRUE («демонического пути»)
горная крепость 2 крепость→крепост 0 1 +1 TRUE («горной крепости»)
девять рангов 2 девять→девят 1 2 +1 МЯГКАЯ НЕТОЧНОСТЬ (см. ниже)

Итого A3A0 ≈ 259 окон, 258 истинных + 1 мягкая. A2A0 = 189 (A2 промахивает 70 истинных «Винного червя» и др. — нет ТОЧНОГО якоря → A2 хуже A3).

Практический эффект — на ПОРЯДОК меньше 259, и число НЕ робастно

Пост-чек срабатывает ПО-ЧАНКОВО (наличие любой принятой формы в чанке), не по вхождениям. Частые имена почти всегда имеют номинатив в том же чанке. «Склонён-ТОЛЬКО (номинатив отсутствует)» — СИЛЬНО зависит от размера блока (2-й путь счёта, самопроверка поймала завышение):

блок ~симв Фан Юань Винный червь Мочэнь Чилянь ИТОГО/25 глав
800 32 22 2 1 57
1500 8 16 1 1 26
3000 0 12 1 0 13
6000 0 6 1 0 7
12000 0 4 0 0 4

Боевой чанк ≈ 12k ТОКЕНОВ (~612k симв) → честный per-chunk промах = ~47 на 25 глав, почти весь — «Винный червь»; «Фан Юань» на реальном размере чанка → 0 (номинатив всегда рядом). Прежняя оценка «~26» была артефактом блока 1.5k симв (меньше боевого чанка) — снята. ⚠ Это ПРОКСИ (не боевой чанкер; faithful — через tmctl export границ чанков). Порядок величины (одноцифровой, один термин) робастен.

A1 отвергнут корректно — вред ГЛОБАЛЬНЫЙ, книго-независимый

M4: softSameStem("Синь","синим")=true, ("Линь","линий")=true, ("Тень","теней")=true — стем совпадает («син/лин/тен»). Голое «ь» в реестр поражает глобальный SameStem, а через него — весь путь declinedFormPresent, т.е. и Postcheck, и SpoilerLeaks, в любой книге. Единственный функциональный потребитель SameStemdeclinedFormPresent (mempostcheck.go:181); seeding.go:138 только конструирует стеммер; voice.go его НЕ подключает.

Adversarial-ревью (author≠reviewer) — что оно сломало

A3 не закрывает коллизию Синь/Линь — оно её ПЕРЕНОСИТ с одно-словного класса на форму [общий-титул] + [ь-фамилия] (самая частая много-словная форма имени в жанре):

  • Перенос (структурно ВЕРНО, здесь НЕ реализовано): гипотетический банк-термин «старейшина Синь» под A3 ложно примет «старейшина синего» (титул «старейшина» — нормальный якорь, «синего»→«син» мягко). В gu-zhenren этого НЕТ: 18 из 30 soft-строк — CJK-транслит (юан/шэн/чилян/мочэн), чьи 3-руновые стемы просто не русские слова. Чистый прогон — транслит-везение корпуса, не свойство гейта. Форма-риска в банке есть («старейшина школы», «вождь четвёртого поколения»); реализуется в книге с русско-коллизионной фамилией. (Проверка: «старейшины синеватый» ×1 в выходе есть, но «синеватый»→«синеват»≠«син» — не сработало бы; точный «старейшина синего» — конструкт, не корпус.)
  • Числовой класс — системно и НЕ защищён (реально на корпусе): каждый ь-финальный кардинал (девять/десять/пять/шесть…) мягко сливается с ординалом/косвенной формой. В выходе: «пятого ранга» ×13, «шестого ранга» ×10, «девятого ранга» ×1 — поверхность 24+ вхождения; якорь («ранга») — общее слово, гейт даёт ноль защиты. «девять рангов»~«девятого ранга» — та самая 1 реализованная мягкая неточность. Класс растёт со счётом рангов (плотный в культивации).
  • Поправка по SpoilerLeaks: мой тезис «A3 не трогает SpoilerLeaks» — неверно обоснован. declinedFormPresent достижим из SpoilerLeaks (mempostcheck.go:254) → A3 его МЕНЯЕТ. Спасает не «неприкосновенность», а то, что SpoilerLeaks — observability-only, не диспозиция (mempostcheck.go:241): мягкий ложняк там = шум, не флип гейта. A3 всё же СТРОГО лучше A1 для SpoilerLeaks (закрывает одно-словный глобальный яд), но не чист (много-словный титул+фамилия остаётся).
  • Ни один вариант не доминирует: min-стем-гейт бесполезен («юан» и «син» оба 3 руны — не разделить); tail-restricted (soft только на последнем компоненте) точнее здесь, но теряет «ветвь Чи/Фан» (голова склоняется) и всё равно ловит хвост-фамилию; стоплист коллизий — хорош как ДОБАВКА, плох соло (открытый список — форма, от которой target-ru.txt:19 уже предостерегает); сид-перечисление — золото для подписного канона, но не масштабируется на авто-майненный хвост.

Вывод и рекомендация (на ратификацию, НЕ строить без слова владельца)

Заострено после 2-го адверсариал-прогона (аудит кода+рекомендации). Код харнесса — TRUSTWORTHY (6/6 NO-BUG, 147 dst сверено с файлом). Рекомендация исправлена: §3 ниже НЕ «ноль действия».

  1. Разрыв реален, но мал: ~47 ложных промахов/25 глав на реальном размере чанка, почти весь — ОДИН термин «Винный червь» («Фан Юань» → 0 per-chunk). Числа окон код-верифицированы.
  2. Голое A3 НЕ рекомендуется: переносит коллизию на титул+фамилию (риск в другой книге) и не защищает числовой класс; чистота на gu-zhenren — транслит-везение (пара-зависимое доказательство).
  3. Стопгэп для finding-1 — сид-перечисление декл-форм для 12 ВИДИМЫХ мягких канон-имён («Винный червь»; «Фан Юань» если важен SpoilerLeaks): declForms матчатся containsWholeWord ТОЧНО — ноль стемминга, ноль коллизии, ноль правок Go; данные книжного канона в сиде — легитимная общность. ⚠ Честно: decl.formsРУКО-ПИСАНЫЙ сид (memseed.go:89), майнер морфологию НЕ майнит (его «decl» = ОТВЕРГНУТЫЕ поверхности, miner_emit.go:60); генератора склонений в Go нет. Значит для ВСЕГО мягкого класса это = ручной труд «142/142-null», который §3 и убивал → сид-перечень оправдан ТОЛЬКО как ограниченный стопгэп на горстку видимых имён, НЕ системно.
  4. Движковый anchor-gated матч — не «отложить» отмашкой, а ЗАГЕЙТИТЬ на корпус-замере частоты ложняков переноса ([титул]+[ь-фамилия]) и числового класса — они НЕ измерены (M4 = 3 руко-выбранные пары, одна книга = предварительно, CLAUDE.md §4), а не опровергнуты. Форма, если строить — СЛОЙНАЯ (tail-restricted + стоплист ь-кардиналов), данные в target-ru.txt, алгоритм generic; но ядро Go не трогать, пока кросс-парный замер (≥2 пары) не оправдает.
  5. Backend-действие сейчас: NB-коммент + регресс-гвоздь (stemmer_test.go, TestSoftSignNamesDoNotCollideWithCommonWords) ОСТАЮТСЯ как страховка от наивного ре-адда «ь». Гвоздь «Линь/линию» инертен — заменить на «Линь/линий» (M4 подтверждает: «линий»→«лин»).
  6. Долгий общий дом (НЕ существует сегодня): майнер-морфология (наблюдать склонённые поверхности, а не кодировать «ь») был бы пара-агностичен — но это ОТДЕЛЬНЫЙ механизм bank-autonomy, не бесплатный и не «минер уже майнит» (ранняя формулировка была неверна — такого пути нет).

Воспроизвести

cd eval/softsign-decl && ./build.sh <work-dir> HEAD
<work-dir>/softsign-decl/softsign-decl \
  -bank ~/books/gu-zhenren/coldrun-a/BANK-FULL.tsv \
  -output ~/books/gu-zhenren/acceptance/guzhenren-25ch-ru.txt \
  -report ~/books/gu-zhenren/softsign-decl/softsign_probe_out.txt

Прогон ~135с (O(слова×термины×режимы)). go.mod/replace — вне репо; второго модуля в git не появляется.