#!/usr/bin/env python3
"""Bank batch-141: social relations, taboo, truth, and authority lexicon."""

from __future__ import annotations

import datetime as dt
import json
import re
import sqlite3
import sys

from process_ledger import ensure_schema, record_event

DB = "/mnt/donto-data/donto-resources/research/translation-training/kuku-yalanji-runpod-2026-06-30/synthetic/claude-synthetic-v1-2026-07-02/synthetic.db"
LABEL = "batch-141"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")

DENY = {
    "nyawa", "jinbi", "jibin", "walba", "julmbu", "wanan", "ngampula",
    "yalama", "ngana-jinji", "jubuny", "wural", "bigan", "jiwuny",
    "warngkul", "wanaba", "jarrkunji", "munjal", "murku", "buni",
    "kunya", "milkan", "kulur-jambul", "janyji", "buburr", "jarrmba",
    "jinji", "daga", "wanka", "yumbul", "kuku-yumbul", "jubi",
    "yilbay", "jarrakay", "janka-yukil", "jukarr", "kunkul",
    "bubu-jandu", "bubu-warral", "jani-warra", "balbay-nja",
    "bunjurril", "buri", "baral", "ngara", "rrunyuji",
    "jabul", "yukil", "bama-jambul",
}


def js(values: list[str]) -> str:
    return json.dumps(values, ensure_ascii=False)


ROWS = [
    (1, "That woman is my friend.",
     "Yinya jalbu ngayku jawun.",
     "yinya jalbu ngayku jawun | that woman my friend/relation",
     "verbless_social_relation", 4, js(["yinya", "jalbu", "ngayu", "jawun"]), js([]),
     "Dictionary jawun = friend/relation; verbless NP frame.", "0.86"),
    (2, "The child went with a friend.",
     "Karrkay jawun-unji dungan.",
     "karrkay jawun-unji dunga-ny | child friend-COMIT go-PAST",
     "kin_comitative_motion", 4, js(["karrkay", "jawun", "dungay"]), js([]),
     "Patz 3.2.3.5(ii) and ex.163 control jawun-unji with COMIT.", "0.88"),
    (3, "My cousin came.",
     "Ngayku bangunji kadan.",
     "ngayku bangunji kada-ny | my cousin come-PAST",
     "kin_possessive_intransitive", 4, js(["ngayu", "bangunji", "kaday"]), js([]),
     "Dictionary bangunji = cousin/full-blood cousin.", "0.86"),
    (4, "The man saw his brother-in-law.",
     "Dingkar-angka nyungu banji nyajin.",
     "dingkar-angka nyungu banji nyaji-ny | man-ERG his brother.in.law/sister.in.law see-PAST",
     "kin_perception_transitive", 5, js(["dingkar", "nyungu", "banji", "nyajil"]), js([]),
     "Dictionary banji = actual brother-in-law/sister-in-law; nyajil transitive with common-noun ERG A.", "0.84"),
    (5, "The woman saw her sister-in-law.",
     "Jalbu-ngku nyungu warrin nyajin.",
     "jalbu-ngku nyungu warrin nyaji-ny | woman-ERG her sister.in.law/brother.in.law see-PAST",
     "kin_perception_transitive", 5, js(["jalbu", "nyungu", "warrin", "nyajil"]), js([]),
     "Dictionary warrin = actual sister-in-law/brother-in-law; kin object left ABS.", "0.84"),
    (6, "The man came with his father-in-law.",
     "Dingkar ngalayin-unji kadan.",
     "dingkar ngalayin-unji kada-ny | man father.in.law-COMIT come-PAST",
     "affinal_comitative_motion", 5, js(["dingkar", "ngalayin", "kaday"]), js([]),
     "Dictionary ngalayin = father-in-law/son-in-law; COMIT pattern follows n-final kin.", "0.84"),
    (7, "The mother-in-law came.",
     "Biwul kadan.",
     "biwul kada-ny | mother.in.law come-PAST",
     "affinal_intransitive", 4, js(["biwul", "kaday"]), js([]),
     "Dictionary biwul = mother-in-law; grammar treats biwul as a neutral-case kin term.", "0.86"),
    (8, "The mother-in-law pierced his nose.",
     "Biwul-ungku dabul bakan.",
     "biwul-ungku dabul baka-ny | mother.in.law-ERG pierced.nose/nose.peg pierce-PAST",
     "source_example_affinal_action", 5, js(["biwul", "dabul", "bakal"]), js([]),
     "Dictionary dabul entry gives Biwulungku dabul bakan for mother-in-law piercing his nose.", "0.90"),
    (9, "The man avoided his mother-in-law.",
     "Dingkar-angka biwul makuku-bawan.",
     "dingkar-angka biwul makuku-bawa-ny | man-ERG mother.in.law avoid-PAST",
     "avoidance_transitive_affinal", 5, js(["dingkar", "biwul", "makuku-bawal"]), js([]),
     "Dictionary makuku-bawal = change direction so you do not meet someone; Patz controls biwul avoidance context.", "0.86"),
    (10, "The woman avoided her father-in-law.",
     "Jalbu-ngku ngalayin makuku-bawan.",
     "jalbu-ngku ngalayin makuku-bawa-ny | woman-ERG father.in.law/son.in.law avoid-PAST",
     "avoidance_transitive_affinal", 5, js(["jalbu", "ngalayin", "makuku-bawal"]), js([]),
     "Patz notes less strict in-law taboos beyond biwul; makuku-bawal provides the lexical avoid frame.", "0.82"),
    (11, "The boss came.",
     "Maja kadan.",
     "maja kada-ny | boss come-PAST",
     "authority_loan_intransitive", 4, js(["maja", "kaday"]), js(["master"]),
     "Dictionary maja = boss, English loan from master; grammar ex.576 attests maja-nji.", "0.84"),
    (12, "The child came with the boss.",
     "Karrkay maja-nji kadan.",
     "karrkay maja-nji kada-ny | child boss-COMIT come-PAST",
     "authority_loan_comitative", 5, js(["karrkay", "maja", "kaday"]), js(["master"]),
     "Patz 4.8 ex.576 controls maja-nji 'with the boss'; subject varied.", "0.88"),
    (13, "They are like that.",
     "Jana yalaku.",
     "jana yalaku | they custom/like.that",
     "custom_source_usage", 4, js(["jana", "yalaku"]), js([]),
     "Dictionary yalaku usage: jana yalaku = they are like that / their custom / way they are.", "0.90"),
    (14, "That is the way.",
     "Yinya yalaku.",
     "yinya yalaku | that custom/way",
     "custom_verbless", 4, js(["yinya", "yalaku"]), js([]),
     "Dictionary yalaku = custom/like; short verbless row, not an invented law noun.", "0.84"),
    (15, "That child's moiety is dabu.",
     "Yinya karrkay-mu mandi dabu.",
     "yinya karrkay-mu mandi dabu | that child-POSS moiety dabu",
     "moiety_possessive_verbless", 5, js(["yinya", "karrkay", "mandi", "dabu"]), js([]),
     "Dictionary mandi = moiety/subsection; dabu is one moiety in Patz 1.2.1.3.", "0.86"),
    (16, "That man's moiety is walarr.",
     "Yinya dingkar-amu mandi walarr.",
     "yinya dingkar-amu mandi walarr | that man-POSS moiety walarr",
     "moiety_possessive_verbless", 5, js(["yinya", "dingkar", "mandi", "walarr"]), js([]),
     "Patz 1.2.1.3 names walarr as the other moiety; dictionary walarr also notes moiety sense.", "0.86"),
    (17, "Dabu is a moiety.",
     "Dabu mandi.",
     "dabu mandi | dabu moiety",
     "moiety_verbless", 4, js(["dabu", "mandi"]), js([]),
     "Dictionary dabu includes bee/honey/moiety senses; this row uses the moiety sense.", "0.84"),
    (18, "Walarr is a moiety.",
     "Walarr mandi.",
     "walarr mandi | walarr moiety",
     "moiety_verbless", 4, js(["walarr", "mandi"]), js([]),
     "Dictionary walarr includes beard/bee/moiety senses; this row uses the moiety sense.", "0.84"),
    (19, "That is a totem.",
     "Yinya mulkal-mulkal.",
     "yinya mulkal-mulkal | that totem",
     "totem_verbless", 4, js(["yinya", "mulkal-mulkal"]), js([]),
     "Dictionary mulkal-mulkal includes totem; no polysemous fighting/spirit extension used here.", "0.82"),
    (20, "Grandmother asked me about the totem.",
     "Babi ngayku-ndu mulkal-mulkal-ka babajin.",
     "babi ngayku-ndu mulkal-mulkal-ka babaji-ny | grandmother me-LOC totem-DAT ask.about-PAST",
     "ask_about_loc_dative", 5, js(["babi", "ngayu", "mulkal-mulkal", "babaji"]), js([]),
     "Patz 4.2.4.4: babaji-y actor ABS, person asked LOC, topic DAT.", "0.86"),
    (21, "The place is taboo.",
     "Bubu yirmbal.",
     "bubu yirmbal | place taboo",
     "taboo_verbless", 4, js(["bubu", "yirmbal"]), js([]),
     "Dictionary yirmbal = taboo (Yalanji); grammar 1.2.1.1 calls story places yirmbal.", "0.86"),
    (22, "The taboo thing grabs you and holds you.",
     "Yirmbalda karrbal, yunun kujilkuda.",
     "yirmbal-da karrba-l, yunun kuji-l-kuda | taboo-source.clitic grab-NONPAST you.ACC hold-NONPAST-TEMP2",
     "taboo_source_usage", 5, js(["yirmbal", "karrbal", "yundu", "kujil"]), js([]),
     "Dictionary yirmbal usage: Yirmbalda karrbal, yunun kujilkuda.", "0.90"),
    (23, "That is a taboo place.",
     "Yinya bubu yirmbal.",
     "yinya bubu yirmbal | that taboo place",
     "taboo_place_verbless", 4, js(["yinya", "bubu yirmbal"]), js([]),
     "Dictionary bubu yirmbal = taboo place; Yalanji yirmbal preferred over Nyungkul jabul.", "0.88"),
    (24, "Ngalba is a taboo name.",
     "Ngalba yirmbal burri.",
     "ngalba yirmbal burri | substitute.name taboo name",
     "taboo_name_verbless", 4, js(["ngalba", "yirmbal", "burri"]), js([]),
     "Dictionary ngalba = taboo substitute name; Patz 1.2.1.7 explains deceased-name replacement.", "0.88"),
    (25, "The old woman said the substitute name.",
     "Mukul-jalbu-ngku ngalba balkan.",
     "mukul-jalbu-ngku ngalba balka-ny | old-woman-ERG substitute.name say-PAST",
     "speech_name_transitive", 5, js(["mukul-jalbu", "ngalba", "balkal"]), js([]),
     "Balkan with object; ngalba used as name-substitute, not the deceased person's original name.", "0.84"),
    (26, "The relatives did not say his name.",
     "Jawun-karra-ngku nyungu burri kari balkan.",
     "jawun-karra-ngku nyungu burri kari balka-ny | relative-KPL-ERG his name NEG say-PAST",
     "negative_name_speech", 5, js(["jawun", "-karra", "nyungu", "burri", "kari", "balkal"]), js([]),
     "Patz 1.2.1.7: deceased names not spoken; kin plural -karra extends to jawun.", "0.82"),
    (27, "Mother named the child.",
     "Ngamu-ngku karrkay burri dalkin.",
     "ngamu-ngku karrkay burri dalki-ny | mother-ERG child name give.name-PAST",
     "naming_transitive", 5, js(["ngamu", "karrkay", "burri", "dalkil"]), js([]),
     "Dictionary dalkil = give a name/call by name; burri is name, not a naming verb.", "0.88"),
    (28, "I gave her a name.",
     "Ngayu nyungunyin burri dalkin.",
     "ngayu nyungunyin burri dalki-ny | I her.ACC name give.name-PAST",
     "naming_transitive_pronoun", 5, js(["ngayu", "nyungunyin", "burri", "dalkil"]), js([]),
     "Dictionary source example: Ngayu nyungunyin burri dalkin Carol; proper name omitted.", "0.88"),
    (29, "The child told the truth.",
     "Karrkay-ngku manubaja balkan.",
     "karrkay-ngku manubaja balka-ny | child-ERG truth say-PAST",
     "truth_speech_transitive", 5, js(["karrkay", "manubaja", "balkal"]), js([]),
     "Dictionary manubaja = truth; balkan supplies the speech predicate.", "0.84"),
    (30, "The man told a lie.",
     "Dingkar-angka jurril balkan.",
     "dingkar-angka jurril balka-ny | man-ERG lie say-PAST",
     "lie_speech_transitive", 5, js(["dingkar", "jurril", "balkal"]), js([]),
     "Dictionary jurril = lie/untruth; balkan supplies the speech predicate.", "0.84"),
    (31, "The child lied.",
     "Karrkay warmbijin.",
     "karrkay warmbiji-ny | child lie/prevaricate-PAST",
     "lie_intransitive", 4, js(["karrkay", "warmbiji"]), js([]),
     "Dictionary warmbiji-y = lie/prevaricate; intransitive actor remains ABS.", "0.86"),
    (32, "The woman lied.",
     "Jalbu warmbijin.",
     "jalbu warmbiji-ny | woman lie/prevaricate-PAST",
     "lie_intransitive", 4, js(["jalbu", "warmbiji"]), js([]),
     "Second warmbiji-y row with a human S; no transitive object forced.", "0.84"),
    (33, "The woman tricked the man by lying.",
     "Jalbu-ngku dingkar jurril-bungan.",
     "jalbu-ngku dingkar jurril-bunga-ny | woman-ERG man lie-CAUS-PAST",
     "trick_by_lying_transitive", 5, js(["jalbu", "dingkar", "jurril-bungal"]), js([]),
     "Dictionary jurril-bungal = trick someone by lying by word or action.", "0.86"),
    (34, "The man tricked the child by lying.",
     "Dingkar-angka karrkay jurril-bungan.",
     "dingkar-angka karrkay jurril-bunga-ny | man-ERG child lie-CAUS-PAST",
     "trick_by_lying_transitive", 5, js(["dingkar", "karrkay", "jurril-bungal"]), js([]),
     "Same source-controlled jurril-bungal frame with a child object.", "0.84"),
    (35, "The child believed mother.",
     "Karrkay-ngku ngamu manubaja-bungan.",
     "karrkay-ngku ngamu manubaja-bunga-ny | child-ERG mother believe-PAST",
     "believe_transitive", 5, js(["karrkay", "ngamu", "manubaja-bungal"]), js([]),
     "Dictionary manubaja-bungal = believe; simple animate object.", "0.82"),
    (36, "They believed the boss.",
     "Jana maja manubaja-bungan.",
     "jana maja manubaja-bunga-ny | they boss believe-PAST",
     "believe_transitive_pronoun_a", 5, js(["jana", "maja", "manubaja-bungal"]), js(["master"]),
     "Manubaja-bungal with pronoun A; maja marked as an English loan.", "0.82"),
    (37, "Is it true that you want to come?",
     "Yalakay yundu kada-nka?",
     "yalakay yundu kada-nka | true?/astonishment you come-PURP",
     "truth_interjection_source_usage", 5, js(["yalakay", "yundu", "kaday"]), js([]),
     "Dictionary yalakay usage is yalakay yundu kadanka?; retained as an interjection, not a y-conjugation verb.", "0.86"),
    (38, "I blamed the man.",
     "Ngayu dingkar nyuyan.",
     "ngayu dingkar nyuya-ny | I man blame/accuse-PAST",
     "blame_transitive_pronoun_a", 5, js(["ngayu", "dingkar", "nyuyal"]), js([]),
     "Dictionary nyuyal = blame/accuse; Patz 4.7 ex.566 controls imperative nyuya.", "0.84"),
    (39, "Do not blame your mother!",
     "Yundu ngamu kari nyuya!",
     "yundu ngamu kari nyuya | you mother NEG blame.IMP",
     "blame_negative_imperative_source", 5, js(["yundu", "ngamu", "kari", "nyuyal"]), js([]),
     "Patz 4.7 ex.566: Yundu ngamu kari nyuya! 'Don't blame your mother!'", "0.90"),
    (40, "The old woman scolded the child.",
     "Mukul-jalbu-ngku karrkay murru-kangan.",
     "mukul-jalbu-ngku karrkay murru-kanga-ny | old-woman-ERG child scold-PAST",
     "scold_transitive_like", 5, js(["mukul-jalbu", "karrkay", "murru-kangal"]), js([]),
     "Dictionary murru-kangal = scold/growl someone; Patz text has murrukanga-ny with object.", "0.86"),
    (41, "The man scolded me.",
     "Dingkar-angka nganya murru-kangan.",
     "dingkar-angka nganya murru-kanga-ny | man-ERG me.ACC scold-PAST",
     "scold_transitive_like_pronoun_o", 5, js(["dingkar", "nganya", "murru-kangal"]), js([]),
     "Same murru-kangal frame with pronominal object.", "0.84"),
    (42, "The child became ashamed.",
     "Karrkay murru-kadan.",
     "karrkay murru-kaday-ny | child become.ashamed-PAST",
     "shame_inchoative_intransitive", 5, js(["karrkay", "murru-kaday"]), js([]),
     "Dictionary murru-kaday = become ashamed/shy/bashful; intransitive S.", "0.86"),
    (43, "The woman is ashamed.",
     "Jalbu milka-murruji.",
     "jalbu milka-murruji | woman ashamed/guilty",
     "shame_adjective", 4, js(["jalbu", "milka-murruji"]), js([]),
     "Dictionary milka-murruji = ashamed/guilty; no invented promise noun.", "0.86"),
    (44, "He shamed the child.",
     "Nyulu karrkay murru-kaday-manin.",
     "nyulu karrkay murru-kaday-mani-ny | he child ashamed-CAUS-PAST",
     "shame_causative_transitive", 5, js(["nyulu", "karrkay", "murru-kaday-manil"]), js([]),
     "Dictionary murru-kaday-manil = shame someone; transitive with pronoun A.", "0.84"),
    (45, "You disapprove of fighting.",
     "Yundu balu kuni-wa-nka.",
     "yundu balu kuni-wa-nka | you NEG2 fight-RECIP-PURP",
     "balu_disapproval_purposive", 5, js(["yundu", "balu", "kuniway"]), js([]),
     "Patz 4.9.2 ex.600 has Ngayu balu kuni-wa-nka; subject varied to avoid duplicate.", "0.88"),
    (46, "They do not like smoking.",
     "Jana balu buymbi-nka.",
     "jana balu buymbi-nka | they NEG2 smoke-PURP",
     "balu_disapproval_purposive", 5, js(["jana", "balu", "buymbil"]), js([]),
     "Patz 4.9.2 ex.601 has Ngayu balu buymbi-nka; plural subject avoids exact duplicate.", "0.86"),
    (47, "Let him go!",
     "Balu nyulu dungay!",
     "balu nyulu dunga-y | NEG2/let he go-IMP",
     "balu_imperative_intransitive", 5, js(["balu", "nyulu", "dungay"]), js([]),
     "Patz 4.7 ex.570: Balu dungay!; explicit subject avoids exact corpus duplicate.", "0.84"),
    (48, "Let the fire burn!",
     "Balu baya wajujika!",
     "balu baya waju-ji-ka | NEG2/let fire burn-REF-IMP",
     "balu_imperative_intransitive", 5, js(["balu", "baya", "wajujil"]), js([]),
     "Patz 4.7 ex.571: Balu wajujika!; explicit fire subject avoids exact corpus duplicate.", "0.84"),
    (49, "The man sang a curse song.",
     "Dingkar kanyil badin.",
     "dingkar kanyil badi-ny | man curse.song sing/cry-PAST",
     "curse_song_intransitive_phrase", 5, js(["dingkar", "kanyil", "badiy"]), js([]),
     "Dictionary kanyil usage: kanyil badi 'sing a curse song'; badi-y actor left ABS like song rows.", "0.86"),
    (50, "They were afraid of the curse song.",
     "Jana yinil kanyil-ku.",
     "jana yinil kanyil-ku | they afraid curse.song-DAT",
     "fear_dative_object", 5, js(["jana", "yinil", "kanyil"]), js([]),
     "Patz 4.9.1 ex.587 controls yinil plus dative complement; kanyil is source-defined as greatly feared.", "0.84"),
]


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"expected 50 rows, got {len(ROWS)}")
    seqs = [r[0] for r in ROWS]
    if seqs != list(range(1, 51)):
        issues.append(f"seqs must be 1..50, got {seqs[:5]}...{seqs[-5:]}")
    seen_ku: set[str] = set()
    for row in ROWS:
        seq, english, kuku, analysis, _frame, _tier, words_used, loans_used, _evidence, _confidence = row
        label = f"{LABEL}.{seq:02d}"
        if not english[:1].isupper() or english.rstrip("'\"").rstrip()[-1:] not in ".!?":
            issues.append(f"{label}: English must be capitalized and final-punctuated")
        if " | " not in analysis:
            issues.append(f"{label}: analysis missing gloss separator")
        if not kuku[:1].isupper():
            issues.append(f"{label}: Kuku surface must start uppercase")
        for field_name, payload in (("words_used", words_used), ("loans_used", loans_used)):
            try:
                json.loads(payload)
            except Exception as exc:
                issues.append(f"{label}: {field_name} is not JSON: {exc}")
        low = f" {kuku.lower()} {analysis.lower()} "
        for word in DENY:
            if re.search(rf"(?<![a-z]){re.escape(word)}(?![a-z])", low):
                issues.append(f"{label}: denied phantom/wrong-sense/blocked token {word!r}")
        if kuku.lower() in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(kuku.lower())
    return issues


def main() -> int:
    issues = scan_static()
    if issues:
        print("pre-bank scan failed:")
        for issue in issues:
            print(f"  - {issue}")
        return 1

    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    db.execute("BEGIN")
    ensure_schema(db)
    existing = db.execute("SELECT id FROM batches WHERE label=?", (LABEL,)).fetchone()
    if existing:
        batch_id = existing[0]
        accepted = db.execute(
            "SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status IN ('accepted','revised')",
            (batch_id,),
        ).fetchone()[0]
        if accepted:
            raise SystemExit(f"{LABEL} already has accepted/revised rows; refusing to overwrite")
        db.execute("DELETE FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)", (batch_id,))
        db.execute("DELETE FROM sentences WHERE batch_id=?", (batch_id,))
        db.execute("UPDATE batches SET notes=?, created_at=? WHERE id=?", ("rebanked under_review", NOW, batch_id))
    else:
        cur = db.execute(
            "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
            (LABEL, "Rule 99 draft: social/taboo/truth lexical control", "operator-guide-2026-07-07", "under_review", NOW),
        )
        batch_id = cur.lastrowid

    dupes = []
    for seq, _english, kuku, *_rest in ROWS:
        hit = db.execute(
            """
            SELECT b.label, s.seq
            FROM sentences s JOIN batches b ON b.id=s.batch_id
            WHERE lower(trim(s.kuku))=lower(trim(?))
              AND s.status!='rejected'
              AND s.batch_id!=?
            """,
            (kuku, batch_id),
        ).fetchone()
        if hit:
            dupes.append(f"{LABEL}.{seq:02d} duplicates {hit[0]}.{hit[1]:02d}: {kuku}")
    if dupes:
        db.rollback()
        print("cross-corpus duplicate check failed:")
        for dupe in dupes:
            print(f"  - {dupe}")
        return 1

    db.executemany(
        """
        INSERT INTO sentences(
            batch_id, seq, english, kuku, analysis, frame, tier, words_used,
            loans_used, evidence, status, confidence, rights_status, author,
            created_at, updated_at
        ) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        """,
        [
            (
                batch_id, seq, english, kuku, analysis, frame, tier, words_used,
                loans_used, evidence, "under_review", confidence,
                "project_approved_synthetic_pending_elder_verification",
                "claude-main", NOW, NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_used, loans_used, evidence, confidence in ROWS
        ],
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="bank-success",
        actor="claude-main",
        detail="Banked 50 hand-authored Rule 99 lexical rows under_review after static and duplicate checks.",
        command="python3 batch_scripts/bank_141.py",
        artifact_path="batch_scripts/bank_141.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="row-inventory",
        actor="claude-main",
        detail="Rows cover kin/in-law/avoidance, mandi/walarr/dabu, yirmbal/ngalba, burri/dalkil, manubaja/jurril/warmbiji, nyuyal/murru-kangal, murru-kaday/milka-murruji, balu, kanyil, and yalaku.",
        artifact_path="process_logs/batch-141-social-law-process.md",
        created_at=NOW,
    )
    db.commit()
    count = db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=?", (batch_id,)).fetchone()[0]
    avg_words = db.execute(
        "SELECT AVG(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1.0) FROM sentences WHERE batch_id=?",
        (batch_id,),
    ).fetchone()[0]
    print(f"banked {LABEL}: {count} rows under_review; avg Kuku words/row={avg_words:.2f}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
