textmachine/eval/exp13_seed_v2.py

160 lines
15 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""exp13 — ФИНАЛИЗАЦИЯ сида v2 (D30.5). Единый воспроизводимый трансформ v1 → v2,
СУПЕРСИДИТ exp12_glossary_v2.py (дефект провенанса/статусов из ревью D30.5):
• спорные термины → status:DRAFT (не approved!): 修炼/修行 (культивация), 人祖 (Первопредок),
род «гу»-зависимые (本命蛊 жизненный гу, 蛊虫 гу-червь), 花酒行者 (перезаписывал пожелание владельца);
• бесспорные правки/добавления (D30.5: 11 правок + 8 добавлений минус спорные) → approved;
• откат тихих промоушенов draft→approved (花酒行者, 本命蛊 остаются draft);
• обогащение мн.ч. (D24.4/полигон-№4): плюрал-формы в decl.forms счётных 元石/凡人/蛊师;
• gu-gender: НЕ флипаю молча (рекоменд. флагманов «мужской») — проектное решение владельца, в sign-off.
Вход: guzhenren-seed.yaml. Выход: guzhenren-seed-v2.yaml + diag/glossary_v2_signoff.md. ВНЕ git. НЕ в прод без подписи+resnapshot.
"""
from __future__ import annotations
import yaml
from pathlib import Path
BOOK = Path("/home/ubuntu/books/gu-zhenren")
SRC = BOOK / "guzhenren-seed.yaml"
OUT = BOOK / "guzhenren-seed-v2.yaml"
SIGNOFF = BOOK / "diag" / "glossary_v2_signoff.md"
DRAFT = "draft" # спорное — до подписи владельца
OK = "approved"
# src -> (new_dst, forms|None, status, contested_reason|None, rationale)
CHANGES = {
"空窍": ("апертура", ["апертуры", "апертуре", "апертуру", "апертурой", "апертуре"], OK, None,
"оба флагмана — топ-проблема: «врата» ломают грамматику («одни врата»); «апертура» = фан-вики жанра"),
"开窍": ("открытие апертуры", ["открытия апертуры", "открытию апертуры", "открытием апертуры", "открытии апертуры"], OK, None,
"согласовано с 空窍→апертура; 开窍大典 = обряд открытия апертуры"),
"真元": ("истинная ци", ["истинной ци", "истинной ци", "истинную ци", "истинной ци", "истинной ци"], OK, None,
"оба: НЕ «истинная энергия» (физикализм); ци — жанровая норма; отделить от 元气"),
"元海": ("море истинной ци", ["моря истинной ци", "морю истинной ци", "морем истинной ци", "море истинной ци"], OK, None,
"согласовано с 真元→истинная ци"),
"元石": ("изначальный камень", ["изначального камня", "изначальному камню", "изначальный камень", "изначальным камнем", "изначальном камне"], OK, None,
"«первокамень» → «двадцать первокамень» читается как «двадцать первый»; согласован с рядом 元"),
"古月山寨": ("селение Гуюэ", ["селения Гуюэ", "селению Гуюэ", "селением Гуюэ", "селении Гуюэ"], OK, None,
"«стан» = воинский лагерь; 山寨 здесь — укреплённое селение/деревня"),
"白家寨": ("селение Бай", ["селения Бай", "селению Бай", "селением Бай", "селении Бай"], OK, None, "согласовано"),
"熊家寨": ("селение Сюн", ["селения Сюн", "селению Сюн", "селением Сюн", "селении Сюн"], OK, None, "согласовано"),
# --- СПОРНЫЕ → DRAFT ---
"本命蛊": ("жизненный гу", ["жизненного гу", "жизненному гу", "жизненного гу", "жизненным гу", "жизненном гу"], DRAFT,
"род «гу» + выбор рендеринга", "оба: НЕ «гу Жизни» (читается как ВИД). СТАТУС, не вид. Согласование зависит от рода «гу»"),
"花酒行者": ("Странник Цветов и Вина", ["Странника Цветов и Вина", "Страннику Цветов и Вина", "Странника Цветов и Вина", "Странником Цветов и Вина", "Страннике Цветов и Вина"], DRAFT,
"перезаписывает пожелание владельца «Винный Странник»", "флагманы: сохранить 花 (=блуд; 采花大盗). Владелец предпочёл «Винный Странник» — за ним"),
"蛊虫": ("гу-червь", ["гу-червя", "гу-червю", "гу-червя", "гу-червём", "гу-черве"], DRAFT,
"род «гу»", "«гу-тварь» пейоративно; «гу-червь»/«гу». Согласование зависит от рода «гу»"),
}
# новые термины: dict(src,dst,type,forms|None,invariant,status,contested_reason|None,note)
ADDITIONS = [
dict(src="长生", dst="бессмертие", type="term", forms=["бессмертия", "бессмертию", "бессмертие", "бессмертием", "бессмертии"],
status=OK, contested=None, note="chángshēng; вечная жизнь/бессмертие — сквозная тема; НЕ «долголетие»"),
dict(src="族长", dst="глава клана", type="title", forms=["главы клана", "главе клана", "главу клана", "главой клана", "главе клана"],
status=OK, contested=None, note="zúzhǎng; глава клана — НАД старейшинами; черновик путал со «старейшиной». ⚠ консистентность род/клан — см. sign-off"),
dict(src="家老", dst="старейшина", type="title", forms=["старейшины", "старейшине", "старейшину", "старейшиной", "старейшине"],
status=OK, contested=None, note="jiālǎo; старейшина клана; развести с 族长"),
dict(src="困境", dst="Беды", type="term", forms=["Бед", "Бедам", "Беды", "Бедами", "Бедах"],
status=OK, contested=None, note="kùnjìng; стая зверей в легенде о Первопредке; имя стаи"),
dict(src="炼化", dst="очистить и подчинить", type="term", forms=[], invariant=True,
status=OK, contested=None, note="liànhuà; очистить-и-подчинить живого гу; НЕ «переплавить». Кратко «очистить»"),
# --- СПОРНЫЕ → DRAFT ---
dict(src="修炼", dst="культивация", type="term", forms=["культивации", "культивации", "культивацию", "культивацией", "культивации"],
status=DRAFT, contested="лит.альтернатива «совершенствование» (флагманы для издания)", note="xiūliàn; культивация/культивировать"),
dict(src="修行", dst="культивация", type="term", forms=["культивации", "культивации", "культивацию", "культивацией", "культивации"],
status=DRAFT, contested="как 修炼", note="xiūxíng; практика культивации; согласовано с 修炼"),
dict(src="人祖", dst="Первопредок", type="name", forms=["Первопредка", "Первопредку", "Первопредка", "Первопредком", "Первопредке"],
status=DRAFT, contested="альт-имя «Жэнь Цзу» (Палладица)", note="Rénzǔ; говорящее имя (Праотец человечества). НЕ «Рен Зу» (англ.релей-ошибка)"),
]
# обогащение мн.ч. (D24.4): src -> плюрал-формы (append к decl.forms), закрывает inflection_gap
PLURALS = {
"元石": ["изначальные камни", "изначальных камней", "изначальным камням", "изначальными камнями", "изначальных камнях"],
"凡人": ["смертные", "смертных", "смертным", "смертными"],
"蛊师": ["гу-мастеров", "гу-мастерам", "гу-мастерами", "гу-мастерах"],
}
GU_FLAG = ("РОД «гу»: сид держит НЕСКЛОНЯЕМЫЙ СРЕДНИЙ; оба флагмана рекомендуют МУЖСКОЙ («этот гу», как «жук»; "
"согласуется с «винный червь — он»). Меняет согласование во ВСЕХ гу-именах — НЕ флипаю молча. "
"Жизненный гу/гу-червь даны в мужском как рекомендация под status:draft. Решение — за владельцем.")
def main():
data = yaml.safe_load(SRC.read_text(encoding="utf-8"))
terms = data["terms"]
changed, added, plural_done, rollbacks = [], [], [], []
for t in terms:
s = t.get("src")
if s in CHANGES:
new_dst, forms, status, contested, rat = CHANGES[s]
old_dst, old_status = t.get("dst"), t.get("status")
t["dst"] = new_dst
if forms is not None:
t["decl"] = {"invariant": False, "forms": list(forms)}
t["status"] = status
tag = "DRAFT⚑" if status == DRAFT else "OK"
t["note"] = f"[v2:{tag}] {rat}. (было: «{old_dst}»). " + t.get("note", "")
changed.append((s, old_dst, new_dst, status, contested, rat))
if old_status == DRAFT and status == DRAFT and contested:
rollbacks.append((s, "оставлен draft (тихий промоушен НЕ применён)"))
if s in PLURALS:
forms = t.setdefault("decl", {}).setdefault("forms", [])
for f in PLURALS[s]:
if f not in forms:
forms.append(f)
t["note"] = t.get("note", "") + " [v2: +мн.ч. D24.4]"
plural_done.append((s, t["dst"], PLURALS[s]))
for a in ADDITIONS:
entry = {"src": a["src"], "dst": a["dst"], "type": a["type"],
"decl": {"invariant": a.get("invariant", False), "forms": list(a.get("forms", []))},
"since_ch": 0, "status": a["status"],
"note": f"[v2-NEW:{'DRAFT⚑' if a['status']==DRAFT else 'OK'}] {a['note']}"}
terms.append(entry)
added.append((a["src"], a["dst"], a["status"], a.get("contested"), a["note"]))
OUT.write_text(yaml.dump(data, allow_unicode=True, sort_keys=False, width=200), encoding="utf-8")
# --- sign-off table для владельца ---
contested_changes = [(s, o, n, c) for s, o, n, st, c, _ in changed if st == DRAFT]
contested_adds = [(s, d, c) for s, d, st, c, _ in added if st == DRAFT]
lg = ["# Глоссарий 蛊真人 — v2 · таблица на ПОДПИСЬ владельца (D30.5)", "",
f"Изменено: {len(changed)} · Добавлено: {len(added)} · Обогащено мн.ч.: {len(plural_done)} · Всего терминов: {len(terms)}",
"Провенанс: единый трансформ `eval/exp13_seed_v2.py` из v1-сида; рекомендации — консенсус флагманов "
"(GPT+Claude, diag/reading/ОЦЕНКА_ФЛАГМАНА.md) + пожелания владельца. **НЕ в прод без подписи + resnapshot.**",
"Статусы: **approved** = применяю (бесспорное); **draft⚑** = НЕ уходит в CONFIRMED-hard-constraints до твоей подписи (memory.go:419/473).", "",
"## A. Спорные — на решение (сейчас status:draft, инъектятся как ⟨проверить⟩)", "",
"| src | вариант в v2 | альтернатива / причина спора | твоё решение |", "|---|---|---|---|"]
for s, o, n, c in contested_changes:
lg.append(f"| {s} | **{n}** (было «{o}») | {c} | ☐ принять / ☐ альтернатива |")
for s, d, c in contested_adds:
lg.append(f"| {s} | **{d}** | {c} | ☐ принять / ☐ альтернатива |")
lg += ["", f"- **Род «гу»** (проектное, влияет на согласование всех гу-имён): {GU_FLAG}", ""]
lg += ["## B. Бесспорные — применены (status:approved)", "",
"| src | было | стало | почему |", "|---|---|---|---|"]
for s, o, n, st, c, rat in changed:
if st == OK:
lg.append(f"| {s} | {o} | **{n}** | {rat} |")
for s, d, st, c, note in added:
if st == OK:
lg.append(f"| {s} | — (новый) | **{d}** | {note} |")
lg += ["", "## C. Обогащение мн.ч. (D24.4/полигон-№4 — закрывает inflection_gap post-check)", "",
"| src | dst | добавленные формы |", "|---|---|---|"]
for s, d, forms in plural_done:
lg.append(f"| {s} | {d} | {', '.join(forms)} |")
lg += ["", "## D. Прочие открытые флаги сида (уже draft в v1, не трогаю без тебя)",
"- 花酒行者/Винный Странник, 甲乙丙丁/разряд А–Г, 蛊虫/гу-червь, 本命蛊/жизненный гу, 南疆/Наньцзян,",
" 一气金光虫/Золотосветный червь, 青丝蛊/гу Чёрных волос, 留影存声蛊 (длинное имя), 沈嬷嬷/матушка Шэнь.",
"- ⚠ Консистентность **род/клан**: 族长→«глава клана» (новый) vs 四代族长→«Четвёртый глава рода» (v1). Унифицировать? ☐ клан ☐ род"]
SIGNOFF.write_text("\n".join(lg), encoding="utf-8")
print(f"v2 seed → {OUT}")
print(f"sign-off → {SIGNOFF}")
print(f"изменено {len(changed)} (спорных→draft: {len(contested_changes)}), "
f"добавлено {len(added)} (спорных→draft: {len(contested_adds)}), мн.ч. {len(plural_done)}, всего {len(terms)}")
print("Спорные (draft):", [s for s, *_ in contested_changes] + [s for s, *_ in contested_adds])
if __name__ == "__main__":
main()