#!/usr/bin/env python3
"""Bank batch-225: Rule 183 object reference lexical expansion after Rule 182."""

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

TARGET_HEADS = {
    "dibarr": "teapot",
    "jakway": "door",
    "juljal": "grave",
    "jurungurr": "spoon",
    "kadirangkal": "clay pipe",
    "mararr": "cloth",
    "mayal": "bottle",
    "wararra": "empty box",
    "yulal": "board",
    "yiki-yiki": "didgeridoo",
}
PRONOUN_A = {"ngayu", "yundu", "jana", "ngana"}
DENY_KUKU = {
    "biyin", "gilaja", "binjin", "julnji", "nyumbirr", "baki", "baybu", "bijikan", "budmandu", "bulin",
    "dakilji", "dingkil-dingkil", "dubal", "duburan", "jamu", "jungkalu", "kaban", "kaljan", "kararr",
    "karra", "kilaja", "kulu", "kurmun", "makarra", "makirr", "marrakan", "mudaka", "murbuy",
    "ngunyin", "ngurran", "warkal", "wuba", "yamba-yamba", "yanday", "yinkar",
    "nuka", "nukan", "waju", "wajun", "mani", "manin", "daji", "dajin", "balkan", "balka",
    "dungan", "dunga", "wandi", "wandin", "yirrka", "burri", "yirrbay", "yirrbal", "wawu-juljal",
}
DENY_ENGLISH = (
    "front", "tea", "drink", "drinks", "drinking", "eat", "eats", "eating", "food", "use", "uses", "used",
    "using", "make", "makes", "made", "making", "old time", "old-time", "smoke", "smokes", "smoking",
    "tobacco", "fill", "fills", "filled", "filling", "carry", "carries", "carried", "carrying", "store",
    "stores", "stored", "storage", "open", "opens", "opened", "close", "closes", "closed", "sail",
    "plaited", "palm", "leaf", "leaves", "plank", "round stick", "stick", "play", "plays", "played",
    "playing", "music", "song", "sound", "corroboree", "instrument", "burial", "bury", "buried",
    "mourning", "death", "dead", "story", "material", "source", "manufacture", "origin", "possess",
    "possession", "plural", "many", "productive reduplication", "reduplicate", "loan", "petrol", "fuel",
    "pin", "glass", "mirror", "tar", "wax", "bee", "bees",
)


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 teapot is here.", "Dibarr yaluy.", "dibarr yaluy | teapot here", "r183_deictic_dibarr_here", ["dibarr", "yaluy"], "Rule 183 admits dibarr as teapot; no tea/drinking/use row."),
    row(2, "The door is here.", "Jakway yaluy.", "jakway yaluy | door here", "r183_deictic_jakway_here", ["jakway", "yaluy"], "Rule 183 admits jakway as door; front sense closed."),
    row(3, "The grave is here.", "Juljal yaluy.", "juljal yaluy | grave here", "r183_deictic_juljal_here", ["juljal", "yaluy"], "Rule 183 admits juljal as grave; no burial/death-event row."),
    row(4, "The spoon is here.", "Jurungurr yaluy.", "jurungurr yaluy | spoon here", "r183_deictic_jurungurr_here", ["jurungurr", "yaluy"], "Rule 183 admits jurungurr as spoon; old-time/making/eating prose closed."),
    row(5, "The clay pipe is here.", "Kadirangkal yaluy.", "kadirangkal yaluy | clay.pipe here", "r183_deictic_kadirangkal_here", ["kadirangkal", "yaluy"], "Rule 183 admits kadirangkal as clay pipe; no smoking/use row."),
    row(6, "The cloth is here.", "Mararr yaluy.", "mararr yaluy | cloth here", "r183_deictic_mararr_here", ["mararr", "yaluy"], "Rule 183 admits mararr as cloth; sail/plaited palm-leaf origin closed."),
    row(7, "The bottle is here.", "Mayal yaluy.", "mayal yaluy | bottle here", "r183_deictic_mayal_here", ["mayal", "yaluy"], "Rule 183 admits mayal as bottle; no filling/drinking/carrying row."),
    row(8, "The empty box is here.", "Wararra yaluy.", "wararra yaluy | empty.box here", "r183_deictic_wararra_here", ["wararra", "yaluy"], "Rule 183 admits wararra as empty box; no possession/storage/use row."),
    row(9, "The board is here.", "Yulal yaluy.", "yulal yaluy | board here", "r183_deictic_yulal_here", ["yulal", "yaluy"], "Rule 183 admits yulal as board; plank and stick contrast closed."),
    row(10, "The didgeridoo is here.", "Yiki-yiki yaluy.", "yiki-yiki yaluy | didgeridoo here", "r183_deictic_yiki_yiki_here", ["yiki-yiki", "yaluy"], "Rule 183 admits yiki-yiki as didgeridoo; no music/play/event row."),
    row(11, "I see the teapot.", "Ngayu dibarr nyajil.", "ngayu dibarr nyaji-l | I teapot see-NONPAST", "r183_present_i_dibarr", ["ngayu", "dibarr", "nyaji-l"], "First-person present perception; target object bare."),
    row(12, "I see the door.", "Ngayu jakway nyajil.", "ngayu jakway nyaji-l | I door see-NONPAST", "r183_present_i_jakway", ["ngayu", "jakway", "nyaji-l"], "First-person present perception; front sense closed."),
    row(13, "I see the grave.", "Ngayu juljal nyajil.", "ngayu juljal nyaji-l | I grave see-NONPAST", "r183_present_i_juljal", ["ngayu", "juljal", "nyaji-l"], "First-person present perception; no burial/death-event row."),
    row(14, "I see the spoon.", "Ngayu jurungurr nyajil.", "ngayu jurungurr nyaji-l | I spoon see-NONPAST", "r183_present_i_jurungurr", ["ngayu", "jurungurr", "nyaji-l"], "First-person present perception; no eating/use row."),
    row(15, "I see the clay pipe.", "Ngayu kadirangkal nyajil.", "ngayu kadirangkal nyaji-l | I clay.pipe see-NONPAST", "r183_present_i_kadirangkal", ["ngayu", "kadirangkal", "nyaji-l"], "First-person present perception; no smoking/use row."),
    row(16, "I see the cloth.", "Ngayu mararr nyajil.", "ngayu mararr nyaji-l | I cloth see-NONPAST", "r183_present_i_mararr", ["ngayu", "mararr", "nyaji-l"], "First-person present perception; sail origin closed."),
    row(17, "I see the bottle.", "Ngayu mayal nyajil.", "ngayu mayal nyaji-l | I bottle see-NONPAST", "r183_present_i_mayal", ["ngayu", "mayal", "nyaji-l"], "First-person present perception; no filling/drinking row."),
    row(18, "I see the empty box.", "Ngayu wararra nyajil.", "ngayu wararra nyaji-l | I empty.box see-NONPAST", "r183_present_i_wararra", ["ngayu", "wararra", "nyaji-l"], "First-person present perception; no storage/use row."),
    row(19, "I see the board.", "Ngayu yulal nyajil.", "ngayu yulal nyaji-l | I board see-NONPAST", "r183_present_i_yulal", ["ngayu", "yulal", "nyaji-l"], "First-person present perception; plank/stick contrast closed."),
    row(20, "I see the didgeridoo.", "Ngayu yiki-yiki nyajil.", "ngayu yiki-yiki nyaji-l | I didgeridoo see-NONPAST", "r183_present_i_yiki_yiki", ["ngayu", "yiki-yiki", "nyaji-l"], "First-person present perception; no music/play row."),
    row(21, "You see the teapot there.", "Yundu dibarr yinyay nyajil.", "yundu dibarr yinyay nyaji-l | you teapot there see-NONPAST", "r183_present_you_dibarr_there", ["yundu", "dibarr", "yinyay", "nyaji-l"], "Second-person present perception with yinyay support."),
    row(22, "You see the door there.", "Yundu jakway yinyay nyajil.", "yundu jakway yinyay nyaji-l | you door there see-NONPAST", "r183_present_you_jakway_there", ["yundu", "jakway", "yinyay", "nyaji-l"], "Second-person present perception; front sense closed."),
    row(23, "You see the grave there.", "Yundu juljal yinyay nyajil.", "yundu juljal yinyay nyaji-l | you grave there see-NONPAST", "r183_present_you_juljal_there", ["yundu", "juljal", "yinyay", "nyaji-l"], "Second-person present perception; no burial/death-event row."),
    row(24, "You see the spoon there.", "Yundu jurungurr yinyay nyajil.", "yundu jurungurr yinyay nyaji-l | you spoon there see-NONPAST", "r183_present_you_jurungurr_there", ["yundu", "jurungurr", "yinyay", "nyaji-l"], "Second-person present perception; no eating/use row."),
    row(25, "You see the clay pipe there.", "Yundu kadirangkal yinyay nyajil.", "yundu kadirangkal yinyay nyaji-l | you clay.pipe there see-NONPAST", "r183_present_you_kadirangkal_there", ["yundu", "kadirangkal", "yinyay", "nyaji-l"], "Second-person present perception; no smoking/use row."),
    row(26, "You see the cloth there.", "Yundu mararr yinyay nyajil.", "yundu mararr yinyay nyaji-l | you cloth there see-NONPAST", "r183_present_you_mararr_there", ["yundu", "mararr", "yinyay", "nyaji-l"], "Second-person present perception; target object bare."),
    row(27, "You see the bottle there.", "Yundu mayal yinyay nyajil.", "yundu mayal yinyay nyaji-l | you bottle there see-NONPAST", "r183_present_you_mayal_there", ["yundu", "mayal", "yinyay", "nyaji-l"], "Second-person present perception; no filling/drinking row."),
    row(28, "You see the empty box there.", "Yundu wararra yinyay nyajil.", "yundu wararra yinyay nyaji-l | you empty.box there see-NONPAST", "r183_present_you_wararra_there", ["yundu", "wararra", "yinyay", "nyaji-l"], "Second-person present perception; no storage/use row."),
    row(29, "You see the board there.", "Yundu yulal yinyay nyajil.", "yundu yulal yinyay nyaji-l | you board there see-NONPAST", "r183_present_you_yulal_there", ["yundu", "yulal", "yinyay", "nyaji-l"], "Second-person present perception; board only."),
    row(30, "You see the didgeridoo there.", "Yundu yiki-yiki yinyay nyajil.", "yundu yiki-yiki yinyay nyaji-l | you didgeridoo there see-NONPAST", "r183_present_you_yiki_yiki_there", ["yundu", "yiki-yiki", "yinyay", "nyaji-l"], "Second-person present perception; no play/music row."),
    row(31, "They saw the teapot there.", "Jana dibarr yinyay nyajin.", "jana dibarr yinyay nyaji-ny | they teapot there see-PAST", "r183_past_they_dibarr_there", ["jana", "dibarr", "yinyay", "nyaji-l"], "Third-person plural past perception with yinyay support."),
    row(32, "They saw the door there.", "Jana jakway yinyay nyajin.", "jana jakway yinyay nyaji-ny | they door there see-PAST", "r183_past_they_jakway_there", ["jana", "jakway", "yinyay", "nyaji-l"], "Third-person plural past perception; front sense closed."),
    row(33, "They saw the grave there.", "Jana juljal yinyay nyajin.", "jana juljal yinyay nyaji-ny | they grave there see-PAST", "r183_past_they_juljal_there", ["jana", "juljal", "yinyay", "nyaji-l"], "Third-person plural past perception; no burial/death-event row."),
    row(34, "They saw the spoon there.", "Jana jurungurr yinyay nyajin.", "jana jurungurr yinyay nyaji-ny | they spoon there see-PAST", "r183_past_they_jurungurr_there", ["jana", "jurungurr", "yinyay", "nyaji-l"], "Third-person plural past perception; no eating/use row."),
    row(35, "They saw the clay pipe there.", "Jana kadirangkal yinyay nyajin.", "jana kadirangkal yinyay nyaji-ny | they clay.pipe there see-PAST", "r183_past_they_kadirangkal_there", ["jana", "kadirangkal", "yinyay", "nyaji-l"], "Third-person plural past perception; no smoking/use row."),
    row(36, "They saw the cloth there.", "Jana mararr yinyay nyajin.", "jana mararr yinyay nyaji-ny | they cloth there see-PAST", "r183_past_they_mararr_there", ["jana", "mararr", "yinyay", "nyaji-l"], "Third-person plural past perception; sail origin closed."),
    row(37, "They saw the bottle there.", "Jana mayal yinyay nyajin.", "jana mayal yinyay nyaji-ny | they bottle there see-PAST", "r183_past_they_mayal_there", ["jana", "mayal", "yinyay", "nyaji-l"], "Third-person plural past perception; no filling/drinking row."),
    row(38, "They saw the empty box there.", "Jana wararra yinyay nyajin.", "jana wararra yinyay nyaji-ny | they empty.box there see-PAST", "r183_past_they_wararra_there", ["jana", "wararra", "yinyay", "nyaji-l"], "Third-person plural past perception; no storage/use row."),
    row(39, "They saw the board there.", "Jana yulal yinyay nyajin.", "jana yulal yinyay nyaji-ny | they board there see-PAST", "r183_past_they_yulal_there", ["jana", "yulal", "yinyay", "nyaji-l"], "Third-person plural past perception; board only."),
    row(40, "They saw the didgeridoo there.", "Jana yiki-yiki yinyay nyajin.", "jana yiki-yiki yinyay nyaji-ny | they didgeridoo there see-PAST", "r183_past_they_yiki_yiki_there", ["jana", "yiki-yiki", "yinyay", "nyaji-l"], "Third-person plural past perception; no play/music row."),
    row(41, "We see the teapot here.", "Ngana dibarr yaluy nyajil.", "ngana dibarr yaluy nyaji-l | we teapot here see-NONPAST", "r183_present_we_dibarr_here", ["ngana", "dibarr", "yaluy", "nyaji-l"], "Inclusive present perception with yaluy support."),
    row(42, "We see the door here.", "Ngana jakway yaluy nyajil.", "ngana jakway yaluy nyaji-l | we door here see-NONPAST", "r183_present_we_jakway_here", ["ngana", "jakway", "yaluy", "nyaji-l"], "Inclusive present perception; front sense closed."),
    row(43, "We see the grave here.", "Ngana juljal yaluy nyajil.", "ngana juljal yaluy nyaji-l | we grave here see-NONPAST", "r183_present_we_juljal_here", ["ngana", "juljal", "yaluy", "nyaji-l"], "Inclusive present perception; no burial/death-event row."),
    row(44, "We see the spoon here.", "Ngana jurungurr yaluy nyajil.", "ngana jurungurr yaluy nyaji-l | we spoon here see-NONPAST", "r183_present_we_jurungurr_here", ["ngana", "jurungurr", "yaluy", "nyaji-l"], "Inclusive present perception; no eating/use row."),
    row(45, "We see the clay pipe here.", "Ngana kadirangkal yaluy nyajil.", "ngana kadirangkal yaluy nyaji-l | we clay.pipe here see-NONPAST", "r183_present_we_kadirangkal_here", ["ngana", "kadirangkal", "yaluy", "nyaji-l"], "Inclusive present perception; no smoking/use row."),
    row(46, "We see the cloth here.", "Ngana mararr yaluy nyajil.", "ngana mararr yaluy nyaji-l | we cloth here see-NONPAST", "r183_present_we_mararr_here", ["ngana", "mararr", "yaluy", "nyaji-l"], "Inclusive present perception; target object bare."),
    row(47, "We see the bottle here.", "Ngana mayal yaluy nyajil.", "ngana mayal yaluy nyaji-l | we bottle here see-NONPAST", "r183_present_we_mayal_here", ["ngana", "mayal", "yaluy", "nyaji-l"], "Inclusive present perception; no filling/drinking row."),
    row(48, "We see the empty box here.", "Ngana wararra yaluy nyajil.", "ngana wararra yaluy nyaji-l | we empty.box here see-NONPAST", "r183_present_we_wararra_here", ["ngana", "wararra", "yaluy", "nyaji-l"], "Inclusive present perception; no storage/use row."),
    row(49, "We see the board here.", "Ngana yulal yaluy nyajil.", "ngana yulal yaluy nyaji-l | we board here see-NONPAST", "r183_present_we_yulal_here", ["ngana", "yulal", "yaluy", "nyaji-l"], "Inclusive present perception; board only."),
    row(50, "We see the didgeridoo here.", "Ngana yiki-yiki yaluy nyajil.", "ngana yiki-yiki yaluy nyaji-l | we didgeridoo here see-NONPAST", "r183_present_we_yiki_yiki_here", ["ngana", "yiki-yiki", "yaluy", "nyaji-l"], "Inclusive present perception; no play/music 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("r183_"):
            issues.append(f"{label}: Rule 183 frame must start with r183_")
        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 183")
        for denied in DENY_KUKU:
            if has_token(low, denied):
                issues.append(f"{label}: denied object/use/old-field 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 183 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 183 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 183 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 object 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 183 draft: object reference lexical expansion after Rule 182", "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 183 source pass selected ten zero-hit artefact/object reference heads: dibarr, jakway, juljal, jurungurr, kadirangkal, mararr, mayal, wararra, yulal, and yiki-yiki. Fresh dictionary readback confirmed selected senses and traps: jakway front sense, jurungurr old-time spoon prose, kadirangkal smoking/use risk, mararr sail/plaited palm-leaf origin, yulal plank/stick contrast, and yiki-yiki play/music/event risk. Rejected near candidates biyin, gilaja, and binjin because they have explicit loanword metadata; rejected julnji and nyumbirr because their entries are material/use-heavy. Patz readback reconfirmed yaluy/yinyay, nyaji-l/nyaji-ny, absolutive bare objects, and bare pronoun A forms.",
                 command="structured dictionary YAML readback; rg grammar readback; sqlite zero-hit corpus readback",
                 artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="scope", event_type="lexical-scope", actor="claude-main",
                 detail="Rule 183 is an object-reference lexical pass for ten bare target heads. It excludes loans, unselected artefact heads, making, using, drinking, eating, smoking, carrying, storing, filling, opening, closing, playing, music, sound, corroboree, death-event, burial, mourning, story, material-source, manufacture, origin, possession, target case, target compounds, plural derivation, productive reduplication, and event claims.",
                 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_225.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="static-guard-pass", actor="claude-main",
                 detail="Static guards passed: exactly one Rule 183 object target per row; all targets bare; no loans; no unselected-object/use/event/polysemy tokens; no target case/compound forms; pronoun A bare; yinyay/yaluy support only.",
                 command="python3 batch_scripts/bank_225.py static scan", artifact_path="batch_scripts/bank_225.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())
