#!/usr/bin/env python3
"""Bank batch-368: Rule 326 low-coverage environment non-singular 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-368"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-368-low-coverage-environment-nonsingular-nonpast-perception-after-rule325-process.md"

TARGET_EVIDENCE = {
    "dalngarri": "Dictionary yaml lines 4979-5001 give dalngarri as noun 'flames which make light' and separately 'urine', with fire-light and toilet examples. Rule 326 admits flame reference only and leaves urine/urination, toilet, light causation, fire making, burning, heat, danger, synonym substitution, and event propositions closed.",
    "jukar-warra": "Dictionary yaml lines 10292-10302 give the exact compound noun jukar-warra 'whirlwind' and identify jukar 'sand' as its first element. Rule 326 admits the whole whirlwind head only and leaves decomposition, productive compounding, sand, wind movement, weather prediction, damage, direction, dust, travel, and event propositions closed.",
    "kambar": "Dictionary yaml lines 12987-12999 give kambar as noun 'white clay' and also 'wet season'; nearby double-r kambarr at 13000-13011 means corpse liquid. Rule 326 admits white-clay reference only and leaves wet-season/time, colour predication, digging, painting, body use, material use, corpse/death/fluid, and event propositions closed.",
    "kurrkal": "Dictionary yaml lines 16451-16470 give kurrkal as noun 'shortcut' and derived kurrkal-warri 'go by a shortcut'. Rule 326 admits shortcut reference only and leaves travel, route choice, destination, direction, speed, use, walking, derived verb, place identity, and event propositions closed.",
    "marrka": "Dictionary yaml lines 18626-18638 give marrka as noun 'calm sea' and separately 'salt pan'. Patz lines 2227-2229 mention marrka only as 'salt pan' inside an etymological note on marrka-bina. Rule 326 admits calm-sea reference only and leaves salt pan, oyster compound, mangrove/swamp, tide, weather cause, water state change, boating, fishing, and event propositions closed.",
    "miral": "Dictionary yaml lines 19714-19726 give miral as noun 'scum on water' and separately 'whale oil'. Rule 326 admits water-scum reference only and leaves whale/oil, animal source, texture/property, pollution, removal, touching, collecting, use, and event propositions closed.",
    "mulawajarr": "Dictionary yaml lines 20370-20380 give mulawajarr as the Yalanji noun 'star', contrasting with Nyungkul dawar. Rule 326 admits Yalanji star reference only and leaves dialect substitution, celestial movement, time/season, direction/navigation, brightness, number, story/totem, weather, and event propositions closed.",
    "nganyil": "Dictionary yaml lines 22642-22651 give nganyil as noun 'haze, mist'. Rule 326 admits haze reference only and leaves mist equivalence claims beyond the entry, weather cause/change, visibility effects, smoke, fire, season, location, and near-form nganyin onion/sugar-bag senses closed.",
    "nyamun": "Dictionary yaml lines 24330-24340 give nyamun as natural debris: dead leaves, beach wrack, or scrub leaf-mould, explicitly not household rubbish. Rule 326 admits natural-debris reference only and leaves household waste, leaf/beach/scrub subtype choice, decay, gardening/growing use, gathering, clearing, burning, disposal, and event propositions closed.",
    "wurrbal": "Dictionary yaml lines 28402-28413 give wurrbal as noun 'fog, mist rising from the river'. Rule 326 admits fog reference only and leaves river/source location, rising/movement, weather cause/change, visibility, travel danger, season/time, mist normalization, and event propositions closed.",
}

SUPPORT_EVIDENCE = (
    "Patz grammar line 1476 explicitly includes geographical/geological items, celestial bodies, weather "
    "phenomena, and other corporeal objects among noun referents. Patz lines 2577-2584 source bare nominative "
    "ngali, ngaliny, yubal, yurra, and ngana; lines 2834-2847 source Yalanji yaluy 'here' and yinyay 'there'. "
    "Patz lines 3043-3049 give nonpast nyaji-l, lines 3149-3151 define its present/future function, and lines "
    "4256-4265 license nominative pronoun A with bare absolutive O for perception. Dictionary lines 24282-24296 "
    "independently attest running-form nyajil and its visual sense. Exact Patz target search finds marrka only at "
    "lines 2227-2229 in the distinct salt-pan etymology of marrka-bina; the other nine targets are absent. Rule "
    "326 takes each selected sense from the dictionary and uses only bare support and nonpast visual perception."
)

TARGET_ORDER = [
    "dalngarri", "jukar-warra", "kambar", "kurrkal", "marrka",
    "miral", "mulawajarr", "nganyil", "nyamun", "wurrbal",
]
TARGET_LABELS = {
    "dalngarri": "dalngarri flames",
    "jukar-warra": "jukar-warra whirlwind",
    "kambar": "kambar white clay",
    "kurrkal": "kurrkal shortcut",
    "marrka": "marrka calm sea",
    "miral": "miral water scum",
    "mulawajarr": "mulawajarr star",
    "nganyil": "nganyil haze",
    "nyamun": "nyamun natural debris",
    "wurrbal": "wurrbal fog",
}
TARGET_GLOSSES = {
    "dalngarri": "dalngarri.flames",
    "jukar-warra": "jukar.warra.whirlwind",
    "kambar": "kambar.white.clay",
    "kurrkal": "kurrkal.shortcut",
    "marrka": "marrka.calm.sea",
    "miral": "miral.water.scum",
    "mulawajarr": "mulawajarr.star",
    "nganyil": "nganyil.haze",
    "nyamun": "nyamun.natural.debris",
    "wurrbal": "wurrbal.fog",
}
TARGET_CONFIDENCE = {
    "dalngarri": "0.86",
    "jukar-warra": "0.89",
    "kambar": "0.86",
    "kurrkal": "0.89",
    "marrka": "0.86",
    "miral": "0.87",
    "mulawajarr": "0.93",
    "nganyil": "0.90",
    "nyamun": "0.88",
    "wurrbal": "0.90",
}
EXPECTED_TARGET_COVERAGE = {
    "dalngarri": 0,
    "jukar-warra": 8,
    "kambar": 9,
    "kurrkal": 0,
    "marrka": 6,
    "miral": 5,
    "mulawajarr": 0,
    "nganyil": 5,
    "nyamun": 0,
    "wurrbal": 6,
}
FRAME_SPECS = [
    ("ngali", "yaluy", "We two, not you, see", "here", "1du.excl"),
    ("ngaliny", "yinyay", "You and I see", "there", "1du.incl"),
    ("yubal", "yaluy", "You two see", "here", "2du"),
    ("yurra", "yinyay", "You all see", "there", "2pl"),
    ("ngana", "yaluy", "We see", "here", "1pl.incl"),
]

DENY_KUKU = {
    "ngayu", "yundu", "nyulu", "yulu", "bula", "jana", "iana", "nganjin",
    "ngalingan", "ngalinyan", "yubalan", "ngalininy", "ngalinyiny", "yubaliny",
    "ngalinka", "ngalinyanka", "yubalanka", "ngalinda", "ngalinyanda", "yubalanda",
    "ngalindamuny", "ngalinyandamuny", "yubalandamuny", "ngalinji", "ngalinyanji", "yubalanji",
    "ngalinga", "ngalinyanga", "yubalanga", "yurrangan", "yurraniny", "yurranka", "yurranda",
    "yurrandamuny", "yurranji", "yurranga", "nganangan", "ngananiny", "ngananka", "ngananda",
    "nganandamuny", "ngananji", "ngananga",
    "wula", "kumbu", "yiwan", "jukar", "kambarr", "kurrkal-warri", "marrka-bina", "marrkabina",
    "dawar", "nganyin", "yalay", "yala", "yanyu", "yinya", "yalungku", "yaluyngku",
    "yinyangka", "yinyayngka", "yalamuny", "yinyamuny", "nyajin", "nyaji-ny", "nyaji-nka",
    "nyaji-l-nyaji", "milka", "baykal", "jalngkay",
}
DENY_ENGLISH = (
    "urine", "urinate", "toilet", "cause", "make", "burn", "heat", "danger", "sand", "wind movement",
    "predict", "damage", "direction", "dust", "travel", "wet season", "paint", "body", "corpse", "death",
    "fluid", "route", "destination", "speed", "walk", "salt pan", "oyster", "mangrove", "swamp", "tide",
    "boat", "fish", "whale", "oil", "animal source", "pollution", "remove", "touch", "collect", "move",
    "time", "season", "navigate", "bright", "number", "story", "totem", "smoke", "fire", "household",
    "garden", "grow", "clear", "dispose", "river", "rise", "visibility", "location", "possess", "event",
    "use", "used", "using",
)


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 lower(value)=?)""",
            (word,),
        ).fetchone()[0]
        for word in words
    }


def row(seq: int, english: str, kuku: str, analysis: str, frame: str, words: list[str]) -> tuple:
    target = words[1]
    return (
        seq, english, kuku, analysis, frame, 3,
        json.dumps(words, ensure_ascii=False), json.dumps([], ensure_ascii=False),
        f"{TARGET_EVIDENCE[target]} {SUPPORT_EVIDENCE}", TARGET_CONFIDENCE[target],
    )


ROWS = [
    row(1, "We two, not you, see the dalngarri flames here.", "Ngali dalngarri yaluy nyajil.", "ngali dalngarri yaluy nyajil | 1du.excl.NOM dalngarri.flames here see.NPST", "r326_1du_excl_here_low_environment_see_dalngarri", ["ngali", "dalngarri", "yaluy", "nyajil"]),
    row(2, "We two, not you, see the jukar-warra whirlwind here.", "Ngali jukar-warra yaluy nyajil.", "ngali jukar-warra yaluy nyajil | 1du.excl.NOM jukar.warra.whirlwind here see.NPST", "r326_1du_excl_here_low_environment_see_jukar_warra", ["ngali", "jukar-warra", "yaluy", "nyajil"]),
    row(3, "We two, not you, see the kambar white clay here.", "Ngali kambar yaluy nyajil.", "ngali kambar yaluy nyajil | 1du.excl.NOM kambar.white.clay here see.NPST", "r326_1du_excl_here_low_environment_see_kambar", ["ngali", "kambar", "yaluy", "nyajil"]),
    row(4, "We two, not you, see the kurrkal shortcut here.", "Ngali kurrkal yaluy nyajil.", "ngali kurrkal yaluy nyajil | 1du.excl.NOM kurrkal.shortcut here see.NPST", "r326_1du_excl_here_low_environment_see_kurrkal", ["ngali", "kurrkal", "yaluy", "nyajil"]),
    row(5, "We two, not you, see the marrka calm sea here.", "Ngali marrka yaluy nyajil.", "ngali marrka yaluy nyajil | 1du.excl.NOM marrka.calm.sea here see.NPST", "r326_1du_excl_here_low_environment_see_marrka", ["ngali", "marrka", "yaluy", "nyajil"]),
    row(6, "We two, not you, see the miral water scum here.", "Ngali miral yaluy nyajil.", "ngali miral yaluy nyajil | 1du.excl.NOM miral.water.scum here see.NPST", "r326_1du_excl_here_low_environment_see_miral", ["ngali", "miral", "yaluy", "nyajil"]),
    row(7, "We two, not you, see the mulawajarr star here.", "Ngali mulawajarr yaluy nyajil.", "ngali mulawajarr yaluy nyajil | 1du.excl.NOM mulawajarr.star here see.NPST", "r326_1du_excl_here_low_environment_see_mulawajarr", ["ngali", "mulawajarr", "yaluy", "nyajil"]),
    row(8, "We two, not you, see the nganyil haze here.", "Ngali nganyil yaluy nyajil.", "ngali nganyil yaluy nyajil | 1du.excl.NOM nganyil.haze here see.NPST", "r326_1du_excl_here_low_environment_see_nganyil", ["ngali", "nganyil", "yaluy", "nyajil"]),
    row(9, "We two, not you, see the nyamun natural debris here.", "Ngali nyamun yaluy nyajil.", "ngali nyamun yaluy nyajil | 1du.excl.NOM nyamun.natural.debris here see.NPST", "r326_1du_excl_here_low_environment_see_nyamun", ["ngali", "nyamun", "yaluy", "nyajil"]),
    row(10, "We two, not you, see the wurrbal fog here.", "Ngali wurrbal yaluy nyajil.", "ngali wurrbal yaluy nyajil | 1du.excl.NOM wurrbal.fog here see.NPST", "r326_1du_excl_here_low_environment_see_wurrbal", ["ngali", "wurrbal", "yaluy", "nyajil"]),
    row(11, "You and I see the dalngarri flames there.", "Ngaliny dalngarri yinyay nyajil.", "ngaliny dalngarri yinyay nyajil | 1du.incl.NOM dalngarri.flames there see.NPST", "r326_1du_incl_there_low_environment_see_dalngarri", ["ngaliny", "dalngarri", "yinyay", "nyajil"]),
    row(12, "You and I see the jukar-warra whirlwind there.", "Ngaliny jukar-warra yinyay nyajil.", "ngaliny jukar-warra yinyay nyajil | 1du.incl.NOM jukar.warra.whirlwind there see.NPST", "r326_1du_incl_there_low_environment_see_jukar_warra", ["ngaliny", "jukar-warra", "yinyay", "nyajil"]),
    row(13, "You and I see the kambar white clay there.", "Ngaliny kambar yinyay nyajil.", "ngaliny kambar yinyay nyajil | 1du.incl.NOM kambar.white.clay there see.NPST", "r326_1du_incl_there_low_environment_see_kambar", ["ngaliny", "kambar", "yinyay", "nyajil"]),
    row(14, "You and I see the kurrkal shortcut there.", "Ngaliny kurrkal yinyay nyajil.", "ngaliny kurrkal yinyay nyajil | 1du.incl.NOM kurrkal.shortcut there see.NPST", "r326_1du_incl_there_low_environment_see_kurrkal", ["ngaliny", "kurrkal", "yinyay", "nyajil"]),
    row(15, "You and I see the marrka calm sea there.", "Ngaliny marrka yinyay nyajil.", "ngaliny marrka yinyay nyajil | 1du.incl.NOM marrka.calm.sea there see.NPST", "r326_1du_incl_there_low_environment_see_marrka", ["ngaliny", "marrka", "yinyay", "nyajil"]),
    row(16, "You and I see the miral water scum there.", "Ngaliny miral yinyay nyajil.", "ngaliny miral yinyay nyajil | 1du.incl.NOM miral.water.scum there see.NPST", "r326_1du_incl_there_low_environment_see_miral", ["ngaliny", "miral", "yinyay", "nyajil"]),
    row(17, "You and I see the mulawajarr star there.", "Ngaliny mulawajarr yinyay nyajil.", "ngaliny mulawajarr yinyay nyajil | 1du.incl.NOM mulawajarr.star there see.NPST", "r326_1du_incl_there_low_environment_see_mulawajarr", ["ngaliny", "mulawajarr", "yinyay", "nyajil"]),
    row(18, "You and I see the nganyil haze there.", "Ngaliny nganyil yinyay nyajil.", "ngaliny nganyil yinyay nyajil | 1du.incl.NOM nganyil.haze there see.NPST", "r326_1du_incl_there_low_environment_see_nganyil", ["ngaliny", "nganyil", "yinyay", "nyajil"]),
    row(19, "You and I see the nyamun natural debris there.", "Ngaliny nyamun yinyay nyajil.", "ngaliny nyamun yinyay nyajil | 1du.incl.NOM nyamun.natural.debris there see.NPST", "r326_1du_incl_there_low_environment_see_nyamun", ["ngaliny", "nyamun", "yinyay", "nyajil"]),
    row(20, "You and I see the wurrbal fog there.", "Ngaliny wurrbal yinyay nyajil.", "ngaliny wurrbal yinyay nyajil | 1du.incl.NOM wurrbal.fog there see.NPST", "r326_1du_incl_there_low_environment_see_wurrbal", ["ngaliny", "wurrbal", "yinyay", "nyajil"]),
    row(21, "You two see the dalngarri flames here.", "Yubal dalngarri yaluy nyajil.", "yubal dalngarri yaluy nyajil | 2du.NOM dalngarri.flames here see.NPST", "r326_2du_here_low_environment_see_dalngarri", ["yubal", "dalngarri", "yaluy", "nyajil"]),
    row(22, "You two see the jukar-warra whirlwind here.", "Yubal jukar-warra yaluy nyajil.", "yubal jukar-warra yaluy nyajil | 2du.NOM jukar.warra.whirlwind here see.NPST", "r326_2du_here_low_environment_see_jukar_warra", ["yubal", "jukar-warra", "yaluy", "nyajil"]),
    row(23, "You two see the kambar white clay here.", "Yubal kambar yaluy nyajil.", "yubal kambar yaluy nyajil | 2du.NOM kambar.white.clay here see.NPST", "r326_2du_here_low_environment_see_kambar", ["yubal", "kambar", "yaluy", "nyajil"]),
    row(24, "You two see the kurrkal shortcut here.", "Yubal kurrkal yaluy nyajil.", "yubal kurrkal yaluy nyajil | 2du.NOM kurrkal.shortcut here see.NPST", "r326_2du_here_low_environment_see_kurrkal", ["yubal", "kurrkal", "yaluy", "nyajil"]),
    row(25, "You two see the marrka calm sea here.", "Yubal marrka yaluy nyajil.", "yubal marrka yaluy nyajil | 2du.NOM marrka.calm.sea here see.NPST", "r326_2du_here_low_environment_see_marrka", ["yubal", "marrka", "yaluy", "nyajil"]),
    row(26, "You two see the miral water scum here.", "Yubal miral yaluy nyajil.", "yubal miral yaluy nyajil | 2du.NOM miral.water.scum here see.NPST", "r326_2du_here_low_environment_see_miral", ["yubal", "miral", "yaluy", "nyajil"]),
    row(27, "You two see the mulawajarr star here.", "Yubal mulawajarr yaluy nyajil.", "yubal mulawajarr yaluy nyajil | 2du.NOM mulawajarr.star here see.NPST", "r326_2du_here_low_environment_see_mulawajarr", ["yubal", "mulawajarr", "yaluy", "nyajil"]),
    row(28, "You two see the nganyil haze here.", "Yubal nganyil yaluy nyajil.", "yubal nganyil yaluy nyajil | 2du.NOM nganyil.haze here see.NPST", "r326_2du_here_low_environment_see_nganyil", ["yubal", "nganyil", "yaluy", "nyajil"]),
    row(29, "You two see the nyamun natural debris here.", "Yubal nyamun yaluy nyajil.", "yubal nyamun yaluy nyajil | 2du.NOM nyamun.natural.debris here see.NPST", "r326_2du_here_low_environment_see_nyamun", ["yubal", "nyamun", "yaluy", "nyajil"]),
    row(30, "You two see the wurrbal fog here.", "Yubal wurrbal yaluy nyajil.", "yubal wurrbal yaluy nyajil | 2du.NOM wurrbal.fog here see.NPST", "r326_2du_here_low_environment_see_wurrbal", ["yubal", "wurrbal", "yaluy", "nyajil"]),
    row(31, "You all see the dalngarri flames there.", "Yurra dalngarri yinyay nyajil.", "yurra dalngarri yinyay nyajil | 2pl.NOM dalngarri.flames there see.NPST", "r326_2pl_there_low_environment_see_dalngarri", ["yurra", "dalngarri", "yinyay", "nyajil"]),
    row(32, "You all see the jukar-warra whirlwind there.", "Yurra jukar-warra yinyay nyajil.", "yurra jukar-warra yinyay nyajil | 2pl.NOM jukar.warra.whirlwind there see.NPST", "r326_2pl_there_low_environment_see_jukar_warra", ["yurra", "jukar-warra", "yinyay", "nyajil"]),
    row(33, "You all see the kambar white clay there.", "Yurra kambar yinyay nyajil.", "yurra kambar yinyay nyajil | 2pl.NOM kambar.white.clay there see.NPST", "r326_2pl_there_low_environment_see_kambar", ["yurra", "kambar", "yinyay", "nyajil"]),
    row(34, "You all see the kurrkal shortcut there.", "Yurra kurrkal yinyay nyajil.", "yurra kurrkal yinyay nyajil | 2pl.NOM kurrkal.shortcut there see.NPST", "r326_2pl_there_low_environment_see_kurrkal", ["yurra", "kurrkal", "yinyay", "nyajil"]),
    row(35, "You all see the marrka calm sea there.", "Yurra marrka yinyay nyajil.", "yurra marrka yinyay nyajil | 2pl.NOM marrka.calm.sea there see.NPST", "r326_2pl_there_low_environment_see_marrka", ["yurra", "marrka", "yinyay", "nyajil"]),
    row(36, "You all see the miral water scum there.", "Yurra miral yinyay nyajil.", "yurra miral yinyay nyajil | 2pl.NOM miral.water.scum there see.NPST", "r326_2pl_there_low_environment_see_miral", ["yurra", "miral", "yinyay", "nyajil"]),
    row(37, "You all see the mulawajarr star there.", "Yurra mulawajarr yinyay nyajil.", "yurra mulawajarr yinyay nyajil | 2pl.NOM mulawajarr.star there see.NPST", "r326_2pl_there_low_environment_see_mulawajarr", ["yurra", "mulawajarr", "yinyay", "nyajil"]),
    row(38, "You all see the nganyil haze there.", "Yurra nganyil yinyay nyajil.", "yurra nganyil yinyay nyajil | 2pl.NOM nganyil.haze there see.NPST", "r326_2pl_there_low_environment_see_nganyil", ["yurra", "nganyil", "yinyay", "nyajil"]),
    row(39, "You all see the nyamun natural debris there.", "Yurra nyamun yinyay nyajil.", "yurra nyamun yinyay nyajil | 2pl.NOM nyamun.natural.debris there see.NPST", "r326_2pl_there_low_environment_see_nyamun", ["yurra", "nyamun", "yinyay", "nyajil"]),
    row(40, "You all see the wurrbal fog there.", "Yurra wurrbal yinyay nyajil.", "yurra wurrbal yinyay nyajil | 2pl.NOM wurrbal.fog there see.NPST", "r326_2pl_there_low_environment_see_wurrbal", ["yurra", "wurrbal", "yinyay", "nyajil"]),
    row(41, "We see the dalngarri flames here.", "Ngana dalngarri yaluy nyajil.", "ngana dalngarri yaluy nyajil | 1pl.incl.NOM dalngarri.flames here see.NPST", "r326_1pl_incl_here_low_environment_see_dalngarri", ["ngana", "dalngarri", "yaluy", "nyajil"]),
    row(42, "We see the jukar-warra whirlwind here.", "Ngana jukar-warra yaluy nyajil.", "ngana jukar-warra yaluy nyajil | 1pl.incl.NOM jukar.warra.whirlwind here see.NPST", "r326_1pl_incl_here_low_environment_see_jukar_warra", ["ngana", "jukar-warra", "yaluy", "nyajil"]),
    row(43, "We see the kambar white clay here.", "Ngana kambar yaluy nyajil.", "ngana kambar yaluy nyajil | 1pl.incl.NOM kambar.white.clay here see.NPST", "r326_1pl_incl_here_low_environment_see_kambar", ["ngana", "kambar", "yaluy", "nyajil"]),
    row(44, "We see the kurrkal shortcut here.", "Ngana kurrkal yaluy nyajil.", "ngana kurrkal yaluy nyajil | 1pl.incl.NOM kurrkal.shortcut here see.NPST", "r326_1pl_incl_here_low_environment_see_kurrkal", ["ngana", "kurrkal", "yaluy", "nyajil"]),
    row(45, "We see the marrka calm sea here.", "Ngana marrka yaluy nyajil.", "ngana marrka yaluy nyajil | 1pl.incl.NOM marrka.calm.sea here see.NPST", "r326_1pl_incl_here_low_environment_see_marrka", ["ngana", "marrka", "yaluy", "nyajil"]),
    row(46, "We see the miral water scum here.", "Ngana miral yaluy nyajil.", "ngana miral yaluy nyajil | 1pl.incl.NOM miral.water.scum here see.NPST", "r326_1pl_incl_here_low_environment_see_miral", ["ngana", "miral", "yaluy", "nyajil"]),
    row(47, "We see the mulawajarr star here.", "Ngana mulawajarr yaluy nyajil.", "ngana mulawajarr yaluy nyajil | 1pl.incl.NOM mulawajarr.star here see.NPST", "r326_1pl_incl_here_low_environment_see_mulawajarr", ["ngana", "mulawajarr", "yaluy", "nyajil"]),
    row(48, "We see the nganyil haze here.", "Ngana nganyil yaluy nyajil.", "ngana nganyil yaluy nyajil | 1pl.incl.NOM nganyil.haze here see.NPST", "r326_1pl_incl_here_low_environment_see_nganyil", ["ngana", "nganyil", "yaluy", "nyajil"]),
    row(49, "We see the nyamun natural debris here.", "Ngana nyamun yaluy nyajil.", "ngana nyamun yaluy nyajil | 1pl.incl.NOM nyamun.natural.debris here see.NPST", "r326_1pl_incl_here_low_environment_see_nyamun", ["ngana", "nyamun", "yaluy", "nyajil"]),
    row(50, "We see the wurrbal fog here.", "Ngana wurrbal yaluy nyajil.", "ngana wurrbal yaluy nyajil | 1pl.incl.NOM wurrbal.fog here see.NPST", "r326_1pl_incl_here_low_environment_see_wurrbal", ["ngana", "wurrbal", "yaluy", "nyajil"]),
]


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")
    if len({r[1] for r in ROWS}) != 50:
        issues.append("duplicate English surface inside batch")
    if len({r[2] for r in ROWS}) != 50:
        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)
        band = (seq - 1) // 10
        target = TARGET_ORDER[(seq - 1) % 10]
        pronoun, demo, prefix, location, person_gloss = FRAME_SPECS[band]
        expected_kuku = f"{pronoun.capitalize()} {target} {demo} nyajil."
        expected_english = f"{prefix} the {TARGET_LABELS[target]} {location}."
        expected_analysis = f"{pronoun} {target} {demo} nyajil | {person_gloss}.NOM {TARGET_GLOSSES[target]} {location} see.NPST"
        low_kuku = kuku.lower()
        low_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\.]|$)", low_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)}\b", low_english)]
        if denied_english:
            issues.append(f"{seq}: denied English drift {denied_english}")
        if kuku != expected_kuku:
            issues.append(f"{seq}: Kuku mismatch expected {expected_kuku!r}, got {kuku!r}")
        if english != expected_english:
            issues.append(f"{seq}: English mismatch expected {expected_english!r}, got {english!r}")
        if analysis != expected_analysis:
            issues.append(f"{seq}: analysis mismatch expected {expected_analysis!r}, got {analysis!r}")
        if words != [pronoun, target, demo, "nyajil"]:
            issues.append(f"{seq}: words mismatch {words}")
        if low_kuku.rstrip(".") != " ".join(words):
            issues.append(f"{seq}: Kuku does not match words_used")
        if not frame.startswith("r326_"):
            issues.append(f"{seq}: frame not r326 {frame}")
        if TARGET_EVIDENCE[target] not in evidence or SUPPORT_EVIDENCE not in evidence:
            issues.append(f"{seq}: evidence contract mismatch")
        if confidence != TARGET_CONFIDENCE[target]:
            issues.append(f"{seq}: confidence mismatch {confidence}")
    return issues


def main() -> int:
    issues = scan_static()
    if issues:
        print("\n".join(issues))
        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")
    target_counts = count_words(db, TARGET_ORDER)
    if target_counts != EXPECTED_TARGET_COVERAGE:
        raise SystemExit(f"target coverage changed; rerun source/scope audit: {target_counts}")
    support_counts = count_words(db, ["ngali", "ngaliny", "yubal", "yurra", "ngana", "yaluy", "yinyay", "nyajil"])
    collisions = []
    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!='rejected'",
            (kuku,),
        ).fetchall()
        if hit:
            collisions.append((kuku, hit))
    if collisions:
        raise SystemExit(f"duplicate preflight failed: {collisions}")
    verified_before, words_before = db.execute(
        "SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) "
        "FROM sentences WHERE status IN ('accepted','revised')"
    ).fetchone()
    notes = (
        "LOW-COVERAGE VISIBLE ENVIRONMENT NON-SINGULAR NONPAST PERCEPTION AFTER RULE 325 (Rule 326). 50 "
        "under-review rows over ten 0-9-use noun heads: dalngarri, jukar-warra, kambar, kurrkal, marrka, miral, "
        "mulawajarr, nganyil, nyamun, wurrbal. SUPPORT: bare nominative ngali, ngaliny, yubal, yurra, ngana; "
        "Yalanji yaluy/yinyay; dictionary-attested nonpast nyajil. Exact conservative environmental labels only. "
        "No urine/death, compound decomposition, wet-season, travel/route use, salt-pan/oyster, whale oil, dialect "
        "substitution, near-form drift, household waste, river/rising, weather event, property, source relation, "
        "target case, past/purposive/reduplicated perception, support inflection, or carried trap."
    )
    cur = db.execute(
        "INSERT INTO batches(label,cheatsheet_version,guide_version,notes,created_at) VALUES(?,?,?,?,?)",
        (LABEL, "Rule 326 under review", "operator-guide-2026-07-10", 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, en, ku, an, fr, ti, wo, lo, ev, "under_review", co,
             "project_approved_synthetic_pending_elder_verification", "codex-main", NOW, NOW)
            for seq, en, ku, an, fr, ti, wo, lo, ev, co in ROWS
        ],
    )
    record_event(db, batch_label=LABEL, phase="source", event_type="source-readback", actor="codex", detail="Reread all ten selected dictionary environment entries; exact-searched every target in Patz (marrka occurs only as the salt-pan etymon in marrka-bina, the other nine do not occur); reread Patz environment noun status, non-singular nominative pronouns, Yalanji local demonstratives, nonpast nyaji-l, perception A/O syntax, and dictionary running-form nyajil before authoring Rule 326.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="candidate-audit", actor="codex", detail="Structured environment audit rejected undefined/place-name walba-murru, culturally specific nurru ancestral home, conflicting jangka, English-loan maji, event-heavy minji/ngarngkul splash variants, ambiguous yikin bank/river, and non-noun locationals. Selected ten direct, visible, lint-safe environment nouns with stable bounded readings and coverage 0-9.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="low-environment-nonsingular-nonpast-scope", actor="codex", detail="Locked ten low-coverage dictionary environment heads to bare reference in five sourced non-singular nonpast-perception frames. Polysemous and near forms are sense-bounded; compounds remain indivisible; no weather event, route use, material use, dialect alignment, source relation, property, case, or productive morphology is opened.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="coverage-audit", actor="codex", 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="codex", detail="All 50 Rule 326 exact Kuku surfaces are internally unique and returned no accepted, revised, or under-review corpus collisions.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="static-guard-pass", actor="codex", detail="Static guard passed: 50 explicit rows, exact Kuku/English/analysis/evidence/confidence contracts, one selected environment head each, bare non-singular nominative pronouns and Yalanji demonstratives, final nyajil, no loans, no denied forms, and no polysemy, near-form, weather-event, route/material-use, dialect, source-relation, productive-morphology, case, or support drift.", command="python3 -m py_compile batch_scripts/bank_368.py batch_scripts/close_368.py && explicit-row/lesson/lexeme AST inventory && scan_static/lint probes", output_excerpt="50 explicit rows; scan_static issues 0; 50 linter probes issues 0; collisions 0", artifact_path="batch_scripts/bank_368.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="codex", detail=f"Banked 50 Rule 326 rows as under_review. Verified remains {verified_before}; Kuku words remain {words_before}.", command="python3 batch_scripts/bank_368.py", output_excerpt=f"banked batch-368: 50 under_review; verified_before={verified_before}; kuku_words_before={words_before}", artifact_path=PROCESS_LOG, created_at=NOW)
    db.commit()
    print("banked batch-368: 50 rows under_review")
    return 0


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