#!/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()