15 KiB
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)dstFormPresentin-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 | A3−A0 | статус |
|---|---|---|---|---|---|---|
| Фан Юань | 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 | МЯГКАЯ НЕТОЧНОСТЬ (см. ниже) |
Итого A3−A0 ≈ 259 окон, 258 истинных + 1 мягкая. A2−A0 = 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 |
Боевой чанк ≈ 1–2k ТОКЕНОВ (~6–12k симв) → честный per-chunk промах = ~4–7 на 25 глав, почти весь —
«Винный червь»; «Фан Юань» на реальном размере чанка → 0 (номинатив всегда рядом). Прежняя оценка
«~26» была артефактом блока 1.5k симв (меньше боевого чанка) — снята. ⚠ Это ПРОКСИ (не боевой чанкер;
faithful — через tmctl export границ чанков). Порядок величины (одноцифровой, один термин) робастен.
A1 отвергнут корректно — вред ГЛОБАЛЬНЫЙ, книго-независимый
M4: softSameStem("Синь","синим")=true, ("Линь","линий")=true, ("Тень","теней")=true — стем
совпадает («син/лин/тен»). Голое «ь» в реестр поражает глобальный SameStem, а через него — весь путь
declinedFormPresent, т.е. и Postcheck, и SpoilerLeaks, в любой книге. Единственный функциональный
потребитель SameStem — declinedFormPresent (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 ниже НЕ «ноль действия».
- Разрыв реален, но мал: ~4–7 ложных промахов/25 глав на реальном размере чанка, почти весь — ОДИН термин «Винный червь» («Фан Юань» → 0 per-chunk). Числа окон код-верифицированы.
- Голое A3 НЕ рекомендуется: переносит коллизию на титул+фамилию (риск в другой книге) и не защищает числовой класс; чистота на gu-zhenren — транслит-везение (пара-зависимое доказательство).
- Стопгэп для finding-1 — сид-перечисление декл-форм для 1–2 ВИДИМЫХ мягких канон-имён
(«Винный червь»; «Фан Юань» если важен SpoilerLeaks):
declFormsматчатсяcontainsWholeWordТОЧНО — ноль стемминга, ноль коллизии, ноль правок Go; данные книжного канона в сиде — легитимная общность. ⚠ Честно:decl.forms— РУКО-ПИСАНЫЙ сид (memseed.go:89), майнер морфологию НЕ майнит (его «decl» = ОТВЕРГНУТЫЕ поверхности, miner_emit.go:60); генератора склонений в Go нет. Значит для ВСЕГО мягкого класса это = ручной труд «142/142-null», который §3 и убивал → сид-перечень оправдан ТОЛЬКО как ограниченный стопгэп на горстку видимых имён, НЕ системно. - Движковый anchor-gated матч — не «отложить» отмашкой, а ЗАГЕЙТИТЬ на корпус-замере частоты
ложняков переноса ([титул]+[ь-фамилия]) и числового класса — они НЕ измерены (M4 = 3 руко-выбранные
пары, одна книга = предварительно, CLAUDE.md §4), а не опровергнуты. Форма, если строить — СЛОЙНАЯ
(tail-restricted + стоплист ь-кардиналов), данные в
target-ru.txt, алгоритм generic; но ядро Go не трогать, пока кросс-парный замер (≥2 пары) не оправдает. - Backend-действие сейчас: NB-коммент + регресс-гвоздь (
stemmer_test.go,TestSoftSignNamesDoNotCollideWithCommonWords) ОСТАЮТСЯ как страховка от наивного ре-адда «ь». Гвоздь «Линь/линию» инертен — заменить на «Линь/линий» (M4 подтверждает: «линий»→«лин»). - Долгий общий дом (НЕ существует сегодня): майнер-морфология (наблюдать склонённые поверхности, а не кодировать «ь») был бы пара-агностичен — но это ОТДЕЛЬНЫЙ механизм 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 не появляется.