#!/usr/bin/env python3
"""Bank batch-367: Rule 325 low-coverage fauna singular/third nonpast perception."""

from __future__ import annotations

import datetime as dt
import json
import re
import sqlite3

from process_ledger import ensure_schema, record_event

DB = "/mnt/donto-data/donto-resources/research/translation-training/kuku-yalanji-runpod-2026-06-30/synthetic/claude-synthetic-v1-2026-07-02/synthetic.db"
LABEL = "batch-367"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-367-low-coverage-fauna-singular-third-nonpast-perception-after-rule324-process.md"

TARGET_EVIDENCE = {
    "dirrmbay": "Dictionary yaml lines 6035-6063 give dirrmbay as noun 'seagull' and separately extend it to storm warning, announcement music, and death-related messaging. Rule 325 admits basic seagull reference only and leaves storm, warning, weather prediction, call/music, corroboree, death, corpse, message, ear, flight, feeding, fishing, and event propositions closed.",
    "diri-diri": "Dictionary yaml lines 5930-5945 give the exact full-reduplicated noun diri-diri 'sandfly', with synonym munyurri. Rule 325 admits the whole sandfly head only and leaves productive reduplication, base diri, synonym substitution, number inference, biting, pain, itch, season, habitat, smoke/repellent, and event propositions closed.",
    "jawurraji": "Dictionary yaml lines 8551-8562 give jawurraji as noun 'large ant bed, ant hill'. Rule 325 admits ant-bed reference only and leaves ants, grubs, habitation, size predication, digging, roasting, food, location, construction, material use, and event propositions closed.",
    "juku-juku": "Dictionary yaml lines 10350-10364 give the exact hyphenated noun juku-juku 'bronzewing dove'. A separate solid jukujuku entry at 10365-10377 carries stranger/death omen prose, while Patz lines 1941 and 1976 use hyphenated juku-juku for plural 'trees'. Rule 325 selects the dictionary bird sense only and leaves tree/wood, solid-variant equivalence, omen, stranger, death, song/call, drinking, hunting, and event propositions closed.",
    "jalan": "Dictionary yaml lines 7340-7350 give jalan as noun 'sea urchin' with prose about prickles, coastal rocks, edibility, and frying. Rule 325 admits sea-urchin reference only and leaves shell/spines, injury, feet, rocks/reef/sea habitat, food, edibility, frying, gathering, carrying, and event propositions closed.",
    "kaya-kaya": "Dictionary yaml lines 13966-13979 give the exact full-reduplicated noun kaya-kaya 'dogs', plural of kaya. Rule 325 admits the whole dogs head only and leaves productive reduplication, singular kaya, dog loan history, number derivation, ownership, biting, hunting, following, feeding, and event propositions closed.",
    "kubu-kubu": "Dictionary yaml lines 14716-14729 give the exact full-reduplicated noun kubu-kubu 'tadpole'; bare kubu at lines 14707-14715 means smoke. Rule 325 admits the whole tadpole head only and leaves productive reduplication, smoke homophone, water/pool habitat, swimming, rain/season, frog transformation, growth, and event propositions closed.",
    "murral": "Dictionary yaml lines 20999-21009 give murral as the Yalanji noun 'tree kangaroo', contrasting with Nyungkul jarrabina. Rule 325 admits Yalanji tree-kangaroo reference only and leaves dialect substitution, tree/canopy habitat, climbing, sleeping, visibility, hunting, spearing, catching, food, and event propositions closed.",
    "ngamu-murramu": "Dictionary yaml lines 22184-22194 give the exact compound noun ngamu-murramu 'dingo'. Rule 325 admits the whole dingo head only and leaves decomposition into ngamu/murramu, mother/kin interpretation, synonym substitution, dog equivalence, wild/domestic property, hunting, biting, ownership, movement, and event propositions closed.",
    "wurarr-wurarr": "Dictionary yaml lines 28305-28318 give the exact full-reduplicated noun wurarr-wurarr 'dragonfly'. Rule 325 admits the whole dragonfly head only and leaves productive reduplication, base wurarr, flight/hovering, water indication, dry-country navigation, life-saving, pools/young, season, habitat, and event propositions closed.",
}

SUPPORT_EVIDENCE = (
    "Patz grammar line 1476 explicitly includes fauna among the corporeal objects denoted by nouns. Patz lines "
    "2577-2586 source bare nominative ngayu, yundu, nyulu, bula, and jana/iana; lines 2834-2847 source Yalanji "
    "yaluy 'here' and yinyay 'there'. Patz lines 3043-3049 give nonpast nyaji-l, lines 3149-3151 define the "
    "nonpast present/future function, and lines 4256-4265 license nominative pronoun A with bare absolutive O for "
    "perception. Dictionary lines 24282-24296 independently attest running-form nyajil and its visual sense. Exact "
    "Patz target search finds juku-juku at lines 1941 and 1976 only in the distinct 'trees' sense; the other nine "
    "targets are absent there. Rule 325 therefore takes every selected fauna sense from the dictionary and uses "
    "only bare support and nonpast visual perception."
)

TARGET_ORDER = [
    "dirrmbay", "diri-diri", "jawurraji", "juku-juku", "jalan",
    "kaya-kaya", "kubu-kubu", "murral", "ngamu-murramu", "wurarr-wurarr",
]
TARGET_LABELS = {
    "dirrmbay": "dirrmbay seagull",
    "diri-diri": "diri-diri sandfly",
    "jawurraji": "jawurraji ant bed",
    "juku-juku": "juku-juku bronzewing dove",
    "jalan": "jalan sea urchin",
    "kaya-kaya": "kaya-kaya dogs",
    "kubu-kubu": "kubu-kubu tadpole",
    "murral": "murral tree kangaroo",
    "ngamu-murramu": "ngamu-murramu dingo",
    "wurarr-wurarr": "wurarr-wurarr dragonfly",
}
TARGET_GLOSSES = {
    "dirrmbay": "dirrmbay.seagull",
    "diri-diri": "diri.diri.sandfly",
    "jawurraji": "jawurraji.ant.bed",
    "juku-juku": "juku.juku.bronzewing.dove",
    "jalan": "jalan.sea.urchin",
    "kaya-kaya": "kaya.kaya.dogs",
    "kubu-kubu": "kubu.kubu.tadpole",
    "murral": "murral.tree.kangaroo",
    "ngamu-murramu": "ngamu.murramu.dingo",
    "wurarr-wurarr": "wurarr.wurarr.dragonfly",
}
TARGET_CONFIDENCE = {
    "dirrmbay": "0.86",
    "diri-diri": "0.89",
    "jawurraji": "0.89",
    "juku-juku": "0.84",
    "jalan": "0.88",
    "kaya-kaya": "0.90",
    "kubu-kubu": "0.90",
    "murral": "0.92",
    "ngamu-murramu": "0.90",
    "wurarr-wurarr": "0.90",
}
EXPECTED_TARGET_COVERAGE = {
    "dirrmbay": 2,
    "diri-diri": 3,
    "jawurraji": 1,
    "juku-juku": 2,
    "jalan": 4,
    "kaya-kaya": 0,
    "kubu-kubu": 2,
    "murral": 4,
    "ngamu-murramu": 0,
    "wurarr-wurarr": 1,
}
FRAME_SPECS = [
    ("ngayu", "yinyay", "I see", "there", "1sg"),
    ("yundu", "yaluy", "You see", "here", "2sg"),
    ("nyulu", "yinyay", "He sees", "there", "3sg"),
    ("bula", "yaluy", "The two of them see", "here", "3du"),
    ("jana", "yinyay", "They see", "there", "3pl"),
]

DENY_KUKU = {
    "ngali", "ngaliny", "yubal", "yurra", "ngana", "nganjin", "yulu", "iana",
    "nganya", "yununy", "nyunguny", "yununiny", "nyunguniny", "ngaykunku", "yununku", "nyungunku",
    "ngaykundu", "yunundu", "nyungundu", "ngaykundumuny", "yunundumuny", "nyungundumuny",
    "ngaykunji", "yununji", "nyungunji", "ngayku", "yunu", "nyunju",
    "munyurri", "diri", "jukujuku", "juku", "kaya", "kubu", "jarrabina", "murramu", "ngamu", "wurarr",
    "yalay", "yala", "yanyu", "yinya", "yalungku", "yaluyngku", "yinyangka", "yinyayngka",
    "yalamuny", "yinyamuny", "nyajin", "nyaji-ny", "nyaji-nka", "nyaji-l-nyaji", "milka", "baykal",
    "jalngkay",
}
DENY_ENGLISH = (
    "storm", "warning", "weather", "music", "corroboree", "dead", "death", "corpse", "message", "ear",
    "fly", "feed", "fish", "bite", "pain", "itch", "season", "smoke", "repel", "ants", "grub", "live",
    "dig", "roast", "food", "construct", "wood", "omen", "stranger", "sing", "call", "drink", "hunt",
    "shell", "spine", "injury", "foot", "reef", "edible", "fry", "gather", "carry", "owner", "follow",
    "rain", "pool", "swim", "frog", "transform", "grow", "climb", "sleep", "catch", "spear", "mother",
    "kin", "wild", "domestic", "hover", "navigate", "save", "habitat", "possess", "event", "use", "used",
    "using",
)


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, "I see the dirrmbay seagull there.", "Ngayu dirrmbay yinyay nyajil.", "ngayu dirrmbay yinyay nyajil | 1sg.NOM dirrmbay.seagull there see.NPST", "r325_1sg_there_low_fauna_see_dirrmbay", ["ngayu", "dirrmbay", "yinyay", "nyajil"]),
    row(2, "I see the diri-diri sandfly there.", "Ngayu diri-diri yinyay nyajil.", "ngayu diri-diri yinyay nyajil | 1sg.NOM diri.diri.sandfly there see.NPST", "r325_1sg_there_low_fauna_see_diri_diri", ["ngayu", "diri-diri", "yinyay", "nyajil"]),
    row(3, "I see the jawurraji ant bed there.", "Ngayu jawurraji yinyay nyajil.", "ngayu jawurraji yinyay nyajil | 1sg.NOM jawurraji.ant.bed there see.NPST", "r325_1sg_there_low_fauna_see_jawurraji", ["ngayu", "jawurraji", "yinyay", "nyajil"]),
    row(4, "I see the juku-juku bronzewing dove there.", "Ngayu juku-juku yinyay nyajil.", "ngayu juku-juku yinyay nyajil | 1sg.NOM juku.juku.bronzewing.dove there see.NPST", "r325_1sg_there_low_fauna_see_juku_juku", ["ngayu", "juku-juku", "yinyay", "nyajil"]),
    row(5, "I see the jalan sea urchin there.", "Ngayu jalan yinyay nyajil.", "ngayu jalan yinyay nyajil | 1sg.NOM jalan.sea.urchin there see.NPST", "r325_1sg_there_low_fauna_see_jalan", ["ngayu", "jalan", "yinyay", "nyajil"]),
    row(6, "I see the kaya-kaya dogs there.", "Ngayu kaya-kaya yinyay nyajil.", "ngayu kaya-kaya yinyay nyajil | 1sg.NOM kaya.kaya.dogs there see.NPST", "r325_1sg_there_low_fauna_see_kaya_kaya", ["ngayu", "kaya-kaya", "yinyay", "nyajil"]),
    row(7, "I see the kubu-kubu tadpole there.", "Ngayu kubu-kubu yinyay nyajil.", "ngayu kubu-kubu yinyay nyajil | 1sg.NOM kubu.kubu.tadpole there see.NPST", "r325_1sg_there_low_fauna_see_kubu_kubu", ["ngayu", "kubu-kubu", "yinyay", "nyajil"]),
    row(8, "I see the murral tree kangaroo there.", "Ngayu murral yinyay nyajil.", "ngayu murral yinyay nyajil | 1sg.NOM murral.tree.kangaroo there see.NPST", "r325_1sg_there_low_fauna_see_murral", ["ngayu", "murral", "yinyay", "nyajil"]),
    row(9, "I see the ngamu-murramu dingo there.", "Ngayu ngamu-murramu yinyay nyajil.", "ngayu ngamu-murramu yinyay nyajil | 1sg.NOM ngamu.murramu.dingo there see.NPST", "r325_1sg_there_low_fauna_see_ngamu_murramu", ["ngayu", "ngamu-murramu", "yinyay", "nyajil"]),
    row(10, "I see the wurarr-wurarr dragonfly there.", "Ngayu wurarr-wurarr yinyay nyajil.", "ngayu wurarr-wurarr yinyay nyajil | 1sg.NOM wurarr.wurarr.dragonfly there see.NPST", "r325_1sg_there_low_fauna_see_wurarr_wurarr", ["ngayu", "wurarr-wurarr", "yinyay", "nyajil"]),
    row(11, "You see the dirrmbay seagull here.", "Yundu dirrmbay yaluy nyajil.", "yundu dirrmbay yaluy nyajil | 2sg.NOM dirrmbay.seagull here see.NPST", "r325_2sg_here_low_fauna_see_dirrmbay", ["yundu", "dirrmbay", "yaluy", "nyajil"]),
    row(12, "You see the diri-diri sandfly here.", "Yundu diri-diri yaluy nyajil.", "yundu diri-diri yaluy nyajil | 2sg.NOM diri.diri.sandfly here see.NPST", "r325_2sg_here_low_fauna_see_diri_diri", ["yundu", "diri-diri", "yaluy", "nyajil"]),
    row(13, "You see the jawurraji ant bed here.", "Yundu jawurraji yaluy nyajil.", "yundu jawurraji yaluy nyajil | 2sg.NOM jawurraji.ant.bed here see.NPST", "r325_2sg_here_low_fauna_see_jawurraji", ["yundu", "jawurraji", "yaluy", "nyajil"]),
    row(14, "You see the juku-juku bronzewing dove here.", "Yundu juku-juku yaluy nyajil.", "yundu juku-juku yaluy nyajil | 2sg.NOM juku.juku.bronzewing.dove here see.NPST", "r325_2sg_here_low_fauna_see_juku_juku", ["yundu", "juku-juku", "yaluy", "nyajil"]),
    row(15, "You see the jalan sea urchin here.", "Yundu jalan yaluy nyajil.", "yundu jalan yaluy nyajil | 2sg.NOM jalan.sea.urchin here see.NPST", "r325_2sg_here_low_fauna_see_jalan", ["yundu", "jalan", "yaluy", "nyajil"]),
    row(16, "You see the kaya-kaya dogs here.", "Yundu kaya-kaya yaluy nyajil.", "yundu kaya-kaya yaluy nyajil | 2sg.NOM kaya.kaya.dogs here see.NPST", "r325_2sg_here_low_fauna_see_kaya_kaya", ["yundu", "kaya-kaya", "yaluy", "nyajil"]),
    row(17, "You see the kubu-kubu tadpole here.", "Yundu kubu-kubu yaluy nyajil.", "yundu kubu-kubu yaluy nyajil | 2sg.NOM kubu.kubu.tadpole here see.NPST", "r325_2sg_here_low_fauna_see_kubu_kubu", ["yundu", "kubu-kubu", "yaluy", "nyajil"]),
    row(18, "You see the murral tree kangaroo here.", "Yundu murral yaluy nyajil.", "yundu murral yaluy nyajil | 2sg.NOM murral.tree.kangaroo here see.NPST", "r325_2sg_here_low_fauna_see_murral", ["yundu", "murral", "yaluy", "nyajil"]),
    row(19, "You see the ngamu-murramu dingo here.", "Yundu ngamu-murramu yaluy nyajil.", "yundu ngamu-murramu yaluy nyajil | 2sg.NOM ngamu.murramu.dingo here see.NPST", "r325_2sg_here_low_fauna_see_ngamu_murramu", ["yundu", "ngamu-murramu", "yaluy", "nyajil"]),
    row(20, "You see the wurarr-wurarr dragonfly here.", "Yundu wurarr-wurarr yaluy nyajil.", "yundu wurarr-wurarr yaluy nyajil | 2sg.NOM wurarr.wurarr.dragonfly here see.NPST", "r325_2sg_here_low_fauna_see_wurarr_wurarr", ["yundu", "wurarr-wurarr", "yaluy", "nyajil"]),
    row(21, "He sees the dirrmbay seagull there.", "Nyulu dirrmbay yinyay nyajil.", "nyulu dirrmbay yinyay nyajil | 3sg.NOM dirrmbay.seagull there see.NPST", "r325_3sg_there_low_fauna_see_dirrmbay", ["nyulu", "dirrmbay", "yinyay", "nyajil"]),
    row(22, "He sees the diri-diri sandfly there.", "Nyulu diri-diri yinyay nyajil.", "nyulu diri-diri yinyay nyajil | 3sg.NOM diri.diri.sandfly there see.NPST", "r325_3sg_there_low_fauna_see_diri_diri", ["nyulu", "diri-diri", "yinyay", "nyajil"]),
    row(23, "He sees the jawurraji ant bed there.", "Nyulu jawurraji yinyay nyajil.", "nyulu jawurraji yinyay nyajil | 3sg.NOM jawurraji.ant.bed there see.NPST", "r325_3sg_there_low_fauna_see_jawurraji", ["nyulu", "jawurraji", "yinyay", "nyajil"]),
    row(24, "He sees the juku-juku bronzewing dove there.", "Nyulu juku-juku yinyay nyajil.", "nyulu juku-juku yinyay nyajil | 3sg.NOM juku.juku.bronzewing.dove there see.NPST", "r325_3sg_there_low_fauna_see_juku_juku", ["nyulu", "juku-juku", "yinyay", "nyajil"]),
    row(25, "He sees the jalan sea urchin there.", "Nyulu jalan yinyay nyajil.", "nyulu jalan yinyay nyajil | 3sg.NOM jalan.sea.urchin there see.NPST", "r325_3sg_there_low_fauna_see_jalan", ["nyulu", "jalan", "yinyay", "nyajil"]),
    row(26, "He sees the kaya-kaya dogs there.", "Nyulu kaya-kaya yinyay nyajil.", "nyulu kaya-kaya yinyay nyajil | 3sg.NOM kaya.kaya.dogs there see.NPST", "r325_3sg_there_low_fauna_see_kaya_kaya", ["nyulu", "kaya-kaya", "yinyay", "nyajil"]),
    row(27, "He sees the kubu-kubu tadpole there.", "Nyulu kubu-kubu yinyay nyajil.", "nyulu kubu-kubu yinyay nyajil | 3sg.NOM kubu.kubu.tadpole there see.NPST", "r325_3sg_there_low_fauna_see_kubu_kubu", ["nyulu", "kubu-kubu", "yinyay", "nyajil"]),
    row(28, "He sees the murral tree kangaroo there.", "Nyulu murral yinyay nyajil.", "nyulu murral yinyay nyajil | 3sg.NOM murral.tree.kangaroo there see.NPST", "r325_3sg_there_low_fauna_see_murral", ["nyulu", "murral", "yinyay", "nyajil"]),
    row(29, "He sees the ngamu-murramu dingo there.", "Nyulu ngamu-murramu yinyay nyajil.", "nyulu ngamu-murramu yinyay nyajil | 3sg.NOM ngamu.murramu.dingo there see.NPST", "r325_3sg_there_low_fauna_see_ngamu_murramu", ["nyulu", "ngamu-murramu", "yinyay", "nyajil"]),
    row(30, "He sees the wurarr-wurarr dragonfly there.", "Nyulu wurarr-wurarr yinyay nyajil.", "nyulu wurarr-wurarr yinyay nyajil | 3sg.NOM wurarr.wurarr.dragonfly there see.NPST", "r325_3sg_there_low_fauna_see_wurarr_wurarr", ["nyulu", "wurarr-wurarr", "yinyay", "nyajil"]),
    row(31, "The two of them see the dirrmbay seagull here.", "Bula dirrmbay yaluy nyajil.", "bula dirrmbay yaluy nyajil | 3du.NOM dirrmbay.seagull here see.NPST", "r325_3du_here_low_fauna_see_dirrmbay", ["bula", "dirrmbay", "yaluy", "nyajil"]),
    row(32, "The two of them see the diri-diri sandfly here.", "Bula diri-diri yaluy nyajil.", "bula diri-diri yaluy nyajil | 3du.NOM diri.diri.sandfly here see.NPST", "r325_3du_here_low_fauna_see_diri_diri", ["bula", "diri-diri", "yaluy", "nyajil"]),
    row(33, "The two of them see the jawurraji ant bed here.", "Bula jawurraji yaluy nyajil.", "bula jawurraji yaluy nyajil | 3du.NOM jawurraji.ant.bed here see.NPST", "r325_3du_here_low_fauna_see_jawurraji", ["bula", "jawurraji", "yaluy", "nyajil"]),
    row(34, "The two of them see the juku-juku bronzewing dove here.", "Bula juku-juku yaluy nyajil.", "bula juku-juku yaluy nyajil | 3du.NOM juku.juku.bronzewing.dove here see.NPST", "r325_3du_here_low_fauna_see_juku_juku", ["bula", "juku-juku", "yaluy", "nyajil"]),
    row(35, "The two of them see the jalan sea urchin here.", "Bula jalan yaluy nyajil.", "bula jalan yaluy nyajil | 3du.NOM jalan.sea.urchin here see.NPST", "r325_3du_here_low_fauna_see_jalan", ["bula", "jalan", "yaluy", "nyajil"]),
    row(36, "The two of them see the kaya-kaya dogs here.", "Bula kaya-kaya yaluy nyajil.", "bula kaya-kaya yaluy nyajil | 3du.NOM kaya.kaya.dogs here see.NPST", "r325_3du_here_low_fauna_see_kaya_kaya", ["bula", "kaya-kaya", "yaluy", "nyajil"]),
    row(37, "The two of them see the kubu-kubu tadpole here.", "Bula kubu-kubu yaluy nyajil.", "bula kubu-kubu yaluy nyajil | 3du.NOM kubu.kubu.tadpole here see.NPST", "r325_3du_here_low_fauna_see_kubu_kubu", ["bula", "kubu-kubu", "yaluy", "nyajil"]),
    row(38, "The two of them see the murral tree kangaroo here.", "Bula murral yaluy nyajil.", "bula murral yaluy nyajil | 3du.NOM murral.tree.kangaroo here see.NPST", "r325_3du_here_low_fauna_see_murral", ["bula", "murral", "yaluy", "nyajil"]),
    row(39, "The two of them see the ngamu-murramu dingo here.", "Bula ngamu-murramu yaluy nyajil.", "bula ngamu-murramu yaluy nyajil | 3du.NOM ngamu.murramu.dingo here see.NPST", "r325_3du_here_low_fauna_see_ngamu_murramu", ["bula", "ngamu-murramu", "yaluy", "nyajil"]),
    row(40, "The two of them see the wurarr-wurarr dragonfly here.", "Bula wurarr-wurarr yaluy nyajil.", "bula wurarr-wurarr yaluy nyajil | 3du.NOM wurarr.wurarr.dragonfly here see.NPST", "r325_3du_here_low_fauna_see_wurarr_wurarr", ["bula", "wurarr-wurarr", "yaluy", "nyajil"]),
    row(41, "They see the dirrmbay seagull there.", "Jana dirrmbay yinyay nyajil.", "jana dirrmbay yinyay nyajil | 3pl.NOM dirrmbay.seagull there see.NPST", "r325_3pl_there_low_fauna_see_dirrmbay", ["jana", "dirrmbay", "yinyay", "nyajil"]),
    row(42, "They see the diri-diri sandfly there.", "Jana diri-diri yinyay nyajil.", "jana diri-diri yinyay nyajil | 3pl.NOM diri.diri.sandfly there see.NPST", "r325_3pl_there_low_fauna_see_diri_diri", ["jana", "diri-diri", "yinyay", "nyajil"]),
    row(43, "They see the jawurraji ant bed there.", "Jana jawurraji yinyay nyajil.", "jana jawurraji yinyay nyajil | 3pl.NOM jawurraji.ant.bed there see.NPST", "r325_3pl_there_low_fauna_see_jawurraji", ["jana", "jawurraji", "yinyay", "nyajil"]),
    row(44, "They see the juku-juku bronzewing dove there.", "Jana juku-juku yinyay nyajil.", "jana juku-juku yinyay nyajil | 3pl.NOM juku.juku.bronzewing.dove there see.NPST", "r325_3pl_there_low_fauna_see_juku_juku", ["jana", "juku-juku", "yinyay", "nyajil"]),
    row(45, "They see the jalan sea urchin there.", "Jana jalan yinyay nyajil.", "jana jalan yinyay nyajil | 3pl.NOM jalan.sea.urchin there see.NPST", "r325_3pl_there_low_fauna_see_jalan", ["jana", "jalan", "yinyay", "nyajil"]),
    row(46, "They see the kaya-kaya dogs there.", "Jana kaya-kaya yinyay nyajil.", "jana kaya-kaya yinyay nyajil | 3pl.NOM kaya.kaya.dogs there see.NPST", "r325_3pl_there_low_fauna_see_kaya_kaya", ["jana", "kaya-kaya", "yinyay", "nyajil"]),
    row(47, "They see the kubu-kubu tadpole there.", "Jana kubu-kubu yinyay nyajil.", "jana kubu-kubu yinyay nyajil | 3pl.NOM kubu.kubu.tadpole there see.NPST", "r325_3pl_there_low_fauna_see_kubu_kubu", ["jana", "kubu-kubu", "yinyay", "nyajil"]),
    row(48, "They see the murral tree kangaroo there.", "Jana murral yinyay nyajil.", "jana murral yinyay nyajil | 3pl.NOM murral.tree.kangaroo there see.NPST", "r325_3pl_there_low_fauna_see_murral", ["jana", "murral", "yinyay", "nyajil"]),
    row(49, "They see the ngamu-murramu dingo there.", "Jana ngamu-murramu yinyay nyajil.", "jana ngamu-murramu yinyay nyajil | 3pl.NOM ngamu.murramu.dingo there see.NPST", "r325_3pl_there_low_fauna_see_ngamu_murramu", ["jana", "ngamu-murramu", "yinyay", "nyajil"]),
    row(50, "They see the wurarr-wurarr dragonfly there.", "Jana wurarr-wurarr yinyay nyajil.", "jana wurarr-wurarr yinyay nyajil | 3pl.NOM wurarr.wurarr.dragonfly there see.NPST", "r325_3pl_there_low_fauna_see_wurarr_wurarr", ["jana", "wurarr-wurarr", "yinyay", "nyajil"]),
]


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"row count {len(ROWS)} != 50")
    if [r[0] for r in ROWS] != list(range(1, 51)):
        issues.append("sequence is not 1..50")
    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} nyajil."
        expected_english = f"{prefix} the {TARGET_LABELS[target]} {location}."
        expected_analysis = f"{pronoun} {target} {demo} nyajil | {person_gloss}.NOM {TARGET_GLOSSES[target]} {location} see.NPST"
        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 expected {expected_kuku!r}, got {kuku!r}")
        if english != expected_english:
            issues.append(f"{seq}: English mismatch expected {expected_english!r}, got {english!r}")
        if analysis != expected_analysis:
            issues.append(f"{seq}: analysis mismatch expected {expected_analysis!r}, got {analysis!r}")
        if words != [pronoun, target, demo, "nyajil"]:
            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("r325_"):
            issues.append(f"{seq}: frame not r325 {frame}")
        if TARGET_EVIDENCE[target] not in evidence or SUPPORT_EVIDENCE not in evidence:
            issues.append(f"{seq}: evidence contract mismatch")
        if confidence != TARGET_CONFIDENCE[target]:
            issues.append(f"{seq}: confidence mismatch {confidence}")
    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; rerun source/scope audit: {target_counts}")
    support_counts = count_words(db, ["ngayu", "yundu", "nyulu", "bula", "jana", "yaluy", "yinyay", "nyajil"])
    collisions = []
    for _seq, _english, kuku, *_rest in ROWS:
        hit = 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 hit:
            collisions.append((kuku, hit))
    if collisions:
        raise SystemExit(f"duplicate preflight failed: {collisions}")
    verified_before, words_before = db.execute(
        "SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) "
        "FROM sentences WHERE status IN ('accepted','revised')"
    ).fetchone()
    notes = (
        "LOW-COVERAGE FAUNA SINGULAR/THIRD-PERSON NONPAST PERCEPTION AFTER RULE 324 (Rule 325). 50 under-review "
        "rows over ten 0-4-use noun heads: dirrmbay, diri-diri, jawurraji, juku-juku, jalan, kaya-kaya, "
        "kubu-kubu, murral, ngamu-murramu, wurarr-wurarr. SUPPORT: bare nominative ngayu, yundu, nyulu, bula, "
        "jana; Yalanji yaluy/yinyay; dictionary-attested nonpast nyajil. Exact conservative fauna labels only. "
        "No dialect substitution, tree/wood homonym, solid/hyphenated equivalence, omen/death, food/injury, habitat, "
        "hunting, ownership, transformation, source-story event, productive compound/reduplication, target case, "
        "past/purposive/reduplicated perception, support inflection, or carried trap."
    )
    cur = db.execute(
        "INSERT INTO batches(label,cheatsheet_version,guide_version,notes,created_at) VALUES(?,?,?,?,?)",
        (LABEL, "Rule 325 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, 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="Reread all ten selected dictionary fauna entries; exact-searched every target in Patz (juku-juku occurs only as the distinct trees sense, the other nine do not occur); reread Patz fauna noun status, singular/third nominative pronouns, Yalanji local demonstratives, nonpast nyaji-l, perception A/O syntax, and dictionary running-form nyajil before authoring Rule 325.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="candidate-audit", actor="codex", detail="Structured dictionary-to-corpus fauna audit rejected Nyungkul-only kanyaji, kumurbina, and yidilnyul; undefined or solid-reduplication-invalid heads; loans; ambiguous place names; and culturally loaded story/totem candidates where a cleaner set existed. Selected ten direct, lint-safe Yalanji or unmarked fauna nouns with stable referential readings and coverage 0-4; all extended source semantics are closed.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="low-fauna-singular-third-nonpast-scope", actor="codex", detail="Locked ten low-coverage dictionary fauna heads to bare reference in five sourced singular/third-person nonpast-perception frames. Juku-juku is the dictionary bronzewing-dove sense, not Patz trees or solid omen form; exact reduplicated/compound heads remain indivisible; no dialect, behavior, habitat, food, danger, ownership, transformation, event, case, or productive morphology is opened.", 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 Rule 325 exact Kuku surfaces are internally unique and returned no accepted, revised, or under-review corpus collisions.", 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 Kuku/English/analysis/evidence/confidence contracts, one selected fauna head each, bare singular/third nominative pronouns and Yalanji demonstratives, final nyajil, no loans, no denied forms, and no dialect, homonym, omen, food/injury, habitat, hunting, ownership, transformation, productive-morphology, case, or support drift.", command="python3 -m py_compile batch_scripts/bank_367.py batch_scripts/close_367.py && explicit-row/lesson/lexeme AST inventory && scan_static/lint probes", output_excerpt="50 explicit rows; scan_static issues 0; 50 linter probes issues 0; collisions 0", artifact_path="batch_scripts/bank_367.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="codex", detail=f"Banked 50 Rule 325 rows as under_review. Verified remains {verified_before}; Kuku words remain {words_before}.", command="python3 batch_scripts/bank_367.py", output_excerpt=f"banked batch-367: 50 under_review; verified_before={verified_before}; kuku_words_before={words_before}", artifact_path=PROCESS_LOG, created_at=NOW)
    db.commit()
    print("banked batch-367: 50 rows under_review")
    return 0


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