#!/usr/bin/env python3
"""Bank batch-351: Rule 309 complementary 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-351"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-351-complementary-flora-tree-nonsingular-past-perception-after-rule308-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 309 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 309 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 309 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 309 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 309 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 309 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 309 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 309 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 309 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 309 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 309 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", "yaluy"),
    ("ngaliny", "yinyay"),
    ("yubal", "yaluy"),
    ("yurra", "yinyay"),
    ("ngana", "yaluy"),
]

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 here.", "Ngali bujabay yaluy nyajin.", "ngali bujabay yaluy nyajin | 1du.EXCL.NOM bujabay.tree here see.PAST", "r309_1du_excl_here_flora_saw_bujabay", ["ngali", "bujabay", "yaluy", "nyajin"]),
    row(2, "We two, not you, saw the jingajalka tree here.", "Ngali jingajalka yaluy nyajin.", "ngali jingajalka yaluy nyajin | 1du.EXCL.NOM jingajalka.tree here see.PAST", "r309_1du_excl_here_flora_saw_jingajalka", ["ngali", "jingajalka", "yaluy", "nyajin"]),
    row(3, "We two, not you, saw the jirimandi coconut tree here.", "Ngali jirimandi yaluy nyajin.", "ngali jirimandi yaluy nyajin | 1du.EXCL.NOM jirimandi.coconut.tree here see.PAST", "r309_1du_excl_here_flora_saw_jirimandi", ["ngali", "jirimandi", "yaluy", "nyajin"]),
    row(4, "We two, not you, saw the julbal tree here.", "Ngali julbal yaluy nyajin.", "ngali julbal yaluy nyajin | 1du.EXCL.NOM julbal.tree here see.PAST", "r309_1du_excl_here_flora_saw_julbal", ["ngali", "julbal", "yaluy", "nyajin"]),
    row(5, "We two, not you, saw the jungka wild cherry tree here.", "Ngali jungka yaluy nyajin.", "ngali jungka yaluy nyajin | 1du.EXCL.NOM jungka.wild.cherry.tree here see.PAST", "r309_1du_excl_here_flora_saw_jungka", ["ngali", "jungka", "yaluy", "nyajin"]),
    row(6, "We two, not you, saw the karranda tree here.", "Ngali karranda yaluy nyajin.", "ngali karranda yaluy nyajin | 1du.EXCL.NOM karranda.tree here see.PAST", "r309_1du_excl_here_flora_saw_karranda", ["ngali", "karranda", "yaluy", "nyajin"]),
    row(7, "We two, not you, saw the karrandal tree here.", "Ngali karrandal yaluy nyajin.", "ngali karrandal yaluy nyajin | 1du.EXCL.NOM karrandal.tree here see.PAST", "r309_1du_excl_here_flora_saw_karrandal", ["ngali", "karrandal", "yaluy", "nyajin"]),
    row(8, "We two, not you, saw the kawarr bloodwood tree here.", "Ngali kawarr yaluy nyajin.", "ngali kawarr yaluy nyajin | 1du.EXCL.NOM kawarr.bloodwood.tree here see.PAST", "r309_1du_excl_here_flora_saw_kawarr", ["ngali", "kawarr", "yaluy", "nyajin"]),
    row(9, "We two, not you, saw the marril ti tree here.", "Ngali marril yaluy nyajin.", "ngali marril yaluy nyajin | 1du.EXCL.NOM marril.ti.tree here see.PAST", "r309_1du_excl_here_flora_saw_marril", ["ngali", "marril", "yaluy", "nyajin"]),
    row(10, "We two, not you, saw the ngurrkul fig tree here.", "Ngali ngurrkul yaluy nyajin.", "ngali ngurrkul yaluy nyajin | 1du.EXCL.NOM ngurrkul.fig.tree here see.PAST", "r309_1du_excl_here_flora_saw_ngurrkul", ["ngali", "ngurrkul", "yaluy", "nyajin"]),
    row(11, "You and I saw the bujabay tree there.", "Ngaliny bujabay yinyay nyajin.", "ngaliny bujabay yinyay nyajin | 1du.INCL.NOM bujabay.tree there see.PAST", "r309_1du_incl_there_flora_saw_bujabay", ["ngaliny", "bujabay", "yinyay", "nyajin"]),
    row(12, "You and I saw the jingajalka tree there.", "Ngaliny jingajalka yinyay nyajin.", "ngaliny jingajalka yinyay nyajin | 1du.INCL.NOM jingajalka.tree there see.PAST", "r309_1du_incl_there_flora_saw_jingajalka", ["ngaliny", "jingajalka", "yinyay", "nyajin"]),
    row(13, "You and I saw the jirimandi coconut tree there.", "Ngaliny jirimandi yinyay nyajin.", "ngaliny jirimandi yinyay nyajin | 1du.INCL.NOM jirimandi.coconut.tree there see.PAST", "r309_1du_incl_there_flora_saw_jirimandi", ["ngaliny", "jirimandi", "yinyay", "nyajin"]),
    row(14, "You and I saw the julbal tree there.", "Ngaliny julbal yinyay nyajin.", "ngaliny julbal yinyay nyajin | 1du.INCL.NOM julbal.tree there see.PAST", "r309_1du_incl_there_flora_saw_julbal", ["ngaliny", "julbal", "yinyay", "nyajin"]),
    row(15, "You and I saw the jungka wild cherry tree there.", "Ngaliny jungka yinyay nyajin.", "ngaliny jungka yinyay nyajin | 1du.INCL.NOM jungka.wild.cherry.tree there see.PAST", "r309_1du_incl_there_flora_saw_jungka", ["ngaliny", "jungka", "yinyay", "nyajin"]),
    row(16, "You and I saw the karranda tree there.", "Ngaliny karranda yinyay nyajin.", "ngaliny karranda yinyay nyajin | 1du.INCL.NOM karranda.tree there see.PAST", "r309_1du_incl_there_flora_saw_karranda", ["ngaliny", "karranda", "yinyay", "nyajin"]),
    row(17, "You and I saw the karrandal tree there.", "Ngaliny karrandal yinyay nyajin.", "ngaliny karrandal yinyay nyajin | 1du.INCL.NOM karrandal.tree there see.PAST", "r309_1du_incl_there_flora_saw_karrandal", ["ngaliny", "karrandal", "yinyay", "nyajin"]),
    row(18, "You and I saw the kawarr bloodwood tree there.", "Ngaliny kawarr yinyay nyajin.", "ngaliny kawarr yinyay nyajin | 1du.INCL.NOM kawarr.bloodwood.tree there see.PAST", "r309_1du_incl_there_flora_saw_kawarr", ["ngaliny", "kawarr", "yinyay", "nyajin"]),
    row(19, "You and I saw the marril ti tree there.", "Ngaliny marril yinyay nyajin.", "ngaliny marril yinyay nyajin | 1du.INCL.NOM marril.ti.tree there see.PAST", "r309_1du_incl_there_flora_saw_marril", ["ngaliny", "marril", "yinyay", "nyajin"]),
    row(20, "You and I saw the ngurrkul fig tree there.", "Ngaliny ngurrkul yinyay nyajin.", "ngaliny ngurrkul yinyay nyajin | 1du.INCL.NOM ngurrkul.fig.tree there see.PAST", "r309_1du_incl_there_flora_saw_ngurrkul", ["ngaliny", "ngurrkul", "yinyay", "nyajin"]),
    row(21, "You two saw the bujabay tree here.", "Yubal bujabay yaluy nyajin.", "yubal bujabay yaluy nyajin | 2du.NOM bujabay.tree here see.PAST", "r309_2du_here_flora_saw_bujabay", ["yubal", "bujabay", "yaluy", "nyajin"]),
    row(22, "You two saw the jingajalka tree here.", "Yubal jingajalka yaluy nyajin.", "yubal jingajalka yaluy nyajin | 2du.NOM jingajalka.tree here see.PAST", "r309_2du_here_flora_saw_jingajalka", ["yubal", "jingajalka", "yaluy", "nyajin"]),
    row(23, "You two saw the jirimandi coconut tree here.", "Yubal jirimandi yaluy nyajin.", "yubal jirimandi yaluy nyajin | 2du.NOM jirimandi.coconut.tree here see.PAST", "r309_2du_here_flora_saw_jirimandi", ["yubal", "jirimandi", "yaluy", "nyajin"]),
    row(24, "You two saw the julbal tree here.", "Yubal julbal yaluy nyajin.", "yubal julbal yaluy nyajin | 2du.NOM julbal.tree here see.PAST", "r309_2du_here_flora_saw_julbal", ["yubal", "julbal", "yaluy", "nyajin"]),
    row(25, "You two saw the jungka wild cherry tree here.", "Yubal jungka yaluy nyajin.", "yubal jungka yaluy nyajin | 2du.NOM jungka.wild.cherry.tree here see.PAST", "r309_2du_here_flora_saw_jungka", ["yubal", "jungka", "yaluy", "nyajin"]),
    row(26, "You two saw the karranda tree here.", "Yubal karranda yaluy nyajin.", "yubal karranda yaluy nyajin | 2du.NOM karranda.tree here see.PAST", "r309_2du_here_flora_saw_karranda", ["yubal", "karranda", "yaluy", "nyajin"]),
    row(27, "You two saw the karrandal tree here.", "Yubal karrandal yaluy nyajin.", "yubal karrandal yaluy nyajin | 2du.NOM karrandal.tree here see.PAST", "r309_2du_here_flora_saw_karrandal", ["yubal", "karrandal", "yaluy", "nyajin"]),
    row(28, "You two saw the kawarr bloodwood tree here.", "Yubal kawarr yaluy nyajin.", "yubal kawarr yaluy nyajin | 2du.NOM kawarr.bloodwood.tree here see.PAST", "r309_2du_here_flora_saw_kawarr", ["yubal", "kawarr", "yaluy", "nyajin"]),
    row(29, "You two saw the marril ti tree here.", "Yubal marril yaluy nyajin.", "yubal marril yaluy nyajin | 2du.NOM marril.ti.tree here see.PAST", "r309_2du_here_flora_saw_marril", ["yubal", "marril", "yaluy", "nyajin"]),
    row(30, "You two saw the ngurrkul fig tree here.", "Yubal ngurrkul yaluy nyajin.", "yubal ngurrkul yaluy nyajin | 2du.NOM ngurrkul.fig.tree here see.PAST", "r309_2du_here_flora_saw_ngurrkul", ["yubal", "ngurrkul", "yaluy", "nyajin"]),
    row(31, "You all saw the bujabay tree there.", "Yurra bujabay yinyay nyajin.", "yurra bujabay yinyay nyajin | 2pl.NOM bujabay.tree there see.PAST", "r309_2pl_there_flora_saw_bujabay", ["yurra", "bujabay", "yinyay", "nyajin"]),
    row(32, "You all saw the jingajalka tree there.", "Yurra jingajalka yinyay nyajin.", "yurra jingajalka yinyay nyajin | 2pl.NOM jingajalka.tree there see.PAST", "r309_2pl_there_flora_saw_jingajalka", ["yurra", "jingajalka", "yinyay", "nyajin"]),
    row(33, "You all saw the jirimandi coconut tree there.", "Yurra jirimandi yinyay nyajin.", "yurra jirimandi yinyay nyajin | 2pl.NOM jirimandi.coconut.tree there see.PAST", "r309_2pl_there_flora_saw_jirimandi", ["yurra", "jirimandi", "yinyay", "nyajin"]),
    row(34, "You all saw the julbal tree there.", "Yurra julbal yinyay nyajin.", "yurra julbal yinyay nyajin | 2pl.NOM julbal.tree there see.PAST", "r309_2pl_there_flora_saw_julbal", ["yurra", "julbal", "yinyay", "nyajin"]),
    row(35, "You all saw the jungka wild cherry tree there.", "Yurra jungka yinyay nyajin.", "yurra jungka yinyay nyajin | 2pl.NOM jungka.wild.cherry.tree there see.PAST", "r309_2pl_there_flora_saw_jungka", ["yurra", "jungka", "yinyay", "nyajin"]),
    row(36, "You all saw the karranda tree there.", "Yurra karranda yinyay nyajin.", "yurra karranda yinyay nyajin | 2pl.NOM karranda.tree there see.PAST", "r309_2pl_there_flora_saw_karranda", ["yurra", "karranda", "yinyay", "nyajin"]),
    row(37, "You all saw the karrandal tree there.", "Yurra karrandal yinyay nyajin.", "yurra karrandal yinyay nyajin | 2pl.NOM karrandal.tree there see.PAST", "r309_2pl_there_flora_saw_karrandal", ["yurra", "karrandal", "yinyay", "nyajin"]),
    row(38, "You all saw the kawarr bloodwood tree there.", "Yurra kawarr yinyay nyajin.", "yurra kawarr yinyay nyajin | 2pl.NOM kawarr.bloodwood.tree there see.PAST", "r309_2pl_there_flora_saw_kawarr", ["yurra", "kawarr", "yinyay", "nyajin"]),
    row(39, "You all saw the marril ti tree there.", "Yurra marril yinyay nyajin.", "yurra marril yinyay nyajin | 2pl.NOM marril.ti.tree there see.PAST", "r309_2pl_there_flora_saw_marril", ["yurra", "marril", "yinyay", "nyajin"]),
    row(40, "You all saw the ngurrkul fig tree there.", "Yurra ngurrkul yinyay nyajin.", "yurra ngurrkul yinyay nyajin | 2pl.NOM ngurrkul.fig.tree there see.PAST", "r309_2pl_there_flora_saw_ngurrkul", ["yurra", "ngurrkul", "yinyay", "nyajin"]),
    row(41, "We saw the bujabay tree here.", "Ngana bujabay yaluy nyajin.", "ngana bujabay yaluy nyajin | 1pl.INCL.NOM bujabay.tree here see.PAST", "r309_1pl_incl_here_flora_saw_bujabay", ["ngana", "bujabay", "yaluy", "nyajin"]),
    row(42, "We saw the jingajalka tree here.", "Ngana jingajalka yaluy nyajin.", "ngana jingajalka yaluy nyajin | 1pl.INCL.NOM jingajalka.tree here see.PAST", "r309_1pl_incl_here_flora_saw_jingajalka", ["ngana", "jingajalka", "yaluy", "nyajin"]),
    row(43, "We saw the jirimandi coconut tree here.", "Ngana jirimandi yaluy nyajin.", "ngana jirimandi yaluy nyajin | 1pl.INCL.NOM jirimandi.coconut.tree here see.PAST", "r309_1pl_incl_here_flora_saw_jirimandi", ["ngana", "jirimandi", "yaluy", "nyajin"]),
    row(44, "We saw the julbal tree here.", "Ngana julbal yaluy nyajin.", "ngana julbal yaluy nyajin | 1pl.INCL.NOM julbal.tree here see.PAST", "r309_1pl_incl_here_flora_saw_julbal", ["ngana", "julbal", "yaluy", "nyajin"]),
    row(45, "We saw the jungka wild cherry tree here.", "Ngana jungka yaluy nyajin.", "ngana jungka yaluy nyajin | 1pl.INCL.NOM jungka.wild.cherry.tree here see.PAST", "r309_1pl_incl_here_flora_saw_jungka", ["ngana", "jungka", "yaluy", "nyajin"]),
    row(46, "We saw the karranda tree here.", "Ngana karranda yaluy nyajin.", "ngana karranda yaluy nyajin | 1pl.INCL.NOM karranda.tree here see.PAST", "r309_1pl_incl_here_flora_saw_karranda", ["ngana", "karranda", "yaluy", "nyajin"]),
    row(47, "We saw the karrandal tree here.", "Ngana karrandal yaluy nyajin.", "ngana karrandal yaluy nyajin | 1pl.INCL.NOM karrandal.tree here see.PAST", "r309_1pl_incl_here_flora_saw_karrandal", ["ngana", "karrandal", "yaluy", "nyajin"]),
    row(48, "We saw the kawarr bloodwood tree here.", "Ngana kawarr yaluy nyajin.", "ngana kawarr yaluy nyajin | 1pl.INCL.NOM kawarr.bloodwood.tree here see.PAST", "r309_1pl_incl_here_flora_saw_kawarr", ["ngana", "kawarr", "yaluy", "nyajin"]),
    row(49, "We saw the marril ti tree here.", "Ngana marril yaluy nyajin.", "ngana marril yaluy nyajin | 1pl.INCL.NOM marril.ti.tree here see.PAST", "r309_1pl_incl_here_flora_saw_marril", ["ngana", "marril", "yaluy", "nyajin"]),
    row(50, "We saw the ngurrkul fig tree here.", "Ngana ngurrkul yaluy nyajin.", "ngana ngurrkul yaluy nyajin | 1pl.INCL.NOM ngurrkul.fig.tree here see.PAST", "r309_1pl_incl_here_flora_saw_ngurrkul", ["ngana", "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("r309_"):
            issues.append(f"{seq}: frame not r309 {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 = (
        "COMPLEMENTARY VISIBLE FLORA/TREE NON-SINGULAR PAST PERCEPTION SWEEP AFTER RULE 308 (Rule 309). "
        "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 309 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 309 flora/tree targets and living grammar non-singular pronoun, demonstrative, and nyaji-ny/nyajin past support before authoring Rule 309.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="complementary-flora-tree-nonsingular-past-scope", actor="claude-main", detail="Selected complementary visible flora/tree non-singular past perception after Rule 308 by reversing the local-demonstrative slot. 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 309 complementary 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_351.py && python3 -c \"import sys; sys.path.insert(0,'batch_scripts'); import bank_351; print(bank_351.scan_static())\"", output_excerpt="issues 0", artifact_path="batch_scripts/bank_351.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="claude-main", detail=f"Banked 50 Rule 309 rows as under_review. Verified total remains {verified_before}; Kuku word count remains {words_before}.", command="python3 batch_scripts/bank_351.py", output_excerpt=f"banked batch-351: 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-351: 50 rows under_review")
    return 0


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