#!/usr/bin/env python3
"""Bank batch-300: Rule 258 complementary shellfish/crustacean perception sweep."""

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-300"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-300-shellfish-complementary-perception-debt-sweep-after-rule257-process.md"

TARGET_HEADS = {
    "baymbay": "large mud clam",
    "darkay": "clam",
    "kaduy": "snail",
    "karraway": "karraway snail",
    "karruku": "ghost crab",
    "kulil": "jellyfish",
    "kuyalin": "small conical shellfish",
    "marrkabina": "oyster",
    "ngarrakaja": "shellfish",
    "nuwi": "squid",
}
SUPPORT = {"ngayu", "nyulu", "yurra", "bula", "jana", "yaluy", "yinyay", "nyajil"}
PRONOUN_SUPPORT = {"ngayu", "nyulu", "yurra", "bula", "jana"}
DENY_KUKU = {
    "balungkul", "juwarru", "marakal", "marku", "marubu", "munji",
    "ngulumuku", "wukuju", "wuybur", "yulba", "kiju", "dakaldakal",
    "kajarrbi", "kunkun", "kurrija", "ngajay", "nikar", "wakal",
    "wuli-wuli", "wurrul", "jirrum", "mili", "jalan", "julul",
    "mukirr", "ngalngal", "wurru-bulan",
    "mayi", "ngumbu", "yilbal", "bana", "kuyu", "bayka-l", "baykal",
    "jindal", "wunba", "dukunjaka", "bayan",
    "nganya", "nyunguny", "yurrangan", "yurraniny", "yurranka", "yurranda",
    "yurranji", "yurranga", "bulangan", "bulaniny", "bulanka", "bulanda",
    "bulanji", "bulanga", "ianangan", "iananiny", "iananka", "iananda",
    "iananji", "iananga", "yinyangka", "yinyayngka", "yinyayanka",
    "yinyamba", "yinyayanda", "yinyamuny", "yinyamundu", "yinyanji",
    "yinyarriny", "yalungku", "yaluyngku", "yaluyunku", "yaluymbu",
    "yaluyundu", "yalamuny", "yalamundu", "yalanji", "nyaji-nka",
    "nyaji-nyaku", "nyaji-l-nyaji",
    "balarr", "dijaraban", "kalburiji", "mawal", "munudumbun", "munyurri",
    "nakawarra", "ngalmnbu", "yarrka", "yiljun", "bala", "darrba",
    "jarrabina", "jayju", "jungarr", "kukur", "mali", "murramu",
    "ngumbi", "yawa", "bajuy", "bilngkumu", "binanjal", "bindimu",
    "bubal", "jijirrbay", "kabul", "kalajarr", "kundurr", "yuday",
    "kubija", "mujarrka", "barrbal", "wuyumbu", "bayil", "dalmbal",
    "balarri", "bilmbin", "jalkay", "jijirr", "birmba", "jamarbina",
}
DENY_ENGLISH = (
    "edible", "food", "eat", "eating", "bait", "use", "used", "uses",
    "cooking", "cook", "fry", "frying", "gather", "gathering", "collect",
    "collecting", "catch", "catching", "fishing", "spear", "spearing",
    "net", "netting", "sea", "reef", "rock", "night", "nocturnal",
    "habitat", "water", "body", "sting", "stinging", "burn", "burning",
    "ink", "throw", "throws", "movement", "danger", "dangerous", "event",
    "species", "fiddler", "synonym", "case", "compound", "reduplication",
    "support inflection", "plural", "many",
)


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 = {
    "baymbay": "Dictionary lines 2122-2130 give baymbay as noun 'large mud clam', semantic domain fauna-shellfish-crustacean. Patz grammar lines 2553-2603 source bare nominative pronouns, lines 2838-2845 source yaluy/yinyay, and lines 3043-3049 source nyaji-l 'see'.",
    "darkay": "Dictionary lines 5280-5289 give darkay as noun 'clam', definition small mud clam, semantic domain fauna-shellfish-crustacean; Rule 258 selects clam only. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "kaduy": "Dictionary lines 11964-11974 give kaduy as noun 'snail', with night/nocturnal source prose closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "karraway": "Dictionary lines 13592-13601 give karraway as noun 'edible snail' with translation snail; Rule 258 selects karraway snail only and leaves edible/food prose closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "karruku": "Dictionary lines 13701-13709 give karruku as noun 'ghost crab, fiddler crab'; Rule 258 selects ghost crab only and leaves fiddler-crab expansion closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "kulil": "Dictionary lines 15209-15220 give kulil as noun 'jellyfish', synonyms mili and wuybur; Rule 258 selects kulil only and leaves synonyms/sting/burn/danger rows closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "kuyalin": "Dictionary lines 16857-16866 give kuyalin as noun 'small conical shellfish', definition includes bait; Rule 258 selects small conical shellfish only and leaves bait/use rows closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "marrkabina": "Dictionary lines 18639-18647 give marrkabina as noun 'oyster', definition edible oyster; Rule 258 selects oyster only and leaves edible/food prose closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "ngarrakaja": "Dictionary lines 22791-22801 give ngarrakaja as noun 'shell fish', with edible/species prose closed; Rule 258 selects shellfish only. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "nuwi": "Dictionary lines 24261-24271 give nuwi as noun 'squid' with an ink-throwing source usage; Rule 258 selects squid only and leaves action/event rows closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
}


ROWS = [
    row(1, "I see the large mud clam there.", "Ngayu baymbay yinyay nyajil.", "ngayu baymbay yinyay nyajil | 1sg.NOM large.mud.clam there see.NPST", "r258_1sg_target_there_see_baymbay", ["ngayu", "baymbay", "yinyay", "nyajil"], EVIDENCE["baymbay"]),
    row(2, "I see the clam there.", "Ngayu darkay yinyay nyajil.", "ngayu darkay yinyay nyajil | 1sg.NOM clam there see.NPST", "r258_1sg_target_there_see_darkay", ["ngayu", "darkay", "yinyay", "nyajil"], EVIDENCE["darkay"]),
    row(3, "I see the snail there.", "Ngayu kaduy yinyay nyajil.", "ngayu kaduy yinyay nyajil | 1sg.NOM snail there see.NPST", "r258_1sg_target_there_see_kaduy", ["ngayu", "kaduy", "yinyay", "nyajil"], EVIDENCE["kaduy"]),
    row(4, "I see the karraway snail there.", "Ngayu karraway yinyay nyajil.", "ngayu karraway yinyay nyajil | 1sg.NOM karraway.snail there see.NPST", "r258_1sg_target_there_see_karraway", ["ngayu", "karraway", "yinyay", "nyajil"], EVIDENCE["karraway"]),
    row(5, "I see the ghost crab there.", "Ngayu karruku yinyay nyajil.", "ngayu karruku yinyay nyajil | 1sg.NOM ghost.crab there see.NPST", "r258_1sg_target_there_see_karruku", ["ngayu", "karruku", "yinyay", "nyajil"], EVIDENCE["karruku"]),
    row(6, "I see the jellyfish there.", "Ngayu kulil yinyay nyajil.", "ngayu kulil yinyay nyajil | 1sg.NOM jellyfish there see.NPST", "r258_1sg_target_there_see_kulil", ["ngayu", "kulil", "yinyay", "nyajil"], EVIDENCE["kulil"]),
    row(7, "I see the small conical shellfish there.", "Ngayu kuyalin yinyay nyajil.", "ngayu kuyalin yinyay nyajil | 1sg.NOM small.conical.shellfish there see.NPST", "r258_1sg_target_there_see_kuyalin", ["ngayu", "kuyalin", "yinyay", "nyajil"], EVIDENCE["kuyalin"]),
    row(8, "I see the oyster there.", "Ngayu marrkabina yinyay nyajil.", "ngayu marrkabina yinyay nyajil | 1sg.NOM oyster there see.NPST", "r258_1sg_target_there_see_marrkabina", ["ngayu", "marrkabina", "yinyay", "nyajil"], EVIDENCE["marrkabina"]),
    row(9, "I see the shellfish there.", "Ngayu ngarrakaja yinyay nyajil.", "ngayu ngarrakaja yinyay nyajil | 1sg.NOM shellfish there see.NPST", "r258_1sg_target_there_see_ngarrakaja", ["ngayu", "ngarrakaja", "yinyay", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(10, "I see the squid there.", "Ngayu nuwi yinyay nyajil.", "ngayu nuwi yinyay nyajil | 1sg.NOM squid there see.NPST", "r258_1sg_target_there_see_nuwi", ["ngayu", "nuwi", "yinyay", "nyajil"], EVIDENCE["nuwi"]),
    row(11, "He sees the large mud clam here.", "Nyulu baymbay yaluy nyajil.", "nyulu baymbay yaluy nyajil | 3sg.NOM large.mud.clam here see.NPST", "r258_3sg_target_here_see_baymbay", ["nyulu", "baymbay", "yaluy", "nyajil"], EVIDENCE["baymbay"]),
    row(12, "He sees the clam here.", "Nyulu darkay yaluy nyajil.", "nyulu darkay yaluy nyajil | 3sg.NOM clam here see.NPST", "r258_3sg_target_here_see_darkay", ["nyulu", "darkay", "yaluy", "nyajil"], EVIDENCE["darkay"]),
    row(13, "He sees the snail here.", "Nyulu kaduy yaluy nyajil.", "nyulu kaduy yaluy nyajil | 3sg.NOM snail here see.NPST", "r258_3sg_target_here_see_kaduy", ["nyulu", "kaduy", "yaluy", "nyajil"], EVIDENCE["kaduy"]),
    row(14, "He sees the karraway snail here.", "Nyulu karraway yaluy nyajil.", "nyulu karraway yaluy nyajil | 3sg.NOM karraway.snail here see.NPST", "r258_3sg_target_here_see_karraway", ["nyulu", "karraway", "yaluy", "nyajil"], EVIDENCE["karraway"]),
    row(15, "He sees the ghost crab here.", "Nyulu karruku yaluy nyajil.", "nyulu karruku yaluy nyajil | 3sg.NOM ghost.crab here see.NPST", "r258_3sg_target_here_see_karruku", ["nyulu", "karruku", "yaluy", "nyajil"], EVIDENCE["karruku"]),
    row(16, "He sees the jellyfish here.", "Nyulu kulil yaluy nyajil.", "nyulu kulil yaluy nyajil | 3sg.NOM jellyfish here see.NPST", "r258_3sg_target_here_see_kulil", ["nyulu", "kulil", "yaluy", "nyajil"], EVIDENCE["kulil"]),
    row(17, "He sees the small conical shellfish here.", "Nyulu kuyalin yaluy nyajil.", "nyulu kuyalin yaluy nyajil | 3sg.NOM small.conical.shellfish here see.NPST", "r258_3sg_target_here_see_kuyalin", ["nyulu", "kuyalin", "yaluy", "nyajil"], EVIDENCE["kuyalin"]),
    row(18, "He sees the oyster here.", "Nyulu marrkabina yaluy nyajil.", "nyulu marrkabina yaluy nyajil | 3sg.NOM oyster here see.NPST", "r258_3sg_target_here_see_marrkabina", ["nyulu", "marrkabina", "yaluy", "nyajil"], EVIDENCE["marrkabina"]),
    row(19, "He sees the shellfish here.", "Nyulu ngarrakaja yaluy nyajil.", "nyulu ngarrakaja yaluy nyajil | 3sg.NOM shellfish here see.NPST", "r258_3sg_target_here_see_ngarrakaja", ["nyulu", "ngarrakaja", "yaluy", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(20, "He sees the squid here.", "Nyulu nuwi yaluy nyajil.", "nyulu nuwi yaluy nyajil | 3sg.NOM squid here see.NPST", "r258_3sg_target_here_see_nuwi", ["nyulu", "nuwi", "yaluy", "nyajil"], EVIDENCE["nuwi"]),
    row(21, "You all see the large mud clam there.", "Yurra baymbay yinyay nyajil.", "yurra baymbay yinyay nyajil | 2pl.NOM large.mud.clam there see.NPST", "r258_2pl_target_there_see_baymbay", ["yurra", "baymbay", "yinyay", "nyajil"], EVIDENCE["baymbay"]),
    row(22, "You all see the clam there.", "Yurra darkay yinyay nyajil.", "yurra darkay yinyay nyajil | 2pl.NOM clam there see.NPST", "r258_2pl_target_there_see_darkay", ["yurra", "darkay", "yinyay", "nyajil"], EVIDENCE["darkay"]),
    row(23, "You all see the snail there.", "Yurra kaduy yinyay nyajil.", "yurra kaduy yinyay nyajil | 2pl.NOM snail there see.NPST", "r258_2pl_target_there_see_kaduy", ["yurra", "kaduy", "yinyay", "nyajil"], EVIDENCE["kaduy"]),
    row(24, "You all see the karraway snail there.", "Yurra karraway yinyay nyajil.", "yurra karraway yinyay nyajil | 2pl.NOM karraway.snail there see.NPST", "r258_2pl_target_there_see_karraway", ["yurra", "karraway", "yinyay", "nyajil"], EVIDENCE["karraway"]),
    row(25, "You all see the ghost crab there.", "Yurra karruku yinyay nyajil.", "yurra karruku yinyay nyajil | 2pl.NOM ghost.crab there see.NPST", "r258_2pl_target_there_see_karruku", ["yurra", "karruku", "yinyay", "nyajil"], EVIDENCE["karruku"]),
    row(26, "You all see the jellyfish there.", "Yurra kulil yinyay nyajil.", "yurra kulil yinyay nyajil | 2pl.NOM jellyfish there see.NPST", "r258_2pl_target_there_see_kulil", ["yurra", "kulil", "yinyay", "nyajil"], EVIDENCE["kulil"]),
    row(27, "You all see the small conical shellfish there.", "Yurra kuyalin yinyay nyajil.", "yurra kuyalin yinyay nyajil | 2pl.NOM small.conical.shellfish there see.NPST", "r258_2pl_target_there_see_kuyalin", ["yurra", "kuyalin", "yinyay", "nyajil"], EVIDENCE["kuyalin"]),
    row(28, "You all see the oyster there.", "Yurra marrkabina yinyay nyajil.", "yurra marrkabina yinyay nyajil | 2pl.NOM oyster there see.NPST", "r258_2pl_target_there_see_marrkabina", ["yurra", "marrkabina", "yinyay", "nyajil"], EVIDENCE["marrkabina"]),
    row(29, "You all see the shellfish there.", "Yurra ngarrakaja yinyay nyajil.", "yurra ngarrakaja yinyay nyajil | 2pl.NOM shellfish there see.NPST", "r258_2pl_target_there_see_ngarrakaja", ["yurra", "ngarrakaja", "yinyay", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(30, "You all see the squid there.", "Yurra nuwi yinyay nyajil.", "yurra nuwi yinyay nyajil | 2pl.NOM squid there see.NPST", "r258_2pl_target_there_see_nuwi", ["yurra", "nuwi", "yinyay", "nyajil"], EVIDENCE["nuwi"]),
    row(31, "The two of them see the large mud clam here.", "Bula baymbay yaluy nyajil.", "bula baymbay yaluy nyajil | 3du.NOM large.mud.clam here see.NPST", "r258_3du_target_here_see_baymbay", ["bula", "baymbay", "yaluy", "nyajil"], EVIDENCE["baymbay"]),
    row(32, "The two of them see the clam here.", "Bula darkay yaluy nyajil.", "bula darkay yaluy nyajil | 3du.NOM clam here see.NPST", "r258_3du_target_here_see_darkay", ["bula", "darkay", "yaluy", "nyajil"], EVIDENCE["darkay"]),
    row(33, "The two of them see the snail here.", "Bula kaduy yaluy nyajil.", "bula kaduy yaluy nyajil | 3du.NOM snail here see.NPST", "r258_3du_target_here_see_kaduy", ["bula", "kaduy", "yaluy", "nyajil"], EVIDENCE["kaduy"]),
    row(34, "The two of them see the karraway snail here.", "Bula karraway yaluy nyajil.", "bula karraway yaluy nyajil | 3du.NOM karraway.snail here see.NPST", "r258_3du_target_here_see_karraway", ["bula", "karraway", "yaluy", "nyajil"], EVIDENCE["karraway"]),
    row(35, "The two of them see the ghost crab here.", "Bula karruku yaluy nyajil.", "bula karruku yaluy nyajil | 3du.NOM ghost.crab here see.NPST", "r258_3du_target_here_see_karruku", ["bula", "karruku", "yaluy", "nyajil"], EVIDENCE["karruku"]),
    row(36, "The two of them see the jellyfish here.", "Bula kulil yaluy nyajil.", "bula kulil yaluy nyajil | 3du.NOM jellyfish here see.NPST", "r258_3du_target_here_see_kulil", ["bula", "kulil", "yaluy", "nyajil"], EVIDENCE["kulil"]),
    row(37, "The two of them see the small conical shellfish here.", "Bula kuyalin yaluy nyajil.", "bula kuyalin yaluy nyajil | 3du.NOM small.conical.shellfish here see.NPST", "r258_3du_target_here_see_kuyalin", ["bula", "kuyalin", "yaluy", "nyajil"], EVIDENCE["kuyalin"]),
    row(38, "The two of them see the oyster here.", "Bula marrkabina yaluy nyajil.", "bula marrkabina yaluy nyajil | 3du.NOM oyster here see.NPST", "r258_3du_target_here_see_marrkabina", ["bula", "marrkabina", "yaluy", "nyajil"], EVIDENCE["marrkabina"]),
    row(39, "The two of them see the shellfish here.", "Bula ngarrakaja yaluy nyajil.", "bula ngarrakaja yaluy nyajil | 3du.NOM shellfish here see.NPST", "r258_3du_target_here_see_ngarrakaja", ["bula", "ngarrakaja", "yaluy", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(40, "The two of them see the squid here.", "Bula nuwi yaluy nyajil.", "bula nuwi yaluy nyajil | 3du.NOM squid here see.NPST", "r258_3du_target_here_see_nuwi", ["bula", "nuwi", "yaluy", "nyajil"], EVIDENCE["nuwi"]),
    row(41, "They see the large mud clam there.", "Jana baymbay yinyay nyajil.", "jana baymbay yinyay nyajil | 3pl.NOM large.mud.clam there see.NPST", "r258_3pl_target_there_see_baymbay", ["jana", "baymbay", "yinyay", "nyajil"], EVIDENCE["baymbay"]),
    row(42, "They see the clam there.", "Jana darkay yinyay nyajil.", "jana darkay yinyay nyajil | 3pl.NOM clam there see.NPST", "r258_3pl_target_there_see_darkay", ["jana", "darkay", "yinyay", "nyajil"], EVIDENCE["darkay"]),
    row(43, "They see the snail there.", "Jana kaduy yinyay nyajil.", "jana kaduy yinyay nyajil | 3pl.NOM snail there see.NPST", "r258_3pl_target_there_see_kaduy", ["jana", "kaduy", "yinyay", "nyajil"], EVIDENCE["kaduy"]),
    row(44, "They see the karraway snail there.", "Jana karraway yinyay nyajil.", "jana karraway yinyay nyajil | 3pl.NOM karraway.snail there see.NPST", "r258_3pl_target_there_see_karraway", ["jana", "karraway", "yinyay", "nyajil"], EVIDENCE["karraway"]),
    row(45, "They see the ghost crab there.", "Jana karruku yinyay nyajil.", "jana karruku yinyay nyajil | 3pl.NOM ghost.crab there see.NPST", "r258_3pl_target_there_see_karruku", ["jana", "karruku", "yinyay", "nyajil"], EVIDENCE["karruku"]),
    row(46, "They see the jellyfish there.", "Jana kulil yinyay nyajil.", "jana kulil yinyay nyajil | 3pl.NOM jellyfish there see.NPST", "r258_3pl_target_there_see_kulil", ["jana", "kulil", "yinyay", "nyajil"], EVIDENCE["kulil"]),
    row(47, "They see the small conical shellfish there.", "Jana kuyalin yinyay nyajil.", "jana kuyalin yinyay nyajil | 3pl.NOM small.conical.shellfish there see.NPST", "r258_3pl_target_there_see_kuyalin", ["jana", "kuyalin", "yinyay", "nyajil"], EVIDENCE["kuyalin"]),
    row(48, "They see the oyster there.", "Jana marrkabina yinyay nyajil.", "jana marrkabina yinyay nyajil | 3pl.NOM oyster there see.NPST", "r258_3pl_target_there_see_marrkabina", ["jana", "marrkabina", "yinyay", "nyajil"], EVIDENCE["marrkabina"]),
    row(49, "They see the shellfish there.", "Jana ngarrakaja yinyay nyajil.", "jana ngarrakaja yinyay nyajil | 3pl.NOM shellfish there see.NPST", "r258_3pl_target_there_see_ngarrakaja", ["jana", "ngarrakaja", "yinyay", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(50, "They see the squid there.", "Jana nuwi yinyay nyajil.", "jana nuwi yinyay nyajil | 3pl.NOM squid there see.NPST", "r258_3pl_target_there_see_nuwi", ["jana", "nuwi", "yinyay", "nyajil"], EVIDENCE["nuwi"]),
]


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


def has_target_morphology(low: str, head: str) -> bool:
    suffixes = "ngka|ngku|angka|nga|ngu|ba|mba|mbu|muny|darr|burr|bu|ka|nka|nku|ji|jirr|ku|mundu|amu|mu|iji|nda|ndu|anda|undu|unji|nji|kari|baka|baja|bungal|bungan|mal|man|rriny|karra|manda|bulal"
    return re.search(rf"(?<![a-z-]){re.escape(head)}-(?:{suffixes})\b", 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_ku: set[str] = set()
    for expected, row_data in enumerate(ROWS, start=1):
        seq, english, kuku, analysis, frame, _tier, words_used, loans_used, evidence, confidence = row_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; expected {expected}")
        if not english[:1].isupper() or english[-1:] not in ".!?":
            issues.append(f"{label}: English punctuation/capitalization failure")
        if " | " not in analysis:
            issues.append(f"{label}: analysis missing gloss separator")
        if not frame.startswith("r258_"):
            issues.append(f"{label}: Rule 258 frame must start with r258_")
        if low in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(low)
        if json.loads(loans_used):
            issues.append(f"{label}: no loans are admitted in Rule 258")
        for denied in DENY_KUKU:
            if has_token(low, denied):
                issues.append(f"{label}: denied carry-forward token {denied!r}")
        targets = [head for head in TARGET_HEADS if has_token(low, head)]
        if len(targets) != 1:
            issues.append(f"{label}: expected exactly one Rule 258 shellfish/crustacean 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 has_target_morphology(low, head):
                issues.append(f"{label}: target head {head} has forbidden morphology")
        for support in SUPPORT:
            if has_token(low, support) and support not in words:
                issues.append(f"{label}: support {support} missing from words_used")
        pronouns = [pronoun for pronoun in PRONOUN_SUPPORT if has_token(low, pronoun)]
        if len(pronouns) != 1:
            issues.append(f"{label}: rows require exactly one bare nominative pronoun, found {pronouns}")
        if 1 <= seq <= 10 and (not low.startswith("ngayu ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 1-10 require Ngayu TARGET yinyay nyajil")
        if 11 <= seq <= 20 and (not low.startswith("nyulu ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 11-20 require Nyulu TARGET yaluy nyajil")
        if 21 <= seq <= 30 and (not low.startswith("yurra ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 21-30 require Yurra TARGET yinyay nyajil")
        if 31 <= seq <= 40 and (not low.startswith("bula ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 31-40 require Bula TARGET yaluy nyajil")
        if 41 <= seq <= 50 and (not low.startswith("jana ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 41-50 require Jana TARGET yinyay nyajil")
        if any(english_has_denied(english_low, bad) for bad in DENY_ENGLISH):
            issues.append(f"{label}: denied shellfish use/event/habitat/source-prose field in English")
        if not evidence or not confidence:
            issues.append(f"{label}: missing evidence/confidence")
    return issues


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)
    existing = db.execute("SELECT id FROM batches WHERE label=?", (LABEL,)).fetchone()
    if existing:
        batch_id = existing[0]
        accepted = db.execute(
            "SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status IN ('accepted','revised')",
            (batch_id,),
        ).fetchone()[0]
        if accepted:
            raise SystemExit(f"{LABEL} already has accepted/revised rows; refusing to overwrite")
        db.execute("DELETE FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)", (batch_id,))
        db.execute("DELETE FROM lessons WHERE batch_id=?", (batch_id,))
        db.execute("DELETE FROM sentences WHERE batch_id=?", (batch_id,))
        db.execute("UPDATE batches SET notes=?, created_at=? WHERE id=?", ("rebanked under_review", NOW, batch_id))
    else:
        db.execute(
            "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
            (LABEL, "Rule 258 draft", "operator-guide-2026-07-09", "draft under review", NOW),
        )
        batch_id = db.execute("SELECT id FROM batches WHERE label=?", (LABEL,)).fetchone()[0]

    collisions: list[str] = []
    for seq, _english, kuku, *_rest in ROWS:
        found = 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,),
        ).fetchall()
        if found:
            collisions.append(f"{seq}: {kuku} -> {found}")
    if collisions:
        raise SystemExit("collision scan failed:\n" + "\n".join(collisions))

    db.executemany(
        """
        INSERT INTO sentences(
          batch_id, seq, english, kuku, analysis, frame, tier, words_used,
          loans_used, evidence, status, confidence, created_at
        )
        VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)
        """,
        [
            (
                batch_id,
                seq,
                english,
                kuku,
                analysis,
                frame,
                tier,
                words_used,
                loans_used,
                evidence,
                "under_review",
                confidence,
                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="Rule 258 source pass checked exact dictionary lines for baymbay large mud clam, darkay clam, kaduy snail, karraway snail, karruku ghost crab, kulil jellyfish, kuyalin small conical shellfish, marrkabina oyster, ngarrakaja shellfish, and nuwi squid; Patz grammar lines 2553-2603 for bare nominative pronouns; lines 2838-2845 for yaluy/yinyay; and lines 3043-3049 for nyaji-l.",
        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 Rule 258 complementary shellfish/crustacean perception debt sweep after Rule 257: same ten checked Rule 213 shellfish labels in zero-collision nominative-pronoun perception bands with opposite here/there pairing. All food, edible, bait, catching, fishing, collecting, habitat, water, shell/body-part, sting, burn, ink/action, source-prose, target-case, pronoun-case, demonstrative-case, and perception-compound semantics remain closed.",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="preflight",
        event_type="coverage-audit",
        actor="claude-main",
        detail="Pre-batch token-aware target coverage among accepted/revised rows after Rule 257: baymbay 11, darkay 16, kaduy 11, karraway 10, karruku 11, kulil 12, kuyalin 10, marrkabina 10, ngarrakaja 10, and nuwi 11. Support coverage: ngayu 1293, nyulu 1774, yurra 223, bula 203, jana 1496, yaluy 2303, yinyay 2546, nyajil 2717.",
        command="sqlite3 synthetic.db token-aware coverage audit",
        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 all 50 planned Rule 258 Kuku surfaces returned zero accepted, revised, or under-review collisions.",
        command="python3 batch_scripts/bank_300.py internal collision scan",
        output_excerpt="0",
        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 guards passed: 50 rows, exactly one Rule 258 target shellfish/crustacean noun per row, exactly one bare nominative pronoun per row, no loans, no target suffixes, no excluded shellfish candidate, no food/use/event/habitat/body/source-prose drift, no pronoun case, no demonstrative case, no perception compound, and five planned frame bands only.",
        command="python3 batch_scripts/bank_300.py",
        artifact_path="batch_scripts/bank_300.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="bank-success",
        actor="claude-main",
        detail="Banked 50 explicit hand-authored Rule 258 complementary shellfish/crustacean perception rows as under_review.",
        command="python3 batch_scripts/bank_300.py",
        artifact_path="batch_scripts/bank_300.py",
        created_at=NOW,
    )
    db.commit()
    print(f"banked {LABEL}: 50 under_review rows")
    return 0


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