#!/usr/bin/env python3
"""Bank batch-301: Rule 259 shellfish/crustacean non-singular pronoun 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-301"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-301-shellfish-nonsingular-pronoun-perception-after-rule258-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 = {"ngali", "ngaliny", "yubal", "nganjin", "ngana", "yaluy", "yinyay", "nyajil"}
PRONOUN_SUPPORT = {"ngali", "ngaliny", "yubal", "nganjin", "ngana"}
DENY_KUKU = {
    "ngayu", "nyulu", "yurra", "bula", "jana", "yundu", "yulu", "iana",
    "ngalingan", "ngalinyan", "yubalan", "nganjinan", "nganangan",
    "ngalininy", "ngalinyiny", "yubaliny", "nganjininy", "ngananiny",
    "ngalinka", "ngalinyanka", "yubalanka", "nganjinanka", "ngananka",
    "ngalinda", "ngalinyanda", "yubalanda", "nganjinanda", "ngananda",
    "ngalindamuny", "ngalinyandamuny", "yubalandamuny", "nganjinandamuny", "nganandamuny",
    "ngalinji", "ngalinyanji", "yubalanji", "nganjinanji", "ngananji",
    "ngalinga", "ngalinyanga", "yubalanga", "nganjinanga", "ngananga",
    "yinyangka", "yinyayngka", "yinyayanka", "yinyamba", "yinyayanda",
    "yinyamuny", "yinyamundu", "yinyanji", "yinyarriny", "yalungku",
    "yaluyngku", "yaluyunku", "yaluymbu", "yaluyundu", "yalamuny",
    "yalamundu", "yalanji", "nyaji-nka", "nyaji-nyaku", "nyaji-l-nyaji",
    "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",
    "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 2577-2603 source bare nominative ngali, ngaliny, yubal, nganjin, and ngana; lines 2682-2684 discuss inclusive/exclusive and second-person non-singular forms; lines 2838-2845 source yaluy/yinyay; 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 259 selects clam only. Patz grammar lines 2577-2603, 2682-2684, 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 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "karraway": "Dictionary lines 13592-13601 give karraway as noun 'edible snail' with translation snail; Rule 259 selects karraway snail only and leaves edible/food prose closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "karruku": "Dictionary lines 13701-13709 give karruku as noun 'ghost crab, fiddler crab'; Rule 259 selects ghost crab only and leaves fiddler-crab expansion closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "kulil": "Dictionary lines 15209-15220 give kulil as noun 'jellyfish', synonyms mili and wuybur; Rule 259 selects kulil only and leaves synonyms/sting/burn/danger rows closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "kuyalin": "Dictionary lines 16857-16866 give kuyalin as noun 'small conical shellfish', definition includes bait; Rule 259 selects small conical shellfish only and leaves bait/use rows closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "marrkabina": "Dictionary lines 18639-18647 give marrkabina as noun 'oyster', definition edible oyster; Rule 259 selects oyster only and leaves edible/food prose closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "ngarrakaja": "Dictionary lines 22791-22801 give ngarrakaja as noun 'shell fish', with edible/species prose closed; Rule 259 selects shellfish only. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "nuwi": "Dictionary lines 24261-24271 give nuwi as noun 'squid' with an ink-throwing source usage; Rule 259 selects squid only and leaves action/event rows closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
}


ROWS = [
    row(1, "We two, not you, see the large mud clam there.", "Ngali baymbay yinyay nyajil.", "ngali baymbay yinyay nyajil | 1du.EXCL.NOM large.mud.clam there see.NPST", "r259_1du_excl_target_there_see_baymbay", ["ngali", "baymbay", "yinyay", "nyajil"], EVIDENCE["baymbay"]),
    row(2, "We two, not you, see the clam there.", "Ngali darkay yinyay nyajil.", "ngali darkay yinyay nyajil | 1du.EXCL.NOM clam there see.NPST", "r259_1du_excl_target_there_see_darkay", ["ngali", "darkay", "yinyay", "nyajil"], EVIDENCE["darkay"]),
    row(3, "We two, not you, see the snail there.", "Ngali kaduy yinyay nyajil.", "ngali kaduy yinyay nyajil | 1du.EXCL.NOM snail there see.NPST", "r259_1du_excl_target_there_see_kaduy", ["ngali", "kaduy", "yinyay", "nyajil"], EVIDENCE["kaduy"]),
    row(4, "We two, not you, see the karraway snail there.", "Ngali karraway yinyay nyajil.", "ngali karraway yinyay nyajil | 1du.EXCL.NOM karraway.snail there see.NPST", "r259_1du_excl_target_there_see_karraway", ["ngali", "karraway", "yinyay", "nyajil"], EVIDENCE["karraway"]),
    row(5, "We two, not you, see the ghost crab there.", "Ngali karruku yinyay nyajil.", "ngali karruku yinyay nyajil | 1du.EXCL.NOM ghost.crab there see.NPST", "r259_1du_excl_target_there_see_karruku", ["ngali", "karruku", "yinyay", "nyajil"], EVIDENCE["karruku"]),
    row(6, "We two, not you, see the jellyfish there.", "Ngali kulil yinyay nyajil.", "ngali kulil yinyay nyajil | 1du.EXCL.NOM jellyfish there see.NPST", "r259_1du_excl_target_there_see_kulil", ["ngali", "kulil", "yinyay", "nyajil"], EVIDENCE["kulil"]),
    row(7, "We two, not you, see the small conical shellfish there.", "Ngali kuyalin yinyay nyajil.", "ngali kuyalin yinyay nyajil | 1du.EXCL.NOM small.conical.shellfish there see.NPST", "r259_1du_excl_target_there_see_kuyalin", ["ngali", "kuyalin", "yinyay", "nyajil"], EVIDENCE["kuyalin"]),
    row(8, "We two, not you, see the oyster there.", "Ngali marrkabina yinyay nyajil.", "ngali marrkabina yinyay nyajil | 1du.EXCL.NOM oyster there see.NPST", "r259_1du_excl_target_there_see_marrkabina", ["ngali", "marrkabina", "yinyay", "nyajil"], EVIDENCE["marrkabina"]),
    row(9, "We two, not you, see the shellfish there.", "Ngali ngarrakaja yinyay nyajil.", "ngali ngarrakaja yinyay nyajil | 1du.EXCL.NOM shellfish there see.NPST", "r259_1du_excl_target_there_see_ngarrakaja", ["ngali", "ngarrakaja", "yinyay", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(10, "We two, not you, see the squid there.", "Ngali nuwi yinyay nyajil.", "ngali nuwi yinyay nyajil | 1du.EXCL.NOM squid there see.NPST", "r259_1du_excl_target_there_see_nuwi", ["ngali", "nuwi", "yinyay", "nyajil"], EVIDENCE["nuwi"]),
    row(11, "You and I see the large mud clam here.", "Ngaliny baymbay yaluy nyajil.", "ngaliny baymbay yaluy nyajil | 1du.INCL.NOM large.mud.clam here see.NPST", "r259_1du_incl_target_here_see_baymbay", ["ngaliny", "baymbay", "yaluy", "nyajil"], EVIDENCE["baymbay"]),
    row(12, "You and I see the clam here.", "Ngaliny darkay yaluy nyajil.", "ngaliny darkay yaluy nyajil | 1du.INCL.NOM clam here see.NPST", "r259_1du_incl_target_here_see_darkay", ["ngaliny", "darkay", "yaluy", "nyajil"], EVIDENCE["darkay"]),
    row(13, "You and I see the snail here.", "Ngaliny kaduy yaluy nyajil.", "ngaliny kaduy yaluy nyajil | 1du.INCL.NOM snail here see.NPST", "r259_1du_incl_target_here_see_kaduy", ["ngaliny", "kaduy", "yaluy", "nyajil"], EVIDENCE["kaduy"]),
    row(14, "You and I see the karraway snail here.", "Ngaliny karraway yaluy nyajil.", "ngaliny karraway yaluy nyajil | 1du.INCL.NOM karraway.snail here see.NPST", "r259_1du_incl_target_here_see_karraway", ["ngaliny", "karraway", "yaluy", "nyajil"], EVIDENCE["karraway"]),
    row(15, "You and I see the ghost crab here.", "Ngaliny karruku yaluy nyajil.", "ngaliny karruku yaluy nyajil | 1du.INCL.NOM ghost.crab here see.NPST", "r259_1du_incl_target_here_see_karruku", ["ngaliny", "karruku", "yaluy", "nyajil"], EVIDENCE["karruku"]),
    row(16, "You and I see the jellyfish here.", "Ngaliny kulil yaluy nyajil.", "ngaliny kulil yaluy nyajil | 1du.INCL.NOM jellyfish here see.NPST", "r259_1du_incl_target_here_see_kulil", ["ngaliny", "kulil", "yaluy", "nyajil"], EVIDENCE["kulil"]),
    row(17, "You and I see the small conical shellfish here.", "Ngaliny kuyalin yaluy nyajil.", "ngaliny kuyalin yaluy nyajil | 1du.INCL.NOM small.conical.shellfish here see.NPST", "r259_1du_incl_target_here_see_kuyalin", ["ngaliny", "kuyalin", "yaluy", "nyajil"], EVIDENCE["kuyalin"]),
    row(18, "You and I see the oyster here.", "Ngaliny marrkabina yaluy nyajil.", "ngaliny marrkabina yaluy nyajil | 1du.INCL.NOM oyster here see.NPST", "r259_1du_incl_target_here_see_marrkabina", ["ngaliny", "marrkabina", "yaluy", "nyajil"], EVIDENCE["marrkabina"]),
    row(19, "You and I see the shellfish here.", "Ngaliny ngarrakaja yaluy nyajil.", "ngaliny ngarrakaja yaluy nyajil | 1du.INCL.NOM shellfish here see.NPST", "r259_1du_incl_target_here_see_ngarrakaja", ["ngaliny", "ngarrakaja", "yaluy", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(20, "You and I see the squid here.", "Ngaliny nuwi yaluy nyajil.", "ngaliny nuwi yaluy nyajil | 1du.INCL.NOM squid here see.NPST", "r259_1du_incl_target_here_see_nuwi", ["ngaliny", "nuwi", "yaluy", "nyajil"], EVIDENCE["nuwi"]),
    row(21, "You two see the large mud clam there.", "Yubal baymbay yinyay nyajil.", "yubal baymbay yinyay nyajil | 2du.NOM large.mud.clam there see.NPST", "r259_2du_target_there_see_baymbay", ["yubal", "baymbay", "yinyay", "nyajil"], EVIDENCE["baymbay"]),
    row(22, "You two see the clam there.", "Yubal darkay yinyay nyajil.", "yubal darkay yinyay nyajil | 2du.NOM clam there see.NPST", "r259_2du_target_there_see_darkay", ["yubal", "darkay", "yinyay", "nyajil"], EVIDENCE["darkay"]),
    row(23, "You two see the snail there.", "Yubal kaduy yinyay nyajil.", "yubal kaduy yinyay nyajil | 2du.NOM snail there see.NPST", "r259_2du_target_there_see_kaduy", ["yubal", "kaduy", "yinyay", "nyajil"], EVIDENCE["kaduy"]),
    row(24, "You two see the karraway snail there.", "Yubal karraway yinyay nyajil.", "yubal karraway yinyay nyajil | 2du.NOM karraway.snail there see.NPST", "r259_2du_target_there_see_karraway", ["yubal", "karraway", "yinyay", "nyajil"], EVIDENCE["karraway"]),
    row(25, "You two see the ghost crab there.", "Yubal karruku yinyay nyajil.", "yubal karruku yinyay nyajil | 2du.NOM ghost.crab there see.NPST", "r259_2du_target_there_see_karruku", ["yubal", "karruku", "yinyay", "nyajil"], EVIDENCE["karruku"]),
    row(26, "You two see the jellyfish there.", "Yubal kulil yinyay nyajil.", "yubal kulil yinyay nyajil | 2du.NOM jellyfish there see.NPST", "r259_2du_target_there_see_kulil", ["yubal", "kulil", "yinyay", "nyajil"], EVIDENCE["kulil"]),
    row(27, "You two see the small conical shellfish there.", "Yubal kuyalin yinyay nyajil.", "yubal kuyalin yinyay nyajil | 2du.NOM small.conical.shellfish there see.NPST", "r259_2du_target_there_see_kuyalin", ["yubal", "kuyalin", "yinyay", "nyajil"], EVIDENCE["kuyalin"]),
    row(28, "You two see the oyster there.", "Yubal marrkabina yinyay nyajil.", "yubal marrkabina yinyay nyajil | 2du.NOM oyster there see.NPST", "r259_2du_target_there_see_marrkabina", ["yubal", "marrkabina", "yinyay", "nyajil"], EVIDENCE["marrkabina"]),
    row(29, "You two see the shellfish there.", "Yubal ngarrakaja yinyay nyajil.", "yubal ngarrakaja yinyay nyajil | 2du.NOM shellfish there see.NPST", "r259_2du_target_there_see_ngarrakaja", ["yubal", "ngarrakaja", "yinyay", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(30, "You two see the squid there.", "Yubal nuwi yinyay nyajil.", "yubal nuwi yinyay nyajil | 2du.NOM squid there see.NPST", "r259_2du_target_there_see_nuwi", ["yubal", "nuwi", "yinyay", "nyajil"], EVIDENCE["nuwi"]),
    row(31, "We all, not you, see the large mud clam here.", "Nganjin baymbay yaluy nyajil.", "nganjin baymbay yaluy nyajil | 1pl.EXCL.NOM large.mud.clam here see.NPST", "r259_1pl_excl_target_here_see_baymbay", ["nganjin", "baymbay", "yaluy", "nyajil"], EVIDENCE["baymbay"]),
    row(32, "We all, not you, see the clam here.", "Nganjin darkay yaluy nyajil.", "nganjin darkay yaluy nyajil | 1pl.EXCL.NOM clam here see.NPST", "r259_1pl_excl_target_here_see_darkay", ["nganjin", "darkay", "yaluy", "nyajil"], EVIDENCE["darkay"]),
    row(33, "We all, not you, see the snail here.", "Nganjin kaduy yaluy nyajil.", "nganjin kaduy yaluy nyajil | 1pl.EXCL.NOM snail here see.NPST", "r259_1pl_excl_target_here_see_kaduy", ["nganjin", "kaduy", "yaluy", "nyajil"], EVIDENCE["kaduy"]),
    row(34, "We all, not you, see the karraway snail here.", "Nganjin karraway yaluy nyajil.", "nganjin karraway yaluy nyajil | 1pl.EXCL.NOM karraway.snail here see.NPST", "r259_1pl_excl_target_here_see_karraway", ["nganjin", "karraway", "yaluy", "nyajil"], EVIDENCE["karraway"]),
    row(35, "We all, not you, see the ghost crab here.", "Nganjin karruku yaluy nyajil.", "nganjin karruku yaluy nyajil | 1pl.EXCL.NOM ghost.crab here see.NPST", "r259_1pl_excl_target_here_see_karruku", ["nganjin", "karruku", "yaluy", "nyajil"], EVIDENCE["karruku"]),
    row(36, "We all, not you, see the jellyfish here.", "Nganjin kulil yaluy nyajil.", "nganjin kulil yaluy nyajil | 1pl.EXCL.NOM jellyfish here see.NPST", "r259_1pl_excl_target_here_see_kulil", ["nganjin", "kulil", "yaluy", "nyajil"], EVIDENCE["kulil"]),
    row(37, "We all, not you, see the small conical shellfish here.", "Nganjin kuyalin yaluy nyajil.", "nganjin kuyalin yaluy nyajil | 1pl.EXCL.NOM small.conical.shellfish here see.NPST", "r259_1pl_excl_target_here_see_kuyalin", ["nganjin", "kuyalin", "yaluy", "nyajil"], EVIDENCE["kuyalin"]),
    row(38, "We all, not you, see the oyster here.", "Nganjin marrkabina yaluy nyajil.", "nganjin marrkabina yaluy nyajil | 1pl.EXCL.NOM oyster here see.NPST", "r259_1pl_excl_target_here_see_marrkabina", ["nganjin", "marrkabina", "yaluy", "nyajil"], EVIDENCE["marrkabina"]),
    row(39, "We all, not you, see the shellfish here.", "Nganjin ngarrakaja yaluy nyajil.", "nganjin ngarrakaja yaluy nyajil | 1pl.EXCL.NOM shellfish here see.NPST", "r259_1pl_excl_target_here_see_ngarrakaja", ["nganjin", "ngarrakaja", "yaluy", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(40, "We all, not you, see the squid here.", "Nganjin nuwi yaluy nyajil.", "nganjin nuwi yaluy nyajil | 1pl.EXCL.NOM squid here see.NPST", "r259_1pl_excl_target_here_see_nuwi", ["nganjin", "nuwi", "yaluy", "nyajil"], EVIDENCE["nuwi"]),
    row(41, "We see the large mud clam there.", "Ngana baymbay yinyay nyajil.", "ngana baymbay yinyay nyajil | 1pl.INC.NOM large.mud.clam there see.NPST", "r259_1pl_incl_target_there_see_baymbay", ["ngana", "baymbay", "yinyay", "nyajil"], EVIDENCE["baymbay"]),
    row(42, "We see the clam there.", "Ngana darkay yinyay nyajil.", "ngana darkay yinyay nyajil | 1pl.INC.NOM clam there see.NPST", "r259_1pl_incl_target_there_see_darkay", ["ngana", "darkay", "yinyay", "nyajil"], EVIDENCE["darkay"]),
    row(43, "We see the snail there.", "Ngana kaduy yinyay nyajil.", "ngana kaduy yinyay nyajil | 1pl.INC.NOM snail there see.NPST", "r259_1pl_incl_target_there_see_kaduy", ["ngana", "kaduy", "yinyay", "nyajil"], EVIDENCE["kaduy"]),
    row(44, "We see the karraway snail there.", "Ngana karraway yinyay nyajil.", "ngana karraway yinyay nyajil | 1pl.INC.NOM karraway.snail there see.NPST", "r259_1pl_incl_target_there_see_karraway", ["ngana", "karraway", "yinyay", "nyajil"], EVIDENCE["karraway"]),
    row(45, "We see the ghost crab there.", "Ngana karruku yinyay nyajil.", "ngana karruku yinyay nyajil | 1pl.INC.NOM ghost.crab there see.NPST", "r259_1pl_incl_target_there_see_karruku", ["ngana", "karruku", "yinyay", "nyajil"], EVIDENCE["karruku"]),
    row(46, "We see the jellyfish there.", "Ngana kulil yinyay nyajil.", "ngana kulil yinyay nyajil | 1pl.INC.NOM jellyfish there see.NPST", "r259_1pl_incl_target_there_see_kulil", ["ngana", "kulil", "yinyay", "nyajil"], EVIDENCE["kulil"]),
    row(47, "We see the small conical shellfish there.", "Ngana kuyalin yinyay nyajil.", "ngana kuyalin yinyay nyajil | 1pl.INC.NOM small.conical.shellfish there see.NPST", "r259_1pl_incl_target_there_see_kuyalin", ["ngana", "kuyalin", "yinyay", "nyajil"], EVIDENCE["kuyalin"]),
    row(48, "We see the oyster there.", "Ngana marrkabina yinyay nyajil.", "ngana marrkabina yinyay nyajil | 1pl.INC.NOM oyster there see.NPST", "r259_1pl_incl_target_there_see_marrkabina", ["ngana", "marrkabina", "yinyay", "nyajil"], EVIDENCE["marrkabina"]),
    row(49, "We see the shellfish there.", "Ngana ngarrakaja yinyay nyajil.", "ngana ngarrakaja yinyay nyajil | 1pl.INC.NOM shellfish there see.NPST", "r259_1pl_incl_target_there_see_ngarrakaja", ["ngana", "ngarrakaja", "yinyay", "nyajil"], EVIDENCE["ngarrakaja"]),
    row(50, "We see the squid there.", "Ngana nuwi yinyay nyajil.", "ngana nuwi yinyay nyajil | 1pl.INC.NOM squid there see.NPST", "r259_1pl_incl_target_there_see_nuwi", ["ngana", "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("r259_"):
            issues.append(f"{label}: Rule 259 frame must start with r259_")
        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 259")
        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 259 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 non-singular pronoun, found {pronouns}")
        if 1 <= seq <= 10 and (not low.startswith("ngali ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 1-10 require Ngali TARGET yinyay nyajil")
        if 11 <= seq <= 20 and (not low.startswith("ngaliny ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 11-20 require Ngaliny TARGET yaluy nyajil")
        if 21 <= seq <= 30 and (not low.startswith("yubal ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 21-30 require Yubal TARGET yinyay nyajil")
        if 31 <= seq <= 40 and (not low.startswith("nganjin ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 31-40 require Nganjin TARGET yaluy nyajil")
        if 41 <= seq <= 50 and (not low.startswith("ngana ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 41-50 require Ngana 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 259 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 259 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 2577-2603 for bare nominative ngali, ngaliny, yubal, nganjin, and ngana; lines 2682-2684 for inclusive/exclusive and second-person non-singular discussion; 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 259 shellfish/crustacean non-singular pronoun perception sweep after Rule 258: same ten checked Rule 213 shellfish labels in zero-collision nominative-pronoun perception bands using ngali, ngaliny, yubal, nganjin, and ngana. 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 258: baymbay 16, darkay 21, kaduy 16, karraway 15, karruku 16, kulil 17, kuyalin 15, marrkabina 15, ngarrakaja 15, and nuwi 16. Support coverage: ngali 159, ngaliny 94, yubal 105, nganjin 111, ngana 859, yaluy 2323, yinyay 2576, nyajil 2767.",
        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 259 Kuku surfaces returned zero accepted, revised, or under-review collisions.",
        command="python3 batch_scripts/bank_301.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 259 target shellfish/crustacean noun per row, exactly one bare nominative non-singular 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_301.py",
        artifact_path="batch_scripts/bank_301.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 259 shellfish/crustacean non-singular pronoun perception rows as under_review.",
        command="python3 batch_scripts/bank_301.py",
        artifact_path="batch_scripts/bank_301.py",
        created_at=NOW,
    )
    db.commit()
    print(f"banked {LABEL}: 50 under_review rows")
    return 0


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