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

TARGET_HEADS = {
    "dajali": "deep water",
    "jalun": "sea",
    "jirka": "spring",
    "bubu": "ground",
    "wawubaja": "river",
    "daburri": "swamp",
    "dulngku": "reef",
    "yilki": "creek",
    "yiri": "waterfall",
    "yirri": "running water",
}
TARGET_ORDER = ["dajali", "jalun", "jirka", "bubu", "wawubaja", "daburri", "dulngku", "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", "nyajil"}
DENY_KUKU = {
    "bana", "manjal", "jukar", "banabila", "jalungkarr", "jirimandi", "marrka", "mukurr-mukurr",
    "yalkan", "yikin", "yirri-damal", "yiril", "yiman", "kalki", "kubirri", "bujung", "wawu-balangaji",
    "ngalingan", "ngalinyan", "yubalan", "yurrangan", "janangan", "ngalininy", "ngalinyiny",
    "yubaliny", "yurraniny", "jananiny", "ngalinka", "ngalinyanka", "yubalanka", "yurranka", "jananka",
    "ngalinda", "ngalinyanda", "yubalanda", "yurranda", "jananda", "ngalinji", "ngalinyanji",
    "yubalanji", "yurranji", "jananji", "ngalinga", "ngalinyanga", "yubalanga", "yurranga", "jananga",
    "nyajin", "nyaji-ny", "nyaji-nka", "nyaji-l-nyaji", "bubujida", "bubungu", "bubumuny", "bububa",
    "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", "fishing", "shell", "snake", "crocodile", "plant",
    "tree", "ecology", "danger", "navigation", "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 = {
    "dajali": "Dictionary lines 4510-4518 give dajali as noun 'deep water'. Rule 281 selects visible deep-water reference only. Patz lines 2577-2586 and 2592-2603 source bare non-singular nominative pronouns; lines 2674-2686 source non-singular person/number contrasts; lines 2838-2845 source yaluy/yinyay; and lines 3043-3049 source nyaji-l 'see' nonpast.",
    "jalun": "Dictionary lines 7612-7620 give jalun as noun 'sea, large body of water'. Rule 281 selects sea reference only and leaves flood, tide, route, and sea-use rows closed.",
    "jirka": "Dictionary lines 9803-9813 give jirka as noun 'spring' and commentary reads it as a water source rather than season. Rule 281 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 281 selects visible ground/earth only and leaves country/place/dirtiness extensions closed.",
    "wawubaja": "Dictionary lines 26813-26821 give wawubaja as noun 'river'. Rule 281 selects visible river reference only and leaves travel, route, crossing, bank, place-name, and water-use rows closed.",
    "daburri": "Dictionary lines 4431-4441 give daburri as noun 'swamp' and specifically brackish swamp water. Rule 281 selects visible swamp reference only.",
    "dulngku": "Dictionary lines 6521-6529 give dulngku as noun 'reef'. Rule 281 selects visible reef reference only.",
    "yilki": "Dictionary lines 30031-30039 give yilki as noun 'creek'. Rule 281 selects visible creek reference only.",
    "yiri": "Dictionary lines 30410-30418 give yiri as noun 'waterfall'. Rule 281 selects visible waterfall reference only.",
    "yirri": "Dictionary lines 30507-30515 give yirri as noun 'running water'. Rule 281 selects visible running-water reference only and leaves yirri-damal 'leak' closed.",
}

ROWS = [
    row(1, "We two (not you) see the deep water there.", "Ngali dajali yinyay nyajil.", "ngali dajali yinyay nyajil | 1du.EXCL.NOM deep.water there see.NPST", "r281_1du_excl_water_landform_there_see_dajali", ["ngali", "dajali", "yinyay", "nyajil"], EVIDENCE["dajali"]),
    row(2, "We two (not you) see the sea there.", "Ngali jalun yinyay nyajil.", "ngali jalun yinyay nyajil | 1du.EXCL.NOM sea there see.NPST", "r281_1du_excl_water_landform_there_see_jalun", ["ngali", "jalun", "yinyay", "nyajil"], EVIDENCE["jalun"]),
    row(3, "We two (not you) see the spring there.", "Ngali jirka yinyay nyajil.", "ngali jirka yinyay nyajil | 1du.EXCL.NOM spring there see.NPST", "r281_1du_excl_water_landform_there_see_jirka", ["ngali", "jirka", "yinyay", "nyajil"], EVIDENCE["jirka"]),
    row(4, "We two (not you) see the ground there.", "Ngali bubu yinyay nyajil.", "ngali bubu yinyay nyajil | 1du.EXCL.NOM ground there see.NPST", "r281_1du_excl_water_landform_there_see_bubu", ["ngali", "bubu", "yinyay", "nyajil"], EVIDENCE["bubu"]),
    row(5, "We two (not you) see the river there.", "Ngali wawubaja yinyay nyajil.", "ngali wawubaja yinyay nyajil | 1du.EXCL.NOM river there see.NPST", "r281_1du_excl_water_landform_there_see_wawubaja", ["ngali", "wawubaja", "yinyay", "nyajil"], EVIDENCE["wawubaja"]),
    row(6, "We two (not you) see the swamp there.", "Ngali daburri yinyay nyajil.", "ngali daburri yinyay nyajil | 1du.EXCL.NOM swamp there see.NPST", "r281_1du_excl_water_landform_there_see_daburri", ["ngali", "daburri", "yinyay", "nyajil"], EVIDENCE["daburri"]),
    row(7, "We two (not you) see the reef there.", "Ngali dulngku yinyay nyajil.", "ngali dulngku yinyay nyajil | 1du.EXCL.NOM reef there see.NPST", "r281_1du_excl_water_landform_there_see_dulngku", ["ngali", "dulngku", "yinyay", "nyajil"], EVIDENCE["dulngku"]),
    row(8, "We two (not you) see the creek there.", "Ngali yilki yinyay nyajil.", "ngali yilki yinyay nyajil | 1du.EXCL.NOM creek there see.NPST", "r281_1du_excl_water_landform_there_see_yilki", ["ngali", "yilki", "yinyay", "nyajil"], EVIDENCE["yilki"]),
    row(9, "We two (not you) see the waterfall there.", "Ngali yiri yinyay nyajil.", "ngali yiri yinyay nyajil | 1du.EXCL.NOM waterfall there see.NPST", "r281_1du_excl_water_landform_there_see_yiri", ["ngali", "yiri", "yinyay", "nyajil"], EVIDENCE["yiri"]),
    row(10, "We two (not you) see the running water there.", "Ngali yirri yinyay nyajil.", "ngali yirri yinyay nyajil | 1du.EXCL.NOM running.water there see.NPST", "r281_1du_excl_water_landform_there_see_yirri", ["ngali", "yirri", "yinyay", "nyajil"], EVIDENCE["yirri"]),
    row(11, "You and I see the deep water here.", "Ngaliny dajali yaluy nyajil.", "ngaliny dajali yaluy nyajil | 1du.INCL.NOM deep.water here see.NPST", "r281_1du_incl_water_landform_here_see_dajali", ["ngaliny", "dajali", "yaluy", "nyajil"], EVIDENCE["dajali"]),
    row(12, "You and I see the sea here.", "Ngaliny jalun yaluy nyajil.", "ngaliny jalun yaluy nyajil | 1du.INCL.NOM sea here see.NPST", "r281_1du_incl_water_landform_here_see_jalun", ["ngaliny", "jalun", "yaluy", "nyajil"], EVIDENCE["jalun"]),
    row(13, "You and I see the spring here.", "Ngaliny jirka yaluy nyajil.", "ngaliny jirka yaluy nyajil | 1du.INCL.NOM spring here see.NPST", "r281_1du_incl_water_landform_here_see_jirka", ["ngaliny", "jirka", "yaluy", "nyajil"], EVIDENCE["jirka"]),
    row(14, "You and I see the ground here.", "Ngaliny bubu yaluy nyajil.", "ngaliny bubu yaluy nyajil | 1du.INCL.NOM ground here see.NPST", "r281_1du_incl_water_landform_here_see_bubu", ["ngaliny", "bubu", "yaluy", "nyajil"], EVIDENCE["bubu"]),
    row(15, "You and I see the river here.", "Ngaliny wawubaja yaluy nyajil.", "ngaliny wawubaja yaluy nyajil | 1du.INCL.NOM river here see.NPST", "r281_1du_incl_water_landform_here_see_wawubaja", ["ngaliny", "wawubaja", "yaluy", "nyajil"], EVIDENCE["wawubaja"]),
    row(16, "You and I see the swamp here.", "Ngaliny daburri yaluy nyajil.", "ngaliny daburri yaluy nyajil | 1du.INCL.NOM swamp here see.NPST", "r281_1du_incl_water_landform_here_see_daburri", ["ngaliny", "daburri", "yaluy", "nyajil"], EVIDENCE["daburri"]),
    row(17, "You and I see the reef here.", "Ngaliny dulngku yaluy nyajil.", "ngaliny dulngku yaluy nyajil | 1du.INCL.NOM reef here see.NPST", "r281_1du_incl_water_landform_here_see_dulngku", ["ngaliny", "dulngku", "yaluy", "nyajil"], EVIDENCE["dulngku"]),
    row(18, "You and I see the creek here.", "Ngaliny yilki yaluy nyajil.", "ngaliny yilki yaluy nyajil | 1du.INCL.NOM creek here see.NPST", "r281_1du_incl_water_landform_here_see_yilki", ["ngaliny", "yilki", "yaluy", "nyajil"], EVIDENCE["yilki"]),
    row(19, "You and I see the waterfall here.", "Ngaliny yiri yaluy nyajil.", "ngaliny yiri yaluy nyajil | 1du.INCL.NOM waterfall here see.NPST", "r281_1du_incl_water_landform_here_see_yiri", ["ngaliny", "yiri", "yaluy", "nyajil"], EVIDENCE["yiri"]),
    row(20, "You and I see the running water here.", "Ngaliny yirri yaluy nyajil.", "ngaliny yirri yaluy nyajil | 1du.INCL.NOM running.water here see.NPST", "r281_1du_incl_water_landform_here_see_yirri", ["ngaliny", "yirri", "yaluy", "nyajil"], EVIDENCE["yirri"]),
    row(21, "You two see the deep water there.", "Yubal dajali yinyay nyajil.", "yubal dajali yinyay nyajil | 2du.NOM deep.water there see.NPST", "r281_2du_water_landform_there_see_dajali", ["yubal", "dajali", "yinyay", "nyajil"], EVIDENCE["dajali"]),
    row(22, "You two see the sea there.", "Yubal jalun yinyay nyajil.", "yubal jalun yinyay nyajil | 2du.NOM sea there see.NPST", "r281_2du_water_landform_there_see_jalun", ["yubal", "jalun", "yinyay", "nyajil"], EVIDENCE["jalun"]),
    row(23, "You two see the spring there.", "Yubal jirka yinyay nyajil.", "yubal jirka yinyay nyajil | 2du.NOM spring there see.NPST", "r281_2du_water_landform_there_see_jirka", ["yubal", "jirka", "yinyay", "nyajil"], EVIDENCE["jirka"]),
    row(24, "You two see the ground there.", "Yubal bubu yinyay nyajil.", "yubal bubu yinyay nyajil | 2du.NOM ground there see.NPST", "r281_2du_water_landform_there_see_bubu", ["yubal", "bubu", "yinyay", "nyajil"], EVIDENCE["bubu"]),
    row(25, "You two see the river there.", "Yubal wawubaja yinyay nyajil.", "yubal wawubaja yinyay nyajil | 2du.NOM river there see.NPST", "r281_2du_water_landform_there_see_wawubaja", ["yubal", "wawubaja", "yinyay", "nyajil"], EVIDENCE["wawubaja"]),
    row(26, "You two see the swamp there.", "Yubal daburri yinyay nyajil.", "yubal daburri yinyay nyajil | 2du.NOM swamp there see.NPST", "r281_2du_water_landform_there_see_daburri", ["yubal", "daburri", "yinyay", "nyajil"], EVIDENCE["daburri"]),
    row(27, "You two see the reef there.", "Yubal dulngku yinyay nyajil.", "yubal dulngku yinyay nyajil | 2du.NOM reef there see.NPST", "r281_2du_water_landform_there_see_dulngku", ["yubal", "dulngku", "yinyay", "nyajil"], EVIDENCE["dulngku"]),
    row(28, "You two see the creek there.", "Yubal yilki yinyay nyajil.", "yubal yilki yinyay nyajil | 2du.NOM creek there see.NPST", "r281_2du_water_landform_there_see_yilki", ["yubal", "yilki", "yinyay", "nyajil"], EVIDENCE["yilki"]),
    row(29, "You two see the waterfall there.", "Yubal yiri yinyay nyajil.", "yubal yiri yinyay nyajil | 2du.NOM waterfall there see.NPST", "r281_2du_water_landform_there_see_yiri", ["yubal", "yiri", "yinyay", "nyajil"], EVIDENCE["yiri"]),
    row(30, "You two see the running water there.", "Yubal yirri yinyay nyajil.", "yubal yirri yinyay nyajil | 2du.NOM running.water there see.NPST", "r281_2du_water_landform_there_see_yirri", ["yubal", "yirri", "yinyay", "nyajil"], EVIDENCE["yirri"]),
    row(31, "You all see the deep water here.", "Yurra dajali yaluy nyajil.", "yurra dajali yaluy nyajil | 2pl.NOM deep.water here see.NPST", "r281_2pl_water_landform_here_see_dajali", ["yurra", "dajali", "yaluy", "nyajil"], EVIDENCE["dajali"]),
    row(32, "You all see the sea here.", "Yurra jalun yaluy nyajil.", "yurra jalun yaluy nyajil | 2pl.NOM sea here see.NPST", "r281_2pl_water_landform_here_see_jalun", ["yurra", "jalun", "yaluy", "nyajil"], EVIDENCE["jalun"]),
    row(33, "You all see the spring here.", "Yurra jirka yaluy nyajil.", "yurra jirka yaluy nyajil | 2pl.NOM spring here see.NPST", "r281_2pl_water_landform_here_see_jirka", ["yurra", "jirka", "yaluy", "nyajil"], EVIDENCE["jirka"]),
    row(34, "You all see the ground here.", "Yurra bubu yaluy nyajil.", "yurra bubu yaluy nyajil | 2pl.NOM ground here see.NPST", "r281_2pl_water_landform_here_see_bubu", ["yurra", "bubu", "yaluy", "nyajil"], EVIDENCE["bubu"]),
    row(35, "You all see the river here.", "Yurra wawubaja yaluy nyajil.", "yurra wawubaja yaluy nyajil | 2pl.NOM river here see.NPST", "r281_2pl_water_landform_here_see_wawubaja", ["yurra", "wawubaja", "yaluy", "nyajil"], EVIDENCE["wawubaja"]),
    row(36, "You all see the swamp here.", "Yurra daburri yaluy nyajil.", "yurra daburri yaluy nyajil | 2pl.NOM swamp here see.NPST", "r281_2pl_water_landform_here_see_daburri", ["yurra", "daburri", "yaluy", "nyajil"], EVIDENCE["daburri"]),
    row(37, "You all see the reef here.", "Yurra dulngku yaluy nyajil.", "yurra dulngku yaluy nyajil | 2pl.NOM reef here see.NPST", "r281_2pl_water_landform_here_see_dulngku", ["yurra", "dulngku", "yaluy", "nyajil"], EVIDENCE["dulngku"]),
    row(38, "You all see the creek here.", "Yurra yilki yaluy nyajil.", "yurra yilki yaluy nyajil | 2pl.NOM creek here see.NPST", "r281_2pl_water_landform_here_see_yilki", ["yurra", "yilki", "yaluy", "nyajil"], EVIDENCE["yilki"]),
    row(39, "You all see the waterfall here.", "Yurra yiri yaluy nyajil.", "yurra yiri yaluy nyajil | 2pl.NOM waterfall here see.NPST", "r281_2pl_water_landform_here_see_yiri", ["yurra", "yiri", "yaluy", "nyajil"], EVIDENCE["yiri"]),
    row(40, "You all see the running water here.", "Yurra yirri yaluy nyajil.", "yurra yirri yaluy nyajil | 2pl.NOM running.water here see.NPST", "r281_2pl_water_landform_here_see_yirri", ["yurra", "yirri", "yaluy", "nyajil"], EVIDENCE["yirri"]),
    row(41, "They see the deep water there.", "Jana dajali yinyay nyajil.", "jana dajali yinyay nyajil | 3pl.NOM deep.water there see.NPST", "r281_3pl_water_landform_there_see_dajali", ["jana", "dajali", "yinyay", "nyajil"], EVIDENCE["dajali"]),
    row(42, "They see the sea there.", "Jana jalun yinyay nyajil.", "jana jalun yinyay nyajil | 3pl.NOM sea there see.NPST", "r281_3pl_water_landform_there_see_jalun", ["jana", "jalun", "yinyay", "nyajil"], EVIDENCE["jalun"]),
    row(43, "They see the spring there.", "Jana jirka yinyay nyajil.", "jana jirka yinyay nyajil | 3pl.NOM spring there see.NPST", "r281_3pl_water_landform_there_see_jirka", ["jana", "jirka", "yinyay", "nyajil"], EVIDENCE["jirka"]),
    row(44, "They see the ground there.", "Jana bubu yinyay nyajil.", "jana bubu yinyay nyajil | 3pl.NOM ground there see.NPST", "r281_3pl_water_landform_there_see_bubu", ["jana", "bubu", "yinyay", "nyajil"], EVIDENCE["bubu"]),
    row(45, "They see the river there.", "Jana wawubaja yinyay nyajil.", "jana wawubaja yinyay nyajil | 3pl.NOM river there see.NPST", "r281_3pl_water_landform_there_see_wawubaja", ["jana", "wawubaja", "yinyay", "nyajil"], EVIDENCE["wawubaja"]),
    row(46, "They see the swamp there.", "Jana daburri yinyay nyajil.", "jana daburri yinyay nyajil | 3pl.NOM swamp there see.NPST", "r281_3pl_water_landform_there_see_daburri", ["jana", "daburri", "yinyay", "nyajil"], EVIDENCE["daburri"]),
    row(47, "They see the reef there.", "Jana dulngku yinyay nyajil.", "jana dulngku yinyay nyajil | 3pl.NOM reef there see.NPST", "r281_3pl_water_landform_there_see_dulngku", ["jana", "dulngku", "yinyay", "nyajil"], EVIDENCE["dulngku"]),
    row(48, "They see the creek there.", "Jana yilki yinyay nyajil.", "jana yilki yinyay nyajil | 3pl.NOM creek there see.NPST", "r281_3pl_water_landform_there_see_yilki", ["jana", "yilki", "yinyay", "nyajil"], EVIDENCE["yilki"]),
    row(49, "They see the waterfall there.", "Jana yiri yinyay nyajil.", "jana yiri yinyay nyajil | 3pl.NOM waterfall there see.NPST", "r281_3pl_water_landform_there_see_yiri", ["jana", "yiri", "yinyay", "nyajil"], EVIDENCE["yiri"]),
    row(50, "They see the running water there.", "Jana yirri yinyay nyajil.", "jana yirri yinyay nyajil | 3pl.NOM running.water there see.NPST", "r281_3pl_water_landform_there_see_yirri", ["jana", "yirri", "yinyay", "nyajil"], EVIDENCE["yirri"]),
]


EXPECTED_BANDS = [
    ("ngali", "yinyay"),
    ("ngaliny", "yaluy"),
    ("yubal", "yinyay"),
    ("yurra", "yaluy"),
    ("jana", "yinyay"),
]


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"row count {len(ROWS)} != 50")
    if [r[0] for r in ROWS] != list(range(1, 51)):
        issues.append("sequence is not 1..50")
    kukus = [r[2] for r in ROWS]
    if len(set(kukus)) != len(kukus):
        issues.append("duplicate Kuku surface inside batch")
    for seq, english, kuku, analysis, frame, _tier, words_json, loans_json, _evidence, _confidence in ROWS:
        words = json.loads(words_json)
        loans = json.loads(loans_json)
        lower_kuku = kuku.lower()
        lower_english = english.lower()
        if loans:
            issues.append(f"{seq}: loans used {loans}")
        denied = sorted({w for w in DENY_KUKU if re.search(rf"(^|\\s){re.escape(w)}([\\s\\.]|$)", lower_kuku)})
        if denied:
            issues.append(f"{seq}: denied Kuku forms {denied}")
        denied_english = [
            term for term in DENY_ENGLISH
            if re.search(rf"\b{re.escape(term.strip())}\b", lower_english)
        ]
        if denied_english:
            issues.append(f"{seq}: denied English drift {denied_english}")
        target_hits = [w for w in TARGET_ORDER if w in words]
        support_hits = [w for w in SUPPORT if w in words]
        if len(target_hits) != 1:
            issues.append(f"{seq}: target hits {target_hits}")
        if len(words) != 4:
            issues.append(f"{seq}: expected 4 words, got {words}")
        if "nyajil" not in words or not lower_kuku.endswith(" nyajil."):
            issues.append(f"{seq}: missing final nyajil")
        if len([w for w in ("yaluy", "yinyay") if w in words]) != 1:
            issues.append(f"{seq}: demonstrative problem {words}")
        expected_pronoun, expected_demo = EXPECTED_BANDS[(seq - 1) // 10]
        if words[0] != expected_pronoun:
            issues.append(f"{seq}: expected pronoun {expected_pronoun}, got {words[0]}")
        if words[2] != expected_demo:
            issues.append(f"{seq}: expected demonstrative {expected_demo}, got {words[2]}")
        if words[1] not in TARGET_HEADS:
            issues.append(f"{seq}: word 2 is not a target {words[1]}")
        if words[3] != "nyajil":
            issues.append(f"{seq}: word 4 is not nyajil {words[3]}")
        if not frame.startswith("r281_"):
            issues.append(f"{seq}: frame not r281 {frame}")
        if not support_hits:
            issues.append(f"{seq}: no support hits")
    return issues


def count_words(db: sqlite3.Connection, words: list[str]) -> dict[str, int]:
    return {
        word: db.execute(
            """
            SELECT COUNT(*)
            FROM sentences s
            WHERE s.status IN ('accepted','revised')
              AND EXISTS (SELECT 1 FROM json_each(s.words_used) WHERE value=?)
            """,
            (word,),
        ).fetchone()[0]
        for word in words
    }


def main() -> int:
    issues = scan_static()
    if issues:
        for issue in issues:
            print(issue)
        raise SystemExit(1)

    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    db.execute("BEGIN")
    ensure_schema(db)
    if db.execute("SELECT 1 FROM batches WHERE label=?", (LABEL,)).fetchone():
        raise SystemExit(f"{LABEL} already exists")
    collisions = []
    for _seq, _english, kuku, *_rest in ROWS:
        existing = db.execute(
            """
            SELECT b.label, s.seq, s.status
            FROM sentences s
            JOIN batches b ON b.id=s.batch_id
            WHERE lower(s.kuku)=lower(?) AND s.status!='rejected'
            """,
            (kuku,),
        ).fetchall()
        if existing:
            collisions.append((kuku, existing))
    if collisions:
        raise SystemExit(f"duplicate preflight failed: {collisions}")

    target_counts = count_words(db, TARGET_ORDER)
    support_counts = count_words(db, ["ngali", "ngaliny", "yubal", "yurra", "jana", "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 NON-SINGULAR COMPLEMENTARY NONPAST PRONOUN PERCEPTION SWEEP (Rule 281). "
        "50 under-review rows over dajali deep water, jalun sea, jirka spring, bubu ground/earth, wawubaja river, "
        "daburri swamp, dulngku reef, yilki creek, yiri waterfall, and yirri running water with bare nominative "
        "ngali, ngaliny, yubal, yurra, and jana. Manjal was excluded after exact duplicate pressure. Only bare visual "
        "reference/perception rows are opened; pronoun case, water use, travel, route, flood, tide, place names, "
        "source-use, bubu country/place extension, wawubaja route/crossing/place use, daburri ecology/use, dulngku "
        "fishing/navigation, 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 281 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 final water/landform targets including replacement wawubaja, and Patz non-singular 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 complementary-deictic non-singular nonpast counterpart to Rule 278 after Rule 280. Initial target set with manjal was rejected after a Batch 194 duplicate; sourced wawubaja river replaced it.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="coverage-audit", actor="claude-main", detail=f"Token-aware accepted/revised coverage before banking from words_used JSON: targets={target_counts}; support={support_counts}.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="duplicate-preflight", actor="claude-main", detail="Initial candidate found duplicate Jana manjal yinyay nyajil. from Batch 194; final 50 Rule 281 surfaces with wawubaja 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 nyajil, no loans, no denied Kuku forms, no water-use/travel/place-name/wawubaja-route/bubu-country/daburri-ecology/dulngku-fishing drift.", command="python3 -m py_compile batch_scripts/bank_323.py && python3 -c \"import sys; sys.path.insert(0,'batch_scripts'); import bank_323; print(bank_323.scan_static())\"", output_excerpt="issues 0", artifact_path="batch_scripts/bank_323.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="claude-main", detail=f"Banked 50 Rule 281 rows as under_review. Verified total remains {verified_before}; Kuku word count remains {words_before}.", command="python3 batch_scripts/bank_323.py", output_excerpt=f"banked batch-323: 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-323: 50 rows under_review")
    return 0


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