#!/usr/bin/env python3
"""Bank batch-176: Rule 134 construction consolidation after Rule 133."""

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

TARGET_HEADS = {
    "bajibay": "bone",
    "bangkarr": "body",
    "biji": "tail",
    "jiba": "liver",
    "malbin": "upper leg",
}
SUPPORT_HEADS = {
    "minya": "meat",
    "kuyu": "fish",
}
DENY = {
    "dakil",
    "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", "nukan", "wajun", "manin", "dajin", "bawan")
SAFE_DAT_TOPICS = {"jiba-ka", "bajibay-ngka", "minya-ka", "kuyu-ku", "bajibay-kari-ka"}


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


ROWS = [
    (1, "Grandfather's upper leg is sore.",
     "Kami malbin kaka.",
     "kami malbin kaka | grandfather upper.leg sore",
     "r134_state_grandfather_upper_leg_sore", 2, js(["kami", "malbin", "kaka"]), js([]),
     "Rule 134 construction consolidation: malbin remains dictionary upper leg; old body-part state frame, no case form.", "0.84"),
    (2, "Mother's upper leg is good.",
     "Ngamu malbin ngulkurr.",
     "ngamu malbin ngulkurr | mother upper.leg good",
     "r134_state_mother_upper_leg_good", 2, js(["ngamu", "malbin", "ngulkurr"]), js([]),
     "Malbin upper-leg sense only; old state predicate frame.", "0.84"),
    (3, "The old man's body is sore.",
     "Mukul-dingkar bangkarr kaka.",
     "mukul-dingkar bangkarr kaka | old-man body sore",
     "r134_state_old_man_body_sore", 2, js(["mukul", "dingkar", "bangkarr", "kaka"]), js([]),
     "Bangkarr body/flesh only, not steak; kaka sore/pain predicate.", "0.86"),
    (4, "The woman's body is dry.",
     "Jalbu bangkarr marral.",
     "jalbu bangkarr marral | woman body dry",
     "r134_state_woman_body_dry", 2, js(["jalbu", "bangkarr", "marral"]), js([]),
     "Bangkarr body/flesh in old adjective state frame.", "0.84"),
    (5, "The dog's tail is dry.",
     "Kaya biji marral.",
     "kaya biji marral | dog tail dry",
     "r134_state_dog_tail_dry", 2, js(["kaya", "biji", "marral"]), js([]),
     "Biji animal tail; whole-part apposition follows Rule 133 source controls.", "0.84"),
    (6, "The fish tail is good.",
     "Kuyu biji ngulkurr.",
     "kuyu biji ngulkurr | fish tail good",
     "r134_state_fish_tail_good", 2, js(["kuyu", "biji", "ngulkurr"]), js([]),
     "Kuyu biji whole-part NP from Patz ex.520; state predicate varied from Rule 133.", "0.84"),
    (7, "The bone is big.",
     "Bajibay yalbay.",
     "bajibay yalbay | bone big",
     "r134_state_bone_big", 2, js(["bajibay", "yalbay"]), js([]),
     "Bajibay literal bone only; simple size predicate.", "0.86"),
    (8, "The bone is very good.",
     "Bajibay ngulkurr bajaku.",
     "bajibay ngulkurr bajaku | bone good very",
     "r134_state_bone_very_good", 2, js(["bajibay", "ngulkurr", "bajaku"]), js([]),
     "Bajibay literal bone; no metaphorical story-bone extension.", "0.86"),
    (9, "The liver is dry.",
     "Jiba marral.",
     "jiba marral | liver dry",
     "r134_state_liver_dry", 2, js(["jiba", "marral"]), js([]),
     "Jiba literal liver only; emotional compounds excluded.", "0.84"),
    (10, "The boneless meat is good.",
     "Minya bajibay-kari ngulkurr.",
     "minya bajibay-kari ngulkurr | meat bone-PRIV good",
     "r134_state_boneless_meat_good", 3, js(["minya", "bajibay", "kari", "ngulkurr"]), js([]),
     "Minya meat/flesh-food support sense; bajibay-kari privative retained.", "0.86"),

    (11, "Father saw mother's upper leg.",
     "Nganjan-angka ngamu malbin nyajin.",
     "nganjan-angka ngamu malbin nyaji-ny | father-ERG mother upper.leg see-PAST",
     "r134_perception_father_mother_upper_leg", 4, js(["nganjan", "ngamu", "malbin", "nyajil"]), js([]),
     "Whole-part O with malbin upper leg; common-noun perceiver ERG.", "0.84"),
    (12, "The child saw grandmother's upper leg.",
     "Karrkay-ngku babi malbin nyajin.",
     "karrkay-ngku babi malbin nyaji-ny | child-ERG grandmother upper.leg see-PAST",
     "r134_perception_child_grandmother_upper_leg", 4, js(["karrkay", "babi", "malbin", "nyajil"]), js([]),
     "Malbin upper leg, no front/hind-leg contrast; common-noun A ERG.", "0.84"),
    (13, "I saw the dog's tail.",
     "Ngayu kaya biji nyajin.",
     "ngayu kaya biji nyaji-ny | I dog tail see-PAST",
     "r134_perception_pronoun_dog_tail", 4, js(["ngayu", "kaya", "biji", "nyajil"]), js([]),
     "Pronoun A bare; biji tail in whole-part O.", "0.84"),
    (14, "Grandfather saw the fish tail.",
     "Kami-ngka kuyu biji nyajin.",
     "kami-ngka kuyu biji nyaji-ny | grandfather-ERG fish tail see-PAST",
     "r134_perception_grandfather_fish_tail", 4, js(["kami", "kuyu", "biji", "nyajil"]), js([]),
     "Kuyu biji whole-part object; common-noun perceiver ERG.", "0.86"),
    (15, "The old woman saw the bone in the bag.",
     "Mukul-jalbu-ngku bajibay balji-nga nyajin.",
     "mukul-jalbu-ngku bajibay balji-nga nyaji-ny | old-woman-ERG bone bag-LOC see-PAST",
     "r134_perception_old_woman_bone_bag", 4, js(["mukul", "jalbu", "bajibay", "balji", "nyajil"]), js([]),
     "Bajibay literal bone in retained bag-location perception frame.", "0.86"),
    (16, "We saw the bone on the ground.",
     "Ngana bajibay bubu-ngu nyajin.",
     "ngana bajibay bubu-ngu nyaji-ny | we bone ground-LOC see-PAST",
     "r134_perception_pronoun_bone_ground", 4, js(["ngana", "bajibay", "bubu", "nyajil"]), js([]),
     "Pronoun A bare; bubu-ngu ground LOC retained.", "0.86"),
    (17, "The man saw liver in the shade.",
     "Dingkar-angka jiba wabarr-ba nyajin.",
     "dingkar-angka jiba wabarr-ba nyaji-ny | man-ERG liver shade-LOC see-PAST",
     "r134_perception_man_liver_shade", 4, js(["dingkar", "jiba", "wabarr", "nyajil"]), js([]),
     "Jiba literal liver; common-noun A ERG; old shade LOC.", "0.84"),
    (18, "Mother saw the child's body.",
     "Ngamu-ngku karrkay bangkarr nyajin.",
     "ngamu-ngku karrkay bangkarr nyaji-ny | mother-ERG child body see-PAST",
     "r134_perception_mother_child_body", 4, js(["ngamu", "karrkay", "bangkarr", "nyajil"]), js([]),
     "Bangkarr body/flesh only in whole-part O; common-noun perceiver ERG.", "0.86"),
    (19, "I saw meat in the bag.",
     "Ngayu minya balji-nga nyajin.",
     "ngayu minya balji-nga nyaji-ny | I meat bag-LOC see-PAST",
     "r134_perception_pronoun_meat_bag", 4, js(["ngayu", "minya", "balji", "nyajil"]), js([]),
     "Minya meat/flesh-food support row; pronoun A bare.", "0.86"),
    (20, "They saw fish on the ground.",
     "Jana kuyu bubu-ngu nyajin.",
     "jana kuyu bubu-ngu nyaji-ny | they fish ground-LOC see-PAST",
     "r134_perception_pronoun_fish_ground", 4, js(["jana", "kuyu", "bubu", "nyajil"]), js([]),
     "Kuyu fish support row; pronoun A bare.", "0.86"),

    (21, "The dog ate the bone in the shade.",
     "Kaya-ngka bajibay wabarr-ba nukan.",
     "kaya-ngka bajibay wabarr-ba nuka-ny | dog-ERG bone shade-LOC eat-PAST",
     "r134_consumption_dog_bone_shade", 4, js(["kaya", "bajibay", "wabarr", "nukal"]), js([]),
     "Patz dog-eats-bone model with old shade LOC; common-noun A ERG.", "0.86"),
    (22, "Father ate liver.",
     "Nganjan-angka jiba nukan.",
     "nganjan-angka jiba nuka-ny | father-ERG liver eat-PAST",
     "r134_consumption_father_liver", 4, js(["nganjan", "jiba", "nukal"]), js([]),
     "Jiba literal liver as food object; common-noun A ERG.", "0.84"),
    (23, "Mother cooked boneless meat.",
     "Ngamu-ngku minya bajibay-kari wajun.",
     "ngamu-ngku minya bajibay-kari waju-ny | mother-ERG meat bone-PRIV cook-PAST",
     "r134_cooking_mother_boneless_meat", 4, js(["ngamu", "minya", "bajibay", "kari", "wajul"]), js([]),
     "Minya meat plus bajibay-kari privative in old cooking frame.", "0.86"),
    (24, "Grandfather cooked fish.",
     "Kami-ngka kuyu wajun.",
     "kami-ngka kuyu waju-ny | grandfather-ERG fish cook-PAST",
     "r134_cooking_grandfather_fish", 4, js(["kami", "kuyu", "wajul"]), js([]),
     "Kuyu fish support row in old waju-l cooking frame.", "0.86"),
    (25, "The child put liver in the bag.",
     "Karrkay-ngku jiba balji-nga yijarrin.",
     "karrkay-ngku jiba balji-nga yijarri-ny | child-ERG liver bag-LOC put-PAST",
     "r134_placement_child_liver_bag", 4, js(["karrkay", "jiba", "balji", "yijarril"]), js([]),
     "Jiba literal liver; bag-location placement frame retained.", "0.84"),
    (26, "Grandmother put the bone on the ground.",
     "Babi-ngka bajibay bubu-ngu yijarrin.",
     "babi-ngka bajibay bubu-ngu yijarri-ny | grandmother-ERG bone ground-LOC put-PAST",
     "r134_placement_grandmother_bone_ground", 4, js(["babi", "bajibay", "bubu", "yijarril"]), js([]),
     "Bajibay bone object in old ground-location placement frame.", "0.86"),
    (27, "Father left liver in the shade.",
     "Nganjan-angka jiba wabarr-ba bawan.",
     "nganjan-angka jiba wabarr-ba bawa-ny | father-ERG liver shade-LOC leave-PAST",
     "r134_leave_father_liver_shade", 4, js(["nganjan", "jiba", "wabarr", "bawal"]), js([]),
     "Jiba literal liver in retained leave-location frame.", "0.84"),
    (28, "I left the bone in the bag.",
     "Ngayu bajibay balji-nga bawan.",
     "ngayu bajibay balji-nga bawa-ny | I bone bag-LOC leave-PAST",
     "r134_leave_pronoun_bone_bag", 4, js(["ngayu", "bajibay", "balji", "bawal"]), js([]),
     "Pronoun A bare; bajibay literal bone; bag LOC retained.", "0.86"),
    (29, "Mother took liver from the ground.",
     "Ngamu-ngku jiba bubu-muny manin.",
     "ngamu-ngku jiba bubu-muny mani-ny | mother-ERG liver ground-ABL get-PAST",
     "r134_get_mother_liver_ground", 4, js(["ngamu", "jiba", "bubu", "manil"]), js([]),
     "Jiba literal liver; bubu-muny source retained from Rule 133.", "0.84"),
    (30, "The young man took meat from the ground.",
     "Warru-ngku minya bubu-muny manin.",
     "warru-ngku minya bubu-muny mani-ny | young.man-ERG meat ground-ABL get-PAST",
     "r134_get_young_man_meat_ground", 4, js(["warru", "minya", "bubu", "manil"]), js([]),
     "Minya meat/flesh-food support row; source ABL already controlled.", "0.86"),

    (31, "Grandmother wants liver.",
     "Babi jiba-ka wawu.",
     "babi jiba-ka wawu | grandmother liver-DAT want",
     "r134_want_grandmother_liver", 3, js(["babi", "jiba", "wawu"]), js([]),
     "Jiba-ka dative retained from Patz Text 76 line 81.", "0.88"),
    (32, "I want a bone.",
     "Ngayu bajibay-ngka wawu.",
     "ngayu bajibay-ngka wawu | I bone-DAT want",
     "r134_want_pronoun_bone", 3, js(["ngayu", "bajibay", "wawu"]), js([]),
     "Bajibay-ngka retained y-final dative topic; pronoun subject bare.", "0.86"),
    (33, "The young man wants fish.",
     "Warru kuyu-ku wawu.",
     "warru kuyu-ku wawu | young.man fish-DAT want",
     "r134_want_young_man_fish", 3, js(["warru", "kuyu", "wawu"]), js([]),
     "Kuyu-ku support dative in old want frame.", "0.86"),
    (34, "Mother wants meat without bones.",
     "Ngamu wawu minya-ka bajibay-kari-ka.",
     "ngamu wawu minya-ka bajibay-kari-ka | mother want meat-DAT bone-PRIV-DAT",
     "r134_want_mother_boneless_meat", 3, js(["ngamu", "minya", "bajibay", "kari", "wawu"]), js([]),
     "Boneless meat dative phrase retained; subject changed for construction consolidation.", "0.86"),
    (35, "Father asked mother about liver.",
     "Nganjan ngamu-ndu jiba-ka babajin.",
     "nganjan ngamu-ndu jiba-ka babaji-ny | father mother-LOC:pt liver-DAT ask.about-PAST",
     "r134_ask_father_mother_liver", 4, js(["nganjan", "ngamu", "jiba", "babajiy"]), js([]),
     "Babaji-y asker unmarked; person LOC:pt, topic DAT.", "0.86"),
    (36, "Grandmother asked the child about a bone.",
     "Babi karrkay-anda bajibay-ngka babajin.",
     "babi karrkay-anda bajibay-ngka babaji-ny | grandmother child-LOC:pt bone-DAT ask.about-PAST",
     "r134_ask_grandmother_child_bone", 4, js(["babi", "karrkay", "bajibay", "babajiy"]), js([]),
     "Ask-about frame retained; no ERG on asker.", "0.86"),
    (37, "The woman asked grandfather about fish.",
     "Jalbu kami-nda kuyu-ku babajin.",
     "jalbu kami-nda kuyu-ku babaji-ny | woman grandfather-LOC:pt fish-DAT ask.about-PAST",
     "r134_ask_woman_grandfather_fish", 4, js(["jalbu", "kami", "kuyu", "babajiy"]), js([]),
     "Kuyu-ku support topic in old ask-about frame.", "0.86"),
    (38, "The old man asked mother about meat without bones.",
     "Mukul-dingkar ngamu-ndu minya-ka bajibay-kari-ka babajin.",
     "mukul-dingkar ngamu-ndu minya-ka bajibay-kari-ka babaji-ny | old-man mother-LOC:pt meat-DAT bone-PRIV-DAT ask.about-PAST",
     "r134_ask_old_man_mother_boneless_meat", 4, js(["mukul", "dingkar", "ngamu", "minya", "bajibay", "kari", "babajiy"]), js([]),
     "Ask-about frame with retained boneless-meat dative phrase; asker unmarked.", "0.84"),
    (39, "Mother gave a bone to the child.",
     "Ngamu-ngku bajibay dajin karrkay-anda.",
     "ngamu-ngku bajibay daji-ny karrkay-anda | mother-ERG bone give-PAST child-LOC:pt",
     "r134_give_mother_bone_child", 4, js(["ngamu", "bajibay", "daya", "karrkay"]), js([]),
     "Bajibay literal bone in retained transfer frame; recipient LOC:pt.", "0.86"),
    (40, "The child gave liver to father.",
     "Karrkay-ngku jiba dajin nganjan-anda.",
     "karrkay-ngku jiba daji-ny nganjan-anda | child-ERG liver give-PAST father-LOC:pt",
     "r134_give_child_liver_father", 4, js(["karrkay", "jiba", "daya", "nganjan"]), js([]),
     "Jiba literal liver object; recipient LOC:pt; common-noun A ERG.", "0.84"),

    (41, "Grandmother washed the child's upper leg.",
     "Babi-ngka karrkay malbin julurrin.",
     "babi-ngka karrkay malbin julurri-ny | grandmother-ERG child upper.leg wash-PAST",
     "r134_wash_grandmother_child_upper_leg", 4, js(["babi", "karrkay", "malbin", "julurril"]), js([]),
     "Malbin upper leg in old transitive wash frame; no case form.", "0.84"),
    (42, "Father washed mother's upper leg.",
     "Nganjan-angka ngamu malbin julurrin.",
     "nganjan-angka ngamu malbin julurri-ny | father-ERG mother upper.leg wash-PAST",
     "r134_wash_father_mother_upper_leg", 4, js(["nganjan", "ngamu", "malbin", "julurril"]), js([]),
     "Whole-part O with malbin; common-noun A ERG.", "0.84"),
    (43, "I washed my upper leg.",
     "Ngayu malbin julurri-jin.",
     "ngayu malbin julurri-ji-ny | I upper.leg wash-REF-PAST",
     "r134_reflexive_wash_pronoun_upper_leg", 3, js(["ngayu", "malbin", "julurriji"]), js([]),
     "Pronoun S bare; malbin upper leg; no possessive suffix.", "0.84"),
    (44, "The dog bit grandfather's upper leg.",
     "Kaya-ngka kami malbin baykan.",
     "kaya-ngka kami malbin bayka-ny | dog-ERG grandfather upper.leg bite-PAST",
     "r134_affected_dog_bite_grandfather_upper_leg", 4, js(["kaya", "kami", "malbin", "baykal"]), js([]),
     "Malbin in affected whole-part O; no front/hind-leg wording.", "0.84"),
    (45, "The crab bit the fish tail.",
     "Ngalngal-angka kuyu biji baykan.",
     "ngalngal-angka kuyu biji bayka-ny | crab-ERG fish tail bite-PAST",
     "r134_affected_crab_bite_fish_tail", 4, js(["ngalngal", "kuyu", "biji", "baykal"]), js([]),
     "Kuyu biji affected whole-part O; common-noun A ERG.", "0.84"),
    (46, "The child washed grandfather's body.",
     "Karrkay-ngku kami bangkarr julurrin.",
     "karrkay-ngku kami bangkarr julurri-ny | child-ERG grandfather body wash-PAST",
     "r134_wash_child_grandfather_body", 4, js(["karrkay", "kami", "bangkarr", "julurril"]), js([]),
     "Bangkarr body/flesh only in transitive wash frame.", "0.86"),
    (47, "The bone lay on the ground.",
     "Bajibay bubu-ngu wunan.",
     "bajibay bubu-ngu wuna-ny | bone ground-LOC lie-PAST",
     "r134_rest_bone_ground", 3, js(["bajibay", "bubu", "wunay"]), js([]),
     "Bajibay literal bone in retained rest-location frame.", "0.86"),
    (48, "The liver lay in the shade.",
     "Jiba wabarr-ba wunan.",
     "jiba wabarr-ba wuna-ny | liver shade-LOC lie-PAST",
     "r134_rest_liver_shade", 3, js(["jiba", "wabarr", "wunay"]), js([]),
     "Jiba literal liver in retained rest-location frame.", "0.84"),
    (49, "The fish tail lay on the ground.",
     "Kuyu biji bubu-ngu wunan.",
     "kuyu biji bubu-ngu wuna-ny | fish tail ground-LOC lie-PAST",
     "r134_rest_fish_tail_ground", 3, js(["kuyu", "biji", "bubu", "wunay"]), js([]),
     "Kuyu biji whole-part subject in rest-location frame.", "0.84"),
    (50, "The boneless meat lay in the bag.",
     "Minya bajibay-kari balji-nga wunan.",
     "minya bajibay-kari balji-nga wuna-ny | meat bone-PRIV bag-LOC lie-PAST",
     "r134_rest_boneless_meat_bag", 3, js(["minya", "bajibay", "kari", "balji", "wunay"]), js([]),
     "Minya meat/flesh-food plus bajibay-kari in retained rest-location frame.", "0.86"),
]


def has_base(low: str, token: str) -> bool:
    return re.search(rf"(?<![a-z]){re.escape(token)}(?:-[a-z]+)*(?![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("r134_"):
            issues.append(f"{label}: Rule 134 frame must start with r134_")
        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 134 construction batch should not introduce loans")
        for denied in DENY:
            if has_base(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 134")
        if "COMIT" in analysis:
            issues.append(f"{label}: no comitative rows in Rule 134")
        if re.search(r"-(?:amu|mu|ndamun|andamun)\b", low):
            issues.append(f"{label}: no alienable possessive suffixes in Rule 134")
        for head, gloss in TARGET_HEADS.items():
            if has_base(low, head) and head not in words:
                issues.append(f"{label}: target head {head} missing from words_used")
            if has_base(low, head):
                if head == "bangkarr":
                    if not any(sense in analysis_low for sense in ("body", "flesh")):
                        issues.append(f"{label}: bangkarr must be glossed body/flesh")
                elif head == "malbin":
                    if "upper.leg" not in analysis_low and "upper leg" not in analysis_low:
                        issues.append(f"{label}: malbin must be glossed upper leg")
                elif gloss not in analysis_low:
                    issues.append(f"{label}: {head} must be glossed with selected sense {gloss!r}")
        for head, gloss in SUPPORT_HEADS.items():
            if has_base(low, head) and head not in words:
                issues.append(f"{label}: support head {head} missing from words_used")
            if has_base(low, head) and gloss not in analysis_low:
                issues.append(f"{label}: support head {head} must be glossed {gloss!r}")
        if has_base(low, "dakil") or "front leg" in english_low or "hind leg" in english_low:
            issues.append(f"{label}: dakil/front-leg/hind-leg disambiguation is excluded from Rule 134")
        if has_base(low, "jiba") and "liver-cry" in analysis_low:
            issues.append(f"{label}: jiba emotional compound excluded; use literal liver only")
        if has_base(low, "bangkarr") and "steak" in analysis_low:
            issues.append(f"{label}: bangkarr steak/flesh-food drift excluded; use body/flesh only")
        if has_base(low, "minya") and "animal" in analysis_low:
            issues.append(f"{label}: minya support rows use meat/flesh-food, not generic animal")
        for dtok in re.findall(r"\b[a-z]+(?:-[a-z]+)+\b", low):
            if dtok.endswith(("-ka", "-ku", "-ngka")) and dtok not in SAFE_DAT_TOPICS and "DAT" in analysis:
                if not dtok.endswith(("-nka", "-anda", "-nda", "-ndu")):
                    issues.append(f"{label}: unapproved dative topic {dtok}")
        if "bajibay-kari" in low and "PRIV" not in analysis:
            issues.append(f"{label}: bajibay-kari must be analysed as PRIV")
        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 "babajin" in low and has_ergative(first):
            issues.append(f"{label}: babaji-y ask-about frame uses unmarked S asker, not ERG")
        if "julurri-jin" in low and has_ergative(first):
            issues.append(f"{label}: reflexive wash S should not be ERG-marked")
        if "malbin-" in low:
            issues.append(f"{label}: no new malbin case forms in Rule 134")
        if not any(has_base(low, head) for head in (set(TARGET_HEADS) | set(SUPPORT_HEADS))):
            issues.append(f"{label}: Rule 134 row must exercise a Rule 133 target/support head")
        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 134 draft: construction consolidation after Rule 133", "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 134 source pass rechecked Rule 133 dictionary entries, Patz examples for dog-eats-bone, "
            "kuyu biji, jiba-ka wawu, and the malbin/dakil narrative boundary; lexeme-table readback; and "
            "Batch 175 surfaces before authoring 50 construction-consolidation rows."
        ),
        command="sqlite lexeme and Batch175 readback; sed dictionary/grammar source snippets",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="scope",
        event_type="construction-scope",
        actor="claude-main",
        detail=(
            "Rule 134 admits no new lexical field. It reuses bajibay, bangkarr, biji, jiba, malbin, minya, and "
            "kuyu in retained state, perception, food/object, want/ask, transfer, wash, affected-object, and "
            "rest-location frames."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="scope",
        event_type="risk-controls",
        actor="claude-main",
        detail=(
            "Controls: no dakil, no front/hind-leg wording, no new malbin case forms, jiba literal liver only, "
            "bangkarr body/flesh only, minya meat/flesh-food only, no instruments, comitatives, possessive "
            "suffixes, temporal clitics, or unsourced analogies."
        ),
        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 176 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 176 stores source readback, construction-scope decisions, 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 134 construction-consolidation rows as under_review after static and exact duplicate checks.",
        command="python3 batch_scripts/bank_176.py",
        output_excerpt="banked batch-176: 50 rows under_review; duplicate scan clean",
        artifact_path="batch_scripts/bank_176.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())
