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

TARGET_EVIDENCE = {
    "jalbil": "Dictionary yaml lines 7374-7386 give jalbil as a fauna-reptile noun whose primary sense is a lizard and whose prose adds colour, size, a mythological being, Snapper Island, and story material. Rule 339 admits only conservative jalbil lizard reference; colour, size, personification, island, cooking story, source, and events remain closed.",
    "kalkarrumbarr": "Dictionary yaml lines 12679-12689 give kalkarrumbarr as a fauna-reptile lizard noun with size and legend prose. Rule 339 admits lizard reference only; measurement, legend, role, behavior, source, possession, and events remain closed.",
    "kurmba-kurmba": "Dictionary yaml lines 16154-16169 give exact hyphenated kurmba-kurmba as a fauna-reptile slippery-lizard noun and also a mythological person with kin/marriage story. Rule 339 uses the whole lizard head only; free-base/productive reduplication, slipperiness predication, personification, kinship, marriage, place/story, source, and events remain closed.",
    "jimalili": "Dictionary yaml lines 9284-9294 give jimalili as a fauna-reptile mountain-lizard noun with a firestick-teaching cultural note. Rule 339 admits conservative jimalili lizard reference only; mountain habitat, teaching, firestick, origin/cultural story, source, and events remain closed.",
    "yuray": "Dictionary yaml lines 31016-31033 contain two exact noun-typed fauna-reptile yuray records, both licensing sea-snake/snake reference. Rule 339 uses conservative yuray snake without adjudicating duplicate-record identity, species, sea habitat, danger, poison, source, possession, or events.",
    "bakay": "Dictionary yaml lines 635-653 contain two exact noun-typed fauna-reptile bakay records licensing water-snake/snake reference and carrying colour/size detail. Rule 339 uses conservative bakay snake without collapsing records or adding size, colour, water habitat, danger, poison, source, possession, or events.",
    "kungkun": "Dictionary yaml lines 15748-15768 contain two exact noun-typed fauna-reptile kungkun records with carpet-snake/North-Queensland-python and brown-tree-snake labels. Rule 339 uses conservative kungkun snake only; duplicate identity, species resolution, tree habitat, colour, body, danger, source, possession, and events remain closed.",
    "bilbil-bilbil": "Dictionary yaml lines 2529-2542 give exact hyphenated bilbil-bilbil as a fully reduplicated fauna-reptile gecko noun. Rule 339 admits the whole gecko head only; free base, new productive rule, size, shelter, night, sound, feeding, habitat, behavior, source, and events remain closed.",
    "bajalji": "Dictionary yaml lines 387-409 contain two exact noun-typed fauna-reptile bajalji records: rock python with distribution/lore and poisonous snake. Rule 339 retains the established conservative bajalji python label without resolving duplicate identity or adding poison, colour/head, distribution/place, chasing lore, baldness/hat, danger, source, possession, or events.",
    "jabil": "Dictionary yaml lines 6929-6951 distinguish transitive verb jabil 'kick' from homophonous fauna-reptile noun jabil 'scrub lizard'. Rule 339 admits only the bare lizard noun O; kicking, verbal inflection, scrub habitat, catching, source, possession, and events remain closed.",
}

SUPPORT_EVIDENCE = (
    "Patz line 1476 includes fauna among noun referents. Lines 2577-2584 source bare nominative ngali, ngaliny, "
    "yubal, yurra, and first-plural-exclusive nganjin; lines 2834-2847 source Yalanji yaluy 'here' and yinyay "
    "'there'. Examples at 2021-2024 and 2568-2571 attest nyaji-ny 'see-PAST'; 3160-3162 define past -ny; and "
    "4256-4265 license nominative pronoun A with bare absolutive O in sensory perception. Dictionary examples "
    "23876-23883 and 23968-23976 contain running nyajin. Fresh Patz target search found no standalone selected "
    "reptile noun: kungkun occurs only inside unrelated kungkunbunga- 'watch closely' at 5504/5520, so it supplies "
    "no snake-noun evidence. Rule 339 relies on direct dictionary noun records plus independently sourced constructional evidence."
)

TARGET_ORDER = ["jalbil", "kalkarrumbarr", "kurmba-kurmba", "jimalili", "yuray", "bakay", "kungkun", "bilbil-bilbil", "bajalji", "jabil"]
TARGET_LABELS = {"jalbil": "jalbil lizard", "kalkarrumbarr": "kalkarrumbarr lizard", "kurmba-kurmba": "kurmba-kurmba lizard", "jimalili": "jimalili lizard", "yuray": "yuray snake", "bakay": "bakay snake", "kungkun": "kungkun snake", "bilbil-bilbil": "bilbil-bilbil gecko", "bajalji": "bajalji python", "jabil": "jabil lizard"}
TARGET_GLOSSES = {"jalbil": "jalbil.lizard", "kalkarrumbarr": "kalkarrumbarr.lizard", "kurmba-kurmba": "kurmba-kurmba.lizard", "jimalili": "jimalili.lizard", "yuray": "yuray.snake", "bakay": "bakay.snake", "kungkun": "kungkun.snake", "bilbil-bilbil": "bilbil-bilbil.gecko", "bajalji": "bajalji.python", "jabil": "jabil.lizard"}
TARGET_CONFIDENCE = {"jalbil": "0.87", "kalkarrumbarr": "0.88", "kurmba-kurmba": "0.87", "jimalili": "0.89", "yuray": "0.89", "bakay": "0.88", "kungkun": "0.86", "bilbil-bilbil": "0.92", "bajalji": "0.84", "jabil": "0.88"}
EXPECTED_TARGET_COVERAGE = {"jalbil": 15, "kalkarrumbarr": 15, "kurmba-kurmba": 15, "jimalili": 16, "yuray": 16, "bakay": 17, "kungkun": 17, "bilbil-bilbil": 18, "bajalji": 19, "jabil": 20}
EXPECTED_SUPPORT_COVERAGE = {"ngali": 623, "ngaliny": 565, "yubal": 575, "yurra": 560, "nganjin": 288, "ngana": 1115, "yaluy": 4362, "yinyay": 4524, "nyajin": 2000}
FRAME_SPECS = [
    ("ngali", "yinyay", "We two, not you, saw", "there", "1du.excl"),
    ("ngaliny", "yaluy", "You and I saw", "here", "1du.incl"),
    ("yubal", "yinyay", "You two saw", "there", "2du"),
    ("yurra", "yaluy", "You all saw", "here", "2pl"),
    ("nganjin", "yinyay", "We, not you, saw", "there", "1pl.excl"),
]

DENY_KUKU = {"ngayu", "yundu", "nyulu", "yulu", "bula", "jana", "iana", "ngana", "bilbil", "kurmba", "yalay", "yala", "yanyu", "yinya", "nyajil", "nyaji-l", "nyaji-nka", "nyaji-l-nyaji", "milka", "baykal", "jalngkay"}
DENY_ENGLISH = ("greenish", "black colour", "inch", "feet long", "myth", "legend", "person", "island", "cook", "husband", "sister", "marriage", "firestick", "teach", "water habitat", "sea habitat", "tree habitat", "brown", "yellow", "white head", "poison", "danger", "chase", "bald", "hat", "kick", "scrub habitat", "catch", "source", "possess", "event", "productive reduplication", "free base")


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, saw the jalbil lizard there.", "Ngali jalbil yinyay nyajin.", "ngali jalbil yinyay nyajin | 1du.excl.NOM jalbil.lizard there see.PST", "r339_1du_excl_there_reptile_saw_jalbil", ["ngali", "jalbil", "yinyay", "nyajin"]),
    row(2, "We two, not you, saw the kalkarrumbarr lizard there.", "Ngali kalkarrumbarr yinyay nyajin.", "ngali kalkarrumbarr yinyay nyajin | 1du.excl.NOM kalkarrumbarr.lizard there see.PST", "r339_1du_excl_there_reptile_saw_kalkarrumbarr", ["ngali", "kalkarrumbarr", "yinyay", "nyajin"]),
    row(3, "We two, not you, saw the kurmba-kurmba lizard there.", "Ngali kurmba-kurmba yinyay nyajin.", "ngali kurmba-kurmba yinyay nyajin | 1du.excl.NOM kurmba-kurmba.lizard there see.PST", "r339_1du_excl_there_reptile_saw_kurmba_kurmba", ["ngali", "kurmba-kurmba", "yinyay", "nyajin"]),
    row(4, "We two, not you, saw the jimalili lizard there.", "Ngali jimalili yinyay nyajin.", "ngali jimalili yinyay nyajin | 1du.excl.NOM jimalili.lizard there see.PST", "r339_1du_excl_there_reptile_saw_jimalili", ["ngali", "jimalili", "yinyay", "nyajin"]),
    row(5, "We two, not you, saw the yuray snake there.", "Ngali yuray yinyay nyajin.", "ngali yuray yinyay nyajin | 1du.excl.NOM yuray.snake there see.PST", "r339_1du_excl_there_reptile_saw_yuray", ["ngali", "yuray", "yinyay", "nyajin"]),
    row(6, "We two, not you, saw the bakay snake there.", "Ngali bakay yinyay nyajin.", "ngali bakay yinyay nyajin | 1du.excl.NOM bakay.snake there see.PST", "r339_1du_excl_there_reptile_saw_bakay", ["ngali", "bakay", "yinyay", "nyajin"]),
    row(7, "We two, not you, saw the kungkun snake there.", "Ngali kungkun yinyay nyajin.", "ngali kungkun yinyay nyajin | 1du.excl.NOM kungkun.snake there see.PST", "r339_1du_excl_there_reptile_saw_kungkun", ["ngali", "kungkun", "yinyay", "nyajin"]),
    row(8, "We two, not you, saw the bilbil-bilbil gecko there.", "Ngali bilbil-bilbil yinyay nyajin.", "ngali bilbil-bilbil yinyay nyajin | 1du.excl.NOM bilbil-bilbil.gecko there see.PST", "r339_1du_excl_there_reptile_saw_bilbil_bilbil", ["ngali", "bilbil-bilbil", "yinyay", "nyajin"]),
    row(9, "We two, not you, saw the bajalji python there.", "Ngali bajalji yinyay nyajin.", "ngali bajalji yinyay nyajin | 1du.excl.NOM bajalji.python there see.PST", "r339_1du_excl_there_reptile_saw_bajalji", ["ngali", "bajalji", "yinyay", "nyajin"]),
    row(10, "We two, not you, saw the jabil lizard there.", "Ngali jabil yinyay nyajin.", "ngali jabil yinyay nyajin | 1du.excl.NOM jabil.lizard there see.PST", "r339_1du_excl_there_reptile_saw_jabil", ["ngali", "jabil", "yinyay", "nyajin"]),
    row(11, "You and I saw the jalbil lizard here.", "Ngaliny jalbil yaluy nyajin.", "ngaliny jalbil yaluy nyajin | 1du.incl.NOM jalbil.lizard here see.PST", "r339_1du_incl_here_reptile_saw_jalbil", ["ngaliny", "jalbil", "yaluy", "nyajin"]),
    row(12, "You and I saw the kalkarrumbarr lizard here.", "Ngaliny kalkarrumbarr yaluy nyajin.", "ngaliny kalkarrumbarr yaluy nyajin | 1du.incl.NOM kalkarrumbarr.lizard here see.PST", "r339_1du_incl_here_reptile_saw_kalkarrumbarr", ["ngaliny", "kalkarrumbarr", "yaluy", "nyajin"]),
    row(13, "You and I saw the kurmba-kurmba lizard here.", "Ngaliny kurmba-kurmba yaluy nyajin.", "ngaliny kurmba-kurmba yaluy nyajin | 1du.incl.NOM kurmba-kurmba.lizard here see.PST", "r339_1du_incl_here_reptile_saw_kurmba_kurmba", ["ngaliny", "kurmba-kurmba", "yaluy", "nyajin"]),
    row(14, "You and I saw the jimalili lizard here.", "Ngaliny jimalili yaluy nyajin.", "ngaliny jimalili yaluy nyajin | 1du.incl.NOM jimalili.lizard here see.PST", "r339_1du_incl_here_reptile_saw_jimalili", ["ngaliny", "jimalili", "yaluy", "nyajin"]),
    row(15, "You and I saw the yuray snake here.", "Ngaliny yuray yaluy nyajin.", "ngaliny yuray yaluy nyajin | 1du.incl.NOM yuray.snake here see.PST", "r339_1du_incl_here_reptile_saw_yuray", ["ngaliny", "yuray", "yaluy", "nyajin"]),
    row(16, "You and I saw the bakay snake here.", "Ngaliny bakay yaluy nyajin.", "ngaliny bakay yaluy nyajin | 1du.incl.NOM bakay.snake here see.PST", "r339_1du_incl_here_reptile_saw_bakay", ["ngaliny", "bakay", "yaluy", "nyajin"]),
    row(17, "You and I saw the kungkun snake here.", "Ngaliny kungkun yaluy nyajin.", "ngaliny kungkun yaluy nyajin | 1du.incl.NOM kungkun.snake here see.PST", "r339_1du_incl_here_reptile_saw_kungkun", ["ngaliny", "kungkun", "yaluy", "nyajin"]),
    row(18, "You and I saw the bilbil-bilbil gecko here.", "Ngaliny bilbil-bilbil yaluy nyajin.", "ngaliny bilbil-bilbil yaluy nyajin | 1du.incl.NOM bilbil-bilbil.gecko here see.PST", "r339_1du_incl_here_reptile_saw_bilbil_bilbil", ["ngaliny", "bilbil-bilbil", "yaluy", "nyajin"]),
    row(19, "You and I saw the bajalji python here.", "Ngaliny bajalji yaluy nyajin.", "ngaliny bajalji yaluy nyajin | 1du.incl.NOM bajalji.python here see.PST", "r339_1du_incl_here_reptile_saw_bajalji", ["ngaliny", "bajalji", "yaluy", "nyajin"]),
    row(20, "You and I saw the jabil lizard here.", "Ngaliny jabil yaluy nyajin.", "ngaliny jabil yaluy nyajin | 1du.incl.NOM jabil.lizard here see.PST", "r339_1du_incl_here_reptile_saw_jabil", ["ngaliny", "jabil", "yaluy", "nyajin"]),
    row(21, "You two saw the jalbil lizard there.", "Yubal jalbil yinyay nyajin.", "yubal jalbil yinyay nyajin | 2du.NOM jalbil.lizard there see.PST", "r339_2du_there_reptile_saw_jalbil", ["yubal", "jalbil", "yinyay", "nyajin"]),
    row(22, "You two saw the kalkarrumbarr lizard there.", "Yubal kalkarrumbarr yinyay nyajin.", "yubal kalkarrumbarr yinyay nyajin | 2du.NOM kalkarrumbarr.lizard there see.PST", "r339_2du_there_reptile_saw_kalkarrumbarr", ["yubal", "kalkarrumbarr", "yinyay", "nyajin"]),
    row(23, "You two saw the kurmba-kurmba lizard there.", "Yubal kurmba-kurmba yinyay nyajin.", "yubal kurmba-kurmba yinyay nyajin | 2du.NOM kurmba-kurmba.lizard there see.PST", "r339_2du_there_reptile_saw_kurmba_kurmba", ["yubal", "kurmba-kurmba", "yinyay", "nyajin"]),
    row(24, "You two saw the jimalili lizard there.", "Yubal jimalili yinyay nyajin.", "yubal jimalili yinyay nyajin | 2du.NOM jimalili.lizard there see.PST", "r339_2du_there_reptile_saw_jimalili", ["yubal", "jimalili", "yinyay", "nyajin"]),
    row(25, "You two saw the yuray snake there.", "Yubal yuray yinyay nyajin.", "yubal yuray yinyay nyajin | 2du.NOM yuray.snake there see.PST", "r339_2du_there_reptile_saw_yuray", ["yubal", "yuray", "yinyay", "nyajin"]),
    row(26, "You two saw the bakay snake there.", "Yubal bakay yinyay nyajin.", "yubal bakay yinyay nyajin | 2du.NOM bakay.snake there see.PST", "r339_2du_there_reptile_saw_bakay", ["yubal", "bakay", "yinyay", "nyajin"]),
    row(27, "You two saw the kungkun snake there.", "Yubal kungkun yinyay nyajin.", "yubal kungkun yinyay nyajin | 2du.NOM kungkun.snake there see.PST", "r339_2du_there_reptile_saw_kungkun", ["yubal", "kungkun", "yinyay", "nyajin"]),
    row(28, "You two saw the bilbil-bilbil gecko there.", "Yubal bilbil-bilbil yinyay nyajin.", "yubal bilbil-bilbil yinyay nyajin | 2du.NOM bilbil-bilbil.gecko there see.PST", "r339_2du_there_reptile_saw_bilbil_bilbil", ["yubal", "bilbil-bilbil", "yinyay", "nyajin"]),
    row(29, "You two saw the bajalji python there.", "Yubal bajalji yinyay nyajin.", "yubal bajalji yinyay nyajin | 2du.NOM bajalji.python there see.PST", "r339_2du_there_reptile_saw_bajalji", ["yubal", "bajalji", "yinyay", "nyajin"]),
    row(30, "You two saw the jabil lizard there.", "Yubal jabil yinyay nyajin.", "yubal jabil yinyay nyajin | 2du.NOM jabil.lizard there see.PST", "r339_2du_there_reptile_saw_jabil", ["yubal", "jabil", "yinyay", "nyajin"]),
    row(31, "You all saw the jalbil lizard here.", "Yurra jalbil yaluy nyajin.", "yurra jalbil yaluy nyajin | 2pl.NOM jalbil.lizard here see.PST", "r339_2pl_here_reptile_saw_jalbil", ["yurra", "jalbil", "yaluy", "nyajin"]),
    row(32, "You all saw the kalkarrumbarr lizard here.", "Yurra kalkarrumbarr yaluy nyajin.", "yurra kalkarrumbarr yaluy nyajin | 2pl.NOM kalkarrumbarr.lizard here see.PST", "r339_2pl_here_reptile_saw_kalkarrumbarr", ["yurra", "kalkarrumbarr", "yaluy", "nyajin"]),
    row(33, "You all saw the kurmba-kurmba lizard here.", "Yurra kurmba-kurmba yaluy nyajin.", "yurra kurmba-kurmba yaluy nyajin | 2pl.NOM kurmba-kurmba.lizard here see.PST", "r339_2pl_here_reptile_saw_kurmba_kurmba", ["yurra", "kurmba-kurmba", "yaluy", "nyajin"]),
    row(34, "You all saw the jimalili lizard here.", "Yurra jimalili yaluy nyajin.", "yurra jimalili yaluy nyajin | 2pl.NOM jimalili.lizard here see.PST", "r339_2pl_here_reptile_saw_jimalili", ["yurra", "jimalili", "yaluy", "nyajin"]),
    row(35, "You all saw the yuray snake here.", "Yurra yuray yaluy nyajin.", "yurra yuray yaluy nyajin | 2pl.NOM yuray.snake here see.PST", "r339_2pl_here_reptile_saw_yuray", ["yurra", "yuray", "yaluy", "nyajin"]),
    row(36, "You all saw the bakay snake here.", "Yurra bakay yaluy nyajin.", "yurra bakay yaluy nyajin | 2pl.NOM bakay.snake here see.PST", "r339_2pl_here_reptile_saw_bakay", ["yurra", "bakay", "yaluy", "nyajin"]),
    row(37, "You all saw the kungkun snake here.", "Yurra kungkun yaluy nyajin.", "yurra kungkun yaluy nyajin | 2pl.NOM kungkun.snake here see.PST", "r339_2pl_here_reptile_saw_kungkun", ["yurra", "kungkun", "yaluy", "nyajin"]),
    row(38, "You all saw the bilbil-bilbil gecko here.", "Yurra bilbil-bilbil yaluy nyajin.", "yurra bilbil-bilbil yaluy nyajin | 2pl.NOM bilbil-bilbil.gecko here see.PST", "r339_2pl_here_reptile_saw_bilbil_bilbil", ["yurra", "bilbil-bilbil", "yaluy", "nyajin"]),
    row(39, "You all saw the bajalji python here.", "Yurra bajalji yaluy nyajin.", "yurra bajalji yaluy nyajin | 2pl.NOM bajalji.python here see.PST", "r339_2pl_here_reptile_saw_bajalji", ["yurra", "bajalji", "yaluy", "nyajin"]),
    row(40, "You all saw the jabil lizard here.", "Yurra jabil yaluy nyajin.", "yurra jabil yaluy nyajin | 2pl.NOM jabil.lizard here see.PST", "r339_2pl_here_reptile_saw_jabil", ["yurra", "jabil", "yaluy", "nyajin"]),
    row(41, "We, not you, saw the jalbil lizard there.", "Nganjin jalbil yinyay nyajin.", "nganjin jalbil yinyay nyajin | 1pl.excl.NOM jalbil.lizard there see.PST", "r339_1pl_excl_there_reptile_saw_jalbil", ["nganjin", "jalbil", "yinyay", "nyajin"]),
    row(42, "We, not you, saw the kalkarrumbarr lizard there.", "Nganjin kalkarrumbarr yinyay nyajin.", "nganjin kalkarrumbarr yinyay nyajin | 1pl.excl.NOM kalkarrumbarr.lizard there see.PST", "r339_1pl_excl_there_reptile_saw_kalkarrumbarr", ["nganjin", "kalkarrumbarr", "yinyay", "nyajin"]),
    row(43, "We, not you, saw the kurmba-kurmba lizard there.", "Nganjin kurmba-kurmba yinyay nyajin.", "nganjin kurmba-kurmba yinyay nyajin | 1pl.excl.NOM kurmba-kurmba.lizard there see.PST", "r339_1pl_excl_there_reptile_saw_kurmba_kurmba", ["nganjin", "kurmba-kurmba", "yinyay", "nyajin"]),
    row(44, "We, not you, saw the jimalili lizard there.", "Nganjin jimalili yinyay nyajin.", "nganjin jimalili yinyay nyajin | 1pl.excl.NOM jimalili.lizard there see.PST", "r339_1pl_excl_there_reptile_saw_jimalili", ["nganjin", "jimalili", "yinyay", "nyajin"]),
    row(45, "We, not you, saw the yuray snake there.", "Nganjin yuray yinyay nyajin.", "nganjin yuray yinyay nyajin | 1pl.excl.NOM yuray.snake there see.PST", "r339_1pl_excl_there_reptile_saw_yuray", ["nganjin", "yuray", "yinyay", "nyajin"]),
    row(46, "We, not you, saw the bakay snake there.", "Nganjin bakay yinyay nyajin.", "nganjin bakay yinyay nyajin | 1pl.excl.NOM bakay.snake there see.PST", "r339_1pl_excl_there_reptile_saw_bakay", ["nganjin", "bakay", "yinyay", "nyajin"]),
    row(47, "We, not you, saw the kungkun snake there.", "Nganjin kungkun yinyay nyajin.", "nganjin kungkun yinyay nyajin | 1pl.excl.NOM kungkun.snake there see.PST", "r339_1pl_excl_there_reptile_saw_kungkun", ["nganjin", "kungkun", "yinyay", "nyajin"]),
    row(48, "We, not you, saw the bilbil-bilbil gecko there.", "Nganjin bilbil-bilbil yinyay nyajin.", "nganjin bilbil-bilbil yinyay nyajin | 1pl.excl.NOM bilbil-bilbil.gecko there see.PST", "r339_1pl_excl_there_reptile_saw_bilbil_bilbil", ["nganjin", "bilbil-bilbil", "yinyay", "nyajin"]),
    row(49, "We, not you, saw the bajalji python there.", "Nganjin bajalji yinyay nyajin.", "nganjin bajalji yinyay nyajin | 1pl.excl.NOM bajalji.python there see.PST", "r339_1pl_excl_there_reptile_saw_bajalji", ["nganjin", "bajalji", "yinyay", "nyajin"]),
    row(50, "We, not you, saw the jabil lizard there.", "Nganjin jabil yinyay nyajin.", "nganjin jabil yinyay nyajin | 1pl.excl.NOM jabil.lizard there see.PST", "r339_1pl_excl_there_reptile_saw_jabil", ["nganjin", "jabil", "yinyay", "nyajin"]),
]


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")
    if len({r[2] for r in ROWS}) != 50: issues.append("duplicate Kuku surface")
    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=FRAME_SPECS[band]
        ek=f"{pronoun.capitalize()} {target} {demo} nyajin."; ee=f"{prefix} the {TARGET_LABELS[target]} {location}."; ea=f"{pronoun} {target} {demo} nyajin | {person}.NOM {TARGET_GLOSSES[target]} {location} see.PST"
        if loans: issues.append(f"{seq}: loans {loans}")
        denied=sorted({w for w in DENY_KUKU if re.search(rf"(^|\s){re.escape(w)}([\s\.]|$)",kuku.lower())})
        if denied: issues.append(f"{seq}: denied Kuku {denied}")
        de=[x for x in DENY_ENGLISH if re.search(rf"\b{re.escape(x)}\b",english.lower())]
        if de: issues.append(f"{seq}: English drift {de}")
        if kuku!=ek: issues.append(f"{seq}: Kuku mismatch")
        if english!=ee: issues.append(f"{seq}: English mismatch")
        if analysis!=ea: issues.append(f"{seq}: analysis mismatch")
        if words!=[pronoun,target,demo,"nyajin"]: issues.append(f"{seq}: words mismatch")
        if kuku.lower().rstrip(".")!=" ".join(words): issues.append(f"{seq}: surface/words mismatch")
        if not frame.startswith("r339_"): issues.append(f"{seq}: frame mismatch")
        if TARGET_EVIDENCE[target] not in evidence or SUPPORT_EVIDENCE not in evidence: issues.append(f"{seq}: evidence mismatch")
        if confidence!=TARGET_CONFIDENCE[target]: issues.append(f"{seq}: confidence mismatch")
    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} exists")
    tc=count_words(db,TARGET_ORDER); sc=count_words(db,list(EXPECTED_SUPPORT_COVERAGE))
    if tc!=EXPECTED_TARGET_COVERAGE: raise SystemExit(f"target coverage changed: {tc}")
    if sc!=EXPECTED_SUPPORT_COVERAGE: raise SystemExit(f"support coverage changed: {sc}")
    selected=[]
    selected_specs=[("ngali","yinyay"),("ngaliny","yaluy"),("yubal","yinyay"),("yurra","yaluy"),("nganjin","yinyay")]
    for p,de in selected_specs:
        for t in TARGET_ORDER:
            surf=f"{p.capitalize()} {t} {de} nyajin."
            hits=db.execute("SELECT b.label,s.seq FROM sentences s JOIN batches b ON b.id=s.batch_id WHERE lower(s.kuku)=lower(?) AND s.status!='rejected' ORDER BY b.label,s.seq",(surf,)).fetchall()
            if hits: selected.append((surf,hits))
    if selected: raise SystemExit(f"selected collisions: {selected}")
    inverse_mismatches=[]
    inverse_specs=[("ngali","yaluy"),("ngaliny","yinyay"),("yubal","yaluy"),("yurra","yinyay"),("nganjin","yaluy")]
    for band,(p,de) in enumerate(inverse_specs):
        for offset,t in enumerate(TARGET_ORDER):
            surf=f"{p.capitalize()} {t} {de} nyajin."
            hits=db.execute("SELECT b.label,s.seq FROM sentences s JOIN batches b ON b.id=s.batch_id WHERE lower(s.kuku)=lower(?) AND s.status!='rejected' ORDER BY b.label,s.seq",(surf,)).fetchall()
            expected=[("batch-379",band*10+offset+1)]
            if hits!=expected: inverse_mismatches.append((surf,hits,expected))
    if inverse_mismatches: raise SystemExit(f"inverse complement changed: {inverse_mismatches}")
    verified,words=db.execute("SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) FROM sentences WHERE status IN ('accepted','revised')").fetchone()
    if (verified,words)!=(19097,131747): raise SystemExit(f"baseline changed {verified}/{words}")
    notes=("REPTILE COMPLEMENTARY NON-SINGULAR PAST PERCEPTION AFTER RULE 338 (Rule 339). 50 under-review rows over the Rule 319/337/338 low-coverage reptile set. Duplicate exact records for yuray, bakay, kungkun, and bajalji remain uncollapsed; noun jabil remains distinct from kick verb; myth/lore, colour, size, habitat, poison/danger, species resolution, reduplication, kin/marriage, firestick, place/story, source, possession, and events remain closed. The selected ngali/ngaliny/yubal/yurra/nganjin orientation is empty and exactly complements Batch 379 / Rule 337. Two-here/three-there completes that paradigm and changes the current deictic gap 162->172; the lower-gap inverse is unavailable because it duplicates Batch 379.")
    cur=db.execute("INSERT INTO batches(label,cheatsheet_version,guide_version,notes,created_at) VALUES(?,?,?,?,?)",(LABEL,"Rule 339 under review","operator-guide-2026-07-10",notes,NOW));bid=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(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",[(bid,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])
    events=[
        ("source","source-readback","Reread all selected, duplicate, and homonymous dictionary records; Patz fauna/non-singular-pronoun/local/past/perception passages; dictionary running nyajin; and the target search, whose only apparent hit is unrelated verb kungkunbunga- rather than snake noun kungkun."),
        ("planning","record-sense-audit","Recorded duplicate exact yuray/bakay/kungkun/bajalji records, jabil noun/verb homonym, reduplicated heads, and all myth/lore/property/poison/habitat exclusions without collapsing them."),
        ("planning","construction-gap-audit","Exact five-pronoun x ten-target x two-deictic matrix has 50 occupied inverse cells, all exactly Batch 379 / Rule 337, and 50 empty selected complementary cells."),
        ("planning","deictic-balance-audit","Before banking yaluy=4362 and yinyay=4524, gap 162; selected two-here/three-there completes the contrast and projects 172; lower-gap inverse projects 152 but exactly duplicates Batch 379."),
        ("preflight","coverage-audit",f"Coverage before banking: targets={tc}; support={sc}."),
        ("preflight","duplicate-preflight","Selected 50 surfaces are internally unique and collision-free; inverse 50 map one-for-one to Batch 379 and are deliberately excluded."),
        ("preflight","static-guard-pass","50 explicit rows; exact complementary non-singular contracts and number/clusivity fidelity; conservative creature labels; no record collapse, homonym, lore/property/event, loan, case, or support drift."),
    ]
    for phase,etype,detail in events: record_event(db,batch_label=LABEL,phase=phase,event_type=etype,actor="codex",detail=detail,artifact_path=PROCESS_LOG,created_at=NOW)
    record_event(db,batch_label=LABEL,phase="bank",event_type="bank-success",actor="codex",detail=f"Banked 50 Rule 339 rows under_review; verified remains {verified}; words {words}.",command="python3 batch_scripts/bank_381.py",output_excerpt="50 under_review",artifact_path=PROCESS_LOG,created_at=NOW)
    db.commit();print("banked batch-381: 50 rows under_review");return 0


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



