#!/usr/bin/env python3
"""Bank batch-321: Rule 279 visible water/landform nonpast pronoun 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-321"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-321-visible-water-landform-nonpast-pronoun-perception-after-rule278-process.md"

TARGET_HEADS = {
    "bana": "water",
    "dajali": "deep water",
    "jalun": "sea",
    "jirka": "spring",
    "bubu": "ground",
    "manjal": "mountain",
    "wawubaja": "river",
    "yilki": "creek",
    "yiri": "waterfall",
    "yirri": "running water",
}
TARGET_ORDER = ["bana", "dajali", "jalun", "jirka", "bubu", "manjal", "wawubaja", "yilki", "yiri", "yirri"]
PRONOUN_SUPPORT = {
    "ngayu": "1sg.NOM",
    "yundu": "2sg.NOM",
    "nyulu": "3sg.NOM",
    "bula": "3du.NOM",
    "ngana": "1pl.INCL.NOM",
}
SUPPORT = set(PRONOUN_SUPPORT) | {"yaluy", "yinyay", "nyajil"}
DENY_KUKU = {
    "jukar", "banabila", "jalungkarr", "jirimandi", "marrka", "mukurr-mukurr", "yalkan", "yikin",
    "yirri-damal", "yiril", "yiman", "kalki", "kubirri", "bujung", "wawu-balangaji",
    "nganya", "yununy", "nyunguny", "bulangan", "nganangan", "nganangan", "nganiny",
    "ngaykunku", "yununku", "nyungunku", "bulanka", "ngananka", "ngaykundu", "yunundu",
    "nyungundu", "bulanda", "ngananda", "ngaykundumuny", "yunundumuny", "nyungundumuny",
    "bulandamuny", "nganandamuny", "ngaykunji", "yununji", "nyungunji", "bulanji", "ngananji",
    "ngayku", "yunu", "nyunju", "bulanga", "ngananga", "nyajin", "nyaji-ny", "nyaji-nka",
    "nyaji-l-nyaji", "bubujida", "bubungu", "bubumuny", "bububa", "bubungu", "bubuwu",
    "yinyangka", "yinyayngka", "yinyamba", "yinyamuny", "yalungku", "yaluyngku",
    "yaluymbu", "yaluyundu", "yalamuny", "yalanji",
}
DENY_ENGLISH = (
    "saw", "look", "looked", "watch", "watched", "watching", "listen", "listening", "hearing", "heard",
    "drink", "drank", "drinking", "get ", "got ", "getting", "fill", "filled", "wash", "washed",
    "washing", "swim", "swam", "swimming", "cross", "crossed", "crossing", "travel", "route",
    "road", "flood", "tide", "salt pan", "whale oil", "season", "place", "country", "belong",
    "dirty", "dirt", "story", "spirit", "fish", "shell", "snake", "crocodile", "plant", "tree",
    "ecology", "danger", "own", "owner", "source", "use", "uses", "used", "case", "compound", "event",
)


def js(values: list[str]) -> str:
    return json.dumps(values, ensure_ascii=False)


def row(seq: int, english: str, kuku: str, analysis: str, frame: str, words: list[str], evidence: str) -> tuple:
    return (seq, english, kuku, analysis, frame, 3, js(words), js([]), evidence, "0.86")


EVIDENCE = {
    "bana": "Dictionary lines 1264-1272 give bana as noun 'water'. Rule 279 selects visible water reference/perception only. Patz lines 2577-2586 and 2592-2603 source bare nominative pronouns; lines 2678-2686 source bula/ngana distinctions; lines 2838-2845 source yaluy/yinyay; and lines 3043-3049 source nyaji-l 'see' nonpast.",
    "dajali": "Dictionary lines 4510-4518 give dajali as noun 'deep water'. Rule 279 selects visible deep-water reference only.",
    "jalun": "Dictionary lines 7612-7620 give jalun as noun 'sea, large body of water'. Rule 279 selects sea reference only and leaves flood rows closed.",
    "jirka": "Dictionary lines 9803-9813 give jirka as noun 'spring' and commentary reads it as a water source rather than season. Rule 279 selects visible water-source spring only, not season.",
    "bubu": "Dictionary lines 3452-3470 give bubu as noun 'ground/earth' with a country/place extension. Rule 279 selects visible ground/earth only and leaves country/place/dirtiness extensions closed.",
    "manjal": "Dictionary lines 17832-17840 give manjal as noun 'mountain'. Rule 279 selects visible mountain reference only.",
    "wawubaja": "Dictionary lines 26813-26821 give wawubaja as noun 'river'. Rule 279 selects visible river reference only.",
    "yilki": "Dictionary lines 30031-30039 give yilki as noun 'creek'. Rule 279 selects visible creek reference only.",
    "yiri": "Dictionary lines 30410-30418 give yiri as noun 'waterfall'. Rule 279 selects visible waterfall reference only.",
    "yirri": "Dictionary lines 30507-30515 give yirri as noun 'running water'. Rule 279 selects visible running-water reference only and leaves yirri-damal 'leak' closed.",
}

ROWS = [
    row(1, "I see the water here.", "Ngayu bana yaluy nyajil.", "ngayu bana yaluy nyajil | 1sg.NOM water here see.NPST", "r279_1sg_water_landform_here_see_bana", ["ngayu", "bana", "yaluy", "nyajil"], EVIDENCE["bana"]),
    row(2, "I see the deep water here.", "Ngayu dajali yaluy nyajil.", "ngayu dajali yaluy nyajil | 1sg.NOM deep.water here see.NPST", "r279_1sg_water_landform_here_see_dajali", ["ngayu", "dajali", "yaluy", "nyajil"], EVIDENCE["dajali"]),
    row(3, "I see the sea here.", "Ngayu jalun yaluy nyajil.", "ngayu jalun yaluy nyajil | 1sg.NOM sea here see.NPST", "r279_1sg_water_landform_here_see_jalun", ["ngayu", "jalun", "yaluy", "nyajil"], EVIDENCE["jalun"]),
    row(4, "I see the spring here.", "Ngayu jirka yaluy nyajil.", "ngayu jirka yaluy nyajil | 1sg.NOM spring here see.NPST", "r279_1sg_water_landform_here_see_jirka", ["ngayu", "jirka", "yaluy", "nyajil"], EVIDENCE["jirka"]),
    row(5, "I see the ground here.", "Ngayu bubu yaluy nyajil.", "ngayu bubu yaluy nyajil | 1sg.NOM ground here see.NPST", "r279_1sg_water_landform_here_see_bubu", ["ngayu", "bubu", "yaluy", "nyajil"], EVIDENCE["bubu"]),
    row(6, "I see the mountain here.", "Ngayu manjal yaluy nyajil.", "ngayu manjal yaluy nyajil | 1sg.NOM mountain here see.NPST", "r279_1sg_water_landform_here_see_manjal", ["ngayu", "manjal", "yaluy", "nyajil"], EVIDENCE["manjal"]),
    row(7, "I see the river here.", "Ngayu wawubaja yaluy nyajil.", "ngayu wawubaja yaluy nyajil | 1sg.NOM river here see.NPST", "r279_1sg_water_landform_here_see_wawubaja", ["ngayu", "wawubaja", "yaluy", "nyajil"], EVIDENCE["wawubaja"]),
    row(8, "I see the creek here.", "Ngayu yilki yaluy nyajil.", "ngayu yilki yaluy nyajil | 1sg.NOM creek here see.NPST", "r279_1sg_water_landform_here_see_yilki", ["ngayu", "yilki", "yaluy", "nyajil"], EVIDENCE["yilki"]),
    row(9, "I see the waterfall here.", "Ngayu yiri yaluy nyajil.", "ngayu yiri yaluy nyajil | 1sg.NOM waterfall here see.NPST", "r279_1sg_water_landform_here_see_yiri", ["ngayu", "yiri", "yaluy", "nyajil"], EVIDENCE["yiri"]),
    row(10, "I see the running water here.", "Ngayu yirri yaluy nyajil.", "ngayu yirri yaluy nyajil | 1sg.NOM running.water here see.NPST", "r279_1sg_water_landform_here_see_yirri", ["ngayu", "yirri", "yaluy", "nyajil"], EVIDENCE["yirri"]),
    row(11, "You see the water there.", "Yundu bana yinyay nyajil.", "yundu bana yinyay nyajil | 2sg.NOM water there see.NPST", "r279_2sg_water_landform_there_see_bana", ["yundu", "bana", "yinyay", "nyajil"], EVIDENCE["bana"]),
    row(12, "You see the deep water there.", "Yundu dajali yinyay nyajil.", "yundu dajali yinyay nyajil | 2sg.NOM deep.water there see.NPST", "r279_2sg_water_landform_there_see_dajali", ["yundu", "dajali", "yinyay", "nyajil"], EVIDENCE["dajali"]),
    row(13, "You see the sea there.", "Yundu jalun yinyay nyajil.", "yundu jalun yinyay nyajil | 2sg.NOM sea there see.NPST", "r279_2sg_water_landform_there_see_jalun", ["yundu", "jalun", "yinyay", "nyajil"], EVIDENCE["jalun"]),
    row(14, "You see the spring there.", "Yundu jirka yinyay nyajil.", "yundu jirka yinyay nyajil | 2sg.NOM spring there see.NPST", "r279_2sg_water_landform_there_see_jirka", ["yundu", "jirka", "yinyay", "nyajil"], EVIDENCE["jirka"]),
    row(15, "You see the ground there.", "Yundu bubu yinyay nyajil.", "yundu bubu yinyay nyajil | 2sg.NOM ground there see.NPST", "r279_2sg_water_landform_there_see_bubu", ["yundu", "bubu", "yinyay", "nyajil"], EVIDENCE["bubu"]),
    row(16, "You see the mountain there.", "Yundu manjal yinyay nyajil.", "yundu manjal yinyay nyajil | 2sg.NOM mountain there see.NPST", "r279_2sg_water_landform_there_see_manjal", ["yundu", "manjal", "yinyay", "nyajil"], EVIDENCE["manjal"]),
    row(17, "You see the river there.", "Yundu wawubaja yinyay nyajil.", "yundu wawubaja yinyay nyajil | 2sg.NOM river there see.NPST", "r279_2sg_water_landform_there_see_wawubaja", ["yundu", "wawubaja", "yinyay", "nyajil"], EVIDENCE["wawubaja"]),
    row(18, "You see the creek there.", "Yundu yilki yinyay nyajil.", "yundu yilki yinyay nyajil | 2sg.NOM creek there see.NPST", "r279_2sg_water_landform_there_see_yilki", ["yundu", "yilki", "yinyay", "nyajil"], EVIDENCE["yilki"]),
    row(19, "You see the waterfall there.", "Yundu yiri yinyay nyajil.", "yundu yiri yinyay nyajil | 2sg.NOM waterfall there see.NPST", "r279_2sg_water_landform_there_see_yiri", ["yundu", "yiri", "yinyay", "nyajil"], EVIDENCE["yiri"]),
    row(20, "You see the running water there.", "Yundu yirri yinyay nyajil.", "yundu yirri yinyay nyajil | 2sg.NOM running.water there see.NPST", "r279_2sg_water_landform_there_see_yirri", ["yundu", "yirri", "yinyay", "nyajil"], EVIDENCE["yirri"]),
    row(21, "He sees the water here.", "Nyulu bana yaluy nyajil.", "nyulu bana yaluy nyajil | 3sg.NOM water here see.NPST", "r279_3sg_water_landform_here_see_bana", ["nyulu", "bana", "yaluy", "nyajil"], EVIDENCE["bana"]),
    row(22, "He sees the deep water here.", "Nyulu dajali yaluy nyajil.", "nyulu dajali yaluy nyajil | 3sg.NOM deep.water here see.NPST", "r279_3sg_water_landform_here_see_dajali", ["nyulu", "dajali", "yaluy", "nyajil"], EVIDENCE["dajali"]),
    row(23, "He sees the sea here.", "Nyulu jalun yaluy nyajil.", "nyulu jalun yaluy nyajil | 3sg.NOM sea here see.NPST", "r279_3sg_water_landform_here_see_jalun", ["nyulu", "jalun", "yaluy", "nyajil"], EVIDENCE["jalun"]),
    row(24, "He sees the spring here.", "Nyulu jirka yaluy nyajil.", "nyulu jirka yaluy nyajil | 3sg.NOM spring here see.NPST", "r279_3sg_water_landform_here_see_jirka", ["nyulu", "jirka", "yaluy", "nyajil"], EVIDENCE["jirka"]),
    row(25, "He sees the ground here.", "Nyulu bubu yaluy nyajil.", "nyulu bubu yaluy nyajil | 3sg.NOM ground here see.NPST", "r279_3sg_water_landform_here_see_bubu", ["nyulu", "bubu", "yaluy", "nyajil"], EVIDENCE["bubu"]),
    row(26, "He sees the mountain here.", "Nyulu manjal yaluy nyajil.", "nyulu manjal yaluy nyajil | 3sg.NOM mountain here see.NPST", "r279_3sg_water_landform_here_see_manjal", ["nyulu", "manjal", "yaluy", "nyajil"], EVIDENCE["manjal"]),
    row(27, "He sees the river here.", "Nyulu wawubaja yaluy nyajil.", "nyulu wawubaja yaluy nyajil | 3sg.NOM river here see.NPST", "r279_3sg_water_landform_here_see_wawubaja", ["nyulu", "wawubaja", "yaluy", "nyajil"], EVIDENCE["wawubaja"]),
    row(28, "He sees the creek here.", "Nyulu yilki yaluy nyajil.", "nyulu yilki yaluy nyajil | 3sg.NOM creek here see.NPST", "r279_3sg_water_landform_here_see_yilki", ["nyulu", "yilki", "yaluy", "nyajil"], EVIDENCE["yilki"]),
    row(29, "He sees the waterfall here.", "Nyulu yiri yaluy nyajil.", "nyulu yiri yaluy nyajil | 3sg.NOM waterfall here see.NPST", "r279_3sg_water_landform_here_see_yiri", ["nyulu", "yiri", "yaluy", "nyajil"], EVIDENCE["yiri"]),
    row(30, "He sees the running water here.", "Nyulu yirri yaluy nyajil.", "nyulu yirri yaluy nyajil | 3sg.NOM running.water here see.NPST", "r279_3sg_water_landform_here_see_yirri", ["nyulu", "yirri", "yaluy", "nyajil"], EVIDENCE["yirri"]),
    row(31, "The two of them see the water there.", "Bula bana yinyay nyajil.", "bula bana yinyay nyajil | 3du.NOM water there see.NPST", "r279_3du_water_landform_there_see_bana", ["bula", "bana", "yinyay", "nyajil"], EVIDENCE["bana"]),
    row(32, "The two of them see the deep water there.", "Bula dajali yinyay nyajil.", "bula dajali yinyay nyajil | 3du.NOM deep.water there see.NPST", "r279_3du_water_landform_there_see_dajali", ["bula", "dajali", "yinyay", "nyajil"], EVIDENCE["dajali"]),
    row(33, "The two of them see the sea there.", "Bula jalun yinyay nyajil.", "bula jalun yinyay nyajil | 3du.NOM sea there see.NPST", "r279_3du_water_landform_there_see_jalun", ["bula", "jalun", "yinyay", "nyajil"], EVIDENCE["jalun"]),
    row(34, "The two of them see the spring there.", "Bula jirka yinyay nyajil.", "bula jirka yinyay nyajil | 3du.NOM spring there see.NPST", "r279_3du_water_landform_there_see_jirka", ["bula", "jirka", "yinyay", "nyajil"], EVIDENCE["jirka"]),
    row(35, "The two of them see the ground there.", "Bula bubu yinyay nyajil.", "bula bubu yinyay nyajil | 3du.NOM ground there see.NPST", "r279_3du_water_landform_there_see_bubu", ["bula", "bubu", "yinyay", "nyajil"], EVIDENCE["bubu"]),
    row(36, "The two of them see the mountain there.", "Bula manjal yinyay nyajil.", "bula manjal yinyay nyajil | 3du.NOM mountain there see.NPST", "r279_3du_water_landform_there_see_manjal", ["bula", "manjal", "yinyay", "nyajil"], EVIDENCE["manjal"]),
    row(37, "The two of them see the river there.", "Bula wawubaja yinyay nyajil.", "bula wawubaja yinyay nyajil | 3du.NOM river there see.NPST", "r279_3du_water_landform_there_see_wawubaja", ["bula", "wawubaja", "yinyay", "nyajil"], EVIDENCE["wawubaja"]),
    row(38, "The two of them see the creek there.", "Bula yilki yinyay nyajil.", "bula yilki yinyay nyajil | 3du.NOM creek there see.NPST", "r279_3du_water_landform_there_see_yilki", ["bula", "yilki", "yinyay", "nyajil"], EVIDENCE["yilki"]),
    row(39, "The two of them see the waterfall there.", "Bula yiri yinyay nyajil.", "bula yiri yinyay nyajil | 3du.NOM waterfall there see.NPST", "r279_3du_water_landform_there_see_yiri", ["bula", "yiri", "yinyay", "nyajil"], EVIDENCE["yiri"]),
    row(40, "The two of them see the running water there.", "Bula yirri yinyay nyajil.", "bula yirri yinyay nyajil | 3du.NOM running.water there see.NPST", "r279_3du_water_landform_there_see_yirri", ["bula", "yirri", "yinyay", "nyajil"], EVIDENCE["yirri"]),
    row(41, "We see the water there.", "Ngana bana yinyay nyajil.", "ngana bana yinyay nyajil | 1pl.INCL.NOM water there see.NPST", "r279_1pl_incl_water_landform_there_see_bana", ["ngana", "bana", "yinyay", "nyajil"], EVIDENCE["bana"]),
    row(42, "We see the deep water there.", "Ngana dajali yinyay nyajil.", "ngana dajali yinyay nyajil | 1pl.INCL.NOM deep.water there see.NPST", "r279_1pl_incl_water_landform_there_see_dajali", ["ngana", "dajali", "yinyay", "nyajil"], EVIDENCE["dajali"]),
    row(43, "We see the sea there.", "Ngana jalun yinyay nyajil.", "ngana jalun yinyay nyajil | 1pl.INCL.NOM sea there see.NPST", "r279_1pl_incl_water_landform_there_see_jalun", ["ngana", "jalun", "yinyay", "nyajil"], EVIDENCE["jalun"]),
    row(44, "We see the spring there.", "Ngana jirka yinyay nyajil.", "ngana jirka yinyay nyajil | 1pl.INCL.NOM spring there see.NPST", "r279_1pl_incl_water_landform_there_see_jirka", ["ngana", "jirka", "yinyay", "nyajil"], EVIDENCE["jirka"]),
    row(45, "We see the ground there.", "Ngana bubu yinyay nyajil.", "ngana bubu yinyay nyajil | 1pl.INCL.NOM ground there see.NPST", "r279_1pl_incl_water_landform_there_see_bubu", ["ngana", "bubu", "yinyay", "nyajil"], EVIDENCE["bubu"]),
    row(46, "We see the mountain there.", "Ngana manjal yinyay nyajil.", "ngana manjal yinyay nyajil | 1pl.INCL.NOM mountain there see.NPST", "r279_1pl_incl_water_landform_there_see_manjal", ["ngana", "manjal", "yinyay", "nyajil"], EVIDENCE["manjal"]),
    row(47, "We see the river there.", "Ngana wawubaja yinyay nyajil.", "ngana wawubaja yinyay nyajil | 1pl.INCL.NOM river there see.NPST", "r279_1pl_incl_water_landform_there_see_wawubaja", ["ngana", "wawubaja", "yinyay", "nyajil"], EVIDENCE["wawubaja"]),
    row(48, "We see the creek there.", "Ngana yilki yinyay nyajil.", "ngana yilki yinyay nyajil | 1pl.INCL.NOM creek there see.NPST", "r279_1pl_incl_water_landform_there_see_yilki", ["ngana", "yilki", "yinyay", "nyajil"], EVIDENCE["yilki"]),
    row(49, "We see the waterfall there.", "Ngana yiri yinyay nyajil.", "ngana yiri yinyay nyajil | 1pl.INCL.NOM waterfall there see.NPST", "r279_1pl_incl_water_landform_there_see_yiri", ["ngana", "yiri", "yinyay", "nyajil"], EVIDENCE["yiri"]),
    row(50, "We see the running water there.", "Ngana yirri yinyay nyajil.", "ngana yirri yinyay nyajil | 1pl.INCL.NOM running.water there see.NPST", "r279_1pl_incl_water_landform_there_see_yirri", ["ngana", "yirri", "yinyay", "nyajil"], EVIDENCE["yirri"]),
]


def has_token(low: str, token: str) -> bool:
    return re.search(rf"(?<![a-z-]){re.escape(token)}(?![a-z-])", low) is not None


def english_has_denied(english_low: str, denied: str) -> bool:
    return denied in english_low if " " in denied else re.search(rf"\b{re.escape(denied.strip())}\b", english_low) is not None


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"{LABEL}: expected 50 rows, found {len(ROWS)}")
    seen: set[str] = set()
    for expected, data in enumerate(ROWS, 1):
        seq, english, kuku, analysis, frame, _tier, words_used, loans_used, evidence, confidence = data
        label = f"{LABEL}.{seq:02d}"
        low = kuku.lower()
        english_low = english.lower()
        words = json.loads(words_used)
        if seq != expected:
            issues.append(f"{label}: sequence mismatch")
        if low in seen:
            issues.append(f"{label}: duplicate surface inside batch")
        seen.add(low)
        if not frame.startswith("r279_"):
            issues.append(f"{label}: frame must start with r279_")
        if " | " not in analysis or "see.NPST" not in analysis:
            issues.append(f"{label}: analysis missing gloss separator or see.NPST")
        if json.loads(loans_used):
            issues.append(f"{label}: loans_used must be empty")
        denied = [bad for bad in DENY_KUKU if has_token(low, bad)]
        if denied:
            issues.append(f"{label}: denied Kuku token(s) present: {denied}")
        targets = [head for head in TARGET_HEADS if has_token(low, head)]
        if len(targets) != 1:
            issues.append(f"{label}: expected one target noun, found {targets}")
        for head in targets:
            selected = TARGET_HEADS[head]
            dotted = selected.replace("-", ".").replace(" ", ".")
            if head not in words:
                issues.append(f"{label}: target head {head} missing from words_used")
            if selected not in english_low:
                issues.append(f"{label}: English must include selected sense {selected!r}")
            if selected not in analysis and dotted not in analysis:
                issues.append(f"{label}: analysis must gloss {head} as {selected}")
            if re.search(rf"(?<![a-z-]){re.escape(head)}-[a-z]+", low):
                issues.append(f"{label}: target head {head} has forbidden suffix/compound")
        pronouns = [pronoun for pronoun in PRONOUN_SUPPORT if has_token(low, pronoun)]
        if len(pronouns) != 1:
            issues.append(f"{label}: expected one selected bare nominative pronoun, found {pronouns}")
        for support in SUPPORT:
            if has_token(low, support) and support not in words:
                issues.append(f"{label}: support {support} missing from words_used")
        if " see " not in f" {english_low} " and " sees " not in f" {english_low} ":
            issues.append(f"{label}: English must use nonpast 'see/sees'")
        if "nyajil" not in words or not low.endswith(" nyajil."):
            issues.append(f"{label}: row must end with nyajil")
        if 1 <= seq <= 10 and (not low.startswith("ngayu ") or not low.endswith(" yaluy nyajil.")):
            issues.append(f"{label}: rows 1-10 require Ngayu TARGET yaluy nyajil")
        if 11 <= seq <= 20 and (not low.startswith("yundu ") or not low.endswith(" yinyay nyajil.")):
            issues.append(f"{label}: rows 11-20 require Yundu TARGET yinyay nyajil")
        if 21 <= seq <= 30 and (not low.startswith("nyulu ") or not low.endswith(" yaluy nyajil.")):
            issues.append(f"{label}: rows 21-30 require Nyulu TARGET yaluy nyajil")
        if 31 <= seq <= 40 and (not low.startswith("bula ") or not low.endswith(" yinyay nyajil.")):
            issues.append(f"{label}: rows 31-40 require Bula TARGET yinyay nyajil")
        if 41 <= seq <= 50 and (not low.startswith("ngana ") or not low.endswith(" yinyay nyajil.")):
            issues.append(f"{label}: rows 41-50 require Ngana TARGET yinyay nyajil")
        if len(low.split()) != 4:
            issues.append(f"{label}: expected four Kuku tokens")
        if any(english_has_denied(english_low, bad) for bad in DENY_ENGLISH):
            issues.append(f"{label}: denied water/landform source-prose or tense field in English")
        if not evidence or not confidence:
            issues.append(f"{label}: missing evidence/confidence")
    return issues


def count_token(db: sqlite3.Connection, token: str) -> int:
    rows = db.execute("SELECT kuku FROM sentences WHERE status IN ('accepted','revised')").fetchall()
    return sum(1 for (kuku,) in rows if has_token(kuku.lower(), token))


def main() -> int:
    issues = scan_static()
    if issues:
        print("pre-bank scan failed:")
        for issue in issues:
            print(f"  - {issue}")
        return 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")
    duplicates = []
    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 IN ('accepted','revised','under_review')
            """,
            (kuku,),
        ).fetchone()
        if hit:
            duplicates.append((kuku, hit))
    if duplicates:
        for kuku, hit in duplicates:
            print(f"duplicate: {kuku} -> {hit}")
        raise SystemExit(f"{LABEL}: duplicate preflight failed")

    target_counts = {head: count_token(db, head) for head in TARGET_ORDER}
    support_counts = {head: count_token(db, head) for head in ["ngayu", "yundu", "nyulu", "bula", "ngana", "yaluy", "yinyay", "nyajil"]}
    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 WATER/LANDFORM NONPAST PRONOUN PERCEPTION SWEEP (Rule 279). 50 under-review rows over "
        "bana water, dajali deep water, jalun sea, jirka spring, bubu ground/earth, manjal mountain, wawubaja river, "
        "yilki creek, yiri waterfall, and yirri running water with bare nominative ngayu, yundu, nyulu, bula, and "
        "ngana. Only bare visual reference/perception rows are opened; pronoun case, water use, travel, route, flood, "
        "tide, place names, source-use, bubu country/place extension, ecology, story, target case, demonstrative case, "
        "compounds, productive reduplication, perception compounds, past/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 279 under review", "operator-guide-2026-07-09", 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 dictionary source lines for water/landform targets including bubu ground/earth, and Patz pronoun, demonstrative, and nyaji-l nonpast support before authoring.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="debt-sweep-scope", actor="claude-main", detail="Selected a singular/third-dual/1pl-inclusive nonpast counterpart to Rule 276. Rejected first jukar candidate after a Batch 194 duplicate and replaced it with sourced bubu ground/earth.", 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: 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="Initial candidate found duplicate Ngana jukar yinyay nyajil. from Batch 194; final 50 Rule 279 surfaces with bubu 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 rows, one target each, bare nominative pronouns, bare yaluy/yinyay, final nyajil, no loans, no denied Kuku forms, no water-use/travel/place-name/bubu-country/event/source-prose drift.", command="python3 -m py_compile batch_scripts/bank_321.py && python3 - <<'PY' import bank_321; print(bank_321.scan_static()) PY", output_excerpt="issues 0", artifact_path="batch_scripts/bank_321.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="claude-main", detail=f"Banked 50 Rule 279 rows as under_review. Verified total remains {verified_before}; Kuku word count remains {words_before}.", command="python3 batch_scripts/bank_321.py", output_excerpt=f"banked batch-321: 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-321: 50 rows under_review")
    return 0


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