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

TARGET_HEADS = {
    "bana": "water",
    "dajali": "deep water",
    "jalun": "sea",
    "jirka": "spring",
    "jukar": "sand",
    "manjal": "mountain",
    "wawubaja": "river",
    "yilki": "creek",
    "yiri": "waterfall",
    "yirri": "running water",
}
TARGET_ORDER = ["bana", "dajali", "jalun", "jirka", "jukar", "manjal", "wawubaja", "yilki", "yiri", "yirri"]
PRONOUN_SUPPORT = {
    "ngali": "1du.EXCL.NOM",
    "ngaliny": "1du.INCL.NOM",
    "yubal": "2du.NOM",
    "yurra": "2pl.NOM",
    "jana": "3pl.NOM",
}
SUPPORT = set(PRONOUN_SUPPORT) | {"yaluy", "yinyay", "nyajin"}
DENY_KUKU = {
    "banabila", "jalungkarr", "jirimandi", "marrka", "mukurr-mukurr", "yalkan", "yikin",
    "yirri-damal", "yiril", "yiman", "kalki", "kubirri", "bujung", "wawu-balangaji",
    "ngalingan", "ngalinyan", "yubalan", "bulangan", "ngalininy", "ngalinyiny", "yubaliny",
    "bulaniny", "ngalinka", "ngalinyanka", "yubalanka", "bulanka", "ngalinda", "ngalinyanda",
    "yubalanda", "bulanda", "ngalindamuny", "ngalinyandamuny", "yubalandamuny", "bulandamuny",
    "ngalinji", "ngalinyanji", "yubalanji", "bulanji", "ngalinga", "ngalinyanga", "yubalanga",
    "bulanga", "nganjinan", "nganangan", "yurrangan", "ianangan", "janangan", "nganjininy",
    "ngananiny", "yurraniny", "iananiny", "jananiny", "nganjinanka", "ngananka", "yurranka",
    "iananka", "jananka", "nganjinanda", "ngananda", "yurranda", "iananda", "jananda",
    "nganjinandamuny", "nganandamuny", "yurrandamuny", "ianandamuny", "janandamuny",
    "nganjinanji", "ngananji", "yurranji", "iananji", "jananji", "nganjinanga", "ngananga",
    "yurranga", "iananga", "jananga", "nyajil", "nyaji-nka", "nyaji-nyaku", "nyaji-l-nyaji",
    "nyajiny", "yinyangka", "yinyayngka", "yinyamba", "yinyamuny", "yalungku", "yaluyngku",
    "yaluymbu", "yaluyundu", "yalamuny", "yalanji",
}
DENY_ENGLISH = (
    "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", "story", "spirit",
    "fish", "shell", "snake", "crocodile", "plant", "tree", "ecology", "danger", "own", "owner",
    "source", "use", "uses", "used", "case", "compound", "watch", "look", "listening", "hearing",
    "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 277 selects visible water reference/perception only. Patz lines 2577-2586 and 2592-2603 source bare non-singular nominative pronouns; lines 2674 and 2682-2686 source inclusive/exclusive and non-singular forms; lines 2838-2845 source yaluy/yinyay; lines 2568-2571 give nyaji-ny see-PAST; and lines 3043-3049 source nyaji-l 'see'.",
    "dajali": "Dictionary lines 4510-4518 give dajali as noun 'deep water'. Rule 277 selects visible deep-water reference only.",
    "jalun": "Dictionary lines 7612-7620 give jalun as noun 'sea, large body of water'. Rule 277 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 277 selects visible water-source spring only, not season.",
    "jukar": "Dictionary lines 10254-10262 give jukar as noun 'sand'. Rule 277 selects visible sand reference only.",
    "manjal": "Dictionary lines 17832-17840 give manjal as noun 'mountain'. Rule 277 selects visible mountain reference only.",
    "wawubaja": "Dictionary lines 26813-26821 give wawubaja as noun 'river'. Rule 277 selects visible river reference only.",
    "yilki": "Dictionary lines 30031-30039 give yilki as noun 'creek'. Rule 277 selects visible creek reference only.",
    "yiri": "Dictionary lines 30410-30418 give yiri as noun 'waterfall'. Rule 277 selects visible waterfall reference only.",
    "yirri": "Dictionary lines 30507-30515 give yirri as noun 'running water'. Rule 277 selects visible running-water reference only and leaves yirri-damal 'leak' closed.",
}

ROWS = [
    row(1, "We two, not you, saw the water here.", "Ngali bana yaluy nyajin.", "ngali bana yaluy nyajin | 1du.EXCL.NOM water here see.PAST", "r277_1du_excl_water_landform_here_saw_bana", ["ngali", "bana", "yaluy", "nyajin"], EVIDENCE["bana"]),
    row(2, "We two, not you, saw the deep water here.", "Ngali dajali yaluy nyajin.", "ngali dajali yaluy nyajin | 1du.EXCL.NOM deep.water here see.PAST", "r277_1du_excl_water_landform_here_saw_dajali", ["ngali", "dajali", "yaluy", "nyajin"], EVIDENCE["dajali"]),
    row(3, "We two, not you, saw the sea here.", "Ngali jalun yaluy nyajin.", "ngali jalun yaluy nyajin | 1du.EXCL.NOM sea here see.PAST", "r277_1du_excl_water_landform_here_saw_jalun", ["ngali", "jalun", "yaluy", "nyajin"], EVIDENCE["jalun"]),
    row(4, "We two, not you, saw the spring here.", "Ngali jirka yaluy nyajin.", "ngali jirka yaluy nyajin | 1du.EXCL.NOM spring here see.PAST", "r277_1du_excl_water_landform_here_saw_jirka", ["ngali", "jirka", "yaluy", "nyajin"], EVIDENCE["jirka"]),
    row(5, "We two, not you, saw the sand here.", "Ngali jukar yaluy nyajin.", "ngali jukar yaluy nyajin | 1du.EXCL.NOM sand here see.PAST", "r277_1du_excl_water_landform_here_saw_jukar", ["ngali", "jukar", "yaluy", "nyajin"], EVIDENCE["jukar"]),
    row(6, "We two, not you, saw the mountain here.", "Ngali manjal yaluy nyajin.", "ngali manjal yaluy nyajin | 1du.EXCL.NOM mountain here see.PAST", "r277_1du_excl_water_landform_here_saw_manjal", ["ngali", "manjal", "yaluy", "nyajin"], EVIDENCE["manjal"]),
    row(7, "We two, not you, saw the river here.", "Ngali wawubaja yaluy nyajin.", "ngali wawubaja yaluy nyajin | 1du.EXCL.NOM river here see.PAST", "r277_1du_excl_water_landform_here_saw_wawubaja", ["ngali", "wawubaja", "yaluy", "nyajin"], EVIDENCE["wawubaja"]),
    row(8, "We two, not you, saw the creek here.", "Ngali yilki yaluy nyajin.", "ngali yilki yaluy nyajin | 1du.EXCL.NOM creek here see.PAST", "r277_1du_excl_water_landform_here_saw_yilki", ["ngali", "yilki", "yaluy", "nyajin"], EVIDENCE["yilki"]),
    row(9, "We two, not you, saw the waterfall here.", "Ngali yiri yaluy nyajin.", "ngali yiri yaluy nyajin | 1du.EXCL.NOM waterfall here see.PAST", "r277_1du_excl_water_landform_here_saw_yiri", ["ngali", "yiri", "yaluy", "nyajin"], EVIDENCE["yiri"]),
    row(10, "We two, not you, saw the running water here.", "Ngali yirri yaluy nyajin.", "ngali yirri yaluy nyajin | 1du.EXCL.NOM running.water here see.PAST", "r277_1du_excl_water_landform_here_saw_yirri", ["ngali", "yirri", "yaluy", "nyajin"], EVIDENCE["yirri"]),
    row(11, "You and I saw the water there.", "Ngaliny bana yinyay nyajin.", "ngaliny bana yinyay nyajin | 1du.INCL.NOM water there see.PAST", "r277_1du_incl_water_landform_there_saw_bana", ["ngaliny", "bana", "yinyay", "nyajin"], EVIDENCE["bana"]),
    row(12, "You and I saw the deep water there.", "Ngaliny dajali yinyay nyajin.", "ngaliny dajali yinyay nyajin | 1du.INCL.NOM deep.water there see.PAST", "r277_1du_incl_water_landform_there_saw_dajali", ["ngaliny", "dajali", "yinyay", "nyajin"], EVIDENCE["dajali"]),
    row(13, "You and I saw the sea there.", "Ngaliny jalun yinyay nyajin.", "ngaliny jalun yinyay nyajin | 1du.INCL.NOM sea there see.PAST", "r277_1du_incl_water_landform_there_saw_jalun", ["ngaliny", "jalun", "yinyay", "nyajin"], EVIDENCE["jalun"]),
    row(14, "You and I saw the spring there.", "Ngaliny jirka yinyay nyajin.", "ngaliny jirka yinyay nyajin | 1du.INCL.NOM spring there see.PAST", "r277_1du_incl_water_landform_there_saw_jirka", ["ngaliny", "jirka", "yinyay", "nyajin"], EVIDENCE["jirka"]),
    row(15, "You and I saw the sand there.", "Ngaliny jukar yinyay nyajin.", "ngaliny jukar yinyay nyajin | 1du.INCL.NOM sand there see.PAST", "r277_1du_incl_water_landform_there_saw_jukar", ["ngaliny", "jukar", "yinyay", "nyajin"], EVIDENCE["jukar"]),
    row(16, "You and I saw the mountain there.", "Ngaliny manjal yinyay nyajin.", "ngaliny manjal yinyay nyajin | 1du.INCL.NOM mountain there see.PAST", "r277_1du_incl_water_landform_there_saw_manjal", ["ngaliny", "manjal", "yinyay", "nyajin"], EVIDENCE["manjal"]),
    row(17, "You and I saw the river there.", "Ngaliny wawubaja yinyay nyajin.", "ngaliny wawubaja yinyay nyajin | 1du.INCL.NOM river there see.PAST", "r277_1du_incl_water_landform_there_saw_wawubaja", ["ngaliny", "wawubaja", "yinyay", "nyajin"], EVIDENCE["wawubaja"]),
    row(18, "You and I saw the creek there.", "Ngaliny yilki yinyay nyajin.", "ngaliny yilki yinyay nyajin | 1du.INCL.NOM creek there see.PAST", "r277_1du_incl_water_landform_there_saw_yilki", ["ngaliny", "yilki", "yinyay", "nyajin"], EVIDENCE["yilki"]),
    row(19, "You and I saw the waterfall there.", "Ngaliny yiri yinyay nyajin.", "ngaliny yiri yinyay nyajin | 1du.INCL.NOM waterfall there see.PAST", "r277_1du_incl_water_landform_there_saw_yiri", ["ngaliny", "yiri", "yinyay", "nyajin"], EVIDENCE["yiri"]),
    row(20, "You and I saw the running water there.", "Ngaliny yirri yinyay nyajin.", "ngaliny yirri yinyay nyajin | 1du.INCL.NOM running.water there see.PAST", "r277_1du_incl_water_landform_there_saw_yirri", ["ngaliny", "yirri", "yinyay", "nyajin"], EVIDENCE["yirri"]),
    row(21, "You two saw the water here.", "Yubal bana yaluy nyajin.", "yubal bana yaluy nyajin | 2du.NOM water here see.PAST", "r277_2du_water_landform_here_saw_bana", ["yubal", "bana", "yaluy", "nyajin"], EVIDENCE["bana"]),
    row(22, "You two saw the deep water here.", "Yubal dajali yaluy nyajin.", "yubal dajali yaluy nyajin | 2du.NOM deep.water here see.PAST", "r277_2du_water_landform_here_saw_dajali", ["yubal", "dajali", "yaluy", "nyajin"], EVIDENCE["dajali"]),
    row(23, "You two saw the sea here.", "Yubal jalun yaluy nyajin.", "yubal jalun yaluy nyajin | 2du.NOM sea here see.PAST", "r277_2du_water_landform_here_saw_jalun", ["yubal", "jalun", "yaluy", "nyajin"], EVIDENCE["jalun"]),
    row(24, "You two saw the spring here.", "Yubal jirka yaluy nyajin.", "yubal jirka yaluy nyajin | 2du.NOM spring here see.PAST", "r277_2du_water_landform_here_saw_jirka", ["yubal", "jirka", "yaluy", "nyajin"], EVIDENCE["jirka"]),
    row(25, "You two saw the sand here.", "Yubal jukar yaluy nyajin.", "yubal jukar yaluy nyajin | 2du.NOM sand here see.PAST", "r277_2du_water_landform_here_saw_jukar", ["yubal", "jukar", "yaluy", "nyajin"], EVIDENCE["jukar"]),
    row(26, "You two saw the mountain here.", "Yubal manjal yaluy nyajin.", "yubal manjal yaluy nyajin | 2du.NOM mountain here see.PAST", "r277_2du_water_landform_here_saw_manjal", ["yubal", "manjal", "yaluy", "nyajin"], EVIDENCE["manjal"]),
    row(27, "You two saw the river here.", "Yubal wawubaja yaluy nyajin.", "yubal wawubaja yaluy nyajin | 2du.NOM river here see.PAST", "r277_2du_water_landform_here_saw_wawubaja", ["yubal", "wawubaja", "yaluy", "nyajin"], EVIDENCE["wawubaja"]),
    row(28, "You two saw the creek here.", "Yubal yilki yaluy nyajin.", "yubal yilki yaluy nyajin | 2du.NOM creek here see.PAST", "r277_2du_water_landform_here_saw_yilki", ["yubal", "yilki", "yaluy", "nyajin"], EVIDENCE["yilki"]),
    row(29, "You two saw the waterfall here.", "Yubal yiri yaluy nyajin.", "yubal yiri yaluy nyajin | 2du.NOM waterfall here see.PAST", "r277_2du_water_landform_here_saw_yiri", ["yubal", "yiri", "yaluy", "nyajin"], EVIDENCE["yiri"]),
    row(30, "You two saw the running water here.", "Yubal yirri yaluy nyajin.", "yubal yirri yaluy nyajin | 2du.NOM running.water here see.PAST", "r277_2du_water_landform_here_saw_yirri", ["yubal", "yirri", "yaluy", "nyajin"], EVIDENCE["yirri"]),
    row(31, "You all saw the water there.", "Yurra bana yinyay nyajin.", "yurra bana yinyay nyajin | 2pl.NOM water there see.PAST", "r277_2pl_water_landform_there_saw_bana", ["yurra", "bana", "yinyay", "nyajin"], EVIDENCE["bana"]),
    row(32, "You all saw the deep water there.", "Yurra dajali yinyay nyajin.", "yurra dajali yinyay nyajin | 2pl.NOM deep.water there see.PAST", "r277_2pl_water_landform_there_saw_dajali", ["yurra", "dajali", "yinyay", "nyajin"], EVIDENCE["dajali"]),
    row(33, "You all saw the sea there.", "Yurra jalun yinyay nyajin.", "yurra jalun yinyay nyajin | 2pl.NOM sea there see.PAST", "r277_2pl_water_landform_there_saw_jalun", ["yurra", "jalun", "yinyay", "nyajin"], EVIDENCE["jalun"]),
    row(34, "You all saw the spring there.", "Yurra jirka yinyay nyajin.", "yurra jirka yinyay nyajin | 2pl.NOM spring there see.PAST", "r277_2pl_water_landform_there_saw_jirka", ["yurra", "jirka", "yinyay", "nyajin"], EVIDENCE["jirka"]),
    row(35, "You all saw the sand there.", "Yurra jukar yinyay nyajin.", "yurra jukar yinyay nyajin | 2pl.NOM sand there see.PAST", "r277_2pl_water_landform_there_saw_jukar", ["yurra", "jukar", "yinyay", "nyajin"], EVIDENCE["jukar"]),
    row(36, "You all saw the mountain there.", "Yurra manjal yinyay nyajin.", "yurra manjal yinyay nyajin | 2pl.NOM mountain there see.PAST", "r277_2pl_water_landform_there_saw_manjal", ["yurra", "manjal", "yinyay", "nyajin"], EVIDENCE["manjal"]),
    row(37, "You all saw the river there.", "Yurra wawubaja yinyay nyajin.", "yurra wawubaja yinyay nyajin | 2pl.NOM river there see.PAST", "r277_2pl_water_landform_there_saw_wawubaja", ["yurra", "wawubaja", "yinyay", "nyajin"], EVIDENCE["wawubaja"]),
    row(38, "You all saw the creek there.", "Yurra yilki yinyay nyajin.", "yurra yilki yinyay nyajin | 2pl.NOM creek there see.PAST", "r277_2pl_water_landform_there_saw_yilki", ["yurra", "yilki", "yinyay", "nyajin"], EVIDENCE["yilki"]),
    row(39, "You all saw the waterfall there.", "Yurra yiri yinyay nyajin.", "yurra yiri yinyay nyajin | 2pl.NOM waterfall there see.PAST", "r277_2pl_water_landform_there_saw_yiri", ["yurra", "yiri", "yinyay", "nyajin"], EVIDENCE["yiri"]),
    row(40, "You all saw the running water there.", "Yurra yirri yinyay nyajin.", "yurra yirri yinyay nyajin | 2pl.NOM running.water there see.PAST", "r277_2pl_water_landform_there_saw_yirri", ["yurra", "yirri", "yinyay", "nyajin"], EVIDENCE["yirri"]),
    row(41, "They saw the water here.", "Jana bana yaluy nyajin.", "jana bana yaluy nyajin | 3pl.NOM water here see.PAST", "r277_3pl_water_landform_here_saw_bana", ["jana", "bana", "yaluy", "nyajin"], EVIDENCE["bana"]),
    row(42, "They saw the deep water here.", "Jana dajali yaluy nyajin.", "jana dajali yaluy nyajin | 3pl.NOM deep.water here see.PAST", "r277_3pl_water_landform_here_saw_dajali", ["jana", "dajali", "yaluy", "nyajin"], EVIDENCE["dajali"]),
    row(43, "They saw the sea here.", "Jana jalun yaluy nyajin.", "jana jalun yaluy nyajin | 3pl.NOM sea here see.PAST", "r277_3pl_water_landform_here_saw_jalun", ["jana", "jalun", "yaluy", "nyajin"], EVIDENCE["jalun"]),
    row(44, "They saw the spring here.", "Jana jirka yaluy nyajin.", "jana jirka yaluy nyajin | 3pl.NOM spring here see.PAST", "r277_3pl_water_landform_here_saw_jirka", ["jana", "jirka", "yaluy", "nyajin"], EVIDENCE["jirka"]),
    row(45, "They saw the sand here.", "Jana jukar yaluy nyajin.", "jana jukar yaluy nyajin | 3pl.NOM sand here see.PAST", "r277_3pl_water_landform_here_saw_jukar", ["jana", "jukar", "yaluy", "nyajin"], EVIDENCE["jukar"]),
    row(46, "They saw the mountain here.", "Jana manjal yaluy nyajin.", "jana manjal yaluy nyajin | 3pl.NOM mountain here see.PAST", "r277_3pl_water_landform_here_saw_manjal", ["jana", "manjal", "yaluy", "nyajin"], EVIDENCE["manjal"]),
    row(47, "They saw the river here.", "Jana wawubaja yaluy nyajin.", "jana wawubaja yaluy nyajin | 3pl.NOM river here see.PAST", "r277_3pl_water_landform_here_saw_wawubaja", ["jana", "wawubaja", "yaluy", "nyajin"], EVIDENCE["wawubaja"]),
    row(48, "They saw the creek here.", "Jana yilki yaluy nyajin.", "jana yilki yaluy nyajin | 3pl.NOM creek here see.PAST", "r277_3pl_water_landform_here_saw_yilki", ["jana", "yilki", "yaluy", "nyajin"], EVIDENCE["yilki"]),
    row(49, "They saw the waterfall here.", "Jana yiri yaluy nyajin.", "jana yiri yaluy nyajin | 3pl.NOM waterfall here see.PAST", "r277_3pl_water_landform_here_saw_yiri", ["jana", "yiri", "yaluy", "nyajin"], EVIDENCE["yiri"]),
    row(50, "They saw the running water here.", "Jana yirri yaluy nyajin.", "jana yirri yaluy nyajin | 3pl.NOM running.water here see.PAST", "r277_3pl_water_landform_here_saw_yirri", ["jana", "yirri", "yaluy", "nyajin"], 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("r277_"):
            issues.append(f"{label}: frame must start with r277_")
        if " | " not in analysis or "see.PAST" not in analysis:
            issues.append(f"{label}: analysis missing gloss separator or see.PAST")
        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 " saw " not in f" {english_low} ":
            issues.append(f"{label}: English must use past 'saw'")
        if "nyajin" not in words or not low.endswith(" nyajin."):
            issues.append(f"{label}: row must end with nyajin")
        if 1 <= seq <= 10 and (not low.startswith("ngali ") or not low.endswith(" yaluy nyajin.")):
            issues.append(f"{label}: rows 1-10 require Ngali TARGET yaluy nyajin")
        if 11 <= seq <= 20 and (not low.startswith("ngaliny ") or not low.endswith(" yinyay nyajin.")):
            issues.append(f"{label}: rows 11-20 require Ngaliny TARGET yinyay nyajin")
        if 21 <= seq <= 30 and (not low.startswith("yubal ") or not low.endswith(" yaluy nyajin.")):
            issues.append(f"{label}: rows 21-30 require Yubal TARGET yaluy nyajin")
        if 31 <= seq <= 40 and (not low.startswith("yurra ") or not low.endswith(" yinyay nyajin.")):
            issues.append(f"{label}: rows 31-40 require Yurra TARGET yinyay nyajin")
        if 41 <= seq <= 50 and (not low.startswith("jana ") or not low.endswith(" yaluy nyajin.")):
            issues.append(f"{label}: rows 41-50 require Jana TARGET yaluy nyajin")
        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 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 ["ngali", "ngaliny", "yubal", "yurra", "jana", "yaluy", "yinyay", "nyajin"]}
    verified_before, words_before = db.execute(
        """
        SELECT COUNT(*), SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1)
        FROM sentences
        WHERE status IN ('accepted','revised')
        """
    ).fetchone()

    notes = (
        "VISIBLE WATER/LANDFORM NON-SINGULAR PAST PRONOUN PERCEPTION SWEEP (Rule 277). 50 under-review rows over "
        "bana water, dajali deep water, jalun sea, jirka spring, jukar sand, manjal mountain, wawubaja river, yilki "
        "creek, yiri waterfall, and yirri running water with bare nominative ngali, ngaliny, yubal, yurra, and jana. "
        "Only bare visual reference/perception rows are opened; pronoun case, water use, travel, route, flood, tide, "
        "place names, source-use, ecology, story, target case, demonstrative case, compounds, productive reduplication, "
        "perception compounds, and support inflection remain closed."
    )
    cur = db.execute(
        "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
        (LABEL, "Rule 277 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="Retained Rule 276 dictionary source pass for water/landform targets and reread Patz non-singular pronoun, demonstrative, and nyaji-l/nyaji-ny 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 complementary non-singular-pronoun sweep over the visible water/landform targets; no water-use, travel, route, place-name, flood/tide, ecology, or event prose opened.", 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="Exact duplicate preflight over the final 50 Rule 277 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 rows, one target each, bare non-singular nominative pronouns, bare yaluy/yinyay, final nyajin, no loans, no denied Kuku forms, no water-use/travel/place-name/event/source-prose drift.", command="python3 -m py_compile batch_scripts/bank_319.py && python3 - <<'PY' import bank_319; print(bank_319.scan_static()) PY", output_excerpt="issues 0", artifact_path="batch_scripts/bank_319.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="claude-main", detail=f"Banked 50 Rule 277 rows as under_review. Verified total remains {verified_before}; Kuku word count remains {words_before}.", command="python3 batch_scripts/bank_319.py", output_excerpt=f"banked batch-319: 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-319: 50 rows under_review")
    return 0


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