#!/usr/bin/env python3
"""Bank batch-378: Rule 336 underrepresented non-singular mammal 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-378"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-378-mammal-underrepresented-nonsingular-past-perception-after-rule335-process.md"

TARGET_EVIDENCE = {
    "kaya-kaya": "Dictionary yaml lines 13966-13979 give exact hyphenated kaya-kaya as a fauna-mammal noun 'dogs', explicitly full reduplication of kaya marking plurality. Rule 336 admits the whole plural dog head as bare O only; free-base substitution, a new productive rule, ownership, behavior, camp, source, and events remain closed.",
    "murral": "Dictionary yaml lines 20999-21009 give murral as the Yalanji fauna-mammal noun 'tree kangaroo', with Nyungkul jarrabina named in prose. Rule 336 admits exact Yalanji murral only; dialect substitution/equivalence, canopy, climbing, sleeping, hunting, food, source, and events remain closed.",
    "jungarr": "Dictionary yaml lines 10882-10892 give jungarr as the fauna-mammal noun 'marsupial native cat', with quoll in commentary. Rule 336 retains the conservative source label; taxonomic identity, size, fierceness, predation, habitat, source, possession, and events remain closed.",
    "jayju": "Dictionary yaml lines 8602-8610 give jayju as the fauna-mammal noun 'kangaroo rat'. Rule 336 admits bare reference only; size, burrow, habitat, hiding, day/night activity, feeding, source, possession, and events remain closed.",
    "ngumbi": "Dictionary yaml lines 23562-23572 give ngumbi as the little-spotted-bandicoot fauna-mammal noun. Rule 336 retains the conservative corpus label 'ngumbi bandicoot'; size/spot predication, burrowing, habitat, hunting, cooking, food, source, possession, and events remain closed.",
    "mali": "Dictionary yaml lines 17566-17577 give mali as a fauna-mammal noun for a species of small bat. Patz lines 5946-5956 separately use homophonous mali for former possession after a locative/ablative possessor. Rule 336 admits only bat noun O; homonym merger, flight, hanging, hollow, night, insects, source, possession, and events remain closed.",
    "kukur": "Dictionary yaml lines 15002-15010 give kukur as the fauna-mammal noun 'rat'. Patz lines 1659 and 1730 show rat-root kukur in potent kukur-uku/kukur-undu. Rule 336 keeps bare kukur; target case, camp/food, predation, source, possession, and events remain closed.",
    "murramu": "Dictionary yaml lines 21010-21020 give murramu as the fauna-mammal noun 'dingo' and list ngamu-murramu as a synonym. Rule 336 admits exact bare murramu; synonym generation, murramungku inflection, decomposition, hunting, tracking, call/family, food, source, possession, and events remain closed.",
    "darrba": "Dictionary yaml lines 5306-5315 give darrba as the fauna-mammal noun 'grass wallaby'; nearby darra is 'narrow, too small'. Rule 336 admits exact darrba only; near-head confusion, size/habitat, feeding, tracks, hunting, food, ceremony, source, possession, and events remain closed.",
    "yawa": "Dictionary yaml lines 29634-29642 give yawa as the fauna-mammal noun 'possum'; nearby yawarr at 29643-29650 is a palm. Rule 336 admits exact yawa only; palm identity, tree/hollow habitat, climbing, night, eye-shine, hunting, food, fur/use, source, possession, and events remain closed.",
}

SUPPORT_EVIDENCE = (
    "Patz line 1476 includes fauna among noun referents. Lines 2577-2584 source bare nominative ngali, ngaliny, "
    "yubal, yurra, and first-plural-exclusive nganjin; lines 2834-2847 source Yalanji yaluy 'here' and yinyay "
    "'there'. Examples at lines 2021-2024 and 2568-2571 attest nyaji-ny 'see-PAST', lines 3160-3162 define past "
    "-ny, and lines 4256-4265 license nominative pronoun A with bare absolutive O in perception. Dictionary "
    "examples at lines 23876-23883 and 23968-23976 contain running nyajin. Fresh target readback retained Patz's "
    "kukur case evidence and mali former-possession homonym boundary. Rule 336 selects nganjin because accepted/"
    "revised coverage is 268 versus 1115 for deferred first-plural-inclusive ngana; this is a corpus-coverage choice, "
    "not a grammatical exclusion of ngana."
)

TARGET_ORDER = ["kaya-kaya", "murral", "jungarr", "jayju", "ngumbi", "mali", "kukur", "murramu", "darrba", "yawa"]
TARGET_LABELS = {"kaya-kaya": "kaya-kaya dogs", "murral": "murral tree kangaroo", "jungarr": "jungarr marsupial native cat", "jayju": "jayju kangaroo rat", "ngumbi": "ngumbi bandicoot", "mali": "mali small bat", "kukur": "kukur rat", "murramu": "murramu dingo", "darrba": "darrba grass wallaby", "yawa": "yawa possum"}
TARGET_GLOSSES = {"kaya-kaya": "kaya.kaya.dogs", "murral": "murral.tree.kangaroo", "jungarr": "marsupial.native.cat", "jayju": "kangaroo.rat", "ngumbi": "bandicoot", "mali": "small.bat", "kukur": "rat", "murramu": "dingo", "darrba": "grass.wallaby", "yawa": "possum"}
TARGET_CONFIDENCE = {"kaya-kaya": "0.93", "murral": "0.94", "jungarr": "0.93", "jayju": "0.94", "ngumbi": "0.93", "mali": "0.90", "kukur": "0.94", "murramu": "0.93", "darrba": "0.94", "yawa": "0.94"}
EXPECTED_TARGET_COVERAGE = {"kaya-kaya": 10, "murral": 14, "jungarr": 32, "jayju": 33, "ngumbi": 33, "mali": 35, "kukur": 36, "murramu": 59, "darrba": 61, "yawa": 64}
EXPECTED_SUPPORT_COVERAGE = {"ngali": 603, "ngaliny": 545, "yubal": 555, "yurra": 540, "nganjin": 268, "ngana": 1115, "yaluy": 4272, "yinyay": 4464, "nyajin": 1850}
FRAME_SPECS = [
    ("ngali", "yaluy", "We two, not you, saw", "here", "1du.excl"),
    ("ngaliny", "yinyay", "You and I saw", "there", "1du.incl"),
    ("yubal", "yaluy", "You two saw", "here", "2du"),
    ("yurra", "yinyay", "You all saw", "there", "2pl"),
    ("nganjin", "yaluy", "We, not you, saw", "here", "1pl.excl"),
]

DENY_KUKU = {
    "ngayu", "yundu", "nyulu", "yulu", "bula", "jana", "iana", "ngana",
    "kaya", "jarrabina", "ngamu-murramu", "murramungku", "kukuruku", "kukur-uku", "kukurundu",
    "kukur-undu", "darra", "yawarr", "yalay", "yala", "yanyu", "yinya", "yalungku", "yaluyngku",
    "yinyangka", "yinyayngka", "yalamuny", "yinyamuny", "nyajil", "nyaji-l", "nyaji-nka",
    "nyaji-l-nyaji", "milka", "baykal", "jalngkay",
}
DENY_ENGLISH = (
    "nyungkul", "synonym", "equivalent", "former possession", "particle", "suffix", "clitic", "plural process",
    "free base", "productive reduplication", "wild dog", "quoll identity", "palm", "narrow", "too small",
    "tree canopy", "climb", "sleep", "burrow", "hide", "seed", "root feeding", "spotted property", "fly",
    "hang", "tree hollow", "night", "insect", "camp food", "predation", "hunt", "track", "call", "howl",
    "family", "spear", "catch", "cook", "fur", "cloak", "source", "possess", "event",
)


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 lower(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]
    return (seq, english, kuku, analysis, frame, 3, json.dumps(words, ensure_ascii=False), json.dumps([], ensure_ascii=False), f"{TARGET_EVIDENCE[target]} {SUPPORT_EVIDENCE}", TARGET_CONFIDENCE[target])


ROWS = [
    row(1, "We two, not you, saw the kaya-kaya dogs here.", "Ngali kaya-kaya yaluy nyajin.", "ngali kaya-kaya yaluy nyajin | 1du.excl.NOM kaya.kaya.dogs here see.PST", "r336_1du_excl_here_mammal_saw_kaya_kaya", ["ngali", "kaya-kaya", "yaluy", "nyajin"]),
    row(2, "We two, not you, saw the murral tree kangaroo here.", "Ngali murral yaluy nyajin.", "ngali murral yaluy nyajin | 1du.excl.NOM murral.tree.kangaroo here see.PST", "r336_1du_excl_here_mammal_saw_murral", ["ngali", "murral", "yaluy", "nyajin"]),
    row(3, "We two, not you, saw the jungarr marsupial native cat here.", "Ngali jungarr yaluy nyajin.", "ngali jungarr yaluy nyajin | 1du.excl.NOM marsupial.native.cat here see.PST", "r336_1du_excl_here_mammal_saw_jungarr", ["ngali", "jungarr", "yaluy", "nyajin"]),
    row(4, "We two, not you, saw the jayju kangaroo rat here.", "Ngali jayju yaluy nyajin.", "ngali jayju yaluy nyajin | 1du.excl.NOM kangaroo.rat here see.PST", "r336_1du_excl_here_mammal_saw_jayju", ["ngali", "jayju", "yaluy", "nyajin"]),
    row(5, "We two, not you, saw the ngumbi bandicoot here.", "Ngali ngumbi yaluy nyajin.", "ngali ngumbi yaluy nyajin | 1du.excl.NOM bandicoot here see.PST", "r336_1du_excl_here_mammal_saw_ngumbi", ["ngali", "ngumbi", "yaluy", "nyajin"]),
    row(6, "We two, not you, saw the mali small bat here.", "Ngali mali yaluy nyajin.", "ngali mali yaluy nyajin | 1du.excl.NOM small.bat here see.PST", "r336_1du_excl_here_mammal_saw_mali", ["ngali", "mali", "yaluy", "nyajin"]),
    row(7, "We two, not you, saw the kukur rat here.", "Ngali kukur yaluy nyajin.", "ngali kukur yaluy nyajin | 1du.excl.NOM rat here see.PST", "r336_1du_excl_here_mammal_saw_kukur", ["ngali", "kukur", "yaluy", "nyajin"]),
    row(8, "We two, not you, saw the murramu dingo here.", "Ngali murramu yaluy nyajin.", "ngali murramu yaluy nyajin | 1du.excl.NOM dingo here see.PST", "r336_1du_excl_here_mammal_saw_murramu", ["ngali", "murramu", "yaluy", "nyajin"]),
    row(9, "We two, not you, saw the darrba grass wallaby here.", "Ngali darrba yaluy nyajin.", "ngali darrba yaluy nyajin | 1du.excl.NOM grass.wallaby here see.PST", "r336_1du_excl_here_mammal_saw_darrba", ["ngali", "darrba", "yaluy", "nyajin"]),
    row(10, "We two, not you, saw the yawa possum here.", "Ngali yawa yaluy nyajin.", "ngali yawa yaluy nyajin | 1du.excl.NOM possum here see.PST", "r336_1du_excl_here_mammal_saw_yawa", ["ngali", "yawa", "yaluy", "nyajin"]),
    row(11, "You and I saw the kaya-kaya dogs there.", "Ngaliny kaya-kaya yinyay nyajin.", "ngaliny kaya-kaya yinyay nyajin | 1du.incl.NOM kaya.kaya.dogs there see.PST", "r336_1du_incl_there_mammal_saw_kaya_kaya", ["ngaliny", "kaya-kaya", "yinyay", "nyajin"]),
    row(12, "You and I saw the murral tree kangaroo there.", "Ngaliny murral yinyay nyajin.", "ngaliny murral yinyay nyajin | 1du.incl.NOM murral.tree.kangaroo there see.PST", "r336_1du_incl_there_mammal_saw_murral", ["ngaliny", "murral", "yinyay", "nyajin"]),
    row(13, "You and I saw the jungarr marsupial native cat there.", "Ngaliny jungarr yinyay nyajin.", "ngaliny jungarr yinyay nyajin | 1du.incl.NOM marsupial.native.cat there see.PST", "r336_1du_incl_there_mammal_saw_jungarr", ["ngaliny", "jungarr", "yinyay", "nyajin"]),
    row(14, "You and I saw the jayju kangaroo rat there.", "Ngaliny jayju yinyay nyajin.", "ngaliny jayju yinyay nyajin | 1du.incl.NOM kangaroo.rat there see.PST", "r336_1du_incl_there_mammal_saw_jayju", ["ngaliny", "jayju", "yinyay", "nyajin"]),
    row(15, "You and I saw the ngumbi bandicoot there.", "Ngaliny ngumbi yinyay nyajin.", "ngaliny ngumbi yinyay nyajin | 1du.incl.NOM bandicoot there see.PST", "r336_1du_incl_there_mammal_saw_ngumbi", ["ngaliny", "ngumbi", "yinyay", "nyajin"]),
    row(16, "You and I saw the mali small bat there.", "Ngaliny mali yinyay nyajin.", "ngaliny mali yinyay nyajin | 1du.incl.NOM small.bat there see.PST", "r336_1du_incl_there_mammal_saw_mali", ["ngaliny", "mali", "yinyay", "nyajin"]),
    row(17, "You and I saw the kukur rat there.", "Ngaliny kukur yinyay nyajin.", "ngaliny kukur yinyay nyajin | 1du.incl.NOM rat there see.PST", "r336_1du_incl_there_mammal_saw_kukur", ["ngaliny", "kukur", "yinyay", "nyajin"]),
    row(18, "You and I saw the murramu dingo there.", "Ngaliny murramu yinyay nyajin.", "ngaliny murramu yinyay nyajin | 1du.incl.NOM dingo there see.PST", "r336_1du_incl_there_mammal_saw_murramu", ["ngaliny", "murramu", "yinyay", "nyajin"]),
    row(19, "You and I saw the darrba grass wallaby there.", "Ngaliny darrba yinyay nyajin.", "ngaliny darrba yinyay nyajin | 1du.incl.NOM grass.wallaby there see.PST", "r336_1du_incl_there_mammal_saw_darrba", ["ngaliny", "darrba", "yinyay", "nyajin"]),
    row(20, "You and I saw the yawa possum there.", "Ngaliny yawa yinyay nyajin.", "ngaliny yawa yinyay nyajin | 1du.incl.NOM possum there see.PST", "r336_1du_incl_there_mammal_saw_yawa", ["ngaliny", "yawa", "yinyay", "nyajin"]),
    row(21, "You two saw the kaya-kaya dogs here.", "Yubal kaya-kaya yaluy nyajin.", "yubal kaya-kaya yaluy nyajin | 2du.NOM kaya.kaya.dogs here see.PST", "r336_2du_here_mammal_saw_kaya_kaya", ["yubal", "kaya-kaya", "yaluy", "nyajin"]),
    row(22, "You two saw the murral tree kangaroo here.", "Yubal murral yaluy nyajin.", "yubal murral yaluy nyajin | 2du.NOM murral.tree.kangaroo here see.PST", "r336_2du_here_mammal_saw_murral", ["yubal", "murral", "yaluy", "nyajin"]),
    row(23, "You two saw the jungarr marsupial native cat here.", "Yubal jungarr yaluy nyajin.", "yubal jungarr yaluy nyajin | 2du.NOM marsupial.native.cat here see.PST", "r336_2du_here_mammal_saw_jungarr", ["yubal", "jungarr", "yaluy", "nyajin"]),
    row(24, "You two saw the jayju kangaroo rat here.", "Yubal jayju yaluy nyajin.", "yubal jayju yaluy nyajin | 2du.NOM kangaroo.rat here see.PST", "r336_2du_here_mammal_saw_jayju", ["yubal", "jayju", "yaluy", "nyajin"]),
    row(25, "You two saw the ngumbi bandicoot here.", "Yubal ngumbi yaluy nyajin.", "yubal ngumbi yaluy nyajin | 2du.NOM bandicoot here see.PST", "r336_2du_here_mammal_saw_ngumbi", ["yubal", "ngumbi", "yaluy", "nyajin"]),
    row(26, "You two saw the mali small bat here.", "Yubal mali yaluy nyajin.", "yubal mali yaluy nyajin | 2du.NOM small.bat here see.PST", "r336_2du_here_mammal_saw_mali", ["yubal", "mali", "yaluy", "nyajin"]),
    row(27, "You two saw the kukur rat here.", "Yubal kukur yaluy nyajin.", "yubal kukur yaluy nyajin | 2du.NOM rat here see.PST", "r336_2du_here_mammal_saw_kukur", ["yubal", "kukur", "yaluy", "nyajin"]),
    row(28, "You two saw the murramu dingo here.", "Yubal murramu yaluy nyajin.", "yubal murramu yaluy nyajin | 2du.NOM dingo here see.PST", "r336_2du_here_mammal_saw_murramu", ["yubal", "murramu", "yaluy", "nyajin"]),
    row(29, "You two saw the darrba grass wallaby here.", "Yubal darrba yaluy nyajin.", "yubal darrba yaluy nyajin | 2du.NOM grass.wallaby here see.PST", "r336_2du_here_mammal_saw_darrba", ["yubal", "darrba", "yaluy", "nyajin"]),
    row(30, "You two saw the yawa possum here.", "Yubal yawa yaluy nyajin.", "yubal yawa yaluy nyajin | 2du.NOM possum here see.PST", "r336_2du_here_mammal_saw_yawa", ["yubal", "yawa", "yaluy", "nyajin"]),
    row(31, "You all saw the kaya-kaya dogs there.", "Yurra kaya-kaya yinyay nyajin.", "yurra kaya-kaya yinyay nyajin | 2pl.NOM kaya.kaya.dogs there see.PST", "r336_2pl_there_mammal_saw_kaya_kaya", ["yurra", "kaya-kaya", "yinyay", "nyajin"]),
    row(32, "You all saw the murral tree kangaroo there.", "Yurra murral yinyay nyajin.", "yurra murral yinyay nyajin | 2pl.NOM murral.tree.kangaroo there see.PST", "r336_2pl_there_mammal_saw_murral", ["yurra", "murral", "yinyay", "nyajin"]),
    row(33, "You all saw the jungarr marsupial native cat there.", "Yurra jungarr yinyay nyajin.", "yurra jungarr yinyay nyajin | 2pl.NOM marsupial.native.cat there see.PST", "r336_2pl_there_mammal_saw_jungarr", ["yurra", "jungarr", "yinyay", "nyajin"]),
    row(34, "You all saw the jayju kangaroo rat there.", "Yurra jayju yinyay nyajin.", "yurra jayju yinyay nyajin | 2pl.NOM kangaroo.rat there see.PST", "r336_2pl_there_mammal_saw_jayju", ["yurra", "jayju", "yinyay", "nyajin"]),
    row(35, "You all saw the ngumbi bandicoot there.", "Yurra ngumbi yinyay nyajin.", "yurra ngumbi yinyay nyajin | 2pl.NOM bandicoot there see.PST", "r336_2pl_there_mammal_saw_ngumbi", ["yurra", "ngumbi", "yinyay", "nyajin"]),
    row(36, "You all saw the mali small bat there.", "Yurra mali yinyay nyajin.", "yurra mali yinyay nyajin | 2pl.NOM small.bat there see.PST", "r336_2pl_there_mammal_saw_mali", ["yurra", "mali", "yinyay", "nyajin"]),
    row(37, "You all saw the kukur rat there.", "Yurra kukur yinyay nyajin.", "yurra kukur yinyay nyajin | 2pl.NOM rat there see.PST", "r336_2pl_there_mammal_saw_kukur", ["yurra", "kukur", "yinyay", "nyajin"]),
    row(38, "You all saw the murramu dingo there.", "Yurra murramu yinyay nyajin.", "yurra murramu yinyay nyajin | 2pl.NOM dingo there see.PST", "r336_2pl_there_mammal_saw_murramu", ["yurra", "murramu", "yinyay", "nyajin"]),
    row(39, "You all saw the darrba grass wallaby there.", "Yurra darrba yinyay nyajin.", "yurra darrba yinyay nyajin | 2pl.NOM grass.wallaby there see.PST", "r336_2pl_there_mammal_saw_darrba", ["yurra", "darrba", "yinyay", "nyajin"]),
    row(40, "You all saw the yawa possum there.", "Yurra yawa yinyay nyajin.", "yurra yawa yinyay nyajin | 2pl.NOM possum there see.PST", "r336_2pl_there_mammal_saw_yawa", ["yurra", "yawa", "yinyay", "nyajin"]),
    row(41, "We, not you, saw the kaya-kaya dogs here.", "Nganjin kaya-kaya yaluy nyajin.", "nganjin kaya-kaya yaluy nyajin | 1pl.excl.NOM kaya.kaya.dogs here see.PST", "r336_1pl_excl_here_mammal_saw_kaya_kaya", ["nganjin", "kaya-kaya", "yaluy", "nyajin"]),
    row(42, "We, not you, saw the murral tree kangaroo here.", "Nganjin murral yaluy nyajin.", "nganjin murral yaluy nyajin | 1pl.excl.NOM murral.tree.kangaroo here see.PST", "r336_1pl_excl_here_mammal_saw_murral", ["nganjin", "murral", "yaluy", "nyajin"]),
    row(43, "We, not you, saw the jungarr marsupial native cat here.", "Nganjin jungarr yaluy nyajin.", "nganjin jungarr yaluy nyajin | 1pl.excl.NOM marsupial.native.cat here see.PST", "r336_1pl_excl_here_mammal_saw_jungarr", ["nganjin", "jungarr", "yaluy", "nyajin"]),
    row(44, "We, not you, saw the jayju kangaroo rat here.", "Nganjin jayju yaluy nyajin.", "nganjin jayju yaluy nyajin | 1pl.excl.NOM kangaroo.rat here see.PST", "r336_1pl_excl_here_mammal_saw_jayju", ["nganjin", "jayju", "yaluy", "nyajin"]),
    row(45, "We, not you, saw the ngumbi bandicoot here.", "Nganjin ngumbi yaluy nyajin.", "nganjin ngumbi yaluy nyajin | 1pl.excl.NOM bandicoot here see.PST", "r336_1pl_excl_here_mammal_saw_ngumbi", ["nganjin", "ngumbi", "yaluy", "nyajin"]),
    row(46, "We, not you, saw the mali small bat here.", "Nganjin mali yaluy nyajin.", "nganjin mali yaluy nyajin | 1pl.excl.NOM small.bat here see.PST", "r336_1pl_excl_here_mammal_saw_mali", ["nganjin", "mali", "yaluy", "nyajin"]),
    row(47, "We, not you, saw the kukur rat here.", "Nganjin kukur yaluy nyajin.", "nganjin kukur yaluy nyajin | 1pl.excl.NOM rat here see.PST", "r336_1pl_excl_here_mammal_saw_kukur", ["nganjin", "kukur", "yaluy", "nyajin"]),
    row(48, "We, not you, saw the murramu dingo here.", "Nganjin murramu yaluy nyajin.", "nganjin murramu yaluy nyajin | 1pl.excl.NOM dingo here see.PST", "r336_1pl_excl_here_mammal_saw_murramu", ["nganjin", "murramu", "yaluy", "nyajin"]),
    row(49, "We, not you, saw the darrba grass wallaby here.", "Nganjin darrba yaluy nyajin.", "nganjin darrba yaluy nyajin | 1pl.excl.NOM grass.wallaby here see.PST", "r336_1pl_excl_here_mammal_saw_darrba", ["nganjin", "darrba", "yaluy", "nyajin"]),
    row(50, "We, not you, saw the yawa possum here.", "Nganjin yawa yaluy nyajin.", "nganjin yawa yaluy nyajin | 1pl.excl.NOM possum here see.PST", "r336_1pl_excl_here_mammal_saw_yawa", ["nganjin", "yawa", "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")
    if len({r[1] for r in ROWS}) != 50: issues.append("duplicate English surface inside batch")
    if len({r[2] for r in ROWS}) != 50: 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); band = (seq - 1) // 10; target = TARGET_ORDER[(seq - 1) % 10]; pronoun, demo, prefix, location, person_gloss = FRAME_SPECS[band]
        expected_kuku = f"{pronoun.capitalize()} {target} {demo} nyajin."; expected_english = f"{prefix} the {TARGET_LABELS[target]} {location}."; expected_analysis = f"{pronoun} {target} {demo} nyajin | {person_gloss}.NOM {TARGET_GLOSSES[target]} {location} see.PST"
        low_kuku = kuku.lower(); low_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\.]|$)", low_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)}\b", low_english)]
        if denied_english: issues.append(f"{seq}: denied English drift {denied_english}")
        if kuku != expected_kuku: issues.append(f"{seq}: Kuku mismatch")
        if english != expected_english: issues.append(f"{seq}: English mismatch")
        if analysis != expected_analysis: issues.append(f"{seq}: analysis mismatch")
        if words != [pronoun, target, demo, "nyajin"]: issues.append(f"{seq}: words mismatch {words}")
        if low_kuku.rstrip(".") != " ".join(words): issues.append(f"{seq}: Kuku does not match words_used")
        if not frame.startswith("r336_"): issues.append(f"{seq}: frame not r336")
        if TARGET_EVIDENCE[target] not in evidence or SUPPORT_EVIDENCE not in evidence: issues.append(f"{seq}: evidence mismatch")
        if confidence != TARGET_CONFIDENCE[target]: issues.append(f"{seq}: confidence mismatch")
    return issues


def main() -> int:
    issues = scan_static()
    if issues: print("\n".join(issues)); 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")
    target_counts = count_words(db, TARGET_ORDER)
    if target_counts != EXPECTED_TARGET_COVERAGE: raise SystemExit(f"target coverage changed: {target_counts}")
    support_counts = count_words(db, list(EXPECTED_SUPPORT_COVERAGE))
    if support_counts != EXPECTED_SUPPORT_COVERAGE: raise SystemExit(f"support coverage changed: {support_counts}")
    grid_hits = []
    for pronoun in ["ngali", "ngaliny", "yubal", "yurra", "nganjin"]:
        for demo in ["yaluy", "yinyay"]:
            for target in TARGET_ORDER:
                surface = f"{pronoun.capitalize()} {target} {demo} nyajin."
                hits = db.execute("SELECT b.label,s.seq FROM sentences s JOIN batches b ON b.id=s.batch_id WHERE lower(s.kuku)=lower(?) AND s.status!='rejected'", (surface,)).fetchall()
                if hits: grid_hits.append((surface, hits))
    if grid_hits: raise SystemExit(f"four-token construction grid changed: {grid_hits}")
    collisions = []
    for _seq, _english, kuku, *_rest in ROWS:
        hits = 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 hits: collisions.append((kuku, hits))
    if collisions: raise SystemExit(f"duplicate preflight failed: {collisions}")
    alternate = []
    for pronoun, demo in [("ngali", "yinyay"), ("ngaliny", "yaluy"), ("yubal", "yinyay"), ("yurra", "yaluy"), ("nganjin", "yinyay")]:
        for target in TARGET_ORDER:
            surface = f"{pronoun.capitalize()} {target} {demo} nyajin."
            hits = db.execute("SELECT b.label,s.seq FROM sentences s JOIN batches b ON b.id=s.batch_id WHERE lower(s.kuku)=lower(?) AND s.status!='rejected'", (surface,)).fetchall()
            if hits: alternate.append((surface, hits))
    if alternate: raise SystemExit(f"alternate orientation unexpectedly collided: {alternate}")
    verified_before, words_before = db.execute("SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) FROM sentences WHERE status IN ('accepted','revised')").fetchone()
    if (verified_before, words_before) != (18947, 131147): raise SystemExit(f"baseline changed: verified={verified_before} words={words_before}")
    notes = ("UNDERREPRESENTED NON-SINGULAR MAMMAL PAST-PERCEPTION EXPANSION AFTER RULE 335 (Rule 336). 50 under-review rows over the same ten source-vetted fauna-mammal heads. Selected A forms are ngali, ngaliny, yubal, yurra, and low-coverage first-plural-exclusive nganjin; first-plural-inclusive ngana is grammatical but deferred because its coverage was 1115 versus nganjin 268. The exact 100-cell selected-pronoun/deictic past grid was empty. Both orientations were collision-free; three-yaluy/two-yinyay reduces the deictic gap from 192 to 182. Exact noun senses only; all Rule 335 dialect, homonym, synonym, case, near-head, ecology, behavior, hunting, food/use, source, possession, cultural-event, nonpast/purposive/reduplicated-perception, and support boundaries remain closed.")
    cur = db.execute("INSERT INTO batches(label,cheatsheet_version,guide_version,notes,created_at) VALUES(?,?,?,?,?)", (LABEL, "Rule 336 under review", "operator-guide-2026-07-10", notes, NOW)); bid = 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(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", [(bid, seq, en, ku, an, fr, ti, wo, lo, ev, "under_review", co, "project_approved_synthetic_pending_elder_verification", "codex-main", NOW, NOW) for seq, en, ku, an, fr, ti, wo, lo, ev, co in ROWS])
    record_event(db, batch_label=LABEL, phase="source", event_type="source-readback", actor="codex", detail="Freshly reread all ten exact mammal records and Rule 335 near-head/dialect/homonym/synonym/case boundaries; reread Patz fauna, full pronoun paradigm, Yalanji locals, past, perception A/O, kukur case examples, mali former-possession section, and dictionary running nyajin.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="participant-selection-audit", actor="codex", detail="Compared all six non-third non-singular nominatives. Selected ngali, ngaliny, yubal, yurra, and underrepresented 1pl-exclusive nganjin (coverage 268); deferred grammatical 1pl-inclusive ngana (coverage 1115). Third dual/plural bula/jana were already covered in Rule 335.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="lexical-boundary-audit", actor="codex", detail="Reconfirmed ten exact unique noun-typed fauna-mammal non-loan records and retained all Rule 335 free-base, dialect, homonym, case, synonym, inflection, near-head, ecology, behavior, hunting, food/use, source, possession, and event exclusions.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="construction-gap-audit", actor="codex", detail="Exact SQLite matrix over five selected non-singular pronouns x ten targets x yaluy/yinyay found zero four-token bare-target surfaces ending nyajin.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="deictic-balance-audit", actor="codex", detail="Both 50-surface orientations were internally unique and collision-free. Before banking yaluy=4272 and yinyay=4464 (gap 192); selected three-yaluy/two-yinyay orientation projects 182, inverse 202.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="coverage-audit", actor="codex", detail=f"Token-aware accepted/revised coverage before banking: 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="codex", detail="All 50 selected Rule 336 exact Kuku surfaces are internally unique and returned no corpus collisions; inverse orientation also collision-free but less balanced.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="static-guard-pass", actor="codex", detail="Static guard passed: 50 explicit rows, exact contracts, one selected mammal head each, selected bare non-singular nominative A and absolutive O, fixed Yalanji locals/final nyajin, no loans or lexical/support drift.", command="python3 -m py_compile batch_scripts/bank_378.py batch_scripts/close_378.py && AST/static/lint probes", output_excerpt="50 explicit rows; scan_static 0; 50 linter probes 0; selected/inverse collisions 0", artifact_path="batch_scripts/bank_378.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="codex", detail=f"Banked 50 Rule 336 rows as under_review. Verified remains {verified_before}; Kuku words remain {words_before}.", command="python3 batch_scripts/bank_378.py", output_excerpt=f"banked batch-378: 50 under_review; verified_before={verified_before}; words_before={words_before}", artifact_path=PROCESS_LOG, created_at=NOW)
    db.commit(); print("banked batch-378: 50 rows under_review"); return 0


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