#!/usr/bin/env python3
"""Bank batch-144: body-part and bodily-state argument structure."""

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-144"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")

DENY = {
    "nyawa", "jinbi", "jibin", "walba", "julmbu", "wanan", "ngampula",
    "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",
    "ngara", "rrunyuji", "murri", "warmba", "dukal", "kalbali",
    "medicine", "hospital", "answer", "wubul",
}


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


ROWS = [
    (1, "The child's hand is good.",
     "Karrkay mara ngulkurr.",
     "karrkay mara ngulkurr | child hand good",
     "body_inalienable_part_apposition_predicate", 4, js(["karrkay", "mara", "ngulkurr"]), js([]),
     "Patz 4.5.2: whole + body part apposition without possessive suffix; Batch 134 controls mara ngulkurr.", "0.87"),
    (2, "The woman's hair is wet.",
     "Jalbu mungka duna.",
     "jalbu mungka duna | woman hair wet",
     "body_inalienable_part_apposition_predicate", 4, js(["jalbu", "mungka", "duna"]), js([]),
     "Patz ex.519 Nyungu mungka duna; Rule 102 varies possessor as appositional whole.", "0.87"),
    (3, "The child's eye is sore.",
     "Karrkay miyil kaka.",
     "karrkay miyil kaka | child eye sore",
     "body_inalienable_sore_part", 4, js(["karrkay", "miyil", "kaka"]), js([]),
     "Dictionary miyil eye plus kaka sore/achey; Patz 4.5.2 permits body-part apposition.", "0.84"),
    (4, "The old man's back is sore.",
     "Mukul-dingkar muku kaka.",
     "mukul-dingkar muku kaka | old-man back sore",
     "body_inalienable_sore_part", 4, js(["mukul-dingkar", "muku", "kaka"]), js([]),
     "Patz causal examples use muku kaka 'back sore'; whole-part possessor varied.", "0.87"),
    (5, "My body is painful.",
     "Ngayu bangkarr bajaybajay.",
     "ngayu bangkarr bajaybajay | I body painful",
     "body_inalienable_painful_body", 4, js(["ngayu", "bangkarr", "bajaybajay"]), js([]),
     "Dictionary bangkarr body/flesh and bajaybajay painful; Batch 143 confirmed solid headword.", "0.86"),
    (6, "The woman's face is wet.",
     "Jalbu walu duna.",
     "jalbu walu duna | woman face wet",
     "body_inalienable_part_apposition_predicate", 4, js(["jalbu", "walu", "duna"]), js([]),
     "Dictionary walu face and duna wet; Patz 4.5.2 body-part apposition.", "0.84"),
    (7, "The child's mouth is dry.",
     "Karrkay ngandal marral.",
     "karrkay ngandal marral | child mouth dry",
     "body_inalienable_part_apposition_predicate", 4, js(["karrkay", "ngandal", "marral"]), js([]),
     "Dictionary ngandal mouth and marral dry; appositional body-part predicate.", "0.83"),
    (8, "The young man's arm is strong.",
     "Warru wakuy junkurr.",
     "warru wakuy junkurr | young.man arm strong",
     "body_inalienable_part_apposition_predicate", 4, js(["warru", "wakuy", "junkurr"]), js([]),
     "Dictionary wakuy arm; existing corpus and dictionary use junkurr/junkurrji for strength.", "0.83"),
    (9, "The dog's head is big.",
     "Kaya dukul yalbay.",
     "kaya dukul yalbay | dog head big",
     "body_inalienable_part_apposition_predicate", 4, js(["kaya", "dukul", "yalbay"]), js([]),
     "Dictionary dukul head and yalbay big; no dukal typo.", "0.86"),
    (10, "The child's teeth are sharp.",
     "Karrkay dirra jinbal.",
     "karrkay dirra jinbal | child tooth sharp",
     "body_inalienable_part_apposition_predicate", 4, js(["karrkay", "dirra", "jinbal"]), js([]),
     "Dictionary dirra tooth and jinbal sharp; Patz 4.5.2 body-part apposition.", "0.84"),

    (11, "Mother put water on the child's hand.",
     "Ngamu-ngku bana yijarrin karrkay-anda mara-nga.",
     "ngamu-ngku bana yijarri-ny karrkay-anda mara-nga | mother-ERG water put-PAST child-LOC hand-LOC",
     "body_locative_whole_part_apposition", 5, js(["ngamu", "bana", "yijarril", "karrkay", "mara"]), js([]),
     "Patz 4.5.2 ex.521 controls whole+part same-case locative; yijarril 'put'.", "0.84"),
    (12, "He dropped the stone on the child's foot.",
     "Nyulu kulji daray-manin karrkay-anda jina-nga.",
     "nyulu kulji dara-y-mani-ny karrkay-anda jina-nga | he stone fall-y-CAUS-PAST child-LOC foot-LOC",
     "body_locative_whole_part_apposition", 5, js(["nyulu", "kulji", "daray-manil", "karrkay", "jina"]), js([]),
     "Patz ex.521 controls dropped stone on girl's foot; possessor varied to child.", "0.90"),
    (13, "Grandmother dropped the stick on the old woman's back.",
     "Babi-ngka juku daray-manin mukul-jalbu-ndu muku-ngu.",
     "babi-ngka juku dara-y-mani-ny mukul-jalbu-ndu muku-ngu | grandmother-ERG stick fall-y-CAUS-PAST old-woman-LOC back-LOC",
     "body_locative_whole_part_apposition", 5, js(["babi", "juku", "daray-manil", "mukul-jalbu", "muku"]), js([]),
     "Same Patz 4.5.2 locative whole+part construction; muku back is dictionary headword.", "0.82"),
    (14, "The young man happened to step on my foot.",
     "Warru-nda ngayu jina narri-jin.",
     "warru-nda ngayu jina narri-ji-ny | young.man-LOC I foot step.on-ITR-PAST",
     "body_chance_passive_step_on_part", 5, js(["warru", "ngayu", "jina", "narri-l"]), js([]),
     "Patz ex.349 Dingkar-anda ngayu jina narri-ji-ny; chance-passive agent in locative.", "0.91"),
    (15, "The child happened to step on my hand.",
     "Karrkay-anda ngayu mara narri-jin.",
     "karrkay-anda ngayu mara narri-ji-ny | child-LOC I hand step.on-ITR-PAST",
     "body_chance_passive_step_on_part", 5, js(["karrkay", "ngayu", "mara", "narri-l"]), js([]),
     "Patz ex.349 chance-passive frame; body part varied from foot to hand.", "0.84"),
    (16, "The dog bit the child's hand.",
     "Kaya-ngka karrkay mara baykan.",
     "kaya-ngka karrkay mara bayka-ny | dog-ERG child hand bite-PAST",
     "body_affected_part_object_bite", 5, js(["kaya", "karrkay", "mara", "baykal"]), js([]),
     "Patz 4.5.2 whole+part apposition for affected object; dictionary baykal transitive bite.", "0.84"),
    (17, "The green ant bit the child's toe.",
     "Yangka-ngka karrkay durru baykan.",
     "yangka-ngka karrkay durru bayka-ny | green.ant-ERG child toe bite-PAST",
     "body_affected_part_object_bite", 5, js(["yangka", "karrkay", "durru", "baykal"]), js([]),
     "Dictionary yangka green ant, durru toe, baykal bite; whole+part affected object.", "0.82"),
    (18, "The child put the stone in grandmother's hand.",
     "Karrkay-ngku kulji yijarrin babi-nda mara-nga.",
     "karrkay-ngku kulji yijarri-ny babi-nda mara-nga | child-ERG stone put-PAST grandmother-LOC hand-LOC",
     "body_locative_whole_part_apposition", 5, js(["karrkay", "kulji", "yijarril", "babi", "mara"]), js([]),
     "Patz 4.5.2 same-case locative body-part NP; babi-nda follows grammar locative pattern.", "0.83"),
    (19, "Mother washed the child's face.",
     "Ngamu-ngku karrkay walu julurrin.",
     "ngamu-ngku karrkay walu julurri-ny | mother-ERG child face wash-PAST",
     "body_affected_part_object_wash", 5, js(["ngamu", "karrkay", "walu", "julurril"]), js([]),
     "Dictionary julurril transitive wash; body part as affected object in whole+part apposition.", "0.84"),
    (20, "Grandmother washed the baby's mouth with water.",
     "Babi-ngka ngawa ngandal julurrin bana-bu.",
     "babi-ngka ngawa ngandal julurri-ny bana-bu | grandmother-ERG baby mouth wash-PAST water-INST",
     "body_affected_part_object_wash_instrument", 5, js(["babi", "ngawa", "ngandal", "julurril", "bana"]), js([]),
     "Transitive julurril plus affected body-part object and bana-bu instrument.", "0.84"),

    (21, "I squeeze fruit like this with my hand.",
     "Ngayu mayi yala-ku dingkal mara-bu.",
     "ngayu mayi yala-ku dingka-l mara-bu | I fruit this-ADV squeeze-NONPAST hand-INST",
     "body_part_instrument_hand", 5, js(["ngayu", "mayi", "yala", "dingkal", "mara"]), js([]),
     "Patz ex.243 controls dingka-l with mara-bu body-part instrument.", "0.92"),
    (22, "The women are walking on foot.",
     "Jana jalbu jina-bu dungay.",
     "jana jalbu jina-bu dunga-y | they woman foot-INST go-NONPAST",
     "body_part_instrument_foot_motion", 5, js(["jana", "jalbu", "jina", "dungay"]), js([]),
     "Patz ex.245 controls jina-bu dunga-y 'walking on foot'.", "0.92"),
    (23, "The girl is kneeling.",
     "Maral bungku-bu janjanay.",
     "maral bungku-bu janjana-y | girl knee-INST stand.REDUP-NONPAST",
     "body_part_instrument_knee_motion", 5, js(["maral", "bungku", "janjanay"]), js([]),
     "Patz ex.246 controls bungku-bu janjana-y 'kneeling'.", "0.92"),
    (24, "I heard the word with my ear.",
     "Ngayu milka-bu kuku nyajin.",
     "ngayu milka-bu kuku nyaji-ny | I ear-INST word hear-PAST",
     "body_part_instrument_hearing", 5, js(["ngayu", "milka", "kuku", "nyajil"]), js([]),
     "Living Rule 67: hear = nyaji-l or milka-bu nyaji-l; no ngara-l hearing.", "0.87"),
    (25, "The child saw the bird with his eyes.",
     "Karrkay-ngku miyil-bu buyi nyajin.",
     "karrkay-ngku miyil-bu buyi nyaji-ny | child-ERG eye-INST bird see-PAST",
     "body_part_instrument_seeing", 5, js(["karrkay", "miyil", "buyi", "nyajil"]), js([]),
     "Dictionary miyil eye and nyajil see; instrumental body-part pattern from Patz 4.1.4.4.", "0.84"),
    (26, "The dog held the bone with its teeth.",
     "Kaya-ngka bajibay dirra-bu karrban.",
     "kaya-ngka bajibay dirra-bu karrba-ny | dog-ERG bone tooth-INST hold-PAST",
     "body_part_instrument_teeth", 5, js(["kaya", "bajibay", "dirra", "karrbal"]), js([]),
     "Dictionary dirra tooth and karrbal hold; instrumental body-part pattern.", "0.83"),
    (27, "Grandmother washed the child's face with her hand.",
     "Babi-ngka karrkay walu julurrin mara-bu.",
     "babi-ngka karrkay walu julurri-ny mara-bu | grandmother-ERG child face wash-PAST hand-INST",
     "body_part_object_plus_body_instrument", 5, js(["babi", "karrkay", "walu", "julurril", "mara"]), js([]),
     "Affected body-part object plus mara-bu instrument; both constructions are source-backed.", "0.83"),
    (28, "The child shouted with his mouth.",
     "Karrkay ngandal-bu yirrkan.",
     "karrkay ngandal-bu yirrka-ny | child mouth-INST shout-PAST",
     "body_part_instrument_mouth_voice", 5, js(["karrkay", "ngandal", "yirrkay"]), js([]),
     "Dictionary ngandal mouth and yirrka-l shout; instrumental body-part pattern.", "0.82"),
    (29, "The old man watched the track with his eyes.",
     "Mukul-dingkar-angka jina nyajin miyil-bu.",
     "mukul-dingkar-angka jina nyaji-ny miyil-bu | old-man-ERG track see-PAST eye-INST",
     "body_part_instrument_seeing", 5, js(["mukul-dingkar", "jina", "nyajil", "miyil"]), js([]),
     "Miyil-bu as eye instrument; jina track is dictionary/common corpus target.", "0.82"),
    (30, "Mother gave food by hand.",
     "Ngamu-ngku mayi dajin mara-bu.",
     "ngamu-ngku mayi daji-ny mara-bu | mother-ERG food give-PAST hand-INST",
     "body_part_instrument_hand_transfer", 5, js(["ngamu", "mayi", "dajil", "mara"]), js([]),
     "Mara-bu hand instrument; daji-l give is common source-backed transitive verb.", "0.82"),

    (31, "The woman cut herself with a knife while cutting meat.",
     "Nyulu jalbu naybu-bu yakajin, minya yaka-l-yaka-nya.",
     "nyulu jalbu naybu-bu yaka-ji-ny minya yaka-l-yaka-nya | she woman knife-INST cut-REF-PAST meat cut-REDUP-SUB",
     "body_reflexive_cut_self_with_instrument", 6, js(["nyulu", "jalbu", "naybu", "yakal", "minya"]), js(["naybu"]),
     "Patz ex.344 controls yaka-ji-ny with naybu-bu while cutting meat.", "0.92"),
    (32, "The old woman poked her foot while digging food.",
     "Mukul-jalbu jina baka-jin mayi baka-l-baka-nya.",
     "mukul-jalbu jina baka-ji-ny mayi baka-l-baka-nya | old-woman foot poke-REF-PAST food dig-REDUP-SUB",
     "body_reflexive_poke_affected_part", 6, js(["mukul-jalbu", "jina", "bakal", "mayi"]), js([]),
     "Patz ex.341 controls affected body part jina with baka-ji-ny while digging food.", "0.90"),
    (33, "The child keeps scratching his hand.",
     "Karrkay mara yirrba-n-yirrba-ji.",
     "karrkay mara yirrba-n-yirrba-ji-y | child hand scratch-REDUP-REF-NONPAST",
     "body_reflexive_reduplicated_scratch_part", 6, js(["karrkay", "mara", "yirrbal"]), js([]),
     "Patz ex.338 controls yirrba-n-yirrba-ji-y; body-part object modeled on ex.341/342.", "0.84"),
    (34, "I paint my lips.",
     "Ngayu yimbi junka-ji.",
     "ngayu yimbi junka-ji-y | I lips paint-REF-NONPAST",
     "body_reflexive_paint_lips", 5, js(["ngayu", "yimbi", "jungal"]), js([]),
     "Patz ex.342 controls Ngayu yimbi junka-ji-y 'I paint my lips'.", "0.92"),
    (35, "The young man hit himself on the arm by mistake.",
     "Warru kanban wakuy-mbu kunijin.",
     "warru kanban wakuy-mbu kuni-ji-ny | young.man by.mistake arm-LOC hit-REF-PAST",
     "body_reflexive_accidental_hit_part", 6, js(["warru", "kanban", "wakuy", "kunil"]), js([]),
     "Patz ex.339 controls kanban kuni-ji-ny; wakuy-mbu marks affected y-final body part location.", "0.84"),
    (36, "I was hit by a stick.",
     "Ngayu juku-bu kunijin.",
     "ngayu juku-bu kuni-ji-ny | I stick-INST hit-ITR-PAST",
     "body_passive_inanimate_instrument", 5, js(["ngayu", "juku", "kunil"]), js([]),
     "Patz ex.248b controls juku-bu remaining instrumental in passive/middle.", "0.92"),
    (37, "I was poked by a stonefish.",
     "Ngayu murraja-nga baka-jin.",
     "ngayu murraja-nga baka-ji-ny | I stonefish-LOC poke-ITR-PAST",
     "body_passive_animate_water_agent", 5, js(["ngayu", "murraja", "bakal"]), js([]),
     "Patz ex.250 allows murraja-nga or murraja-mun-du with baka-ji-ny.", "0.90"),
    (38, "The young man was bitten by a shark.",
     "Warru yaburr-undu bayka-jin.",
     "warru yaburr-undu bayka-ji-ny | young.man shark-LOC bite-ITR-PAST",
     "body_passive_bite_shark", 5, js(["warru", "yaburr", "baykal"]), js([]),
     "Patz ex.345b controls Warru yaburr-undu bayka-ji-ny.", "0.92"),
    (39, "The child was bitten by a snake.",
     "Karrkay jarba-nga bayka-jin.",
     "karrkay jarba-nga bayka-ji-ny | child snake-LOC bite-ITR-PAST",
     "body_passive_bite_snake", 5, js(["karrkay", "jarba", "baykal"]), js([]),
     "Patz ex.252b allows jarba-nga or jarba-bu bayka-ji-ny.", "0.90"),
    (40, "The child was burnt by a jellyfish.",
     "Karrkay mili-nga waju-jin.",
     "karrkay mili-nga waju-ji-ny | child jellyfish-LOC burn-ITR-PAST",
     "body_passive_burn_jellyfish", 5, js(["karrkay", "mili", "wayjuji"]), js([]),
     "Patz ex.251 allows mili-nga or mili-bu waju-ji-ny for jellyfish burn.", "0.90"),

    (41, "That snake is vicious.",
     "Yinya jarba kuli-ji.",
     "yinya jarba kuli-ji | that snake rage-COMIT",
     "body_state_comitative_corporeal_mental", 4, js(["yinya", "jarba", "kuli"]), js([]),
     "Patz ex.542 controls jarba kuli-ji 'vicious snake'; avoids duplicate dog-state row.", "0.90"),
    (42, "The child needs to pee.",
     "Nyulu karrkay kumbu-ji.",
     "nyulu karrkay kumbu-ji | he child urine-COMIT",
     "body_state_comitative_need_to_urinate", 4, js(["nyulu", "karrkay", "kumbu"]), js([]),
     "Patz ex.532 controls kumbu-ji corporeal state; dictionary kumbu urine.", "0.92"),
    (43, "The young man has a cough.",
     "Warru ngiki-ji.",
     "warru ngiki-ji | young.man cough-COMIT",
     "body_state_comitative_cough", 4, js(["warru", "ngiki"]), js([]),
     "Patz ex.545 and Rule 101 control ngiki-ji cough/cold comitative.", "0.88"),
    (44, "The child has no cold; the child got well.",
     "Karrkay ngiki-kari, ngulkurrman.",
     "karrkay ngiki-kari ngulkurr-ma-ny | child cold-PRIV well-INCHO-PAST",
     "body_state_privative_recovery", 4, js(["karrkay", "ngiki", "kari", "ngulkurr"]), js([]),
     "Patz ex.556 controls ngiki-kari, ngulkurr-ma-ny.", "0.90"),
    (45, "The old man has no teeth.",
     "Mukul-dingkar dirra-kari.",
     "mukul-dingkar dirra-kari | old-man tooth-PRIV",
     "body_state_privative_toothless", 4, js(["mukul-dingkar", "dirra"]), js([]),
     "Patz ex.557 controls dirra-kari toothless; possessor varied to old man.", "0.88"),
    (46, "The child is deaf.",
     "Karrkay milka-kari.",
     "karrkay milka-kari | child ear-PRIV",
     "body_state_privative_deaf", 4, js(["karrkay", "milka"]), js([]),
     "Living Rule 67: milka-kari is the body-part privative for deaf/cannot hear.", "0.84"),
    (47, "Mother gave water to the young man with a cough.",
     "Ngamu-ngku bana dajin warru-nda ngiki-ji-nda.",
     "ngamu-ngku bana daji-ny warru-nda ngiki-ji-nda | mother-ERG water give-PAST young.man-LOC cough-COMIT-LOC",
     "body_state_comitative_locative_agreement", 6, js(["ngamu", "bana", "dajil", "warru", "ngiki"]), js([]),
     "Patz ex.545 controls ngiki-ji-nda agreeing with locative human recipient.", "0.90"),
    (48, "Your back is sore from gathering firewood yesterday.",
     "Yundu muku kaka, baya muja-nya yilayku.",
     "yundu muku kaka baya muja-nya yilayku | you back sore firewood gather-SUB yesterday",
     "body_state_causal_sore_back", 6, js(["yundu", "muku", "kaka", "baya", "mujal", "yilayku"]), js([]),
     "Patz causal body-state example: muku kaka from gathering firewood yesterday.", "0.90"),
    (49, "The woman's back is sore from carrying the baby.",
     "Jalbu muku kaka, ngawa jarra-kulba-nya.",
     "jalbu muku kaka ngawa jarra-kulba-nya | woman back sore baby carry-SUB",
     "body_state_causal_sore_back", 6, js(["jalbu", "muku", "kaka", "ngawa", "jarra-kulbal"]), js([]),
     "Muku kaka causal subordinate frame modeled on Patz sore-back examples.", "0.84"),
    (50, "He sleeps when his body is tired.",
     "Nyulu warngku-wunay, kaki nyulu bangkarr bajaburray.",
     "nyulu warngku-wuna-y kaki nyulu bangkarr bajaburray | he sleep-lie-NONPAST if he body tired",
     "body_state_conditional_body_tired", 6, js(["nyulu", "warngku-wunay", "kaki", "bangkarr", "bajaburray"]), js([]),
     "Patz example controls kaki nyulu bangkarr bajaburray 'if his body is tired'.", "0.88"),
]


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}"
        e_start = english.lstrip("'\"")
        e_end = english.rstrip("'\"").rstrip()
        if not e_start[:1].isupper() or e_end[-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")
        k_start = kuku.lstrip("'\"")
        if not k_start[:1].isupper():
            issues.append(f"{label}: Kuku surface must start uppercase or with a quote before 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 102 draft: body-part and bodily-state argument structure", "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

    record_event(
        db,
        batch_label=LABEL,
        phase="source",
        event_type="source-pass",
        actor="claude-main",
        detail=(
            "Rule 102 source pass checked Patz 4.5.2 inalienable whole-part apposition, 4.1.4.4 body-part instruments, "
            "4.2.4.2 reflexive body-part affected objects, 4.2.4.3 passive/chance-passive injury frames, 4.6 comitative/"
            "privative bodily states, and dictionary/living-book body-part guards."
        ),
        command="rg/sed over grammar.md, dictionary.yaml, living books",
        artifact_path="process_logs/batch-144-body-state-process.md",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="scope",
        event_type="risk-controls",
        actor="claude-main",
        detail=(
            "Rejected or fenced off risky forms: no dukal, no wubul heart rows, no body-part possessive -mu in target "
            "appositions, no kanban in chance-passive rows, no ngara-l hearing, no doctor/medicine/hospital terms, and no "
            "unsourced English-only body-part paraphrases."
        ),
        artifact_path="process_logs/batch-144-body-state-process.md",
        created_at=NOW,
    )
    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 102 body-part and bodily-state construction rows under_review after static and duplicate checks.",
        command="python3 batch_scripts/bank_144.py",
        artifact_path="batch_scripts/bank_144.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="row-inventory",
        actor="claude-main",
        detail=(
            "Rows 1-10 whole-part apposition; 11-20 affected locative/object body parts; 21-30 body-part instruments; "
            "31-40 reflexive/passive/chance injury frames; 41-50 comitative/privative bodily states and sore-back causes."
        ),
        artifact_path="process_logs/batch-144-body-state-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())
