#!/usr/bin/env python3
"""Bank batch-138: handling-verb argument structure and case frames.

Rows are hand-authored in the main loop from Patz case-frame, passive,
antipassive/reflexive, give-recipient, and handling-verb examples. The
script only stores the rows and guards duplicates.
"""

from __future__ import annotations

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

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-138"
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", "table", "gate", "bucket", "truck",
    "motorcar", "cassette", "brake", "torch", "store",
}


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


ROWS = [
    (1, "The man got the spear.",
     "Dingkar-angka kalka manin.",
     "dingkar-angka kalka mani-ny | man-ERG spear get-PAST",
     "handling_transitive_erg_abs", 4, js(["dingkar", "kalka", "manil"]), js([]),
     "Patz §3.2.1: ERG marks transitive A; Rule 95 manil get/take.", "0.86"),
    (2, "I got the spear.",
     "Ngayu kalka manin.",
     "ngayu kalka mani-ny | I spear get-PAST",
     "handling_pronoun_a_abs_o", 4, js(["ngayu", "kalka", "manil"]), js([]),
     "Patz pronouns do not show ERG/ABS split in A/S; contrasts row 1.", "0.86"),
    (3, "The spear, the man got.",
     "Kalka dingkar-angka manin.",
     "kalka dingkar-angka mani-ny | spear man-ERG get-PAST",
     "handling_object_fronting", 5, js(["kalka", "dingkar", "manil"]), js([]),
     "Patz §5.4.1: any constituent can be fronted for localised highlighting.", "0.84"),
    (4, "The woman got food for the child.",
     "Jalbu-ngku mayi karrkay-nka manin.",
     "jalbu-ngku mayi karrkay-nka mani-ny | woman-ERG food child-DAT get-PAST",
     "handling_transitive_dative_beneficiary", 5, js(["jalbu", "mayi", "karrkay", "manil"]), js([]),
     "Patz §3.2.1/§4.1.4.4: DAT can mark goal/reason of purposeful action.", "0.84"),
    (5, "The child got food.",
     "Karrkay-ngku mayi manin.",
     "karrkay-ngku mayi mani-ny | child-ERG food get-PAST",
     "handling_transitive_erg_abs", 4, js(["karrkay", "mayi", "manil"]), js([]),
     "Simple ERG/ABS handling frame with familiar noun vocabulary.", "0.84"),

    (6, "I put the meat in the ground oven.",
     "Ngayu minya kurrma-nga yijarrin.",
     "ngayu minya kurrma-nga yijarri-ny | I meat ground.oven-LOC put-PAST",
     "handling_placement_loc_pronoun_a", 5, js(["ngayu", "minya", "kurrma", "yijarril"]), js([]),
     "Patz text 51 line 65: kurrma-nga yijarri-nya minya; pronoun A contrast.", "0.90"),
    (7, "The child put firewood on the ground.",
     "Karrkay-ngku baya bubu-ngu yijarrin.",
     "karrkay-ngku baya bubu-ngu yijarri-ny | child-ERG firewood ground-LOC put-PAST",
     "handling_placement_loc", 5, js(["karrkay", "baya", "bubu", "yijarril"]), js([]),
     "Patz local case: LOC marks placement target; Rule 95 yijarril.", "0.86"),
    (8, "The man put the stone on the ground.",
     "Dingkar-angka kulji bubu-ngu yijarrin.",
     "dingkar-angka kulji bubu-ngu yijarri-ny | man-ERG stone ground-LOC put-PAST",
     "handling_placement_loc", 5, js(["dingkar", "kulji", "bubu", "yijarril"]), js([]),
     "Placement target is LOC; object remains ABS.", "0.86"),
    (9, "On the ground, the man put the stone.",
     "Bubu-ngu dingkar-angka kulji yijarrin.",
     "bubu-ngu dingkar-angka kulji yijarri-ny | ground-LOC man-ERG stone put-PAST",
     "handling_loc_fronting", 5, js(["bubu", "dingkar", "kulji", "yijarril"]), js([]),
     "Patz §5.4.1: localised highlighting can front a locative constituent.", "0.84"),
    (10, "In the bag, mother put food.",
     "Kurmun-ba ngamu-ngku mayi buyandin.",
     "kurmun-ba ngamu-ngku mayi buyandi-ny | bag-LOC mother-ERG food put.in.container-PAST",
     "handling_container_loc_fronting", 5, js(["kurmun", "ngamu", "mayi", "buyandi-l"]), js([]),
     "Patz-added buyandi-l 'put in container'; LOC container fronted.", "0.86"),
    (11, "Mother put food in the bag.",
     "Ngamu-ngku mayi kurmun-ba buyandin.",
     "ngamu-ngku mayi kurmun-ba buyandi-ny | mother-ERG food bag-LOC put.in.container-PAST",
     "handling_container_loc", 5, js(["ngamu", "mayi", "kurmun", "buyandi-l"]), js([]),
     "Container placement counterpart to row 10.", "0.86"),
    (12, "The woman put food in the bag.",
     "Jalbu-ngku mayi kurmun-ba yijarrin.",
     "jalbu-ngku mayi kurmun-ba yijarri-ny | woman-ERG food bag-LOC put-PAST",
     "handling_placement_loc", 5, js(["jalbu", "mayi", "kurmun", "yijarril"]), js([]),
     "Yijarril placement into a LOC container; contrasts buyandi-l rows.", "0.84"),
    (13, "The bag, the woman closed.",
     "Kurmun jalbu-ngku nandan.",
     "kurmun jalbu-ngku nanda-ny | bag woman-ERG close-PAST",
     "handling_object_fronting", 5, js(["kurmun", "jalbu", "nandal"]), js([]),
     "Patz §5.4.1 object-fronting; nandal close/shut/bury.", "0.84"),
    (14, "The woman closed the bag today.",
     "Jalbu-ngku kurmun nandan nyiku.",
     "jalbu-ngku kurmun nanda-ny nyiku | woman-ERG bag close-PAST today",
     "handling_transitive_time_adverb", 4, js(["jalbu", "kurmun", "nandal", "nyiku"]), js([]),
     "Simple transitive close row with time adverb; avoids loan gate/store objects.", "0.82"),

    (15, "I gave food to mother.",
     "Ngayu mayi ngamu-ndu dajin.",
     "ngayu mayi ngamu-ndu daji-ny | I food mother-LOC:pt give-PAST",
     "give_loc_recipient", 5, js(["ngayu", "mayi", "ngamu", "dajil"]), js([]),
     "Patz §4.5.5: recipient of giving may be LOC; Rule 95 dajil.", "0.88"),
    (16, "I gave mother her food.",
     "Ngayu ngamu-mu mayi dajin.",
     "ngayu ngamu-mu mayi daji-ny | I mother-POSS food give-PAST",
     "give_possessive_object", 5, js(["ngayu", "ngamu", "mayi", "dajil"]), js([]),
     "Patz ex.530: Ngayu ngamu-mu mayi daji-ny; possessive NP in O function.", "0.94"),
    (17, "The two women gave food to the man.",
     "Bula jalbu-bulal-angka mayi dingkar-anda dajin.",
     "bula jalbu-bulal-angka mayi dingkar-anda daji-ny | they.two woman-pair-ERG food man-LOC:pt give-PAST",
     "give_loc_recipient", 5, js(["bula", "jalbu", "-bulal", "mayi", "dingkar", "dajil"]), js([]),
     "Patz ex.529 give frame with jalbu-bulal-angka and dingkar-anda recipient.", "0.92"),
    (18, "Mother gave green ant to the child.",
     "Ngamu-ngku yangka karrkay-anda dajin.",
     "ngamu-ngku yangka karrkay-anda daji-ny | mother-ERG green.ant child-LOC:pt give-PAST",
     "give_loc_recipient", 5, js(["ngamu", "yangka", "karrkay", "dajil"]), js([]),
     "Patz ex.545 has yangka daji-ny karrkay-anda.", "0.90"),
    (19, "They gave clothes to the woman.",
     "Jana kambi jalbu-ndu dajin.",
     "jana kambi jalbu-ndu daji-ny | they clothes woman-LOC:pt give-PAST",
     "give_loc_recipient", 5, js(["jana", "kambi", "jalbu", "dajil"]), js([]),
     "Patz ex.561: clothes given to woman in LOC:pt.", "0.88"),
    (20, "To the woman, they gave clothes.",
     "Jalbu-ndu jana kambi dajin.",
     "jalbu-ndu jana kambi daji-ny | woman-LOC:pt they clothes give-PAST",
     "give_recipient_fronting", 5, js(["jalbu", "jana", "kambi", "dajil"]), js([]),
     "Fronted LOC recipient under Patz free word order/localised highlighting.", "0.84"),
    (21, "The clothes, they gave to the woman.",
     "Kambi jana jalbu-ndu dajin.",
     "kambi jana jalbu-ndu daji-ny | clothes they woman-LOC:pt give-PAST",
     "give_object_fronting", 5, js(["kambi", "jana", "jalbu", "dajil"]), js([]),
     "Object-fronted give row; recipient stays LOC:pt.", "0.84"),
    (22, "I gave clothes to the childless woman.",
     "Ngayu kambi jalbu-ndu kangkal-kari-nda dajin.",
     "ngayu kambi jalbu-ndu kangkal-kari-nda daji-ny | I clothes woman-LOC:pt own.child-PRIV-LOC give-PAST",
     "give_privative_recipient_np", 6, js(["ngayu", "kambi", "jalbu", "kangkal", "-kari", "dajil"]), js([]),
     "Patz ex.561: Ngayu kambi daji-ny jalbu-ndu kangkal-kari-nda.", "0.90"),

    (23, "Mother cut the meat with a knife.",
     "Ngamu-ngku minya naybu-bu yakan.",
     "ngamu-ngku minya naybu-bu yaka-ny | mother-ERG meat knife-INST cut-PAST",
     "handling_instrument", 5, js(["ngamu", "minya", "naybu", "yakal"]), js([]),
     "Patz ex.247a/344: cutting meat with knife uses naybu-bu INST.", "0.90"),
    (24, "The woman cut the fish with a knife.",
     "Jalbu-ngku kuyu naybu-bu yakan.",
     "jalbu-ngku kuyu naybu-bu yaka-ny | woman-ERG fish knife-INST cut-PAST",
     "handling_instrument", 5, js(["jalbu", "kuyu", "naybu", "yakal"]), js([]),
     "Instrumental implement remains distinct from ERG A.", "0.86"),
    (25, "The child broke the stick with a stone.",
     "Karrkay-ngku jaba kulji-bu dumbarrin.",
     "karrkay-ngku jaba kulji-bu dumbarri-ny | child-ERG stick stone-INST break-PAST",
     "handling_instrument", 5, js(["karrkay", "jaba", "kulji", "dumbarril"]), js([]),
     "Patz §3.2.2: kulji-bu is neutral instrumental; dumbarril break.", "0.86"),
    (26, "The man broke the billycan with a stone.",
     "Dingkar-angka jungkalu kulji-bu dumbarrin.",
     "dingkar-angka jungkalu kulji-bu dumbarri-ny | man-ERG billycan stone-INST break-PAST",
     "handling_instrument", 5, js(["dingkar", "jungkalu", "kulji", "dumbarril"]), js([]),
     "Patz negation examples anchor jungkalu dumbarrin; INST adds implement.", "0.84"),
    (27, "The woman tied the bag with rope.",
     "Jalbu-ngku kurmun karra-bu kuna-bungan.",
     "jalbu-ngku kurmun karra-bu kuna-bunga-ny | woman-ERG bag rope-INST join/tie-CAUS-PAST",
     "handling_join_instrument", 5, js(["jalbu", "kurmun", "karra", "kuna-bungal"]), js([]),
     "Kuna-bungal join/tie plus instrumental rope; loan objects avoided.", "0.84"),
    (28, "The young man tied the spear with rope.",
     "Warru-ngku kalka karra-bu kuna-bungan.",
     "warru-ngku kalka karra-bu kuna-bunga-ny | young.man-ERG spear rope-INST join/tie-CAUS-PAST",
     "handling_join_instrument", 5, js(["warru", "kalka", "karra", "kuna-bungal"]), js([]),
     "Instrumental contrast using already verified kalka and karra-bu.", "0.84"),

    (29, "The man did not break the billycan.",
     "Dingkar-angka jungkalu kari dumbarrin.",
     "dingkar-angka jungkalu kari dumbarri-ny | man-ERG billycan NEG break-PAST",
     "handling_predicate_negation", 5, js(["dingkar", "jungkalu", "kari", "dumbarril"]), js([]),
     "Patz ex.593: kari immediately before verb can negate the predicate.", "0.88"),
    (30, "It was not the man who broke the billycan.",
     "Dingkar-angka kari jungkalu dumbarrin.",
     "dingkar-angka kari jungkalu dumbarri-ny | man-ERG NEG billycan break-PAST",
     "handling_subject_negation", 5, js(["dingkar", "jungkalu", "kari", "dumbarril"]), js([]),
     "Patz ex.595: kari after the subject can negate that subject constituent.", "0.86"),
    (31, "The child did not open the bag.",
     "Karrkay-ngku kurmun kari walngan.",
     "karrkay-ngku kurmun kari walnga-ny | child-ERG bag NEG open-PAST",
     "handling_predicate_negation", 5, js(["karrkay", "kurmun", "kari", "walngal"]), js([]),
     "Patz negation placement applied to Rule 95 walngal with object-sensitive sense.", "0.84"),
    (32, "Mother did not wake the baby.",
     "Ngamu-ngku ngawa kari walngan.",
     "ngamu-ngku ngawa kari walnga-ny | mother-ERG baby NEG wake-PAST",
     "handling_predicate_negation", 5, js(["ngamu", "ngawa", "kari", "walngal"]), js([]),
     "Dictionary walngal usage has Ngawa kari walnga 'Don't wake the baby'.", "0.88"),

    (33, "The young man was taken by a groper.",
     "Warru wara-nda manijin.",
     "warru wara-nda mani-ji-ny | young.man groper-LOC:pt take-ITR-PAST",
     "handling_topic_passive", 6, js(["warru", "wara", "manil", "-ji-"]), js([]),
     "Patz ex.353: Warru wara-nda mani-ji-ny topic passive.", "0.94"),
    (34, "The child was grabbed by a crocodile.",
     "Karrkay bilngkumu-ndu karrbajin.",
     "karrkay bilngkumu-ndu karrba-ji-ny | child crocodile-LOC:pt grab-ITR-PAST",
     "handling_topic_passive", 6, js(["karrkay", "bilngkumu", "karrbal", "-ji-"]), js([]),
     "Patz passive: underlying A becomes LOC:pt; karrba-ji-ny is attested in ex.357.", "0.88"),
    (35, "Meat was cut.",
     "Minya yakajin.",
     "minya yaka-ji-ny | meat cut-ITR-PAST",
     "handling_agentless_passive", 6, js(["minya", "yakal", "-ji-"]), js([]),
     "Patz text 51 line 62: minya yaka-ji-nya in agentless passive context.", "0.88"),
    (36, "The bag is closed today.",
     "Kurmun nandaji nyiku.",
     "kurmun nanda-ji-y nyiku | bag close-ITR-NONPAST today",
     "handling_agentless_passive", 6, js(["kurmun", "nandal", "-ji-", "nyiku"]), js([]),
     "Patz ex.359: nanda-ji-y nyiku 'is closed today'; surface drops final y after i; loan store replaced with bag.", "0.86"),
    (37, "The woman cut herself with a knife.",
     "Jalbu naybu-bu yakajin.",
     "jalbu naybu-bu yaka-ji-ny | woman knife-INST cut-REF-PAST",
     "handling_reflexive_instrument", 6, js(["jalbu", "naybu", "yakal", "-ji-"]), js([]),
     "Patz ex.344: woman cut herself with knife; instrument remains INST.", "0.92"),
    (38, "The man got tangled in the fishing line.",
     "Dingkar baduru-bu kajajin.",
     "dingkar baduru-bu kaja-ji-ny | man fishing.line-INST tie-ITR-PAST",
     "handling_reflexive_passive_instrument", 6, js(["dingkar", "baduru", "kajal", "-ji-"]), js([]),
     "Patz ex.343: kaja-ji-ny baduru-bu 'got tangled in the fishing line'.", "0.90"),

    (39, "He got a stone and placed it.",
     "Nyulu kulji manin, yijarrin.",
     "nyulu kulji mani-ny, yijarri-ny | he stone get-PAST put-PAST",
     "handling_sequence_zero_object", 5, js(["nyulu", "kulji", "manil", "yijarril"]), js([]),
     "Patz ex.117/text 12 line 60: kulji mani-ny, yijarri-ny.", "0.92"),
    (40, "The man got the stone and put it on the ground.",
     "Dingkar-angka kulji manin, bubu-ngu yijarrin.",
     "dingkar-angka kulji mani-ny, bubu-ngu yijarri-ny | man-ERG stone get-PAST ground-LOC put-PAST",
     "handling_sequence_zero_object", 5, js(["dingkar", "kulji", "manil", "bubu", "yijarril"]), js([]),
     "Patz §4.3.1: same A/O action chains may delete repeated O after introduction.", "0.86"),
    (41, "The two young men pulled out bamboo and tied it.",
     "Bula warru-ngku nambarr kulban, kajan.",
     "bula warru-ngku nambarr kulba-ny, kaja-ny | they.two young.man-ERG bamboo pull-PAST tie-PAST",
     "handling_sequence_zero_object", 5, js(["bula", "warru", "nambarr", "kulbal", "kajal"]), js([]),
     "Patz ex.384: nambarr kulba-ny, kaja-ny with same object.", "0.92"),
    (42, "The two young men pulled out bamboo, tied it, and went on.",
     "Bula warru-ngku nambarr kulban, kajan, dungan-baja.",
     "bula warru-ngku nambarr kulba-ny, kaja-ny, dunga-ny-baja | they.two young.man-ERG bamboo pull-PAST tie-PAST go-PAST-again",
     "handling_sequence_transitive_intransitive", 6, js(["bula", "warru", "nambarr", "kulbal", "kajal", "dungay", "-baja"]), js([]),
     "Patz ex.384 full chain: pull bamboo, wrap/tie, go on again.", "0.92"),
    (43, "They got mussels and threw them.",
     "Jana mukirr manin, yilban.",
     "jana mukirr mani-ny, yilba-ny | they mussel get-PAST throw-PAST",
     "handling_sequence_zero_object", 5, js(["jana", "mukirr", "manil", "yilbal"]), js([]),
     "Patz text 36 line 28: mukirr mani-nya, yilba-ny.", "0.88"),
    (44, "They gathered mussels and threw them.",
     "Jana mukirr mujan, yilban.",
     "jana mukirr muja-ny, yilba-ny | they mussel gather-PAST throw-PAST",
     "handling_sequence_zero_object", 5, js(["jana", "mukirr", "mujal", "yilbal"]), js([]),
     "Patz text 36 has gather/throw sequence with mukirr.", "0.86"),
    (45, "Onto the ground, they threw some.",
     "Bubu-ngu jana kulur yilban.",
     "bubu-ngu jana kulur yilba-ny | ground-LOC they some throw-PAST",
     "handling_loc_fronting", 5, js(["bubu", "jana", "kulur", "yilbal"]), js([]),
     "Patz text 36 line 29: Bubu-ngu yilba-ny kulur; LOC fronting retained.", "0.88"),

    (46, "The woman bailed water from the boat.",
     "Jalbu-ngku bana buwun-muny wuljin.",
     "jalbu-ngku bana buwun-muny wulji-ny | woman-ERG water boat-ABL bail.out-PAST",
     "handling_source_abl", 5, js(["jalbu", "bana", "buwun", "wuljil"]), js([]),
     "Patz ex.506: bana wulji-l-wulji-nya buwun-muny; ABL source retained.", "0.88"),
    (47, "Water from the boat, the woman bailed.",
     "Bana buwun-muny jalbu-ngku wuljin.",
     "bana buwun-muny jalbu-ngku wulji-ny | water boat-ABL woman-ERG bail.out-PAST",
     "handling_source_object_fronting", 5, js(["bana", "buwun", "jalbu", "wuljil"]), js([]),
     "Object plus ABL source fronted under Patz free word order.", "0.84"),
    (48, "They emptied the honey.",
     "Jana wunba wuljin.",
     "jana wunba wulji-ny | they honey empty.out-PAST",
     "handling_transitive_empty", 4, js(["jana", "wunba", "wuljil"]), js([]),
     "Dictionary wuljil = empty/bail out; honey object from dictionary example domain.", "0.84"),
    (49, "The honey, they emptied.",
     "Wunba jana wuljin.",
     "wunba jana wulji-ny | honey they empty.out-PAST",
     "handling_object_fronting", 5, js(["wunba", "jana", "wuljil"]), js([]),
     "Object-fronting counterpart to row 48.", "0.84"),
    (50, "The woman put the mussels in the bag.",
     "Jalbu-ngku mukirr kurmun-ba buyandin.",
     "jalbu-ngku mukirr kurmun-ba buyandi-ny | woman-ERG mussel bag-LOC put.in.container-PAST",
     "handling_container_loc", 5, js(["jalbu", "mukirr", "kurmun", "buyandi-l"]), js([]),
     "Container placement with buyandi-l; uses Patz text-36 mukirr noun and avoids loan containers.", "0.86"),
]


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/loan 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")
    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 96 draft: handling 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

    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
        ],
    )
    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())
