#!/usr/bin/env python3
"""Bank batch-154: clause-combining and discourse-relation frames."""

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

DENY = {
    "yah",
    "ngawuya",
    "yala-coordination",
    "walnga",
}
DENY_STANDALONE = {"ba", "ma"}


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


ROWS = [
    (1, "I went with a fishing line, but there was a strong wind.",
     "Ngayu badur-iji dungan, kari muyar junkuji.",
     "ngayu badur-iji dunga-ny, kari muyar junkuji | I fishing.line-COMIT go-PAST but wind strong",
     "kari_event_hindrance", 5, js(["ngayu", "badur", "dungay", "kari", "muyar", "junkuji"]), js([]),
     "Patz 4.3.2 ex.388 event-hindrance frame; badur-iji is source-checked in Patz ex.158.", "0.88"),
    (2, "That woman wants to go with a fishing line, but she is sick.",
     "Jalbu yinya wawu badur-iji dunga-nka, kari nyulu bambay.",
     "jalbu yinya wawu badur-iji dunga-nka, kari nyulu bambay | woman that want fishing.line-COMIT go-PURP but she sick",
     "kari_event_hindrance_desire", 5, js(["jalbu", "yinya", "wawu", "badur", "dungay", "kari", "nyulu", "bambay"]), js([]),
     "Patz 4.3.2 ex.389 gives this desire-plus-hindrance pattern.", "0.90"),
    (3, "The child wants to hide, but mother saw him.",
     "Karrkay wawu ngaki-ji-nka, kari ngamu-ngku nyunguny nyajin.",
     "karrkay wawu ngaki-ji-nka, kari ngamu-ngku nyunguny nyaji-ny | child want hide-REF-PURP but mother-ERG him see-PAST",
     "kari_event_hindrance_desire", 4, js(["karrkay", "wawu", "ngakiji", "kari", "ngamu", "nyunguny", "nyajil"]), js([]),
     "Extends the Patz 4.3.2 kari hindrance frame with source-checked wawu plus purposive from Patz ex.590.", "0.80"),
    (4, "The man wants to make a spear, but the wood broke.",
     "Dingkar wawu kalka balka-nka, kari juku dumbarrin.",
     "dingkar wawu kalka balka-nka, kari juku dumbarri-ny | man want spear make-PURP but wood break-PAST",
     "kari_event_hindrance_desire", 4, js(["dingkar", "wawu", "kalka", "balkal", "kari", "juku", "dumbarri"]), js([]),
     "Patz 4.9.1 ex.589 licenses wawu kalka balka-nka; second clause follows kari event-hindrance semantics.", "0.80"),
    (5, "The children love to play, but the rain comes.",
     "Karrkay-karrkay wawu jirray kinka-n-kinka-nka, kari kaba kaday.",
     "karrkay-karrkay wawu jirray kinka-n-kinka-nka, kari kaba kada-y | children like much play-n-REDUP-PURP but rain come-NONPAST",
     "kari_event_hindrance_desire", 4, js(["karrkay", "wawu", "jirray", "kinkay", "kari", "kaba", "kaday"]), js([]),
     "Patz 4.9.1 ex.588 licenses the wawu jirray play purposive; kari introduces the hindering event.", "0.80"),
    (6, "I am trying to see that person, but he is too far away.",
     "Ngayu nyaji-nka yinya bama, kari kalakalbay-bajaku.",
     "ngayu nyaji-nka yinya bama, kari kalakalbay-bajaku | I see-PURP that person but far.away-very",
     "kari_event_hindrance_purpose", 5, js(["ngayu", "nyajil", "yinya", "bama", "kari", "kalakalbay", "bajaku"]), js([]),
     "Patz 4.3.2 ex.390 gives this trying-to-see but too-far-away frame.", "0.92"),
    (7, "The young man got a fish, but it kept struggling.",
     "Warru-ngku kuyu manin, kari burrki-n-burrki-n.",
     "warru-ngku kuyu mani-ny, kari burrki-n-burrki-ny | young.man-ERG fish get-PAST but fidget-n-REDUP-PAST",
     "kari_event_hindrance_result", 5, js(["warru", "kuyu", "manil", "kari", "burrki"]), js([]),
     "Patz 4.3.2 ex.391 gives this fish-on-line but struggling frame.", "0.92"),
    (8, "I take water, but the billycan broke.",
     "Ngayu bana wundil, kari jungkalu dumbarrin.",
     "ngayu bana wundi-l, kari jungkalu dumbarri-ny | I water take-NONPAST but billycan break-PAST",
     "kari_event_hindrance_object", 4, js(["ngayu", "bana", "wundil", "kari", "jungkalu", "dumbarri"]), js([]),
     "A conservative event-hindrance extension anchored by Patz 4.3.2 and dictionary jungkalu/dumbarril.", "0.78"),

    (9, "That suitcase is small, but it is heavy.",
     "Yinya budmandu karrkay, yamba kulngkul.",
     "yinya budmandu karrkay, yamba kulngkul | that suitcase small but heavy",
     "yamba_state_qualification", 5, js(["yinya", "budmandu", "karrkay", "yamba", "kulngkul"]), js([]),
     "Patz 4.3.2 ex.392 gives the state-qualification yamba frame.", "0.92"),
    (10, "The fire is small, but it is hot.",
     "Baya buban, yamba wumbul.",
     "baya buban, yamba wumbul | fire small but hot",
     "yamba_state_qualification", 4, js(["baya", "buban", "yamba", "wumbul"]), js([]),
     "Extends Patz ex.392 with source-checked adjectives in a state contrast.", "0.80"),
    (11, "The tree is small, but it is hard.",
     "Juku buban, yamba dandi.",
     "juku buban, yamba dandi | tree small but hard",
     "yamba_state_qualification", 4, js(["juku", "buban", "yamba", "dandi"]), js([]),
     "State contrast modeled on Patz 4.3.2 ex.392; all open-class words are dictionary-checked.", "0.78"),
    (12, "The house is small, but it is clean.",
     "Bayan buban, yamba dayirr.",
     "bayan buban, yamba dayirr | house small but clean",
     "yamba_state_qualification", 4, js(["bayan", "buban", "yamba", "dayirr"]), js([]),
     "State contrast modeled on Patz 4.3.2 ex.392; dayirr is dictionary clean/clear/bright.", "0.78"),
    (13, "The fish is small, but it is good.",
     "Kuyu buban, yamba ngulkurr.",
     "kuyu buban, yamba ngulkurr | fish small but good",
     "yamba_state_qualification", 4, js(["kuyu", "buban", "yamba", "ngulkurr"]), js([]),
     "State contrast modeled on Patz 4.3.2 ex.392 with source-checked kuyu/ngulkurr.", "0.78"),
    (14, "The black goanna is not vicious, but its teeth are bad.",
     "Walkarr kuli-ji kari, yamba dirra buyun.",
     "walkarr kuli-ji kari, yamba dirra buyun | black.goanna anger-COMIT NEG but teeth bad",
     "yamba_state_qualification_negative", 5, js(["walkarr", "kuli", "kari", "yamba", "dirra", "buyun"]), js([]),
     "Patz 5.5.3 ex.656 licenses yamba after a negative state description.", "0.88"),
    (15, "They are expert hunters like Aboriginal people, but their country is different.",
     "Jana mala-minya yala bama, yamba jananga bubu walu-yindu.",
     "jana mala-minya yala bama, yamba jana-nga bubu walu-yindu | they expert-meat like Aboriginal.person but their country face-other",
     "yamba_state_qualification_comparison", 5, js(["jana", "mala-minya", "yala", "bama", "yamba", "bubu", "walu-yindu"]), js([]),
     "Patz 5.5.3 ex.657 gives the yala comparison plus yamba state contrast.", "0.88"),

    (16, "If you hit that dog with a stick, it will bite you.",
     "Kaki yundu yinya kaya juku-bu kunil, yununy baykal.",
     "kaki yundu yinya kaya juku-bu kuni-l, yununy bayka-l | if you that dog stick-INST hit-NONPAST you.ACC bite-NONPAST",
     "kaki_conditional_first", 5, js(["kaki", "yundu", "yinya", "kaya", "juku", "kunil", "yununy", "baykal"]), js([]),
     "Patz 4.3.2 ex.393 gives this non-past conditional frame.", "0.92"),
    (17, "If we have coughs, we break green ants and eat them.",
     "Kaki ngana ngiki-ji, ngana yangka dumbarril, nukal.",
     "kaki ngana ngiki-ji, ngana yangka dumbarri-l, nuka-l | if we cough-COMIT we green.ant break-NONPAST eat-NONPAST",
     "kaki_conditional_first", 5, js(["kaki", "ngana", "ngiki", "yangka", "dumbarri", "nukal"]), js([]),
     "Patz 4.3.2 ex.394 gives the cough/green-ant conditional; subject varied after duplicate preflight.", "0.88"),
    (18, "The child has a sleep if he is tired.",
     "Karrkay warngku-wunay, kaki nyulu bangkarr bajaburray.",
     "karrkay warngku-wuna-y, kaki nyulu bangkarr bajaburray | child sleep-lie-NONPAST if he body tired",
     "kaki_conditional_second", 4, js(["karrkay", "warngku-wunay", "kaki", "nyulu", "bangkarr", "bajaburray"]), js([]),
     "Patz 4.3.2 ex.395 licenses kaki in second position; subject varied to avoid an exact corpus duplicate.", "0.84"),
    (19, "If a strong wind comes, we will not go to the sea.",
     "Kaki muyar junkurrji kaday, ngana jalun-ba kari dungay.",
     "kaki muyar junkurr-ji kada-y, ngana jalun-ba kari dunga-y | if wind strength-COMIT come-NONPAST we sea-LOC NEG go-NONPAST",
     "kaki_conditional_weather", 4, js(["kaki", "muyar", "junkurrji", "kaday", "ngana", "jalun", "kari", "dungay"]), js([]),
     "Non-past kaki conditional; Patz ex.388 anchors strong wind as a hindering condition.", "0.78"),
    (20, "If the child is hungry, grandmother will give food.",
     "Kaki karrkay dakuy, babi-ngka mayi dajil.",
     "kaki karrkay dakuy, babi-ngka mayi daji-l | if child hungry grandmother-ERG food give-NONPAST",
     "kaki_conditional_first", 4, js(["kaki", "karrkay", "dakuy", "babi", "mayi", "dajil"]), js([]),
     "Simple non-past conditional; avoids past-tense kaki prohibited by Patz 4.3.2.", "0.78"),
    (21, "If the water is deep, the children will not swim.",
     "Kaki bana bada, karrkay-karrkay kari maraji.",
     "kaki bana bada, karrkay-karrkay kari maraji | if water deep children NEG swim",
     "kaki_conditional_first", 4, js(["kaki", "bana", "bada", "karrkay", "kari", "maraji"]), js([]),
     "Simple non-past/stative conditional with kari negation in the second clause.", "0.76"),
    (22, "If you see fish, tell me.",
     "Kaki yundu kuyu nyajil, nganya balka.",
     "kaki yundu kuyu nyaji-l, nganya balka | if you fish see-NONPAST me.ACC tell.IMP",
     "kaki_conditional_imperative", 4, js(["kaki", "yundu", "kuyu", "nyajil", "nganya", "balkal"]), js([]),
     "Kaki first-position condition plus imperative main clause; all content words source-checked.", "0.76"),
    (23, "I will cook food if the children are hungry.",
     "Ngayu mayi wajul, kaki karrkay-karrkay dakuy.",
     "ngayu mayi waju-l, kaki karrkay-karrkay dakuy | I food cook-NONPAST if children hungry",
     "kaki_conditional_second", 4, js(["ngayu", "mayi", "wajul", "kaki", "karrkay", "dakuy"]), js([]),
     "Patz 4.3.2 permits kaki in second position; row keeps both clauses non-past/stative.", "0.76"),

    (24, "I take water in case there is no water along the road.",
     "Ngayu bana wundil, kunka bana kari baral-darr.",
     "ngayu bana wundi-l, kunka bana kari baral-darr | I water take-NONPAST in.case water NEG road-PERL",
     "kunka_hypothetical_reason", 5, js(["ngayu", "bana", "wundil", "kunka", "kari", "baral"]), js([]),
     "Patz 4.3.2 ex.397 gives this kunka/nganganjirrka frame.", "0.92"),
    (25, "I sweep and clean the house in case my friend comes.",
     "Ngayu bayan kidal, dayirr-bungal, kunka ngayku jawun kada-nya-mun-ku.",
     "ngayu bayan kida-l, dayirr-bunga-l, kunka ngayku jawun kada-nya-mun-ku | I house sweep-NONPAST clean-CAUS-NONPAST in.case my friend come-SUB-mun-DAT",
     "kunka_hypothetical_reason_nyamunku", 5, js(["ngayu", "bayan", "kidal", "dayirr", "bunga", "kunka", "ngayku", "jawun", "kaday"]), js([]),
     "Patz 4.4.4.4 ex.499 gives this hypothetical-reason construction.", "0.92"),
    (26, "Grandfather takes medicinal root for the road in case he gets sick.",
     "Ngaji-ngka bangkan wundil baral-ka, kunka nyulu bambay-ma-nya-mun-ku.",
     "ngaji-ngka bangkan wundi-l baral-ka, kunka nyulu bambay-ma-nya-mun-ku | grandfather-ERG medicine.root take-NONPAST road-DAT in.case he sick-INCHO-SUB-mun-DAT",
     "kunka_hypothetical_reason_coreferential", 5, js(["ngaji", "bangkan", "wundil", "baral", "kunka", "nyulu", "bambay"]), js([]),
     "Patz 4.4.4.4 ex.500 gives the medicine-for-road in-case-he-gets-sick frame; bangkan replaces English medicine.", "0.88"),
    (27, "Uncle goes with a stick in case the dog bites him.",
     "Kalnga-ngka juku-ji dungay, kunka kaya-ngka nyunguny bayka-nya-mun-ku.",
     "kalnga-ngka juku-ji dunga-y, kunka kaya-ngka nyunguny bayka-nya-mun-ku | uncle-ERG stick-COMIT go-NONPAST in.case dog-ERG him bite-SUB-mun-DAT",
     "kunka_hypothetical_reason_coreferential", 5, js(["kalnga", "juku", "dungay", "kunka", "kaya", "nyunguny", "baykal"]), js([]),
     "Patz 4.4.4.4 ex.502 gives this stick/dog-bite hypothetical reason.", "0.92"),
    (28, "They will not go in case the rain comes.",
     "Jana kari dungay, kaba kada-nya-mun-ku.",
     "jana kari dunga-y, kaba kada-nya-mun-ku | they NEG go-NONPAST rain come-SUB-mun-DAT",
     "hypothetical_reason_without_particle", 5, js(["jana", "kari", "dungay", "kaba", "kaday"]), js([]),
     "Patz 4.4.4.4 ex.498 shows hypothetical reason can be encoded by verb + -nya-mun-ku without overt kunka.", "0.90"),
    (29, "The girl took a billycan in case she came across quandong fruit.",
     "Maral-angka jungkalu wundin, kunka nyungu-ndu janbal nyaji-ji-nya-mun-ku.",
     "maral-angka jungkalu wundi-ny, kunka nyungu-ndu janbal nyaji-ji-nya-mun-ku | girl-ERG billycan take-PAST in.case her-LOC quandong see-ITR-SUB-mun-DAT",
     "kunka_hypothetical_reason_past_main", 5, js(["maral", "jungkalu", "wundil", "kunka", "nyungu", "janbal", "nyajiji"]), js([]),
     "Patz 4.4.4.4 ex.501 attests a past main clause with kunka hypothetical reason.", "0.90"),
    (30, "I take a spear just in case a pig comes.",
     "Ngayu kalka wundil, nganganjirrka bikibiki kada-nya-mun-ku.",
     "ngayu kalka wundi-l, nganganjirrka bikibiki kada-nya-mun-ku | I spear take-NONPAST just.in.case pig come-SUB-mun-DAT",
     "nganganjirrka_hypothetical_reason", 5, js(["ngayu", "kalka", "wundil", "nganganjirrka", "bikibiki", "kaday"]), js([]),
     "Patz 4.3.2 and dictionary nganganjirrka example license the just-in-case particle.", "0.88"),
    (31, "I take water just in case there is no water along the road.",
     "Ngayu bana wundil, nganganjirrka bana kari baral-darr.",
     "ngayu bana wundi-l, nganganjirrka bana kari baral-darr | I water take-NONPAST just.in.case water NEG road-PERL",
     "nganganjirrka_hypothetical_reason", 5, js(["ngayu", "bana", "wundil", "nganganjirrka", "kari", "baral"]), js([]),
     "Patz 4.3.2 ex.397 explicitly allows kunka/nganganjirrka in this frame.", "0.90"),

    (32, "I take a spear; perhaps I will happen to see pigs.",
     "Ngayu kalka wundil, nguba ngaykundu bikibiki nyaji-ji.",
     "ngayu kalka wundi-l, nguba ngayku-ndu bikibiki nyaji-ji-y | I spear take-NONPAST perhaps my-LOC pig see-ITR-NONPAST",
     "nguba_possibility_reason", 5, js(["ngayu", "kalka", "wundil", "nguba", "ngayku", "bikibiki", "nyajiji"]), js([]),
     "Patz 4.4.4.4 ex.503 recommends independent nguba clauses as a less cumbersome alternative to hypothetical reason.", "0.92"),
    (33, "I take a fishing line; perhaps the fish will bite.",
     "Ngayu badur wundil, nguba kuyu-ngku baykal.",
     "ngayu badur wundi-l, nguba kuyu-ngku bayka-l | I fishing.line take-NONPAST perhaps fish-ERG bite-NONPAST",
     "nguba_possibility_reason", 4, js(["ngayu", "badur", "wundil", "nguba", "kuyu", "baykal"]), js([]),
     "Modeled on Patz ex.503: an independent nguba possibility can provide the reason in context.", "0.80"),
    (34, "We will stay at the camp; perhaps a friend will come.",
     "Ngana bayan-ba bundanday, nguba jawun kaday.",
     "ngana bayan-ba bundanda-y, nguba jawun kada-y | we camp-LOC stay-NONPAST perhaps friend come-NONPAST",
     "nguba_possibility_reason", 4, js(["ngana", "bayan", "bundanday", "nguba", "jawun", "kaday"]), js([]),
     "Independent nguba possibility after a main action, following Patz's preferred discourse strategy.", "0.78"),
    (35, "Grandmother takes a billycan; perhaps water is there.",
     "Babi-ngka jungkalu wundil, nguba bana yinyay.",
     "babi-ngka jungkalu wundi-l, nguba bana yinyay | grandmother-ERG billycan take-NONPAST perhaps water there",
     "nguba_possibility_reason", 4, js(["babi", "jungkalu", "wundil", "nguba", "bana", "yinyay"]), js([]),
     "Independent nguba possibility after a practical action, avoiding overuse of cumbersome kunka clauses.", "0.78"),
    (36, "I take water; perhaps there is no water along the road.",
     "Ngayu bana wundil, nguba bana kari baral-darr.",
     "ngayu bana wundi-l, nguba bana kari baral-darr | I water take-NONPAST perhaps water NEG road-PERL",
     "nguba_possibility_reason", 5, js(["ngayu", "bana", "wundil", "nguba", "kari", "baral"]), js([]),
     "Uses Patz 4.4.4.4's recommended nguba strategy for the same content as ex.397.", "0.86"),
    (37, "The child hides in the scrub; perhaps mother will see him.",
     "Karrkay madja-nga ngaki-ji, nguba ngamu-ngku nyunguny nyajil.",
     "karrkay madja-nga ngaki-ji-y, nguba ngamu-ngku nyunguny nyaji-l | child scrub-LOC hide-REF-NONPAST perhaps mother-ERG him see-NONPAST",
     "nguba_possibility_reason", 4, js(["karrkay", "madja", "ngakiji", "nguba", "ngamu", "nyunguny", "nyajil"]), js([]),
     "Independent nguba possibility after a source-checked hiding clause.", "0.76"),

    (38, "I thought you meant to roast the fish, but you are boiling it in water.",
     "Balu yundu kuyu dinda-nka, kari yundu bana-nga wajul.",
     "balu yundu kuyu dinda-nka, kari yundu bana-nga waju-l | wrong you fish roast-PURP but you water-LOC cook-NONPAST",
     "balu_wrong_assumption_correction", 5, js(["balu", "yundu", "kuyu", "dindal", "kari", "bana", "wajul"]), js([]),
     "Patz 5.5.3 ex.658 gives the wrong-assumption plus kari correction frame; object varied from minya to kuyu.", "0.86"),
    (39, "I thought he had come back, but he is staying at the camp.",
     "Balu nyulu kadan-baja, kari nyulu bayan-ba bundanday.",
     "balu nyulu kada-ny-baja, kari nyulu bayan-ba bundanda-y | wrong he come-PAST-again but he camp-LOC stay-NONPAST",
     "balu_wrong_assumption_correction", 5, js(["balu", "nyulu", "kaday", "baja", "kari", "bayan", "bundanday"]), js([]),
     "Patz 5.5.3 ex.659 gives Balu nyulu kada-ny-baja; second clause makes the corrected alternative explicit.", "0.88"),
    (40, "I thought you were going to the water, but you are staying at the camp.",
     "Balu yundu bana-ka dungay, kari yundu bayan-ba bundanday.",
     "balu yundu bana-ka dunga-y, kari yundu bayan-ba bundanda-y | wrong you water-DAT go-NONPAST but you camp-LOC stay-NONPAST",
     "balu_wrong_assumption_correction", 4, js(["balu", "yundu", "bana", "dungay", "kari", "bayan", "bundanday"]), js([]),
     "Balu wrong-assumption discourse use, kept speaker-related as Patz 5.5.3 requires.", "0.78"),
    (41, "I thought rain was coming, but wind is coming.",
     "Balu kaba kaday, kari muyar kaday.",
     "balu kaba kada-y, kari muyar kada-y | wrong rain come-NONPAST but wind come-NONPAST",
     "balu_wrong_assumption_correction", 4, js(["balu", "kaba", "kaday", "kari", "muyar"]), js([]),
     "Balu marks a wrong assumption; kari introduces the corrected weather alternative.", "0.76"),
    (42, "I thought the child would sleep, but he wants to play.",
     "Balu karrkay warngku-wunay, kari nyulu wawu kinka-n-kinka-nka.",
     "balu karrkay warngku-wuna-y, kari nyulu wawu kinka-n-kinka-nka | wrong child sleep-lie-NONPAST but he want play-n-REDUP-PURP",
     "balu_wrong_assumption_correction", 4, js(["balu", "karrkay", "warngku-wunay", "kari", "nyulu", "wawu", "kinkay"]), js([]),
     "Wrong-assumption frame with a kari correction; play purposive follows Patz 4.9.1 ex.588.", "0.76"),
    (43, "I thought I broke the spear, but the dog broke the spear.",
     "Balu ngayu kalka dumbarrin, kari kaya-ngka kalka dumbarrin.",
     "balu ngayu kalka dumbarri-ny, kari kaya-ngka kalka dumbarri-ny | wrong I spear break-PAST but dog-ERG spear break-PAST",
     "balu_wrong_assumption_correction", 4, js(["balu", "ngayu", "kalka", "dumbarri", "kari", "kaya"]), js([]),
     "Balu is used as wrong assumption, not plain negation; the correction is explicit with kari.", "0.76"),

    (44, "Yes, if the rain comes, we will stay at the camp.",
     "Yuwu, kaki kaba kaday, ngana bayan-ba bundanday.",
     "yuwu, kaki kaba kada-y, ngana bayan-ba bundanda-y | yes if rain come-NONPAST we camp-LOC stay-NONPAST",
     "interjection_plus_kaki", 4, js(["yuwu", "kaki", "kaba", "kaday", "ngana", "bayan", "bundanday"]), js([]),
     "Patz 4.10 yuwu answer word plus Patz 4.3.2 non-past kaki conditional.", "0.82"),
    (45, "No, do not go, in case the rain comes.",
     "Kari, yundu kari dungay, kaba kada-nya-mun-ku.",
     "kari, yundu kari dunga-y, kaba kada-nya-mun-ku | no you NEG go-NONPAST rain come-SUB-mun-DAT",
     "interjection_plus_hypothetical_reason", 4, js(["kari", "yundu", "dungay", "kaba", "kaday"]), js([]),
     "Patz 4.10 answer kari plus Patz 4.4.4.4 ex.498 hypothetical-reason morphology.", "0.82"),
    (46, "Wait for me, I am taking water in case there is no water along the road.",
     "Kaku, ngayu bana wundil, kunka bana kari baral-darr.",
     "kaku, ngayu bana wundi-l, kunka bana kari baral-darr | wait.for.me I water take-NONPAST in.case water NEG road-PERL",
     "interjection_plus_kunka", 4, js(["kaku", "ngayu", "bana", "wundil", "kunka", "kari", "baral"]), js([]),
     "Patz 4.10 kaku plus Patz 4.3.2 ex.397 in-case frame.", "0.82"),
    (47, "I see, but I came from up there.",
     "Ngay, kari wangkar-muny ngayu kankadan.",
     "ngay, kari wangkar-muny ngayu kankada-ny | I.see but up-ABL I come.REDUP-PAST",
     "interjection_plus_kari_collateral", 5, js(["ngay", "kari", "wangkar", "ngayu", "kankaday"]), js([]),
     "Patz 5.5.3 ex.655 gives ngay plus kari collateral contrast.", "0.92"),
    (48, "Settled then, the house is small but clean.",
     "Yalada, bayan buban, yamba dayirr.",
     "yalada, bayan buban, yamba dayirr | settled.then house small but clean",
     "interjection_plus_yamba", 4, js(["yalada", "bayan", "buban", "yamba", "dayirr"]), js([]),
     "Patz 4.10 yalada plus yamba state-qualification from Patz 4.3.2.", "0.80"),
    (49, "Nonsense, that is not true. That is a lie.",
     "Juyuy, yinya kari manubaja. Yinya jurril.",
     "juyuy, yinya kari manubaja, yinya jurril | nonsense that NEG true that lie",
     "interjection_plus_kari_negation", 4, js(["juyuy", "yinya", "kari", "manubaja", "jurril"]), js([]),
     "Patz 4.10 juyuy plus ordinary kari negation of manubaja.", "0.80"),
    (50, "Never mind, we will go again if the day is good.",
     "Wumba, ngana baja dungay, kaki wungar ngulkurr.",
     "wumba, ngana baja dunga-y, kaki wungar ngulkurr | never.mind we again go-NONPAST if day good",
     "interjection_plus_kaki_second", 4, js(["wumba", "ngana", "baja", "dungay", "kaki", "wungar", "ngulkurr"]), js([]),
     "Patz 4.10 wumba plus second-position non-past kaki conditional.", "0.80"),
]


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()} {evidence.lower()} "
        for word in DENY:
            if re.search(rf"(?<![a-z]){re.escape(word)}(?![a-z])", low):
                issues.append(f"{label}: denied blocked/special token {word!r}")
        for tok in re.sub(r"[.!?,]", " ", kuku.lower()).split():
            if tok.strip("'\"") in DENY_STANDALONE:
                issues.append(f"{label}: denied standalone special interjection {tok!r}")
        if "yala coordination" in english.lower() or " yala-coordination " in low:
            issues.append(f"{label}: yala coordination is excluded by Patz 4.9.3 evidence note")
        if row[4].startswith("kaki") and ("-PAST" in analysis or "-ny |" in analysis):
            issues.append(f"{label}: kaki row must not use past-tense conditional structure")
        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 112 draft: clause-combining and discourse-relation frames", "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 112 source pass reread Patz 4.3.2 coordination particles, 4.4.4.4 hypothetical reason, "
            "4.9.3 nguba/yala, 4.10 interjections, and 5.5.3 collateral kari/yamba/balu. Key controls: "
            "kari introduces event hindrance or collateral alternatives; yamba qualifies states; kaki is "
            "non-past and can occur first or second; kunka/nganganjirrka mark in-case/hypothetical reason; "
            "nguba is the preferred independent possibility strategy when hypothetical reason clauses are cumbersome."
        ),
        command="sed/rg over grammar.md sections 4.3.2, 4.4.4.4, 4.9.3, 4.10, 5.5.3",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="source",
        event_type="lexeme-source-check",
        actor="claude-main",
        detail=(
            "Targeted dictionary checks covered kari, yamba, kaki, kunka, nganganjirrka, balu, nguba, yuwu, "
            "kaku, ngay, yalada, juyuy, wumba, badur, bambay, bangkan, baral, budmandu, dayirr, jungkalu, "
            "junkurrji/junkuji source-shaped strong, kalka, kulngkul, kuli, manubaja, minya, muyar, ngiki, "
            "wajul, wumbul, yangka, walkarr, yinya, and jurril."
        ),
        command="targeted rg/sed dictionary.yaml entry checks",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="scope",
        event_type="risk-controls",
        actor="claude-main",
        detail=(
            "Risk controls for Rule 112: no yala coordination; no past-tense kaki conditionals; no use of "
            "kunka/nganganjirrka as generic maybe; no flattening of balu into plain negation; yamba kept mostly "
            "to state/qualification contrast, while kari handles event hindrance/collateral contrast."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="preflight",
        event_type="candidate-risk-notes",
        actor="claude-main",
        detail=(
            "Candidate design avoided known exact collisions from Batch 153 and earlier: the buwun strong-wind "
            "surface, the exact Balu yundu minya dinda-nka row, exact Kaki kaba kaday wunkuny row, exact "
            "Kalka wundi nganganjirrka row, and the exact Patz 395 sleep-if-tired row already present in Batch 144. "
            "Where source examples are reused, the process log records whether the surface is exact-source, varied, "
            "or source-shaped for future elder review."
        ),
        command="sqlite exact-surface probes over sentences joined to batches",
        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-authored Rule 112 clause-combining/discourse-relation rows under_review after static and duplicate checks.",
        command="python3 batch_scripts/bank_154.py",
        artifact_path="batch_scripts/bank_154.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="row-inventory",
        actor="claude-main",
        detail=(
            "Rows 1-8 cover kari event hindrance/collateral setup; 9-15 yamba state/qualification contrast; "
            "16-23 non-past kaki conditionals, including first and second position; 24-31 kunka/nganganjirrka "
            "hypothetical reason and -nya-mun-ku morphology; 32-37 nguba independent possibility as an alternative "
            "reason strategy; 38-43 balu wrong-assumption with explicit correction; 44-50 answer/interjection words "
            "combined with kaki, kunka, kari collateral, yamba, and nguba-adjacent discourse frames."
        ),
        artifact_path="batch_scripts/bank_154.py",
        created_at=NOW,
    )
    db.commit()
    avg_words = sum(len(row[2].replace("!", " !").replace("?", " ?").split()) for row in ROWS) / len(ROWS)
    print(f"banked {LABEL}: 50 rows under_review; avg Kuku words/row={avg_words:.2f}")
    return 0


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