#!/usr/bin/env python3
"""Bank batch-348: Rule 306 visible flora/tree singular/third-person past 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-348"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-348-visible-flora-tree-singular-third-past-perception-after-rule305-process.md"

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

SUPPORT_EVIDENCE = (
    "Living grammar line 2568 gives Ngayu ... nyaji-ny with gloss see-PAST; pronoun table lines 2577-2582 source "
    "bare nominative ngayu, yundu, nyulu, bula, and jana with non-nominative case forms separated in the same table; "
    "lines 2684-2686 discuss second/third person non-singular history and jana; demonstrative lines 2834-2845 "
    "source yaluy 'here' and yinyay 'there'. Rule 306 uses only bare support forms and corpus-conventional nyajin "
    "as the surface of nyaji-ny."
)

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", "yununy", "nyunguny", "yununiny", "nyunguniny", "ngaykunku", "yununku",
    "nyungunku", "ngaykundu", "yunundu", "nyungundu", "ngaykundumuny", "yunundumuny",
    "nyungundumuny", "ngaykunji", "yununji", "nyungunji", "ngayku", "yunu", "nyunju",
    "bulangan", "bulaniny", "bulanka", "bulanda", "bulandamuny", "bulanji", "bulanga",
    "ianangan", "iananiny", "iananka", "iananda", "ianandamuny", "iananji", "iananga",
    "yulu", "iana", "ngali", "ngaliny", "yubal", "yurra", "ngana", "nganjin",
    "yalungku", "yaluyngku", "yinyangka", "yinyayngka", "yalamuny", "yinyamuny",
    "nyajil", "nyaji-l", "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 saw the bujabay tree here.", "Ngayu bujabay yaluy nyajin.", "ngayu bujabay yaluy nyajin | 1sg.NOM bujabay.tree here see.PAST", "r306_1sg_here_flora_saw_bujabay", ["ngayu", "bujabay", "yaluy", "nyajin"]),
    row(2, "I saw the jingajalka tree here.", "Ngayu jingajalka yaluy nyajin.", "ngayu jingajalka yaluy nyajin | 1sg.NOM jingajalka.tree here see.PAST", "r306_1sg_here_flora_saw_jingajalka", ["ngayu", "jingajalka", "yaluy", "nyajin"]),
    row(3, "I saw the jirimandi coconut tree here.", "Ngayu jirimandi yaluy nyajin.", "ngayu jirimandi yaluy nyajin | 1sg.NOM jirimandi.coconut.tree here see.PAST", "r306_1sg_here_flora_saw_jirimandi", ["ngayu", "jirimandi", "yaluy", "nyajin"]),
    row(4, "I saw the julbal tree here.", "Ngayu julbal yaluy nyajin.", "ngayu julbal yaluy nyajin | 1sg.NOM julbal.tree here see.PAST", "r306_1sg_here_flora_saw_julbal", ["ngayu", "julbal", "yaluy", "nyajin"]),
    row(5, "I saw the jungka wild cherry tree here.", "Ngayu jungka yaluy nyajin.", "ngayu jungka yaluy nyajin | 1sg.NOM jungka.wild.cherry.tree here see.PAST", "r306_1sg_here_flora_saw_jungka", ["ngayu", "jungka", "yaluy", "nyajin"]),
    row(6, "I saw the karranda tree here.", "Ngayu karranda yaluy nyajin.", "ngayu karranda yaluy nyajin | 1sg.NOM karranda.tree here see.PAST", "r306_1sg_here_flora_saw_karranda", ["ngayu", "karranda", "yaluy", "nyajin"]),
    row(7, "I saw the karrandal tree here.", "Ngayu karrandal yaluy nyajin.", "ngayu karrandal yaluy nyajin | 1sg.NOM karrandal.tree here see.PAST", "r306_1sg_here_flora_saw_karrandal", ["ngayu", "karrandal", "yaluy", "nyajin"]),
    row(8, "I saw the kawarr bloodwood tree here.", "Ngayu kawarr yaluy nyajin.", "ngayu kawarr yaluy nyajin | 1sg.NOM kawarr.bloodwood.tree here see.PAST", "r306_1sg_here_flora_saw_kawarr", ["ngayu", "kawarr", "yaluy", "nyajin"]),
    row(9, "I saw the marril ti tree here.", "Ngayu marril yaluy nyajin.", "ngayu marril yaluy nyajin | 1sg.NOM marril.ti.tree here see.PAST", "r306_1sg_here_flora_saw_marril", ["ngayu", "marril", "yaluy", "nyajin"]),
    row(10, "I saw the ngurrkul fig tree here.", "Ngayu ngurrkul yaluy nyajin.", "ngayu ngurrkul yaluy nyajin | 1sg.NOM ngurrkul.fig.tree here see.PAST", "r306_1sg_here_flora_saw_ngurrkul", ["ngayu", "ngurrkul", "yaluy", "nyajin"]),
    row(11, "You saw the bujabay tree there.", "Yundu bujabay yinyay nyajin.", "yundu bujabay yinyay nyajin | 2sg.NOM bujabay.tree there see.PAST", "r306_2sg_there_flora_saw_bujabay", ["yundu", "bujabay", "yinyay", "nyajin"]),
    row(12, "You saw the jingajalka tree there.", "Yundu jingajalka yinyay nyajin.", "yundu jingajalka yinyay nyajin | 2sg.NOM jingajalka.tree there see.PAST", "r306_2sg_there_flora_saw_jingajalka", ["yundu", "jingajalka", "yinyay", "nyajin"]),
    row(13, "You saw the jirimandi coconut tree there.", "Yundu jirimandi yinyay nyajin.", "yundu jirimandi yinyay nyajin | 2sg.NOM jirimandi.coconut.tree there see.PAST", "r306_2sg_there_flora_saw_jirimandi", ["yundu", "jirimandi", "yinyay", "nyajin"]),
    row(14, "You saw the julbal tree there.", "Yundu julbal yinyay nyajin.", "yundu julbal yinyay nyajin | 2sg.NOM julbal.tree there see.PAST", "r306_2sg_there_flora_saw_julbal", ["yundu", "julbal", "yinyay", "nyajin"]),
    row(15, "You saw the jungka wild cherry tree there.", "Yundu jungka yinyay nyajin.", "yundu jungka yinyay nyajin | 2sg.NOM jungka.wild.cherry.tree there see.PAST", "r306_2sg_there_flora_saw_jungka", ["yundu", "jungka", "yinyay", "nyajin"]),
    row(16, "You saw the karranda tree there.", "Yundu karranda yinyay nyajin.", "yundu karranda yinyay nyajin | 2sg.NOM karranda.tree there see.PAST", "r306_2sg_there_flora_saw_karranda", ["yundu", "karranda", "yinyay", "nyajin"]),
    row(17, "You saw the karrandal tree there.", "Yundu karrandal yinyay nyajin.", "yundu karrandal yinyay nyajin | 2sg.NOM karrandal.tree there see.PAST", "r306_2sg_there_flora_saw_karrandal", ["yundu", "karrandal", "yinyay", "nyajin"]),
    row(18, "You saw the kawarr bloodwood tree there.", "Yundu kawarr yinyay nyajin.", "yundu kawarr yinyay nyajin | 2sg.NOM kawarr.bloodwood.tree there see.PAST", "r306_2sg_there_flora_saw_kawarr", ["yundu", "kawarr", "yinyay", "nyajin"]),
    row(19, "You saw the marril ti tree there.", "Yundu marril yinyay nyajin.", "yundu marril yinyay nyajin | 2sg.NOM marril.ti.tree there see.PAST", "r306_2sg_there_flora_saw_marril", ["yundu", "marril", "yinyay", "nyajin"]),
    row(20, "You saw the ngurrkul fig tree there.", "Yundu ngurrkul yinyay nyajin.", "yundu ngurrkul yinyay nyajin | 2sg.NOM ngurrkul.fig.tree there see.PAST", "r306_2sg_there_flora_saw_ngurrkul", ["yundu", "ngurrkul", "yinyay", "nyajin"]),
    row(21, "He saw the bujabay tree here.", "Nyulu bujabay yaluy nyajin.", "nyulu bujabay yaluy nyajin | 3sg.NOM bujabay.tree here see.PAST", "r306_3sg_here_flora_saw_bujabay", ["nyulu", "bujabay", "yaluy", "nyajin"]),
    row(22, "He saw the jingajalka tree here.", "Nyulu jingajalka yaluy nyajin.", "nyulu jingajalka yaluy nyajin | 3sg.NOM jingajalka.tree here see.PAST", "r306_3sg_here_flora_saw_jingajalka", ["nyulu", "jingajalka", "yaluy", "nyajin"]),
    row(23, "He saw the jirimandi coconut tree here.", "Nyulu jirimandi yaluy nyajin.", "nyulu jirimandi yaluy nyajin | 3sg.NOM jirimandi.coconut.tree here see.PAST", "r306_3sg_here_flora_saw_jirimandi", ["nyulu", "jirimandi", "yaluy", "nyajin"]),
    row(24, "He saw the julbal tree here.", "Nyulu julbal yaluy nyajin.", "nyulu julbal yaluy nyajin | 3sg.NOM julbal.tree here see.PAST", "r306_3sg_here_flora_saw_julbal", ["nyulu", "julbal", "yaluy", "nyajin"]),
    row(25, "He saw the jungka wild cherry tree here.", "Nyulu jungka yaluy nyajin.", "nyulu jungka yaluy nyajin | 3sg.NOM jungka.wild.cherry.tree here see.PAST", "r306_3sg_here_flora_saw_jungka", ["nyulu", "jungka", "yaluy", "nyajin"]),
    row(26, "He saw the karranda tree here.", "Nyulu karranda yaluy nyajin.", "nyulu karranda yaluy nyajin | 3sg.NOM karranda.tree here see.PAST", "r306_3sg_here_flora_saw_karranda", ["nyulu", "karranda", "yaluy", "nyajin"]),
    row(27, "He saw the karrandal tree here.", "Nyulu karrandal yaluy nyajin.", "nyulu karrandal yaluy nyajin | 3sg.NOM karrandal.tree here see.PAST", "r306_3sg_here_flora_saw_karrandal", ["nyulu", "karrandal", "yaluy", "nyajin"]),
    row(28, "He saw the kawarr bloodwood tree here.", "Nyulu kawarr yaluy nyajin.", "nyulu kawarr yaluy nyajin | 3sg.NOM kawarr.bloodwood.tree here see.PAST", "r306_3sg_here_flora_saw_kawarr", ["nyulu", "kawarr", "yaluy", "nyajin"]),
    row(29, "He saw the marril ti tree here.", "Nyulu marril yaluy nyajin.", "nyulu marril yaluy nyajin | 3sg.NOM marril.ti.tree here see.PAST", "r306_3sg_here_flora_saw_marril", ["nyulu", "marril", "yaluy", "nyajin"]),
    row(30, "He saw the ngurrkul fig tree here.", "Nyulu ngurrkul yaluy nyajin.", "nyulu ngurrkul yaluy nyajin | 3sg.NOM ngurrkul.fig.tree here see.PAST", "r306_3sg_here_flora_saw_ngurrkul", ["nyulu", "ngurrkul", "yaluy", "nyajin"]),
    row(31, "The two of them saw the bujabay tree there.", "Bula bujabay yinyay nyajin.", "bula bujabay yinyay nyajin | 3du.NOM bujabay.tree there see.PAST", "r306_3du_there_flora_saw_bujabay", ["bula", "bujabay", "yinyay", "nyajin"]),
    row(32, "The two of them saw the jingajalka tree there.", "Bula jingajalka yinyay nyajin.", "bula jingajalka yinyay nyajin | 3du.NOM jingajalka.tree there see.PAST", "r306_3du_there_flora_saw_jingajalka", ["bula", "jingajalka", "yinyay", "nyajin"]),
    row(33, "The two of them saw the jirimandi coconut tree there.", "Bula jirimandi yinyay nyajin.", "bula jirimandi yinyay nyajin | 3du.NOM jirimandi.coconut.tree there see.PAST", "r306_3du_there_flora_saw_jirimandi", ["bula", "jirimandi", "yinyay", "nyajin"]),
    row(34, "The two of them saw the julbal tree there.", "Bula julbal yinyay nyajin.", "bula julbal yinyay nyajin | 3du.NOM julbal.tree there see.PAST", "r306_3du_there_flora_saw_julbal", ["bula", "julbal", "yinyay", "nyajin"]),
    row(35, "The two of them saw the jungka wild cherry tree there.", "Bula jungka yinyay nyajin.", "bula jungka yinyay nyajin | 3du.NOM jungka.wild.cherry.tree there see.PAST", "r306_3du_there_flora_saw_jungka", ["bula", "jungka", "yinyay", "nyajin"]),
    row(36, "The two of them saw the karranda tree there.", "Bula karranda yinyay nyajin.", "bula karranda yinyay nyajin | 3du.NOM karranda.tree there see.PAST", "r306_3du_there_flora_saw_karranda", ["bula", "karranda", "yinyay", "nyajin"]),
    row(37, "The two of them saw the karrandal tree there.", "Bula karrandal yinyay nyajin.", "bula karrandal yinyay nyajin | 3du.NOM karrandal.tree there see.PAST", "r306_3du_there_flora_saw_karrandal", ["bula", "karrandal", "yinyay", "nyajin"]),
    row(38, "The two of them saw the kawarr bloodwood tree there.", "Bula kawarr yinyay nyajin.", "bula kawarr yinyay nyajin | 3du.NOM kawarr.bloodwood.tree there see.PAST", "r306_3du_there_flora_saw_kawarr", ["bula", "kawarr", "yinyay", "nyajin"]),
    row(39, "The two of them saw the marril ti tree there.", "Bula marril yinyay nyajin.", "bula marril yinyay nyajin | 3du.NOM marril.ti.tree there see.PAST", "r306_3du_there_flora_saw_marril", ["bula", "marril", "yinyay", "nyajin"]),
    row(40, "The two of them saw the ngurrkul fig tree there.", "Bula ngurrkul yinyay nyajin.", "bula ngurrkul yinyay nyajin | 3du.NOM ngurrkul.fig.tree there see.PAST", "r306_3du_there_flora_saw_ngurrkul", ["bula", "ngurrkul", "yinyay", "nyajin"]),
    row(41, "They saw the bujabay tree here.", "Jana bujabay yaluy nyajin.", "jana bujabay yaluy nyajin | 3pl.NOM bujabay.tree here see.PAST", "r306_3pl_here_flora_saw_bujabay", ["jana", "bujabay", "yaluy", "nyajin"]),
    row(42, "They saw the jingajalka tree here.", "Jana jingajalka yaluy nyajin.", "jana jingajalka yaluy nyajin | 3pl.NOM jingajalka.tree here see.PAST", "r306_3pl_here_flora_saw_jingajalka", ["jana", "jingajalka", "yaluy", "nyajin"]),
    row(43, "They saw the jirimandi coconut tree here.", "Jana jirimandi yaluy nyajin.", "jana jirimandi yaluy nyajin | 3pl.NOM jirimandi.coconut.tree here see.PAST", "r306_3pl_here_flora_saw_jirimandi", ["jana", "jirimandi", "yaluy", "nyajin"]),
    row(44, "They saw the julbal tree here.", "Jana julbal yaluy nyajin.", "jana julbal yaluy nyajin | 3pl.NOM julbal.tree here see.PAST", "r306_3pl_here_flora_saw_julbal", ["jana", "julbal", "yaluy", "nyajin"]),
    row(45, "They saw the jungka wild cherry tree here.", "Jana jungka yaluy nyajin.", "jana jungka yaluy nyajin | 3pl.NOM jungka.wild.cherry.tree here see.PAST", "r306_3pl_here_flora_saw_jungka", ["jana", "jungka", "yaluy", "nyajin"]),
    row(46, "They saw the karranda tree here.", "Jana karranda yaluy nyajin.", "jana karranda yaluy nyajin | 3pl.NOM karranda.tree here see.PAST", "r306_3pl_here_flora_saw_karranda", ["jana", "karranda", "yaluy", "nyajin"]),
    row(47, "They saw the karrandal tree here.", "Jana karrandal yaluy nyajin.", "jana karrandal yaluy nyajin | 3pl.NOM karrandal.tree here see.PAST", "r306_3pl_here_flora_saw_karrandal", ["jana", "karrandal", "yaluy", "nyajin"]),
    row(48, "They saw the kawarr bloodwood tree here.", "Jana kawarr yaluy nyajin.", "jana kawarr yaluy nyajin | 3pl.NOM kawarr.bloodwood.tree here see.PAST", "r306_3pl_here_flora_saw_kawarr", ["jana", "kawarr", "yaluy", "nyajin"]),
    row(49, "They saw the marril ti tree here.", "Jana marril yaluy nyajin.", "jana marril yaluy nyajin | 3pl.NOM marril.ti.tree here see.PAST", "r306_3pl_here_flora_saw_marril", ["jana", "marril", "yaluy", "nyajin"]),
    row(50, "They saw the ngurrkul fig tree here.", "Jana ngurrkul yaluy nyajin.", "jana ngurrkul yaluy nyajin | 3pl.NOM ngurrkul.fig.tree here see.PAST", "r306_3pl_here_flora_saw_ngurrkul", ["jana", "ngurrkul", "yaluy", "nyajin"]),
]


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}")
        if " saw " not in f" {lower_english} ":
            issues.append(f"{seq}: English is not past saw")
        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 "nyajin" not in words or not lower_kuku.endswith(" nyajin."):
            issues.append(f"{seq}: missing final nyajin")
        if "nyajil" in lower_kuku:
            issues.append(f"{seq}: nonpast nyajil 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] != "nyajin":
            issues.append(f"{seq}: word 4 is not nyajin {words[3]}")
        if lower_kuku.rstrip(".") != " ".join(words):
            issues.append(f"{seq}: Kuku does not match words_used {words}")
        if not frame.startswith("r306_"):
            issues.append(f"{seq}: frame not r306 {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", "nyajin"])
    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 PAST PERCEPTION SWEEP AFTER RULE 305 (Rule 306). "
        "50 under-review rows over ten 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 nyajin. Only bare visible object past perception/reference rows are opened; fruit, "
        "seed, edible/food, leaching, soaking, water preparation, bark, basket-making, cooking, hot stones, "
        "bones/death, wet-season/ripening, leaves, colour, crushed leaves, cooking-leaf translation drift, boils, "
        "remedies, medicine, hardwood, oars, mangrove/water expansion, timber, firewood, deep-scrub ecology, "
        "place/story, target case, pronoun case, demonstrative case, compounds, productive reduplication, "
        "nonpast/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 306 under review", "operator-guide-2026-07-10", 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 306 flora/tree targets and living grammar singular/third-person pronoun, demonstrative, and nyaji-ny/nyajin past support before authoring Rule 306.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="flora-tree-singular-third-past-scope", actor="claude-main", detail="Selected visible flora/tree singular/third-person past perception after Rule 305. The batch uses ngayu, yundu, nyulu, bula, and jana with nyajin and opens no plant use, fruit, food, bark, cooking, medicine, ecology, place/story, case, compound, nonpast, 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 306 visible flora/tree singular/third-person past 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 nyajin, no loans, no denied Kuku forms, no nonpast nyajil leakage, and no fruit/seed/food/bark/cooking/medicine/ecology/place-story/source-prose drift.", command="python3 -m py_compile batch_scripts/bank_348.py && python3 -c \"import sys; sys.path.insert(0,'batch_scripts'); import bank_348; print(bank_348.scan_static())\"", output_excerpt="issues 0", artifact_path="batch_scripts/bank_348.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="claude-main", detail=f"Banked 50 Rule 306 rows as under_review. Verified total remains {verified_before}; Kuku word count remains {words_before}.", command="python3 batch_scripts/bank_348.py", output_excerpt=f"banked batch-348: 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-348: 50 rows under_review")
    return 0


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