#!/usr/bin/env python3
"""Bank batch-174: Rule 132 construction consolidation after Rule 131."""

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-174"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-174-construction-consolidation-after-rule131-process.md"

BODY_HEADS = {
    "mara": "hand",
    "jina": "foot",
    "walu": "face",
    "milka": "ear",
    "miyil": "eye",
    "ngandal": "mouth",
    "dukul": "head",
    "muku": "back",
    "bibar": "shin",
}
DENY = {"yah", "ngawuya", "walnga", "maja-nga", "yunga", "bada-bada", "wangkar-wangkar", "biyul", "warrur"}
FORBIDDEN_MARKERS = ("-ngarr", "-ngurr", "-kuda", "-l-urr", "wawu-")
PRONOUN_A = {"ngayu", "ngana", "nganjin", "yundu", "yurra", "jana", "nyulu"}
TRANSITIVE_PAST = ("nyajin", "julurrin", "yijarrin", "baykan", "bijan", "daray-manin")


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


ROWS = [
    (1, "The woman's hand is sore.",
     "Jalbu mara kaka.",
     "jalbu mara kaka | woman hand sore",
     "r132_state_woman_hand_sore", 2, js(["jalbu", "mara", "kaka"]), js([]),
     "Rule 131 body-head consolidation: mara hand in inalienable state frame; kaka sore/pain only.", "0.86"),
    (2, "The old man's foot is good.",
     "Mukul-dingkar jina ngulkurr.",
     "mukul-dingkar jina ngulkurr | old-man foot good",
     "r132_state_old_man_foot_good", 2, js(["mukul", "dingkar", "jina", "ngulkurr"]), js([]),
     "Jina foot sense selected; inalienable body-part apposition.", "0.86"),
    (3, "The child's face is dry.",
     "Karrkay walu marral.",
     "karrkay walu marral | child face dry",
     "r132_state_child_face_dry", 2, js(["karrkay", "walu", "marral"]), js([]),
     "Walu face in old state-predicate frame.", "0.86"),
    (4, "Grandmother's ear is wet.",
     "Babi milka duna.",
     "babi milka duna | grandmother ear wet",
     "r132_state_grandmother_ear_wet", 2, js(["babi", "milka", "duna"]), js([]),
     "Milka ear in body-part state frame; no compound extension.", "0.86"),
    (5, "Mother's eye is good.",
     "Ngamu miyil ngulkurr.",
     "ngamu miyil ngulkurr | mother eye good",
     "r132_state_mother_eye_good", 2, js(["ngamu", "miyil", "ngulkurr"]), js([]),
     "Miyil eye sense selected; tree homonym excluded.", "0.86"),
    (6, "Father's mouth is sore.",
     "Nganjan ngandal kaka.",
     "nganjan ngandal kaka | father mouth sore",
     "r132_state_father_mouth_sore", 2, js(["nganjan", "ngandal", "kaka"]), js([]),
     "Ngandal mouth plus sourced kaka sore/pain predicate.", "0.86"),
    (7, "The young man's head is sore.",
     "Warru dukul kaka.",
     "warru dukul kaka | young.man head sore",
     "r132_state_young_man_head_sore", 2, js(["warru", "dukul", "kaka"]), js([]),
     "Dukul head core sense; extensions excluded.", "0.86"),
    (8, "Grandfather's back is sore.",
     "Kami muku kaka.",
     "kami muku kaka | grandfather back sore",
     "r132_state_grandfather_back_sore", 2, js(["kami", "muku", "kaka"]), js([]),
     "Muku back in Patz sore-back frame with new owner.", "0.88"),
    (9, "The old woman's shin is dry.",
     "Mukul-jalbu bibar marral.",
     "mukul-jalbu bibar marral | old-woman shin dry",
     "r132_state_old_woman_shin_dry", 2, js(["mukul", "jalbu", "bibar", "marral"]), js([]),
     "Bibar shin selected; ankle excluded.", "0.84"),
    (10, "The baby's face is good.",
     "Ngawa walu ngulkurr.",
     "ngawa walu ngulkurr | baby face good",
     "r132_state_baby_face_good", 2, js(["ngawa", "walu", "ngulkurr"]), js([]),
     "Walu face in state frame; no new lexical head.", "0.86"),

    (11, "Mother saw grandfather's hand.",
     "Ngamu-ngku kami mara nyajin.",
     "ngamu-ngku kami mara nyaji-ny | mother-ERG grandfather hand see-PAST",
     "r132_perception_mother_grandfather_hand", 4, js(["ngamu", "kami", "mara", "nyajil"]), js([]),
     "Whole-part O apposition; common-noun perceiver ERG.", "0.86"),
    (12, "I saw father's foot.",
     "Ngayu nganjan jina nyajin.",
     "ngayu nganjan jina nyaji-ny | I father foot see-PAST",
     "r132_perception_pronoun_father_foot", 4, js(["ngayu", "nganjan", "jina", "nyajil"]), js([]),
     "Pronoun A bare; jina foot only.", "0.86"),
    (13, "Grandmother saw mother's face.",
     "Babi-ngka ngamu walu nyajin.",
     "babi-ngka ngamu walu nyaji-ny | grandmother-ERG mother face see-PAST",
     "r132_perception_grandmother_mother_face", 4, js(["babi", "ngamu", "walu", "nyajil"]), js([]),
     "Walu face in perceived whole-part O; common-noun A ERG.", "0.86"),
    (14, "The man saw the child's ear.",
     "Dingkar-angka karrkay milka nyajin.",
     "dingkar-angka karrkay milka nyaji-ny | man-ERG child ear see-PAST",
     "r132_perception_man_child_ear", 4, js(["dingkar", "karrkay", "milka", "nyajil"]), js([]),
     "Milka ear in old perception frame.", "0.86"),
    (15, "The woman saw the young man's eye.",
     "Jalbu-ngku warru miyil nyajin.",
     "jalbu-ngku warru miyil nyaji-ny | woman-ERG young.man eye see-PAST",
     "r132_perception_woman_young_man_eye", 4, js(["jalbu", "warru", "miyil", "nyajil"]), js([]),
     "Miyil eye selected; common-noun A ERG.", "0.86"),
    (16, "Father saw grandmother's mouth.",
     "Nganjan-angka babi ngandal nyajin.",
     "nganjan-angka babi ngandal nyaji-ny | father-ERG grandmother mouth see-PAST",
     "r132_perception_father_grandmother_mouth", 4, js(["nganjan", "babi", "ngandal", "nyajil"]), js([]),
     "Ngandal mouth in perceived whole-part object.", "0.86"),
    (17, "The child saw the old man's head.",
     "Karrkay-ngku mukul-dingkar dukul nyajin.",
     "karrkay-ngku mukul-dingkar dukul nyaji-ny | child-ERG old-man head see-PAST",
     "r132_perception_child_old_man_head", 4, js(["karrkay", "mukul", "dingkar", "dukul", "nyajil"]), js([]),
     "Dukul head core sense; common-noun A ERG.", "0.86"),
    (18, "I saw the old woman's back.",
     "Ngayu mukul-jalbu muku nyajin.",
     "ngayu mukul-jalbu muku nyaji-ny | I old-woman back see-PAST",
     "r132_perception_pronoun_old_woman_back", 4, js(["ngayu", "mukul", "jalbu", "muku", "nyajil"]), js([]),
     "Pronoun A bare; muku back only.", "0.86"),
    (19, "Grandfather saw the baby's shin.",
     "Kami-ngka ngawa bibar nyajin.",
     "kami-ngka ngawa bibar nyaji-ny | grandfather-ERG baby shin see-PAST",
     "r132_perception_grandfather_baby_shin", 4, js(["kami", "ngawa", "bibar", "nyajil"]), js([]),
     "Bibar shin selected; common-noun perceiver ERG.", "0.84"),
    (20, "The old woman saw the girl's face.",
     "Mukul-jalbu-ngku maral walu nyajin.",
     "mukul-jalbu-ngku maral walu nyaji-ny | old-woman-ERG girl face see-PAST",
     "r132_perception_old_woman_girl_face", 4, js(["mukul", "jalbu", "maral", "walu", "nyajil"]), js([]),
     "Walu face in old perception frame.", "0.86"),

    (21, "Father washed mother's hand.",
     "Nganjan-angka ngamu mara julurrin.",
     "nganjan-angka ngamu mara julurri-ny | father-ERG mother hand wash-PAST",
     "r132_wash_father_mother_hand", 4, js(["nganjan", "ngamu", "mara", "julurril"]), js([]),
     "Mara hand in whole-part O; transitive common-noun A ERG.", "0.86"),
    (22, "Grandmother washed the girl's foot.",
     "Babi-ngka maral jina julurrin.",
     "babi-ngka maral jina julurri-ny | grandmother-ERG girl foot wash-PAST",
     "r132_wash_grandmother_girl_foot", 4, js(["babi", "maral", "jina", "julurril"]), js([]),
     "Jina foot only; transitive wash frame.", "0.86"),
    (23, "Mother washed father's face.",
     "Ngamu-ngku nganjan walu julurrin.",
     "ngamu-ngku nganjan walu julurri-ny | mother-ERG father face wash-PAST",
     "r132_wash_mother_father_face", 4, js(["ngamu", "nganjan", "walu", "julurril"]), js([]),
     "Walu face in retained wash frame.", "0.86"),
    (24, "The old man washed the baby's mouth.",
     "Mukul-dingkar-angka ngawa ngandal julurrin.",
     "mukul-dingkar-angka ngawa ngandal julurri-ny | old-man-ERG baby mouth wash-PAST",
     "r132_wash_old_man_baby_mouth", 4, js(["mukul", "dingkar", "ngawa", "ngandal", "julurril"]), js([]),
     "Ngandal mouth in wash frame; no instrument imported.", "0.86"),
    (25, "The woman washed grandmother's shin.",
     "Jalbu-ngku babi bibar julurrin.",
     "jalbu-ngku babi bibar julurri-ny | woman-ERG grandmother shin wash-PAST",
     "r132_wash_woman_grandmother_shin", 4, js(["jalbu", "babi", "bibar", "julurril"]), js([]),
     "Bibar shin only; common-noun A ERG.", "0.84"),
    (26, "Grandfather washed his hand.",
     "Kami mara julurri-jin.",
     "kami mara julurri-ji-ny | grandfather hand wash-REF-PAST",
     "r132_reflexive_grandfather_wash_hand", 3, js(["kami", "mara", "julurriji"]), js([]),
     "Reflexive hand-wash frame; S bare.", "0.86"),
    (27, "The young man washed his face.",
     "Warru walu julurri-jin.",
     "warru walu julurri-ji-ny | young.man face wash-REF-PAST",
     "r132_reflexive_young_man_wash_face", 3, js(["warru", "walu", "julurriji"]), js([]),
     "Reflexive wash frame; no ERG on S.", "0.86"),
    (28, "The old woman washed her foot.",
     "Mukul-jalbu jina julurri-jin.",
     "mukul-jalbu jina julurri-ji-ny | old-woman foot wash-REF-PAST",
     "r132_reflexive_old_woman_wash_foot", 3, js(["mukul", "jalbu", "jina", "julurriji"]), js([]),
     "Jina foot in reflexive wash; S bare.", "0.86"),
    (29, "The girl washed her shin.",
     "Maral bibar julurri-jin.",
     "maral bibar julurri-ji-ny | girl shin wash-REF-PAST",
     "r132_reflexive_girl_wash_shin", 3, js(["maral", "bibar", "julurriji"]), js([]),
     "Bibar shin in reflexive wash; shin sense selected.", "0.84"),

    (30, "Mother put water on father's hand.",
     "Ngamu-ngku bana yijarrin nganjan-anda mara-nga.",
     "ngamu-ngku bana yijarri-ny nganjan-anda mara-nga | mother-ERG water put-PAST father-LOC:pt hand-LOC",
     "r132_place_water_father_hand", 4, js(["ngamu", "bana", "yijarril", "nganjan", "mara"]), js([]),
     "Mara-nga body-location; person LOC:pt; common-noun A ERG.", "0.86"),
    (31, "Grandmother put water on the girl's foot.",
     "Babi-ngka bana yijarrin maral-anda jina-nga.",
     "babi-ngka bana yijarri-ny maral-anda jina-nga | grandmother-ERG water put-PAST girl-LOC:pt foot-LOC",
     "r132_place_water_girl_foot", 4, js(["babi", "bana", "yijarril", "maral", "jina"]), js([]),
     "Jina-nga foot-LOC directly backed by Patz foot-location model.", "0.88"),
    (32, "Father put water on mother's back.",
     "Nganjan-angka bana yijarrin ngamu-ndu muku-ngu.",
     "nganjan-angka bana yijarri-ny ngamu-ndu muku-ngu | father-ERG water put-PAST mother-LOC:pt back-LOC",
     "r132_place_water_mother_back", 4, js(["nganjan", "bana", "yijarril", "ngamu", "muku"]), js([]),
     "Muku-ngu body-location in retained placement frame.", "0.84"),
    (33, "The woman put water in the baby's mouth.",
     "Jalbu-ngku bana yijarrin ngawa-nda ngandal-ba.",
     "jalbu-ngku bana yijarri-ny ngawa-nda ngandal-ba | woman-ERG water put-PAST baby-LOC:pt mouth-LOC",
     "r132_place_water_baby_mouth", 4, js(["jalbu", "bana", "yijarril", "ngawa", "ngandal"]), js([]),
     "Ngandal-ba mouth-LOC; no instrument or possessive import.", "0.84"),
    (34, "The old man put water on the child's shin.",
     "Mukul-dingkar-angka bana yijarrin karrkay-anda bibar-a.",
     "mukul-dingkar-angka bana yijarri-ny karrkay-anda bibar-a | old-man-ERG water put-PAST child-LOC:pt shin-LOC",
     "r132_place_water_child_shin", 4, js(["mukul", "dingkar", "bana", "yijarril", "karrkay", "bibar"]), js([]),
     "Bibar-a shin-LOC is directly grammar-backed.", "0.86"),
    (35, "The child put a flower in mother's hand.",
     "Karrkay-ngku nganka yijarrin ngamu-ndu mara-nga.",
     "karrkay-ngku nganka yijarri-ny ngamu-ndu mara-nga | child-ERG flower put-PAST mother-LOC:pt hand-LOC",
     "r132_place_flower_mother_hand", 4, js(["karrkay", "nganka", "yijarril", "ngamu", "mara"]), js([]),
     "Nganka is already verified; mara-nga body-location; common-noun A ERG.", "0.84"),
    (36, "Grandfather put the stone on the old woman's foot.",
     "Kami-ngka kulji yijarrin mukul-jalbu-ndu jina-nga.",
     "kami-ngka kulji yijarri-ny mukul-jalbu-ndu jina-nga | grandfather-ERG stone put-PAST old-woman-LOC:pt foot-LOC",
     "r132_place_stone_old_woman_foot", 4, js(["kami", "kulji", "yijarril", "mukul", "jalbu", "jina"]), js([]),
     "Kulji stone only; jina-nga foot-location.", "0.86"),
    (37, "The man put the stick on grandfather's back.",
     "Dingkar-angka juku yijarrin kami-nda muku-ngu.",
     "dingkar-angka juku yijarri-ny kami-nda muku-ngu | man-ERG stick put-PAST grandfather-LOC:pt back-LOC",
     "r132_place_stick_grandfather_back", 4, js(["dingkar", "juku", "yijarril", "kami", "muku"]), js([]),
     "Juku stick object; muku-ngu back-location; no instrument use.", "0.84"),
    (38, "I put water on my foot.",
     "Ngayu bana yijarrin jina-nga.",
     "ngayu bana yijarri-ny jina-nga | I water put-PAST foot-LOC",
     "r132_place_pronoun_water_own_foot", 4, js(["ngayu", "bana", "yijarril", "jina"]), js([]),
     "Pronoun A bare; inalienable foot location without possessive suffix.", "0.86"),
    (39, "We put water on our hands.",
     "Ngana bana yijarrin mara-nga.",
     "ngana bana yijarri-ny mara-nga | we water put-PAST hand-LOC",
     "r132_place_pronoun_water_own_hands", 4, js(["ngana", "bana", "yijarril", "mara"]), js([]),
     "Pronoun A bare; mara-nga body-location.", "0.84"),

    (40, "The dog bit mother's hand.",
     "Kaya-ngka ngamu mara baykan.",
     "kaya-ngka ngamu mara bayka-ny | dog-ERG mother hand bite-PAST",
     "r132_affected_dog_bite_mother_hand", 4, js(["kaya", "ngamu", "mara", "baykal"]), js([]),
     "Whole-part affected O; common-noun A ERG.", "0.86"),
    (41, "The crab bit father's foot.",
     "Ngalngal-angka nganjan jina baykan.",
     "ngalngal-angka nganjan jina bayka-ny | crab-ERG father foot bite-PAST",
     "r132_affected_crab_bite_father_foot", 4, js(["ngalngal", "nganjan", "jina", "baykal"]), js([]),
     "Jina foot affected object; no track/path sense.", "0.84"),
    (42, "The dog licked the child's head.",
     "Kaya-ngka karrkay dukul bijan.",
     "kaya-ngka karrkay dukul bija-ny | dog-ERG child head lick-PAST",
     "r132_affected_dog_lick_child_head", 4, js(["kaya", "karrkay", "dukul", "bijal"]), js([]),
     "Dukul head core sense in affected-object frame.", "0.84"),
    (43, "Grandmother happened to step on my hand.",
     "Babi-nda ngayu mara narri-jin.",
     "babi-nda ngayu mara narri-ji-ny | grandmother-LOC:pt I hand step.on-ITR-PAST",
     "r132_step_grandmother_my_hand", 3, js(["babi", "ngayu", "mara", "narrijil"]), js([]),
     "Chance-passive stepping frame; person LOC:pt trigger, pronoun possessor, body-part O/S.", "0.84"),
    (44, "The child happened to step on my shin.",
     "Karrkay-anda ngayu bibar narri-jin.",
     "karrkay-anda ngayu bibar narri-ji-ny | child-LOC:pt I shin step.on-ITR-PAST",
     "r132_step_child_my_shin", 3, js(["karrkay", "ngayu", "bibar", "narrijil"]), js([]),
     "Chance-passive stepping frame; bibar shin selected.", "0.84"),
    (45, "Father dropped the stone on mother's foot.",
     "Nganjan-angka kulji daray-manin ngamu-ndu jina-nga.",
     "nganjan-angka kulji dara-y-mani-ny ngamu-ndu jina-nga | father-ERG stone fall-y-CAUS-PAST mother-LOC:pt foot-LOC",
     "r132_drop_stone_mother_foot", 4, js(["nganjan", "kulji", "daraymanil", "ngamu", "jina"]), js([]),
     "Drop frame modelled on Patz stone-on-foot example; kulji stone, jina foot.", "0.88"),
    (46, "Grandmother dropped the stick on the child's back.",
     "Babi-ngka juku daray-manin karrkay-anda muku-ngu.",
     "babi-ngka juku dara-y-mani-ny karrkay-anda muku-ngu | grandmother-ERG stick fall-y-CAUS-PAST child-LOC:pt back-LOC",
     "r132_drop_stick_child_back", 4, js(["babi", "juku", "daraymanil", "karrkay", "muku"]), js([]),
     "Muku-ngu back-location in retained drop frame.", "0.84"),
    (47, "The child dropped the stone on grandmother's hand.",
     "Karrkay-ngku kulji daray-manin babi-nda mara-nga.",
     "karrkay-ngku kulji dara-y-mani-ny babi-nda mara-nga | child-ERG stone fall-y-CAUS-PAST grandmother-LOC:pt hand-LOC",
     "r132_drop_stone_grandmother_hand", 4, js(["karrkay", "kulji", "daraymanil", "babi", "mara"]), js([]),
     "Mara-nga body-location in retained drop frame; common-noun A ERG.", "0.84"),

    (48, "Father is deaf.",
     "Nganjan milka-kari.",
     "nganjan milka-kari | father ear-PRIV",
     "r132_privative_father_deaf", 3, js(["nganjan", "milka", "kari"]), js([]),
     "Milka-kari sourced privative compound; owner changed only.", "0.88"),
    (49, "Mother is sleepless.",
     "Ngamu miyil-dandi.",
     "ngamu miyil-dandi | mother eye-hard",
     "r132_compound_mother_sleepless", 3, js(["ngamu", "miyil", "dandi"]), js([]),
     "Miyil-dandi sourced eye-hard compound; miyil eye sense.", "0.88"),
    (50, "The dog turned around.",
     "Kaya walu-wukurrin.",
     "kaya walu-wukurri-ny | dog face-follow-PAST",
     "r132_compound_dog_turn_around", 3, js(["kaya", "walu", "wukurril"]), js([]),
     "Walu-wukurri-ny sourced face-follow compound; new subject only.", "0.88"),
]


def has_token(low: str, token: str) -> bool:
    return re.search(rf"(?<![a-z-]){re.escape(token)}(?![a-z-])", low) is not None


def first_word(low: str) -> str:
    return low.split()[0].strip(".,!?;:\"")


def has_ergative(word: str) -> bool:
    return bool(re.search(r"-(?:ngka|angka|ngku)\b", word))


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"{LABEL}: expected 50 rows, found {len(ROWS)}")
    seen_ku: set[str] = set()
    for expected, row in enumerate(ROWS, start=1):
        seq, english, kuku, analysis, frame, _tier, words_used, loans_used, evidence, confidence = row
        label = f"{LABEL}.{seq:02d}"
        low = kuku.lower()
        english_low = english.lower()
        analysis_low = analysis.lower()
        try:
            words = json.loads(words_used)
            loans = json.loads(loans_used)
        except json.JSONDecodeError as exc:
            issues.append(f"{label}: JSON field failed: {exc}")
            words = []
            loans = []
        if seq != expected:
            issues.append(f"{label}: sequence mismatch, expected {expected}")
        if not english[:1].isupper() or english[-1:] not in ".!?":
            issues.append(f"{label}: English punctuation/capitalization failure")
        if " | " not in analysis:
            issues.append(f"{label}: analysis missing gloss separator")
        if not frame.startswith("r132_"):
            issues.append(f"{label}: Rule 132 frame must start with r132_")
        if low in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(low)
        if loans:
            issues.append(f"{label}: Rule 132 construction batch should not introduce loans")
        for denied in DENY:
            if has_token(low, denied):
                issues.append(f"{label}: denied or out-of-scope token {denied!r}")
        for marker in FORBIDDEN_MARKERS:
            if marker in low:
                issues.append(f"{label}: forbidden construction marker {marker}")
        if "INST" in analysis or "-bu" in analysis_low or re.search(r"\b[a-z]+-bu\b", low):
            issues.append(f"{label}: no instrument rows in Rule 132")
        if "COMIT" in analysis:
            issues.append(f"{label}: no comitative rows in Rule 132")
        if re.search(r"-(?:amu|mu|ndamun|andamun)\b", low):
            issues.append(f"{label}: no alienable possessive suffixes in Rule 132")
        for head, gloss in BODY_HEADS.items():
            if has_token(low, head) and head not in words:
                issues.append(f"{label}: body head {head} missing from words_used")
            if has_token(low, head) and gloss not in analysis_low and not (
                head == "milka" and "ear-" in analysis_low
            ) and not (head == "miyil" and "eye-" in analysis_low) and not (
                head == "walu" and "face-" in analysis_low
            ):
                issues.append(f"{label}: {head} must be glossed with selected body-part sense {gloss!r}")
        if has_token(low, "jina") and ("track" in english_low or "track" in analysis_low):
            issues.append(f"{label}: jina must be foot only, not track/path")
        if has_token(low, "miyil") and "tree" in analysis_low:
            issues.append(f"{label}: miyil tree sense is excluded")
        if has_token(low, "bibar") and "ankle" in analysis_low:
            issues.append(f"{label}: bibar must use shin sense in Rule 132")
        if has_token(low, "kaka") and not any(word in analysis_low for word in ("sore", "pain")):
            issues.append(f"{label}: kaka must be glossed sore/pain")
        first = first_word(low)
        if any(verb in low for verb in TRANSITIVE_PAST):
            if first not in PRONOUN_A and not has_ergative(first):
                issues.append(f"{label}: common-noun A in transitive past frame should be ERG-marked")
        if "nyajin" in low and first not in PRONOUN_A and not has_ergative(first):
            issues.append(f"{label}: common-noun perceiver with nyaji-l should be ERG-marked")
        if "julurri-jin" in low and has_ergative(first):
            issues.append(f"{label}: reflexive wash S should not be ERG-marked")
        if "narri-jin" in low and not re.search(r"^[a-z-]+-(?:anda|ndu|nda|anka)\b", low):
            issues.append(f"{label}: narri-ji stepping frame should keep person LOC:pt trigger")
        if any(loc in low for loc in ("mara-nga", "jina-nga", "muku-ngu", "ngandal-ba", "bibar-a")):
            if "LOC" not in analysis:
                issues.append(f"{label}: body-part location row must show LOC analysis")
        if not evidence or not confidence:
            issues.append(f"{label}: missing evidence/confidence")
    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 132 draft: construction consolidation after Rule 131", "operator-guide-2026-07-07", "under_review", NOW),
        )
        batch_id = cur.lastrowid

    dupes: list[str] = []
    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 132 source pass rechecked Rule 131 body-head decisions, Patz body-part examples, lexeme-table rows, "
            "and prior corpus surfaces before authoring 50 hand-written construction-consolidation rows."
        ),
        command="sqlite lexeme readback; sed/rg grammar body-part examples; exact duplicate scan over planned rows",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="source",
        event_type="body-head-source-check",
        actor="claude-main",
        detail=(
            "Body-head controls retained mara hand, jina foot, walu face, milka ear, miyil eye, ngandal mouth, "
            "dukul head, muku back, and bibar shin. Kaka remained only sore/pain. Jina track/path, miyil tree, "
            "bibar ankle, and all secondary dukul/muku senses were excluded."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="scope",
        event_type="risk-controls",
        actor="claude-main",
        detail=(
            "Controls: construction consolidation only; no new lexical field; no instruments, comitatives, alienable "
            "possessives, temporal clitic rows, or generator-authored content. Common-noun transitive A must be ERG; "
            "pronoun A bare; reflexive S bare."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="preflight",
        event_type="existing-surface-scan",
        actor="claude-main",
        detail="Final planned-surface scan found 50 planned rows, 50 unique within-batch surfaces, and zero exact cross-corpus duplicates.",
        command="sqlite exact-surface query over planned Batch 174 rows",
        output_excerpt="planned 50 unique 50; no duplicate lines emitted",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="process",
        event_type="process-capture-expanded",
        actor="claude-main",
        detail=(
            "Batch 174 stores source readback, construction scope, static guards, duplicate scan, bank outcome, and "
            "later lint/review/close/export/docs evidence as process_events plus this process log."
        ),
        artifact_path=PROCESS_LOG,
        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-written Rule 132 body-part construction-consolidation rows as under_review after static and exact duplicate checks.",
        command="python3 batch_scripts/bank_174.py",
        output_excerpt="banked batch-174: 50 rows under_review; duplicate scan clean",
        artifact_path="batch_scripts/bank_174.py",
        created_at=NOW,
    )
    db.commit()
    count = db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=?", (batch_id,)).fetchone()[0]
    under_review = db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status='under_review'", (batch_id,)).fetchone()[0]
    verified = db.execute("SELECT COUNT(*) FROM sentences WHERE status IN ('accepted','revised')").fetchone()[0]
    print(f"banked {LABEL}: rows={count}, under_review={under_review}, verified_before_close={verified}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
