#!/usr/bin/env python3
"""Bank batch-231: Rule 189 weapon/implement reference lexical expansion."""

from __future__ import annotations

import datetime as dt
import json
import re
import sqlite3

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-231"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-231-weapon-implement-reference-lexical-expansion-after-rule188-process.md"

TARGET_HEADS = {
    "birungubay": "paddle",
    "durray": "fighting stick",
    "jinil": "spear barb",
    "kukuni": "big spear",
    "kulbanmuku": "axe",
    "kurajan": "turtle spear point",
    "kuyan": "war spear",
    "mirru": "club",
    "nala-nala": "waddy club",
    "ngurmu": "woomera butt",
}
PRONOUN_A = {"ngayu", "yundu", "jana", "ngana"}
DENY_KUKU = {
    "dikarra", "jindal", "mirrba", "naybu", "balur", "wangal", "kunjuri", "kalka",
    "dama", "daman", "dama-ny", "mumal", "muman", "yaka", "yakal", "yakan",
    "warrbi", "waymbil", "makarr", "yinba", "jarrngkurr", "biyul", "kurrajan",
    "jurruja", "yangkija", "karrkany", "juwal", "marrkin",
}
DENY_ENGLISH = (
    "loan", "english", "place name", "homograph", "rifle", "knife", "boomerang", "shield",
    "throw", "throws", "threw", "thrown", "throwing", "spear with", "speared", "spearing",
    "cut", "cuts", "cutting", "chop", "chops", "chopping", "hit", "hits", "hitting",
    "kill", "kills", "killed", "killing", "hunt", "hunts", "hunting", "catch", "caught",
    "catching", "fish with", "fight with", "fought", "defend", "defence", "defense", "ward",
    "wound", "wounds", "injure", "injury", "blood", "barbed", "barb from", "quartz",
    "stone source", "wood", "wooden", "timber", "wattle", "bamboo source", "material",
    "make", "makes", "made", "making", "ownership", "own", "owns", "possess", "possession",
    "instrumental", "with the", "case", "plural", "many", "productive reduplication", "event",
)


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


def row(seq: int, english: str, kuku: str, analysis: str, frame: str, words: list[str], evidence: str) -> tuple:
    return (seq, english, kuku, analysis, frame, 4, js(words), js([]), evidence, "0.84")


ROWS = [
    row(1, "The paddle is here.", "Birungubay yaluy.", "birungubay yaluy | paddle here", "r189_deictic_birungubay_here", ["birungubay", "yaluy"], "Dictionary head birungubay 'paddle, oar'; reference noun only."),
    row(2, "The fighting stick is here.", "Durray yaluy.", "durray yaluy | fighting.stick here", "r189_deictic_durray_here", ["durray", "yaluy"], "Dictionary head durray 'fighting stick'; reference noun only."),
    row(3, "The spear barb is here.", "Jinil yaluy.", "jinil yaluy | spear.barb here", "r189_deictic_jinil_here", ["jinil", "yaluy"], "Dictionary head jinil 'barb of spear'; component reference only."),
    row(4, "The big spear is here.", "Kukuni yaluy.", "kukuni yaluy | big.spear here", "r189_deictic_kukuni_here", ["kukuni", "yaluy"], "Dictionary head kukuni 'big spear'; no turtle-spearing event opened."),
    row(5, "The axe is here.", "Kulbanmuku yaluy.", "kulbanmuku yaluy | axe here", "r189_deictic_kulbanmuku_here", ["kulbanmuku", "yaluy"], "Dictionary head kulbanmuku 'axe, originally stone axe'; reference noun only."),
    row(6, "The turtle spear point is here.", "Kurajan yaluy.", "kurajan yaluy | turtle.spear.point here", "r189_deictic_kurajan_here", ["kurajan", "yaluy"], "Dictionary head kurajan 'turtle spear point', Yalanji form; no turtle event opened."),
    row(7, "The war spear is here.", "Kuyan yaluy.", "kuyan yaluy | war.spear here", "r189_deictic_kuyan_here", ["kuyan", "yaluy"], "Dictionary head kuyan 'war spear with quartz head'; quartz/material prose closed."),
    row(8, "The club is here.", "Mirru yaluy.", "mirru yaluy | club here", "r189_deictic_mirru_here", ["mirru", "yaluy"], "Dictionary head mirru 'club, nullanulla'; reference noun only."),
    row(9, "The waddy club is here.", "Nala-nala yaluy.", "nala-nala yaluy | waddy.club here", "r189_deictic_nala_nala_here", ["nala-nala", "yaluy"], "Dictionary head nala-nala 'waddy club'; reduplicated lexical head only."),
    row(10, "The woomera butt is here.", "Ngurmu yaluy.", "ngurmu yaluy | woomera.butt here", "r189_deictic_ngurmu_here", ["ngurmu", "yaluy"], "Dictionary head ngurmu 'decorated butt of woomera'; component reference only."),
    row(11, "I see the paddle.", "Ngayu birungubay nyajil.", "ngayu birungubay nyaji-l | I paddle see-NONPAST", "r189_present_i_birungubay", ["ngayu", "birungubay", "nyaji-l"], "First-person present perception; paddle remains bare."),
    row(12, "I see the fighting stick.", "Ngayu durray nyajil.", "ngayu durray nyaji-l | I fighting.stick see-NONPAST", "r189_present_i_durray", ["ngayu", "durray", "nyaji-l"], "First-person present perception; no fighting event opened."),
    row(13, "I see the spear barb.", "Ngayu jinil nyajil.", "ngayu jinil nyaji-l | I spear.barb see-NONPAST", "r189_present_i_jinil", ["ngayu", "jinil", "nyaji-l"], "First-person present perception; component remains bare."),
    row(14, "I see the big spear.", "Ngayu kukuni nyajil.", "ngayu kukuni nyaji-l | I big.spear see-NONPAST", "r189_present_i_kukuni", ["ngayu", "kukuni", "nyaji-l"], "First-person present perception; no spearing event opened."),
    row(15, "I see the axe.", "Ngayu kulbanmuku nyajil.", "ngayu kulbanmuku nyaji-l | I axe see-NONPAST", "r189_present_i_kulbanmuku", ["ngayu", "kulbanmuku", "nyaji-l"], "First-person present perception; no cutting or material-source row."),
    row(16, "I see the turtle spear point.", "Ngayu kurajan nyajil.", "ngayu kurajan nyaji-l | I turtle.spear.point see-NONPAST", "r189_present_i_kurajan", ["ngayu", "kurajan", "nyaji-l"], "First-person present perception; Yalanji form retained."),
    row(17, "I see the war spear.", "Ngayu kuyan nyajil.", "ngayu kuyan nyaji-l | I war.spear see-NONPAST", "r189_present_i_kuyan", ["ngayu", "kuyan", "nyaji-l"], "First-person present perception; no fighting/throwing row."),
    row(18, "I see the club.", "Ngayu mirru nyajil.", "ngayu mirru nyaji-l | I club see-NONPAST", "r189_present_i_mirru", ["ngayu", "mirru", "nyaji-l"], "First-person present perception; club remains bare."),
    row(19, "I see the waddy club.", "Ngayu nala-nala nyajil.", "ngayu nala-nala nyaji-l | I waddy.club see-NONPAST", "r189_present_i_nala_nala", ["ngayu", "nala-nala", "nyaji-l"], "First-person present perception; reduplicated head retained."),
    row(20, "I see the woomera butt.", "Ngayu ngurmu nyajil.", "ngayu ngurmu nyaji-l | I woomera.butt see-NONPAST", "r189_present_i_ngurmu", ["ngayu", "ngurmu", "nyaji-l"], "First-person present perception; component remains bare."),
    row(21, "You see the paddle there.", "Yundu birungubay yinyay nyajil.", "yundu birungubay yinyay nyaji-l | you paddle there see-NONPAST", "r189_present_you_birungubay_there", ["yundu", "birungubay", "yinyay", "nyaji-l"], "Second-person present perception with yinyay support."),
    row(22, "You see the fighting stick there.", "Yundu durray yinyay nyajil.", "yundu durray yinyay nyaji-l | you fighting.stick there see-NONPAST", "r189_present_you_durray_there", ["yundu", "durray", "yinyay", "nyaji-l"], "Second-person present perception; no fighting event opened."),
    row(23, "You see the spear barb there.", "Yundu jinil yinyay nyajil.", "yundu jinil yinyay nyaji-l | you spear.barb there see-NONPAST", "r189_present_you_jinil_there", ["yundu", "jinil", "yinyay", "nyaji-l"], "Second-person present perception; no spear-use row."),
    row(24, "You see the big spear there.", "Yundu kukuni yinyay nyajil.", "yundu kukuni yinyay nyaji-l | you big.spear there see-NONPAST", "r189_present_you_kukuni_there", ["yundu", "kukuni", "yinyay", "nyaji-l"], "Second-person present perception; no harpoon-use row."),
    row(25, "You see the axe there.", "Yundu kulbanmuku yinyay nyajil.", "yundu kulbanmuku yinyay nyaji-l | you axe there see-NONPAST", "r189_present_you_kulbanmuku_there", ["yundu", "kulbanmuku", "yinyay", "nyaji-l"], "Second-person present perception; no cutting row."),
    row(26, "You see the turtle spear point there.", "Yundu kurajan yinyay nyajil.", "yundu kurajan yinyay nyaji-l | you turtle.spear.point there see-NONPAST", "r189_present_you_kurajan_there", ["yundu", "kurajan", "yinyay", "nyaji-l"], "Second-person present perception; no turtle row."),
    row(27, "You see the war spear there.", "Yundu kuyan yinyay nyajil.", "yundu kuyan yinyay nyaji-l | you war.spear there see-NONPAST", "r189_present_you_kuyan_there", ["yundu", "kuyan", "yinyay", "nyaji-l"], "Second-person present perception; quartz/source prose closed."),
    row(28, "You see the club there.", "Yundu mirru yinyay nyajil.", "yundu mirru yinyay nyaji-l | you club there see-NONPAST", "r189_present_you_mirru_there", ["yundu", "mirru", "yinyay", "nyaji-l"], "Second-person present perception; no striking row."),
    row(29, "You see the waddy club there.", "Yundu nala-nala yinyay nyajil.", "yundu nala-nala yinyay nyaji-l | you waddy.club there see-NONPAST", "r189_present_you_nala_nala_there", ["yundu", "nala-nala", "yinyay", "nyaji-l"], "Second-person present perception; lexical reduplication only."),
    row(30, "You see the woomera butt there.", "Yundu ngurmu yinyay nyajil.", "yundu ngurmu yinyay nyaji-l | you woomera.butt there see-NONPAST", "r189_present_you_ngurmu_there", ["yundu", "ngurmu", "yinyay", "nyaji-l"], "Second-person present perception; no throwing/defence row."),
    row(31, "They saw the paddle there.", "Jana birungubay yinyay nyajin.", "jana birungubay yinyay nyaji-ny | they paddle there see-PAST", "r189_past_they_birungubay_there", ["jana", "birungubay", "yinyay", "nyaji-l"], "Third-person plural past perception with yinyay support."),
    row(32, "They saw the fighting stick there.", "Jana durray yinyay nyajin.", "jana durray yinyay nyaji-ny | they fighting.stick there see-PAST", "r189_past_they_durray_there", ["jana", "durray", "yinyay", "nyaji-l"], "Third-person plural past perception; no fighting event opened."),
    row(33, "They saw the spear barb there.", "Jana jinil yinyay nyajin.", "jana jinil yinyay nyaji-ny | they spear.barb there see-PAST", "r189_past_they_jinil_there", ["jana", "jinil", "yinyay", "nyaji-l"], "Third-person plural past perception; component remains bare."),
    row(34, "They saw the big spear there.", "Jana kukuni yinyay nyajin.", "jana kukuni yinyay nyaji-ny | they big.spear there see-PAST", "r189_past_they_kukuni_there", ["jana", "kukuni", "yinyay", "nyaji-l"], "Third-person plural past perception; no spearing event opened."),
    row(35, "They saw the axe there.", "Jana kulbanmuku yinyay nyajin.", "jana kulbanmuku yinyay nyaji-ny | they axe there see-PAST", "r189_past_they_kulbanmuku_there", ["jana", "kulbanmuku", "yinyay", "nyaji-l"], "Third-person plural past perception; no chopping/cutting row."),
    row(36, "They saw the turtle spear point there.", "Jana kurajan yinyay nyajin.", "jana kurajan yinyay nyaji-ny | they turtle.spear.point there see-PAST", "r189_past_they_kurajan_there", ["jana", "kurajan", "yinyay", "nyaji-l"], "Third-person plural past perception; Yalanji form retained."),
    row(37, "They saw the war spear there.", "Jana kuyan yinyay nyajin.", "jana kuyan yinyay nyaji-ny | they war.spear there see-PAST", "r189_past_they_kuyan_there", ["jana", "kuyan", "yinyay", "nyaji-l"], "Third-person plural past perception; no quartz/source row."),
    row(38, "They saw the club there.", "Jana mirru yinyay nyajin.", "jana mirru yinyay nyaji-ny | they club there see-PAST", "r189_past_they_mirru_there", ["jana", "mirru", "yinyay", "nyaji-l"], "Third-person plural past perception; no striking row."),
    row(39, "They saw the waddy club there.", "Jana nala-nala yinyay nyajin.", "jana nala-nala yinyay nyaji-ny | they waddy.club there see-PAST", "r189_past_they_nala_nala_there", ["jana", "nala-nala", "yinyay", "nyaji-l"], "Third-person plural past perception; no weapon-use row."),
    row(40, "They saw the woomera butt there.", "Jana ngurmu yinyay nyajin.", "jana ngurmu yinyay nyaji-ny | they woomera.butt there see-PAST", "r189_past_they_ngurmu_there", ["jana", "ngurmu", "yinyay", "nyaji-l"], "Third-person plural past perception; no throwing/defence row."),
    row(41, "We see the paddle here.", "Ngana birungubay yaluy nyajil.", "ngana birungubay yaluy nyaji-l | we paddle here see-NONPAST", "r189_present_we_birungubay_here", ["ngana", "birungubay", "yaluy", "nyaji-l"], "Inclusive present perception with yaluy support."),
    row(42, "We see the fighting stick here.", "Ngana durray yaluy nyajil.", "ngana durray yaluy nyaji-l | we fighting.stick here see-NONPAST", "r189_present_we_durray_here", ["ngana", "durray", "yaluy", "nyaji-l"], "Inclusive present perception; no fighting event opened."),
    row(43, "We see the spear barb here.", "Ngana jinil yaluy nyajil.", "ngana jinil yaluy nyaji-l | we spear.barb here see-NONPAST", "r189_present_we_jinil_here", ["ngana", "jinil", "yaluy", "nyaji-l"], "Inclusive present perception; component remains bare."),
    row(44, "We see the big spear here.", "Ngana kukuni yaluy nyajil.", "ngana kukuni yaluy nyaji-l | we big.spear here see-NONPAST", "r189_present_we_kukuni_here", ["ngana", "kukuni", "yaluy", "nyaji-l"], "Inclusive present perception; no harpoon-use row."),
    row(45, "We see the axe here.", "Ngana kulbanmuku yaluy nyajil.", "ngana kulbanmuku yaluy nyaji-l | we axe here see-NONPAST", "r189_present_we_kulbanmuku_here", ["ngana", "kulbanmuku", "yaluy", "nyaji-l"], "Inclusive present perception; no cutting/tool-use row."),
    row(46, "We see the turtle spear point here.", "Ngana kurajan yaluy nyajil.", "ngana kurajan yaluy nyaji-l | we turtle.spear.point here see-NONPAST", "r189_present_we_kurajan_here", ["ngana", "kurajan", "yaluy", "nyaji-l"], "Inclusive present perception; no turtle event opened."),
    row(47, "We see the war spear here.", "Ngana kuyan yaluy nyajil.", "ngana kuyan yaluy nyaji-l | we war.spear here see-NONPAST", "r189_present_we_kuyan_here", ["ngana", "kuyan", "yaluy", "nyaji-l"], "Inclusive present perception; no fighting/throwing row."),
    row(48, "We see the club here.", "Ngana mirru yaluy nyajil.", "ngana mirru yaluy nyaji-l | we club here see-NONPAST", "r189_present_we_mirru_here", ["ngana", "mirru", "yaluy", "nyaji-l"], "Inclusive present perception; club remains bare."),
    row(49, "We see the waddy club here.", "Ngana nala-nala yaluy nyajil.", "ngana nala-nala yaluy nyaji-l | we waddy.club here see-NONPAST", "r189_present_we_nala_nala_here", ["ngana", "nala-nala", "yaluy", "nyaji-l"], "Inclusive present perception; lexical reduplication only."),
    row(50, "We see the woomera butt here.", "Ngana ngurmu yaluy nyajil.", "ngana ngurmu yaluy nyaji-l | we woomera.butt here see-NONPAST", "r189_present_we_ngurmu_here", ["ngana", "ngurmu", "yaluy", "nyaji-l"], "Inclusive present perception; no throwing/defence row."),
]


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_target_case(low: str, head: str) -> bool:
    return re.search(rf"(?<![a-z-]){re.escape(head)}-(?:ngka|ngku|angka|nga|ngu|ba|mba|mbu|muny|darr|burr|bu|ka|nka|nku|ji|jirr|ku|mundu|amu|mu|iji|nda|ndu|anda|undu|unji|nji|kari|baka)\b", low) is not None


def english_has_denied(english_low: str, denied: str) -> bool:
    return denied in english_low if " " in denied else re.search(rf"\b{re.escape(denied)}\b", english_low) is not None


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_data in enumerate(ROWS, start=1):
        seq, english, kuku, analysis, frame, _tier, words_used, loans_used, evidence, confidence = row_data
        label = f"{LABEL}.{seq:02d}"
        low = kuku.lower()
        english_low = english.lower()
        words = json.loads(words_used)
        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("r189_"):
            issues.append(f"{label}: Rule 189 frame must start with r189_")
        if low in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(low)
        if json.loads(loans_used):
            issues.append(f"{label}: no loans are admitted in Rule 189")
        for denied in DENY_KUKU:
            if has_token(low, denied):
                issues.append(f"{label}: denied weapon/use/loan/homograph token {denied!r}")
        targets = [head for head in TARGET_HEADS if has_token(low, head)]
        if len(targets) != 1:
            issues.append(f"{label}: expected exactly one Rule 189 target head, found {targets}")
        for head in targets:
            selected = TARGET_HEADS[head]
            if head not in words:
                issues.append(f"{label}: target head {head} missing from words_used")
            if selected.replace(" ", ".") not in analysis:
                issues.append(f"{label}: target head {head} must be glossed as {selected}")
            if selected not in english_low:
                issues.append(f"{label}: {head} must be translated with selected sense {selected!r}")
            if has_target_case(low, head):
                issues.append(f"{label}: target head {head} has forbidden case/compound morphology")
        if seq <= 10:
            if "nyajil" in low or "nyajin" in low or "nyaji-l" in words:
                issues.append(f"{label}: deictic rows must not use nyaji-l")
            if not has_token(low, "yaluy") or "yaluy" not in words or "here" not in english_low:
                issues.append(f"{label}: Rule 189 deictic rows require yaluy/here")
        else:
            if ("nyajin" not in low and "nyajil" not in low) or "nyaji-l" not in words:
                issues.append(f"{label}: Rule 189 perception rows must use nyaji-l")
            first = first_word(low)
            if first not in PRONOUN_A:
                issues.append(f"{label}: perception A should be an approved bare pronoun")
            if 11 <= seq <= 20 and first != "ngayu":
                issues.append(f"{label}: rows 11-20 require ngayu")
            if 21 <= seq <= 30 and (first != "yundu" or not has_token(low, "yinyay") or "there" not in english_low):
                issues.append(f"{label}: rows 21-30 require yundu + yinyay/there")
            if 31 <= seq <= 40 and (first != "jana" or not has_token(low, "yinyay") or "saw" not in english_low):
                issues.append(f"{label}: rows 31-40 require jana + yinyay + past saw")
            if 41 <= seq <= 50 and (first != "ngana" or not has_token(low, "yaluy") or "here" not in english_low):
                issues.append(f"{label}: rows 41-50 require ngana + yaluy/here")
            if "nyajin" in low and "see-PAST" not in analysis:
                issues.append(f"{label}: nyajin must be glossed as see-PAST")
            if "nyajil" in low and "see-NONPAST" not in analysis:
                issues.append(f"{label}: nyajil must be glossed as see-NONPAST")
        if any(english_has_denied(english_low, bad) for bad in DENY_ENGLISH):
            issues.append(f"{label}: denied weapon-use/material/event/polysemy field in English")
        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 189 draft: weapon/implement reference lexical expansion", "operator-guide-2026-07-08", "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 189 source pass selected ten dictionary-attested weapon/implement reference heads: birungubay paddle/oar, durray fighting stick, jinil spear barb, kukuni big spear, kulbanmuku axe, kurajan turtle spear point, kuyan war spear, mirru club, nala-nala waddy club, and ngurmu decorated butt of woomera. Grammar grep found no target construction license beyond general noun/reference and established yaluy/yinyay/nyaji-l/pronoun frames; dikarra was rejected because Patz also uses it as a place name, while jindal, mirrba, naybu, kalka, balur, wangal, kunjuri, and other high-pressure heads remain outside this batch.",
                 command="rg dictionary.yaml and grammar.md for selected and rejected heads; sqlite corpus-frequency check",
                 artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="scope", event_type="lexical-scope", actor="claude-main",
                 detail="Rule 189 is a bounded lexical reference pass. It admits bare target nouns only and excludes throwing, spearing, cutting, chopping, hitting, killing, hunting, catching, fighting events, instrumental/comitative/possessive/case morphology, material-source claims, loan imports, place-name homographs, body-part/euphemism drift, target compounds, plural derivation, and productive reduplication.",
                 artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="duplicate-preflight", actor="claude-main",
                 detail="Exact duplicate preflight found 50 planned Kuku surfaces, 50 unique within-batch surfaces, and no exact accepted/revised/under_review cross-corpus duplicates.",
                 command="sqlite exact lower(trim(kuku)) duplicate scan before banking", artifact_path="batch_scripts/bank_231.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="static-guard-pass", actor="claude-main",
                 detail="Static guards passed: all 50 rows have exactly one Rule 189 target, no loans, no denied Kuku tokens, no denied English import fields, no target case/compound morphology, required deictic/perception support, bare pronoun A where applicable, and correct nyajil/nyajin glosses.",
                 command="python3 batch_scripts/bank_231.py internal scan_static()", artifact_path="batch_scripts/bank_231.py", created_at=NOW)

    db.executemany(
        """
        INSERT INTO sentences(
          batch_id, seq, english, kuku, analysis, frame, tier, words_used,
          loans_used, evidence, status, confidence, author, created_at, updated_at
        )
        VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        """,
        [(batch_id, *row_data[:9], "under_review", row_data[9], "claude-main", NOW, NOW) for row_data in ROWS],
    )
    db.commit()

    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={len(ROWS)}, under_review={under_review}, verified_before_close={verified}")
    return 0


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