#!/usr/bin/env python3
"""Bank batch-345: Rule 303 visible flora/tree singular/third nonpast perception."""

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-345"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-345-visible-flora-tree-singular-third-nonpast-perception-after-rule302-process.md"

TARGET_EVIDENCE = {
    "bujabay": "Dictionary yaml line 3572 gives bujabay as noun 'tree', a species with edible fruit needing leaching in water. Rule 303 selects visible bujabay-tree reference only and leaves fruit, edibility, leaching, water, and preparation rows closed.",
    "jingajalka": "Dictionary yaml line 9465 gives jingajalka as noun 'tree' with source prose about fruit, bark baskets, cooking, hot stones, and bones. Rule 303 selects visible jingajalka-tree reference only and leaves all use, food, bark, fire, cooking, and death/bone source prose closed.",
    "jirimandi": "Dictionary yaml line 9765 gives jirimandi as noun 'tree' with definition 'coconut'. Rule 303 selects visible jirimandi/coconut-tree reference only and leaves coconut food, introduced plant history, and use rows closed.",
    "julbal": "Dictionary yaml lines 10479 and 10491 give julbal as noun 'tree', with small edible green apple fruit and wet-season ripening source prose. Rule 303 selects visible julbal-tree reference only and leaves fruit, food, season, and ripening rows closed.",
    "jungka": "Dictionary yaml lines 10893 and 10903 give jungka as noun 'tree', wild cherry tree and fruit. Rule 303 selects visible jungka/wild-cherry-tree reference only and leaves fruit, food, gathering, and ecology rows closed.",
    "karranda": "Dictionary yaml line 13556 gives karranda as noun 'tree', species of tree with English name unknown. Rule 303 selects visible karranda-tree reference only.",
    "karrandal": "Dictionary yaml line 13568 gives karrandal as noun 'tree' with source prose about white-under leaves and crushed leaves good for boils. Rule 303 selects visible karrandal-tree reference only and leaves leaves, colour, crushing, boil, remedy, and medicine rows closed.",
    "kawarr": "Dictionary yaml lines 13872 and 13884 give kawarr as noun 'tree' / 'bloodwood tree'. Rule 303 selects visible kawarr/bloodwood-tree reference only and leaves timber, wood, sap, firewood, and source-use rows closed.",
    "marril": "Dictionary yaml lines 18574 and 18586 give marril as noun 'tree' / 'mangrove', including ti tree and mangrove source prose with hard wood used for oars. Rule 303 selects visible marril/ti-tree reference only and leaves mangrove expansion, hardwood, oar, wood, water, and use rows closed.",
    "ngurrkul": "Dictionary yaml lines 23921 and 23933 give ngurrkul as noun 'tree', species with edible fig / edible fruit in deep scrub. Rule 303 selects visible ngurrkul/fig-tree reference only and leaves fruit, edible, deep-scrub, food, and ecology rows closed.",
}

SUPPORT_EVIDENCE = (
    "Living grammar pronoun table lines 2577-2586 source bare nominative ngayu, yundu, nyulu, bula, and jana; "
    "lines 2663-2686 independently source the same singular/third-person forms and modern jana; demonstrative lines "
    "2834-2845 source yaluy 'here' and yinyay 'there'; verb lines 3043-3051 source nyaji-l and surface nyajil as "
    "nonpast perception. Rule 303 uses only bare support forms."
)

TARGET_ORDER = ["bujabay", "jingajalka", "jirimandi", "julbal", "jungka", "karranda", "karrandal", "kawarr", "marril", "ngurrkul"]
TARGET_LABELS = {
    "bujabay": "bujabay tree",
    "jingajalka": "jingajalka tree",
    "jirimandi": "jirimandi coconut tree",
    "julbal": "julbal tree",
    "jungka": "jungka wild cherry tree",
    "karranda": "karranda tree",
    "karrandal": "karrandal tree",
    "kawarr": "kawarr bloodwood tree",
    "marril": "marril ti tree",
    "ngurrkul": "ngurrkul fig tree",
}
FRAME_SPECS = [
    ("ngayu", "yaluy"),
    ("yundu", "yinyay"),
    ("nyulu", "yaluy"),
    ("bula", "yinyay"),
    ("jana", "yaluy"),
]

DENY_KUKU = {
    "nganya", "ngaykunku", "ngaykundu", "ngaykundumuny", "ngaykunji", "ngayku",
    "yununy", "yununku", "yunundu", "yunundumuny", "yununji", "yunu",
    "nyunguny", "nyungunku", "nyungundu", "nyungundumuny", "nyungunji", "nyungu",
    "bulangan", "bulanka", "bulanda", "bulandamuny", "bulanji", "bulanga",
    "janangan", "jananka", "jananda", "janandamuny", "jananji", "jananga",
    "yalungku", "yaluyngku", "yinyangka", "yinyayngka", "yalamuny", "yinyamuny",
    "nyajin", "nyaji-ny", "nyaji-nka", "nyaji-l-nyaji", "milka", "baykal", "jalngkay",
}
DENY_ENGLISH = (
    "fruit", "seed", "seeds", "edible", "food", "leach", "leached", "leaching", "soak", "soaked", "soaking",
    "water", "preparation", "prepare", "bark", "basket", "baskets", "cook", "cooking", "stone", "stones",
    "bone", "bones", "dead", "death", "blind", "old woman", "season", "wet season", "ripe", "ripening",
    "leaf", "leaves", "white", "crush", "crushed", "boil", "boils", "remedy", "medicine", "hardwood",
    "oar", "oars", "mangrove", "timber", "wood", "firewood", "scrub", "ecology", "place", "story",
    "use", "used", "gather", "gathering",
)


def count_words(db: sqlite3.Connection, words: list[str]) -> dict[str, int]:
    return {
        word: db.execute(
            """
            SELECT COUNT(*)
            FROM sentences s
            WHERE s.status IN ('accepted','revised')
              AND EXISTS (SELECT 1 FROM json_each(s.words_used) WHERE value=?)
            """,
            (word,),
        ).fetchone()[0]
        for word in words
    }


def row(seq: int, english: str, kuku: str, analysis: str, frame: str, words: list[str]) -> tuple:
    target = words[1]
    evidence = f"{TARGET_EVIDENCE[target]} {SUPPORT_EVIDENCE}"
    return (seq, english, kuku, analysis, frame, 3, json.dumps(words, ensure_ascii=False), json.dumps([], ensure_ascii=False), evidence, "0.86")


ROWS = [
    row(1, "I see the bujabay tree here.", "Ngayu bujabay yaluy nyajil.", "ngayu bujabay yaluy nyajil | 1sg.NOM bujabay.tree here see.NPST", "r303_1sg_here_flora_see_bujabay", ["ngayu", "bujabay", "yaluy", "nyajil"]),
    row(2, "I see the jingajalka tree here.", "Ngayu jingajalka yaluy nyajil.", "ngayu jingajalka yaluy nyajil | 1sg.NOM jingajalka.tree here see.NPST", "r303_1sg_here_flora_see_jingajalka", ["ngayu", "jingajalka", "yaluy", "nyajil"]),
    row(3, "I see the jirimandi coconut tree here.", "Ngayu jirimandi yaluy nyajil.", "ngayu jirimandi yaluy nyajil | 1sg.NOM jirimandi.coconut.tree here see.NPST", "r303_1sg_here_flora_see_jirimandi", ["ngayu", "jirimandi", "yaluy", "nyajil"]),
    row(4, "I see the julbal tree here.", "Ngayu julbal yaluy nyajil.", "ngayu julbal yaluy nyajil | 1sg.NOM julbal.tree here see.NPST", "r303_1sg_here_flora_see_julbal", ["ngayu", "julbal", "yaluy", "nyajil"]),
    row(5, "I see the jungka wild cherry tree here.", "Ngayu jungka yaluy nyajil.", "ngayu jungka yaluy nyajil | 1sg.NOM jungka.wild.cherry.tree here see.NPST", "r303_1sg_here_flora_see_jungka", ["ngayu", "jungka", "yaluy", "nyajil"]),
    row(6, "I see the karranda tree here.", "Ngayu karranda yaluy nyajil.", "ngayu karranda yaluy nyajil | 1sg.NOM karranda.tree here see.NPST", "r303_1sg_here_flora_see_karranda", ["ngayu", "karranda", "yaluy", "nyajil"]),
    row(7, "I see the karrandal tree here.", "Ngayu karrandal yaluy nyajil.", "ngayu karrandal yaluy nyajil | 1sg.NOM karrandal.tree here see.NPST", "r303_1sg_here_flora_see_karrandal", ["ngayu", "karrandal", "yaluy", "nyajil"]),
    row(8, "I see the kawarr bloodwood tree here.", "Ngayu kawarr yaluy nyajil.", "ngayu kawarr yaluy nyajil | 1sg.NOM kawarr.bloodwood.tree here see.NPST", "r303_1sg_here_flora_see_kawarr", ["ngayu", "kawarr", "yaluy", "nyajil"]),
    row(9, "I see the marril ti tree here.", "Ngayu marril yaluy nyajil.", "ngayu marril yaluy nyajil | 1sg.NOM marril.ti.tree here see.NPST", "r303_1sg_here_flora_see_marril", ["ngayu", "marril", "yaluy", "nyajil"]),
    row(10, "I see the ngurrkul fig tree here.", "Ngayu ngurrkul yaluy nyajil.", "ngayu ngurrkul yaluy nyajil | 1sg.NOM ngurrkul.fig.tree here see.NPST", "r303_1sg_here_flora_see_ngurrkul", ["ngayu", "ngurrkul", "yaluy", "nyajil"]),
    row(11, "You see the bujabay tree there.", "Yundu bujabay yinyay nyajil.", "yundu bujabay yinyay nyajil | 2sg.NOM bujabay.tree there see.NPST", "r303_2sg_there_flora_see_bujabay", ["yundu", "bujabay", "yinyay", "nyajil"]),
    row(12, "You see the jingajalka tree there.", "Yundu jingajalka yinyay nyajil.", "yundu jingajalka yinyay nyajil | 2sg.NOM jingajalka.tree there see.NPST", "r303_2sg_there_flora_see_jingajalka", ["yundu", "jingajalka", "yinyay", "nyajil"]),
    row(13, "You see the jirimandi coconut tree there.", "Yundu jirimandi yinyay nyajil.", "yundu jirimandi yinyay nyajil | 2sg.NOM jirimandi.coconut.tree there see.NPST", "r303_2sg_there_flora_see_jirimandi", ["yundu", "jirimandi", "yinyay", "nyajil"]),
    row(14, "You see the julbal tree there.", "Yundu julbal yinyay nyajil.", "yundu julbal yinyay nyajil | 2sg.NOM julbal.tree there see.NPST", "r303_2sg_there_flora_see_julbal", ["yundu", "julbal", "yinyay", "nyajil"]),
    row(15, "You see the jungka wild cherry tree there.", "Yundu jungka yinyay nyajil.", "yundu jungka yinyay nyajil | 2sg.NOM jungka.wild.cherry.tree there see.NPST", "r303_2sg_there_flora_see_jungka", ["yundu", "jungka", "yinyay", "nyajil"]),
    row(16, "You see the karranda tree there.", "Yundu karranda yinyay nyajil.", "yundu karranda yinyay nyajil | 2sg.NOM karranda.tree there see.NPST", "r303_2sg_there_flora_see_karranda", ["yundu", "karranda", "yinyay", "nyajil"]),
    row(17, "You see the karrandal tree there.", "Yundu karrandal yinyay nyajil.", "yundu karrandal yinyay nyajil | 2sg.NOM karrandal.tree there see.NPST", "r303_2sg_there_flora_see_karrandal", ["yundu", "karrandal", "yinyay", "nyajil"]),
    row(18, "You see the kawarr bloodwood tree there.", "Yundu kawarr yinyay nyajil.", "yundu kawarr yinyay nyajil | 2sg.NOM kawarr.bloodwood.tree there see.NPST", "r303_2sg_there_flora_see_kawarr", ["yundu", "kawarr", "yinyay", "nyajil"]),
    row(19, "You see the marril ti tree there.", "Yundu marril yinyay nyajil.", "yundu marril yinyay nyajil | 2sg.NOM marril.ti.tree there see.NPST", "r303_2sg_there_flora_see_marril", ["yundu", "marril", "yinyay", "nyajil"]),
    row(20, "You see the ngurrkul fig tree there.", "Yundu ngurrkul yinyay nyajil.", "yundu ngurrkul yinyay nyajil | 2sg.NOM ngurrkul.fig.tree there see.NPST", "r303_2sg_there_flora_see_ngurrkul", ["yundu", "ngurrkul", "yinyay", "nyajil"]),
    row(21, "He sees the bujabay tree here.", "Nyulu bujabay yaluy nyajil.", "nyulu bujabay yaluy nyajil | 3sg.NOM bujabay.tree here see.NPST", "r303_3sg_here_flora_see_bujabay", ["nyulu", "bujabay", "yaluy", "nyajil"]),
    row(22, "He sees the jingajalka tree here.", "Nyulu jingajalka yaluy nyajil.", "nyulu jingajalka yaluy nyajil | 3sg.NOM jingajalka.tree here see.NPST", "r303_3sg_here_flora_see_jingajalka", ["nyulu", "jingajalka", "yaluy", "nyajil"]),
    row(23, "He sees the jirimandi coconut tree here.", "Nyulu jirimandi yaluy nyajil.", "nyulu jirimandi yaluy nyajil | 3sg.NOM jirimandi.coconut.tree here see.NPST", "r303_3sg_here_flora_see_jirimandi", ["nyulu", "jirimandi", "yaluy", "nyajil"]),
    row(24, "He sees the julbal tree here.", "Nyulu julbal yaluy nyajil.", "nyulu julbal yaluy nyajil | 3sg.NOM julbal.tree here see.NPST", "r303_3sg_here_flora_see_julbal", ["nyulu", "julbal", "yaluy", "nyajil"]),
    row(25, "He sees the jungka wild cherry tree here.", "Nyulu jungka yaluy nyajil.", "nyulu jungka yaluy nyajil | 3sg.NOM jungka.wild.cherry.tree here see.NPST", "r303_3sg_here_flora_see_jungka", ["nyulu", "jungka", "yaluy", "nyajil"]),
    row(26, "He sees the karranda tree here.", "Nyulu karranda yaluy nyajil.", "nyulu karranda yaluy nyajil | 3sg.NOM karranda.tree here see.NPST", "r303_3sg_here_flora_see_karranda", ["nyulu", "karranda", "yaluy", "nyajil"]),
    row(27, "He sees the karrandal tree here.", "Nyulu karrandal yaluy nyajil.", "nyulu karrandal yaluy nyajil | 3sg.NOM karrandal.tree here see.NPST", "r303_3sg_here_flora_see_karrandal", ["nyulu", "karrandal", "yaluy", "nyajil"]),
    row(28, "He sees the kawarr bloodwood tree here.", "Nyulu kawarr yaluy nyajil.", "nyulu kawarr yaluy nyajil | 3sg.NOM kawarr.bloodwood.tree here see.NPST", "r303_3sg_here_flora_see_kawarr", ["nyulu", "kawarr", "yaluy", "nyajil"]),
    row(29, "He sees the marril ti tree here.", "Nyulu marril yaluy nyajil.", "nyulu marril yaluy nyajil | 3sg.NOM marril.ti.tree here see.NPST", "r303_3sg_here_flora_see_marril", ["nyulu", "marril", "yaluy", "nyajil"]),
    row(30, "He sees the ngurrkul fig tree here.", "Nyulu ngurrkul yaluy nyajil.", "nyulu ngurrkul yaluy nyajil | 3sg.NOM ngurrkul.fig.tree here see.NPST", "r303_3sg_here_flora_see_ngurrkul", ["nyulu", "ngurrkul", "yaluy", "nyajil"]),
    row(31, "The two of them see the bujabay tree there.", "Bula bujabay yinyay nyajil.", "bula bujabay yinyay nyajil | 3du.NOM bujabay.tree there see.NPST", "r303_3du_there_flora_see_bujabay", ["bula", "bujabay", "yinyay", "nyajil"]),
    row(32, "The two of them see the jingajalka tree there.", "Bula jingajalka yinyay nyajil.", "bula jingajalka yinyay nyajil | 3du.NOM jingajalka.tree there see.NPST", "r303_3du_there_flora_see_jingajalka", ["bula", "jingajalka", "yinyay", "nyajil"]),
    row(33, "The two of them see the jirimandi coconut tree there.", "Bula jirimandi yinyay nyajil.", "bula jirimandi yinyay nyajil | 3du.NOM jirimandi.coconut.tree there see.NPST", "r303_3du_there_flora_see_jirimandi", ["bula", "jirimandi", "yinyay", "nyajil"]),
    row(34, "The two of them see the julbal tree there.", "Bula julbal yinyay nyajil.", "bula julbal yinyay nyajil | 3du.NOM julbal.tree there see.NPST", "r303_3du_there_flora_see_julbal", ["bula", "julbal", "yinyay", "nyajil"]),
    row(35, "The two of them see the jungka wild cherry tree there.", "Bula jungka yinyay nyajil.", "bula jungka yinyay nyajil | 3du.NOM jungka.wild.cherry.tree there see.NPST", "r303_3du_there_flora_see_jungka", ["bula", "jungka", "yinyay", "nyajil"]),
    row(36, "The two of them see the karranda tree there.", "Bula karranda yinyay nyajil.", "bula karranda yinyay nyajil | 3du.NOM karranda.tree there see.NPST", "r303_3du_there_flora_see_karranda", ["bula", "karranda", "yinyay", "nyajil"]),
    row(37, "The two of them see the karrandal tree there.", "Bula karrandal yinyay nyajil.", "bula karrandal yinyay nyajil | 3du.NOM karrandal.tree there see.NPST", "r303_3du_there_flora_see_karrandal", ["bula", "karrandal", "yinyay", "nyajil"]),
    row(38, "The two of them see the kawarr bloodwood tree there.", "Bula kawarr yinyay nyajil.", "bula kawarr yinyay nyajil | 3du.NOM kawarr.bloodwood.tree there see.NPST", "r303_3du_there_flora_see_kawarr", ["bula", "kawarr", "yinyay", "nyajil"]),
    row(39, "The two of them see the marril ti tree there.", "Bula marril yinyay nyajil.", "bula marril yinyay nyajil | 3du.NOM marril.ti.tree there see.NPST", "r303_3du_there_flora_see_marril", ["bula", "marril", "yinyay", "nyajil"]),
    row(40, "The two of them see the ngurrkul fig tree there.", "Bula ngurrkul yinyay nyajil.", "bula ngurrkul yinyay nyajil | 3du.NOM ngurrkul.fig.tree there see.NPST", "r303_3du_there_flora_see_ngurrkul", ["bula", "ngurrkul", "yinyay", "nyajil"]),
    row(41, "They see the bujabay tree here.", "Jana bujabay yaluy nyajil.", "jana bujabay yaluy nyajil | 3pl.NOM bujabay.tree here see.NPST", "r303_3pl_here_flora_see_bujabay", ["jana", "bujabay", "yaluy", "nyajil"]),
    row(42, "They see the jingajalka tree here.", "Jana jingajalka yaluy nyajil.", "jana jingajalka yaluy nyajil | 3pl.NOM jingajalka.tree here see.NPST", "r303_3pl_here_flora_see_jingajalka", ["jana", "jingajalka", "yaluy", "nyajil"]),
    row(43, "They see the jirimandi coconut tree here.", "Jana jirimandi yaluy nyajil.", "jana jirimandi yaluy nyajil | 3pl.NOM jirimandi.coconut.tree here see.NPST", "r303_3pl_here_flora_see_jirimandi", ["jana", "jirimandi", "yaluy", "nyajil"]),
    row(44, "They see the julbal tree here.", "Jana julbal yaluy nyajil.", "jana julbal yaluy nyajil | 3pl.NOM julbal.tree here see.NPST", "r303_3pl_here_flora_see_julbal", ["jana", "julbal", "yaluy", "nyajil"]),
    row(45, "They see the jungka wild cherry tree here.", "Jana jungka yaluy nyajil.", "jana jungka yaluy nyajil | 3pl.NOM jungka.wild.cherry.tree here see.NPST", "r303_3pl_here_flora_see_jungka", ["jana", "jungka", "yaluy", "nyajil"]),
    row(46, "They see the karranda tree here.", "Jana karranda yaluy nyajil.", "jana karranda yaluy nyajil | 3pl.NOM karranda.tree here see.NPST", "r303_3pl_here_flora_see_karranda", ["jana", "karranda", "yaluy", "nyajil"]),
    row(47, "They see the karrandal tree here.", "Jana karrandal yaluy nyajil.", "jana karrandal yaluy nyajil | 3pl.NOM karrandal.tree here see.NPST", "r303_3pl_here_flora_see_karrandal", ["jana", "karrandal", "yaluy", "nyajil"]),
    row(48, "They see the kawarr bloodwood tree here.", "Jana kawarr yaluy nyajil.", "jana kawarr yaluy nyajil | 3pl.NOM kawarr.bloodwood.tree here see.NPST", "r303_3pl_here_flora_see_kawarr", ["jana", "kawarr", "yaluy", "nyajil"]),
    row(49, "They see the marril ti tree here.", "Jana marril yaluy nyajil.", "jana marril yaluy nyajil | 3pl.NOM marril.ti.tree here see.NPST", "r303_3pl_here_flora_see_marril", ["jana", "marril", "yaluy", "nyajil"]),
    row(50, "They see the ngurrkul fig tree here.", "Jana ngurrkul yaluy nyajil.", "jana ngurrkul yaluy nyajil | 3pl.NOM ngurrkul.fig.tree here see.NPST", "r303_3pl_here_flora_see_ngurrkul", ["jana", "ngurrkul", "yaluy", "nyajil"]),
]


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"row count {len(ROWS)} != 50")
    if [r[0] for r in ROWS] != list(range(1, 51)):
        issues.append("sequence is not 1..50")
    kukus = [r[2] for r in ROWS]
    if len(set(kukus)) != len(kukus):
        issues.append("duplicate Kuku surface inside batch")
    for seq, english, kuku, _analysis, frame, _tier, words_json, loans_json, _evidence, _confidence in ROWS:
        words = json.loads(words_json)
        loans = json.loads(loans_json)
        lower_kuku = kuku.lower()
        lower_english = english.lower()
        if loans:
            issues.append(f"{seq}: loans used {loans}")
        denied = sorted({w for w in DENY_KUKU if re.search(rf"(^|\s){re.escape(w)}([\s\.]|$)", lower_kuku)})
        if denied:
            issues.append(f"{seq}: denied Kuku forms {denied}")
        denied_english = [term for term in DENY_ENGLISH if re.search(rf"\b{re.escape(term.strip())}\b", lower_english)]
        if denied_english:
            issues.append(f"{seq}: denied English drift {denied_english}")
        target_hits = [w for w in TARGET_ORDER if w in words]
        if len(target_hits) != 1:
            issues.append(f"{seq}: target hits {target_hits}")
        if len(words) != 4:
            issues.append(f"{seq}: expected 4 words, got {words}")
        if "nyajil" not in words or not lower_kuku.endswith(" nyajil."):
            issues.append(f"{seq}: missing final nyajil")
        if "nyajin" in lower_kuku:
            issues.append(f"{seq}: past nyajin leakage")
        if len([w for w in ("yaluy", "yinyay") if w in words]) != 1:
            issues.append(f"{seq}: demonstrative problem {words}")
        expected_pronoun, expected_demo = FRAME_SPECS[(seq - 1) // 10]
        if words[0] != expected_pronoun:
            issues.append(f"{seq}: expected pronoun {expected_pronoun}, got {words[0]}")
        if words[2] != expected_demo:
            issues.append(f"{seq}: expected demonstrative {expected_demo}, got {words[2]}")
        if words[1] != TARGET_ORDER[(seq - 1) % 10]:
            issues.append(f"{seq}: target order mismatch {words[1]}")
        if words[3] != "nyajil":
            issues.append(f"{seq}: word 4 is not nyajil {words[3]}")
        if lower_kuku.rstrip(".") != " ".join(words):
            issues.append(f"{seq}: Kuku does not match words_used {words}")
        if not frame.startswith("r303_"):
            issues.append(f"{seq}: frame not r303 {frame}")
    return issues


def main() -> int:
    issues = scan_static()
    if issues:
        for issue in issues:
            print(issue)
        raise SystemExit(1)

    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    db.execute("BEGIN")
    ensure_schema(db)
    if db.execute("SELECT 1 FROM batches WHERE label=?", (LABEL,)).fetchone():
        raise SystemExit(f"{LABEL} already exists")
    collisions = []
    for _seq, _english, kuku, *_rest in ROWS:
        existing = db.execute(
            """
            SELECT b.label, s.seq, s.status
            FROM sentences s
            JOIN batches b ON b.id=s.batch_id
            WHERE lower(s.kuku)=lower(?) AND s.status!='rejected'
            """,
            (kuku,),
        ).fetchall()
        if existing:
            collisions.append((kuku, existing))
    if collisions:
        raise SystemExit(f"duplicate preflight failed: {collisions}")

    target_counts = count_words(db, TARGET_ORDER)
    support_counts = count_words(db, ["ngayu", "yundu", "nyulu", "bula", "jana", "yaluy", "yinyay", "nyajil"])
    verified_before, words_before = db.execute(
        """
        SELECT COUNT(*), SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1)
        FROM sentences
        WHERE status IN ('accepted','revised')
        """
    ).fetchone()

    notes = (
        "VISIBLE FLORA/TREE SINGULAR/THIRD-PERSON NONPAST PERCEPTION SWEEP AFTER RULE 302 (Rule 303). "
        "50 under-review rows over ten zero-count checked flora/tree target heads: bujabay tree, jingajalka tree, "
        "jirimandi coconut tree, julbal tree, jungka wild cherry tree, karranda tree, karrandal tree, kawarr "
        "bloodwood tree, marril ti tree, and ngurrkul fig tree. SUPPORT: bare nominative ngayu, yundu, nyulu, "
        "bula, and jana, plus yaluy/yinyay and nyajil. Only bare visible object nonpast perception/reference rows "
        "are opened; fruit, seed, edible/food, leaching, soaking, water preparation, bark, basket-making, cooking, "
        "hot stones, bones/death, blindness, old-woman source prose, wet-season/ripening, wild-cherry fruit, leaves, "
        "colour, crushed leaves, boils, remedies, medicine, hardwood, oars, mangrove/water expansion, timber, "
        "firewood, deep-scrub ecology, place/story, target case, pronoun case, demonstrative case, compounds, "
        "productive reduplication, past/purposive/reduplicated perception drift, and support inflection remain closed."
    )
    cur = db.execute(
        "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
        (LABEL, "Rule 303 under review", "operator-guide-2026-07-09", notes, NOW),
    )
    batch_id = cur.lastrowid
    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", NOW, NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_used, loans_used, evidence, confidence in ROWS
        ],
    )
    record_event(db, batch_label=LABEL, phase="source", event_type="source-readback", actor="claude-main", detail="Reread direct dictionary yaml entries for ten Rule 303 zero-count flora/tree targets and living grammar pronoun, demonstrative, and nyaji-l/nyajil nonpast support before authoring Rule 303.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="visible-flora-tree-singular-third-nonpast-scope", actor="claude-main", detail="Selected a fresh visible flora/tree singular/third-person nonpast perception field. The batch uses ngayu, yundu, nyulu, bula, and jana with nyajil and opens no plant use, fruit, food, bark, cooking, medicine, ecology, place/story, case, compound, past, purposive, reduplicated perception, or support-inflection semantics.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="coverage-audit", actor="claude-main", detail=f"Token-aware accepted/revised coverage before banking from words_used JSON: targets={target_counts}; support={support_counts}.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="duplicate-preflight", actor="claude-main", detail="Final 50 Rule 303 visible flora/tree singular/third-person nonpast surfaces returned no accepted, revised, or under-review collisions.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="static-guard-pass", actor="claude-main", detail="Static guard passed: 50 explicit hand-authored rows, one target each, bare singular/third-person nominative pronoun bands, bare yaluy/yinyay, final nyajil, no loans, no denied Kuku forms, and no fruit/seed/food/bark/cooking/medicine/ecology/place-story/source-prose drift.", command="python3 -m py_compile batch_scripts/bank_345.py && python3 -c \"import sys; sys.path.insert(0,'batch_scripts'); import bank_345; print(bank_345.scan_static())\"", output_excerpt="issues 0", artifact_path="batch_scripts/bank_345.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="claude-main", detail=f"Banked 50 Rule 303 rows as under_review. Verified total remains {verified_before}; Kuku word count remains {words_before}.", command="python3 batch_scripts/bank_345.py", output_excerpt=f"banked batch-345: 50 rows under_review; verified_before={verified_before}; kuku_words_before={words_before}", artifact_path=PROCESS_LOG, created_at=NOW)
    db.commit()
    print("banked batch-345: 50 rows under_review")
    return 0


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