#!/usr/bin/env python3
"""Bank batch-350: Rule 308 visible flora/tree non-singular 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-350"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-350-visible-flora-tree-nonsingular-past-perception-after-rule307-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 308 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 308 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 308 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 308 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 308 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 308 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 308 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 308 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 308 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 308 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 ngali, ngaliny, yubal, yurra, and ngana with non-nominative case forms separated in the same "
    "table; lines 2674-2684 discuss ngali dual exclusive, ngaliny dual inclusive by increment, ngana plural "
    "inclusive, and yubal/yurra second-person non-singular forms; demonstrative lines 2834-2845 source yaluy 'here' "
    "and yinyay 'there'. Rule 308 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"]
FRAME_SPECS = [
    ("ngali", "yinyay"),
    ("ngaliny", "yaluy"),
    ("yubal", "yinyay"),
    ("yurra", "yaluy"),
    ("ngana", "yinyay"),
]

DENY_KUKU = {
    "ngayu", "yundu", "nyulu", "bula", "jana", "yulu", "iana", "nganjin",
    "nganya", "yununy", "nyunguny", "yununiny", "nyunguniny", "ngaykunku", "yununku",
    "nyungunku", "ngaykundu", "yunundu", "nyungundu", "ngaykundumuny", "yunundumuny",
    "nyungundumuny", "ngaykunji", "yununji", "nyungunji", "ngayku", "yunu", "nyunju",
    "ngalingan", "ngalinyan", "yubalan", "bulangan", "nganjinan", "nganangan", "yurrangan",
    "ianangan", "ngalininy", "ngalinyiny", "yubaliny", "bulaniny", "nganjininy", "ngananiny",
    "yurraniny", "iananiny", "ngalinka", "ngalinyanka", "yubalanka", "bulanka", "nganjinanka",
    "ngananka", "yurranka", "iananka", "ngalinda", "ngalinyanda", "yubalanda", "bulanda",
    "nganjinanda", "ngananda", "yurranda", "iananda", "ngalindamuny", "ngalinyandamuny",
    "yubalandamuny", "bulandamuny", "nganjinandamuny", "nganandamuny", "yurrandamuny",
    "ianandamuny", "ngalinji", "ngalinyanji", "yubalanji", "bulanji", "nganjinanji",
    "ngananji", "yurranji", "iananji", "ngalinga", "ngalinyanga", "yubalanga", "bulanga",
    "nganjinanga", "ngananga", "yurranga", "iananga",
    "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, "We two, not you, saw the bujabay tree there.", "Ngali bujabay yinyay nyajin.", "ngali bujabay yinyay nyajin | 1du.EXCL.NOM bujabay.tree there see.PAST", "r308_1du_excl_there_flora_saw_bujabay", ["ngali", "bujabay", "yinyay", "nyajin"]),
    row(2, "We two, not you, saw the jingajalka tree there.", "Ngali jingajalka yinyay nyajin.", "ngali jingajalka yinyay nyajin | 1du.EXCL.NOM jingajalka.tree there see.PAST", "r308_1du_excl_there_flora_saw_jingajalka", ["ngali", "jingajalka", "yinyay", "nyajin"]),
    row(3, "We two, not you, saw the jirimandi coconut tree there.", "Ngali jirimandi yinyay nyajin.", "ngali jirimandi yinyay nyajin | 1du.EXCL.NOM jirimandi.coconut.tree there see.PAST", "r308_1du_excl_there_flora_saw_jirimandi", ["ngali", "jirimandi", "yinyay", "nyajin"]),
    row(4, "We two, not you, saw the julbal tree there.", "Ngali julbal yinyay nyajin.", "ngali julbal yinyay nyajin | 1du.EXCL.NOM julbal.tree there see.PAST", "r308_1du_excl_there_flora_saw_julbal", ["ngali", "julbal", "yinyay", "nyajin"]),
    row(5, "We two, not you, saw the jungka wild cherry tree there.", "Ngali jungka yinyay nyajin.", "ngali jungka yinyay nyajin | 1du.EXCL.NOM jungka.wild.cherry.tree there see.PAST", "r308_1du_excl_there_flora_saw_jungka", ["ngali", "jungka", "yinyay", "nyajin"]),
    row(6, "We two, not you, saw the karranda tree there.", "Ngali karranda yinyay nyajin.", "ngali karranda yinyay nyajin | 1du.EXCL.NOM karranda.tree there see.PAST", "r308_1du_excl_there_flora_saw_karranda", ["ngali", "karranda", "yinyay", "nyajin"]),
    row(7, "We two, not you, saw the karrandal tree there.", "Ngali karrandal yinyay nyajin.", "ngali karrandal yinyay nyajin | 1du.EXCL.NOM karrandal.tree there see.PAST", "r308_1du_excl_there_flora_saw_karrandal", ["ngali", "karrandal", "yinyay", "nyajin"]),
    row(8, "We two, not you, saw the kawarr bloodwood tree there.", "Ngali kawarr yinyay nyajin.", "ngali kawarr yinyay nyajin | 1du.EXCL.NOM kawarr.bloodwood.tree there see.PAST", "r308_1du_excl_there_flora_saw_kawarr", ["ngali", "kawarr", "yinyay", "nyajin"]),
    row(9, "We two, not you, saw the marril ti tree there.", "Ngali marril yinyay nyajin.", "ngali marril yinyay nyajin | 1du.EXCL.NOM marril.ti.tree there see.PAST", "r308_1du_excl_there_flora_saw_marril", ["ngali", "marril", "yinyay", "nyajin"]),
    row(10, "We two, not you, saw the ngurrkul fig tree there.", "Ngali ngurrkul yinyay nyajin.", "ngali ngurrkul yinyay nyajin | 1du.EXCL.NOM ngurrkul.fig.tree there see.PAST", "r308_1du_excl_there_flora_saw_ngurrkul", ["ngali", "ngurrkul", "yinyay", "nyajin"]),
    row(11, "You and I saw the bujabay tree here.", "Ngaliny bujabay yaluy nyajin.", "ngaliny bujabay yaluy nyajin | 1du.INCL.NOM bujabay.tree here see.PAST", "r308_1du_incl_here_flora_saw_bujabay", ["ngaliny", "bujabay", "yaluy", "nyajin"]),
    row(12, "You and I saw the jingajalka tree here.", "Ngaliny jingajalka yaluy nyajin.", "ngaliny jingajalka yaluy nyajin | 1du.INCL.NOM jingajalka.tree here see.PAST", "r308_1du_incl_here_flora_saw_jingajalka", ["ngaliny", "jingajalka", "yaluy", "nyajin"]),
    row(13, "You and I saw the jirimandi coconut tree here.", "Ngaliny jirimandi yaluy nyajin.", "ngaliny jirimandi yaluy nyajin | 1du.INCL.NOM jirimandi.coconut.tree here see.PAST", "r308_1du_incl_here_flora_saw_jirimandi", ["ngaliny", "jirimandi", "yaluy", "nyajin"]),
    row(14, "You and I saw the julbal tree here.", "Ngaliny julbal yaluy nyajin.", "ngaliny julbal yaluy nyajin | 1du.INCL.NOM julbal.tree here see.PAST", "r308_1du_incl_here_flora_saw_julbal", ["ngaliny", "julbal", "yaluy", "nyajin"]),
    row(15, "You and I saw the jungka wild cherry tree here.", "Ngaliny jungka yaluy nyajin.", "ngaliny jungka yaluy nyajin | 1du.INCL.NOM jungka.wild.cherry.tree here see.PAST", "r308_1du_incl_here_flora_saw_jungka", ["ngaliny", "jungka", "yaluy", "nyajin"]),
    row(16, "You and I saw the karranda tree here.", "Ngaliny karranda yaluy nyajin.", "ngaliny karranda yaluy nyajin | 1du.INCL.NOM karranda.tree here see.PAST", "r308_1du_incl_here_flora_saw_karranda", ["ngaliny", "karranda", "yaluy", "nyajin"]),
    row(17, "You and I saw the karrandal tree here.", "Ngaliny karrandal yaluy nyajin.", "ngaliny karrandal yaluy nyajin | 1du.INCL.NOM karrandal.tree here see.PAST", "r308_1du_incl_here_flora_saw_karrandal", ["ngaliny", "karrandal", "yaluy", "nyajin"]),
    row(18, "You and I saw the kawarr bloodwood tree here.", "Ngaliny kawarr yaluy nyajin.", "ngaliny kawarr yaluy nyajin | 1du.INCL.NOM kawarr.bloodwood.tree here see.PAST", "r308_1du_incl_here_flora_saw_kawarr", ["ngaliny", "kawarr", "yaluy", "nyajin"]),
    row(19, "You and I saw the marril ti tree here.", "Ngaliny marril yaluy nyajin.", "ngaliny marril yaluy nyajin | 1du.INCL.NOM marril.ti.tree here see.PAST", "r308_1du_incl_here_flora_saw_marril", ["ngaliny", "marril", "yaluy", "nyajin"]),
    row(20, "You and I saw the ngurrkul fig tree here.", "Ngaliny ngurrkul yaluy nyajin.", "ngaliny ngurrkul yaluy nyajin | 1du.INCL.NOM ngurrkul.fig.tree here see.PAST", "r308_1du_incl_here_flora_saw_ngurrkul", ["ngaliny", "ngurrkul", "yaluy", "nyajin"]),
    row(21, "You two saw the bujabay tree there.", "Yubal bujabay yinyay nyajin.", "yubal bujabay yinyay nyajin | 2du.NOM bujabay.tree there see.PAST", "r308_2du_there_flora_saw_bujabay", ["yubal", "bujabay", "yinyay", "nyajin"]),
    row(22, "You two saw the jingajalka tree there.", "Yubal jingajalka yinyay nyajin.", "yubal jingajalka yinyay nyajin | 2du.NOM jingajalka.tree there see.PAST", "r308_2du_there_flora_saw_jingajalka", ["yubal", "jingajalka", "yinyay", "nyajin"]),
    row(23, "You two saw the jirimandi coconut tree there.", "Yubal jirimandi yinyay nyajin.", "yubal jirimandi yinyay nyajin | 2du.NOM jirimandi.coconut.tree there see.PAST", "r308_2du_there_flora_saw_jirimandi", ["yubal", "jirimandi", "yinyay", "nyajin"]),
    row(24, "You two saw the julbal tree there.", "Yubal julbal yinyay nyajin.", "yubal julbal yinyay nyajin | 2du.NOM julbal.tree there see.PAST", "r308_2du_there_flora_saw_julbal", ["yubal", "julbal", "yinyay", "nyajin"]),
    row(25, "You two saw the jungka wild cherry tree there.", "Yubal jungka yinyay nyajin.", "yubal jungka yinyay nyajin | 2du.NOM jungka.wild.cherry.tree there see.PAST", "r308_2du_there_flora_saw_jungka", ["yubal", "jungka", "yinyay", "nyajin"]),
    row(26, "You two saw the karranda tree there.", "Yubal karranda yinyay nyajin.", "yubal karranda yinyay nyajin | 2du.NOM karranda.tree there see.PAST", "r308_2du_there_flora_saw_karranda", ["yubal", "karranda", "yinyay", "nyajin"]),
    row(27, "You two saw the karrandal tree there.", "Yubal karrandal yinyay nyajin.", "yubal karrandal yinyay nyajin | 2du.NOM karrandal.tree there see.PAST", "r308_2du_there_flora_saw_karrandal", ["yubal", "karrandal", "yinyay", "nyajin"]),
    row(28, "You two saw the kawarr bloodwood tree there.", "Yubal kawarr yinyay nyajin.", "yubal kawarr yinyay nyajin | 2du.NOM kawarr.bloodwood.tree there see.PAST", "r308_2du_there_flora_saw_kawarr", ["yubal", "kawarr", "yinyay", "nyajin"]),
    row(29, "You two saw the marril ti tree there.", "Yubal marril yinyay nyajin.", "yubal marril yinyay nyajin | 2du.NOM marril.ti.tree there see.PAST", "r308_2du_there_flora_saw_marril", ["yubal", "marril", "yinyay", "nyajin"]),
    row(30, "You two saw the ngurrkul fig tree there.", "Yubal ngurrkul yinyay nyajin.", "yubal ngurrkul yinyay nyajin | 2du.NOM ngurrkul.fig.tree there see.PAST", "r308_2du_there_flora_saw_ngurrkul", ["yubal", "ngurrkul", "yinyay", "nyajin"]),
    row(31, "You all saw the bujabay tree here.", "Yurra bujabay yaluy nyajin.", "yurra bujabay yaluy nyajin | 2pl.NOM bujabay.tree here see.PAST", "r308_2pl_here_flora_saw_bujabay", ["yurra", "bujabay", "yaluy", "nyajin"]),
    row(32, "You all saw the jingajalka tree here.", "Yurra jingajalka yaluy nyajin.", "yurra jingajalka yaluy nyajin | 2pl.NOM jingajalka.tree here see.PAST", "r308_2pl_here_flora_saw_jingajalka", ["yurra", "jingajalka", "yaluy", "nyajin"]),
    row(33, "You all saw the jirimandi coconut tree here.", "Yurra jirimandi yaluy nyajin.", "yurra jirimandi yaluy nyajin | 2pl.NOM jirimandi.coconut.tree here see.PAST", "r308_2pl_here_flora_saw_jirimandi", ["yurra", "jirimandi", "yaluy", "nyajin"]),
    row(34, "You all saw the julbal tree here.", "Yurra julbal yaluy nyajin.", "yurra julbal yaluy nyajin | 2pl.NOM julbal.tree here see.PAST", "r308_2pl_here_flora_saw_julbal", ["yurra", "julbal", "yaluy", "nyajin"]),
    row(35, "You all saw the jungka wild cherry tree here.", "Yurra jungka yaluy nyajin.", "yurra jungka yaluy nyajin | 2pl.NOM jungka.wild.cherry.tree here see.PAST", "r308_2pl_here_flora_saw_jungka", ["yurra", "jungka", "yaluy", "nyajin"]),
    row(36, "You all saw the karranda tree here.", "Yurra karranda yaluy nyajin.", "yurra karranda yaluy nyajin | 2pl.NOM karranda.tree here see.PAST", "r308_2pl_here_flora_saw_karranda", ["yurra", "karranda", "yaluy", "nyajin"]),
    row(37, "You all saw the karrandal tree here.", "Yurra karrandal yaluy nyajin.", "yurra karrandal yaluy nyajin | 2pl.NOM karrandal.tree here see.PAST", "r308_2pl_here_flora_saw_karrandal", ["yurra", "karrandal", "yaluy", "nyajin"]),
    row(38, "You all saw the kawarr bloodwood tree here.", "Yurra kawarr yaluy nyajin.", "yurra kawarr yaluy nyajin | 2pl.NOM kawarr.bloodwood.tree here see.PAST", "r308_2pl_here_flora_saw_kawarr", ["yurra", "kawarr", "yaluy", "nyajin"]),
    row(39, "You all saw the marril ti tree here.", "Yurra marril yaluy nyajin.", "yurra marril yaluy nyajin | 2pl.NOM marril.ti.tree here see.PAST", "r308_2pl_here_flora_saw_marril", ["yurra", "marril", "yaluy", "nyajin"]),
    row(40, "You all saw the ngurrkul fig tree here.", "Yurra ngurrkul yaluy nyajin.", "yurra ngurrkul yaluy nyajin | 2pl.NOM ngurrkul.fig.tree here see.PAST", "r308_2pl_here_flora_saw_ngurrkul", ["yurra", "ngurrkul", "yaluy", "nyajin"]),
    row(41, "We saw the bujabay tree there.", "Ngana bujabay yinyay nyajin.", "ngana bujabay yinyay nyajin | 1pl.INCL.NOM bujabay.tree there see.PAST", "r308_1pl_incl_there_flora_saw_bujabay", ["ngana", "bujabay", "yinyay", "nyajin"]),
    row(42, "We saw the jingajalka tree there.", "Ngana jingajalka yinyay nyajin.", "ngana jingajalka yinyay nyajin | 1pl.INCL.NOM jingajalka.tree there see.PAST", "r308_1pl_incl_there_flora_saw_jingajalka", ["ngana", "jingajalka", "yinyay", "nyajin"]),
    row(43, "We saw the jirimandi coconut tree there.", "Ngana jirimandi yinyay nyajin.", "ngana jirimandi yinyay nyajin | 1pl.INCL.NOM jirimandi.coconut.tree there see.PAST", "r308_1pl_incl_there_flora_saw_jirimandi", ["ngana", "jirimandi", "yinyay", "nyajin"]),
    row(44, "We saw the julbal tree there.", "Ngana julbal yinyay nyajin.", "ngana julbal yinyay nyajin | 1pl.INCL.NOM julbal.tree there see.PAST", "r308_1pl_incl_there_flora_saw_julbal", ["ngana", "julbal", "yinyay", "nyajin"]),
    row(45, "We saw the jungka wild cherry tree there.", "Ngana jungka yinyay nyajin.", "ngana jungka yinyay nyajin | 1pl.INCL.NOM jungka.wild.cherry.tree there see.PAST", "r308_1pl_incl_there_flora_saw_jungka", ["ngana", "jungka", "yinyay", "nyajin"]),
    row(46, "We saw the karranda tree there.", "Ngana karranda yinyay nyajin.", "ngana karranda yinyay nyajin | 1pl.INCL.NOM karranda.tree there see.PAST", "r308_1pl_incl_there_flora_saw_karranda", ["ngana", "karranda", "yinyay", "nyajin"]),
    row(47, "We saw the karrandal tree there.", "Ngana karrandal yinyay nyajin.", "ngana karrandal yinyay nyajin | 1pl.INCL.NOM karrandal.tree there see.PAST", "r308_1pl_incl_there_flora_saw_karrandal", ["ngana", "karrandal", "yinyay", "nyajin"]),
    row(48, "We saw the kawarr bloodwood tree there.", "Ngana kawarr yinyay nyajin.", "ngana kawarr yinyay nyajin | 1pl.INCL.NOM kawarr.bloodwood.tree there see.PAST", "r308_1pl_incl_there_flora_saw_kawarr", ["ngana", "kawarr", "yinyay", "nyajin"]),
    row(49, "We saw the marril ti tree there.", "Ngana marril yinyay nyajin.", "ngana marril yinyay nyajin | 1pl.INCL.NOM marril.ti.tree there see.PAST", "r308_1pl_incl_there_flora_saw_marril", ["ngana", "marril", "yinyay", "nyajin"]),
    row(50, "We saw the ngurrkul fig tree there.", "Ngana ngurrkul yinyay nyajin.", "ngana ngurrkul yinyay nyajin | 1pl.INCL.NOM ngurrkul.fig.tree there see.PAST", "r308_1pl_incl_there_flora_saw_ngurrkul", ["ngana", "ngurrkul", "yinyay", "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("r308_"):
            issues.append(f"{seq}: frame not r308 {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, ["ngali", "ngaliny", "yubal", "yurra", "ngana", "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 NON-SINGULAR PAST PERCEPTION SWEEP AFTER RULE 307 (Rule 308). "
        "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 ngali, ngaliny, yubal, yurra, and ngana, "
        "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 308 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 308 flora/tree targets and living grammar non-singular pronoun, demonstrative, and nyaji-ny/nyajin past support before authoring Rule 308.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="visible-flora-tree-nonsingular-past-scope", actor="claude-main", detail="Selected visible flora/tree non-singular past perception after Rule 307. The batch uses ngali, ngaliny, yubal, yurra, and ngana 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 308 visible flora/tree non-singular 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 non-singular 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_350.py && python3 -c \"import sys; sys.path.insert(0,'batch_scripts'); import bank_350; print(bank_350.scan_static())\"", output_excerpt="issues 0", artifact_path="batch_scripts/bank_350.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="claude-main", detail=f"Banked 50 Rule 308 rows as under_review. Verified total remains {verified_before}; Kuku word count remains {words_before}.", command="python3 batch_scripts/bank_350.py", output_excerpt=f"banked batch-350: 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-350: 50 rows under_review")
    return 0


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