#!/usr/bin/env python3
"""Bank Batch 386: Rule 344 sense-qualified reptile-set-C non-singular perception."""

from __future__ import annotations

import datetime as dt
import hashlib
import json
import re
import sqlite3

import yaml

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"
DICTIONARY = "/mnt/donto-data/workspace/mobtranslate.com/dictionaries/kuku_yalanji/dictionary.yaml"
GRAMMAR = "/mnt/donto-data/workspace/mobtranslate.com/dictionaries/kuku_yalanji/grammar.md"
LABEL = "batch-386"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-386-sense-qualified-reptile-set-c-nonsingular-past-perception-after-rule343-process.md"

EXPECTED_HASHES = {
    DICTIONARY: "b575ab035ecc60f09f7bfc5537116555f3d290869fe75b2edf0fd8db60fe1b37",
    GRAMMAR: "5f21305f1fca9ee2cb64668e18156d1f322e3bd9e59850fda662bf0673714153",
}
TARGET_ORDER = [
    "kalkamuku", "karrkurr", "jinkalmu", "barka", "yalnga",
    "biwul", "yirku-yirku", "jadi", "walkarr", "bajabaja",
]
RECORD_COUNTS = {
    "kalkamuku": 2, "karrkurr": 1, "jinkalmu": 2, "barka": 3, "yalnga": 1,
    "biwul": 2, "yirku-yirku": 1, "jadi": 1, "walkarr": 1, "bajabaja": 1,
}
SUPPORT_RECORD_COUNTS = {"ngali": 1, "ngaliny": 0, "yubal": 1, "yurra": 1, "nganjin": 1, "ngana": 1}
TARGET_LABELS = {
    "kalkamuku": "kalkamuku snake", "karrkurr": "karrkurr frog",
    "jinkalmu": "jinkalmu snake", "barka": "barka snake", "yalnga": "yalnga turtle",
    "biwul": "biwul snake", "yirku-yirku": "yirku-yirku frog", "jadi": "jadi goanna",
    "walkarr": "walkarr goanna", "bajabaja": "bajabaja lizard",
}
TARGET_GLOSSES = {
    "kalkamuku": "kalkamuku.snake", "karrkurr": "karrkurr.frog",
    "jinkalmu": "jinkalmu.snake", "barka": "barka.snake", "yalnga": "yalnga.turtle",
    "biwul": "biwul.snake", "yirku-yirku": "yirku-yirku.frog", "jadi": "jadi.goanna",
    "walkarr": "walkarr.goanna", "bajabaja": "bajabaja.lizard",
}
TARGET_CONFIDENCE = {
    "kalkamuku": "0.90", "karrkurr": "0.91", "jinkalmu": "0.83", "barka": "0.80",
    "yalnga": "0.88", "biwul": "0.82", "yirku-yirku": "0.91", "jadi": "0.88",
    "walkarr": "0.90", "bajabaja": "0.85",
}
EXPECTED_TARGET_COVERAGE = {
    "kalkamuku": 15, "karrkurr": 20, "jinkalmu": 21, "barka": 23, "yalnga": 23,
    "biwul": 24, "yirku-yirku": 24, "jadi": 28, "walkarr": 30, "bajabaja": 31,
}
EXPECTED_SUPPORT_COVERAGE = {
    "ngali": 653, "ngaliny": 595, "yubal": 605, "yurra": 590, "nganjin": 318,
    "ngana": 1115, "yaluy": 4482, "yinyay": 4654, "nyajin": 2250,
}
FRAME_SPECS = [
    ("ngali", "yaluy", "We two, not you, saw", "here", "1du.excl"),
    ("ngaliny", "yinyay", "You and I saw", "there", "1du.incl"),
    ("yubal", "yaluy", "You two saw", "here", "2du"),
    ("yurra", "yinyay", "You all saw", "there", "2pl"),
    ("nganjin", "yaluy", "We, not you, saw", "here", "1pl.excl"),
]

TARGET_EVIDENCE = {
    "kalkamuku": "Fresh dictionary yaml lines 12625-12645 retain two green-tree-snake records. Rule 344 uses generic kalkamuku snake only; duplicate identity, colour/tree habitat, movement, diet, danger, source, possession and events remain closed.",
    "karrkurr": "Fresh dictionary yaml lines 13659-13670 retain one green-frog record. Rule 344 uses generic karrkurr frog only; colour, habitat, rain/season, sound, feeding, body, knowledge, source, possession and events remain closed.",
    "jinkalmu": "Fresh dictionary yaml lines 9600-9625 retain two unresolved brown-snake/taipan records, one with boys/eyes/fierce-fighter prose. Rule 344 uses generic jinkalmu snake only; taxonomy, body, culture, bite, kill, source, possession and events remain closed.",
    "barka": "Fresh dictionary yaml lines 1745-1777 retain two snake records and one nut/flora record, including one mixed-definition card. Rule 344 selects generic barka snake only; flora, record alignment, property/habitat, food, source, possession and events remain closed.",
    "yalnga": "Fresh dictionary yaml lines 29117-29129 retain one sea-turtle record with poisonous-sac food-safety prose. Rule 344 uses generic yalnga turtle only; sea habitat, poison, sacs, removal/butchery, food, source, possession and events remain closed.",
    "biwul": "Fresh dictionary yaml lines 3300-3325 retain distinct mother-in-law/avoidance and black-snake records. Rule 344 selects generic biwul snake only; kinship, register, colour/size, case, social events, danger, source and possession remain closed.",
    "yirku-yirku": "Fresh dictionary yaml lines 30457-30470 retain exact fully reduplicated yirku-yirku frog. Rule 344 keeps the whole head; free base, productive reduplication, sound imitation, habitat, source, possession and events remain closed.",
    "jadi": "Fresh dictionary yaml lines 7015-7026 retain jadi sand goanna with old-people food-taboo prose. Rule 344 uses generic jadi goanna only; habitat, age/permission, taboo, eating/food, hunting, source, possession and events remain closed.",
    "walkarr": "Fresh dictionary yaml lines 25492-25503 retain walkarr black scrub goanna; Patz 1616 attests walkarr-angka. Rule 344 uses bare generic walkarr goanna only; property/habitat, case, size, hunting/food, source, possession and events remain closed.",
    "bajabaja": "Fresh dictionary yaml lines 293-307 retain whole bajabaja blue-tongue lizard plus spring/place/story sense; Patz 719/3932 delimit myth and bite contexts. Rule 344 uses generic lizard only; base/process, place/story, property/body, eggs/river, bite/fight/kill, source, possession and events remain closed.",
}
SUPPORT_EVIDENCE = (
    "Patz Table 3.6 lines 2577-2584 controls participant morphology: ngali 1du exclusive, ngaliny 1du inclusive, "
    "yubal 2du, yurra 2pl, nganjin 1pl exclusive and ngana 1pl inclusive. Dictionary lines 21859-21870 "
    "misgloss ngali as inclusive and omit exact ngaliny; Rule 344 records that defect and follows Patz rather than "
    "flattening clusivity. Deferred ngana remains grammatical. Patz 2834-2847 sources Yalanji yaluy/yinyay; "
    "2021-2024, 2568-2571 and 3160-3162 license nyaji-ny; 4256-4265 licenses nominative pronoun A with bare "
    "absolutive O in perception; dictionary 23876-23883 and 23968-23976 contain running nyajin."
)

DENY_KUKU = {
    "ngayu", "yundu", "nyulu", "bula", "jana", "ngana", "yulu", "iana", "ngalingan",
    "ngalinyan", "yubalan", "yurrangan", "nganjinan", "yirku", "baja", "yalay", "yala",
    "yanyu", "yinya", "nyajil", "nyaji-l", "nyaji-nka", "nyaji-l-nyaji", "milka", "baykal",
    "jalngkay",
}
DENY_ENGLISH = (
    "green", "brown", "yellow", "black", "blue-tongue", "tree habitat", "water habitat",
    "sea habitat", "scrub habitat", "sand habitat", "taipan", "nut", "fruit", "mother-in-law",
    "avoidance language", "poison", "danger", "sac", "remove", "butcher", "eyes", "boys", "eat",
    "fierce", "fight", "bite", "kill", "old people", "taboo", "spring", "story", "place", "eggs",
    "hatch", "river", "rocks", "sound", "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 kalkamuku snake here.", "Ngali kalkamuku yaluy nyajin.", "ngali kalkamuku yaluy nyajin | 1du.excl.NOM kalkamuku.snake here see.PST", "r344_1du_excl_here_reptile_c_saw_kalkamuku", ["ngali", "kalkamuku", "yaluy", "nyajin"]),
    row(2, "We two, not you, saw the karrkurr frog here.", "Ngali karrkurr yaluy nyajin.", "ngali karrkurr yaluy nyajin | 1du.excl.NOM karrkurr.frog here see.PST", "r344_1du_excl_here_reptile_c_saw_karrkurr", ["ngali", "karrkurr", "yaluy", "nyajin"]),
    row(3, "We two, not you, saw the jinkalmu snake here.", "Ngali jinkalmu yaluy nyajin.", "ngali jinkalmu yaluy nyajin | 1du.excl.NOM jinkalmu.snake here see.PST", "r344_1du_excl_here_reptile_c_saw_jinkalmu", ["ngali", "jinkalmu", "yaluy", "nyajin"]),
    row(4, "We two, not you, saw the barka snake here.", "Ngali barka yaluy nyajin.", "ngali barka yaluy nyajin | 1du.excl.NOM barka.snake here see.PST", "r344_1du_excl_here_reptile_c_saw_barka", ["ngali", "barka", "yaluy", "nyajin"]),
    row(5, "We two, not you, saw the yalnga turtle here.", "Ngali yalnga yaluy nyajin.", "ngali yalnga yaluy nyajin | 1du.excl.NOM yalnga.turtle here see.PST", "r344_1du_excl_here_reptile_c_saw_yalnga", ["ngali", "yalnga", "yaluy", "nyajin"]),
    row(6, "We two, not you, saw the biwul snake here.", "Ngali biwul yaluy nyajin.", "ngali biwul yaluy nyajin | 1du.excl.NOM biwul.snake here see.PST", "r344_1du_excl_here_reptile_c_saw_biwul", ["ngali", "biwul", "yaluy", "nyajin"]),
    row(7, "We two, not you, saw the yirku-yirku frog here.", "Ngali yirku-yirku yaluy nyajin.", "ngali yirku-yirku yaluy nyajin | 1du.excl.NOM yirku-yirku.frog here see.PST", "r344_1du_excl_here_reptile_c_saw_yirku_yirku", ["ngali", "yirku-yirku", "yaluy", "nyajin"]),
    row(8, "We two, not you, saw the jadi goanna here.", "Ngali jadi yaluy nyajin.", "ngali jadi yaluy nyajin | 1du.excl.NOM jadi.goanna here see.PST", "r344_1du_excl_here_reptile_c_saw_jadi", ["ngali", "jadi", "yaluy", "nyajin"]),
    row(9, "We two, not you, saw the walkarr goanna here.", "Ngali walkarr yaluy nyajin.", "ngali walkarr yaluy nyajin | 1du.excl.NOM walkarr.goanna here see.PST", "r344_1du_excl_here_reptile_c_saw_walkarr", ["ngali", "walkarr", "yaluy", "nyajin"]),
    row(10, "We two, not you, saw the bajabaja lizard here.", "Ngali bajabaja yaluy nyajin.", "ngali bajabaja yaluy nyajin | 1du.excl.NOM bajabaja.lizard here see.PST", "r344_1du_excl_here_reptile_c_saw_bajabaja", ["ngali", "bajabaja", "yaluy", "nyajin"]),
    row(11, "You and I saw the kalkamuku snake there.", "Ngaliny kalkamuku yinyay nyajin.", "ngaliny kalkamuku yinyay nyajin | 1du.incl.NOM kalkamuku.snake there see.PST", "r344_1du_incl_there_reptile_c_saw_kalkamuku", ["ngaliny", "kalkamuku", "yinyay", "nyajin"]),
    row(12, "You and I saw the karrkurr frog there.", "Ngaliny karrkurr yinyay nyajin.", "ngaliny karrkurr yinyay nyajin | 1du.incl.NOM karrkurr.frog there see.PST", "r344_1du_incl_there_reptile_c_saw_karrkurr", ["ngaliny", "karrkurr", "yinyay", "nyajin"]),
    row(13, "You and I saw the jinkalmu snake there.", "Ngaliny jinkalmu yinyay nyajin.", "ngaliny jinkalmu yinyay nyajin | 1du.incl.NOM jinkalmu.snake there see.PST", "r344_1du_incl_there_reptile_c_saw_jinkalmu", ["ngaliny", "jinkalmu", "yinyay", "nyajin"]),
    row(14, "You and I saw the barka snake there.", "Ngaliny barka yinyay nyajin.", "ngaliny barka yinyay nyajin | 1du.incl.NOM barka.snake there see.PST", "r344_1du_incl_there_reptile_c_saw_barka", ["ngaliny", "barka", "yinyay", "nyajin"]),
    row(15, "You and I saw the yalnga turtle there.", "Ngaliny yalnga yinyay nyajin.", "ngaliny yalnga yinyay nyajin | 1du.incl.NOM yalnga.turtle there see.PST", "r344_1du_incl_there_reptile_c_saw_yalnga", ["ngaliny", "yalnga", "yinyay", "nyajin"]),
    row(16, "You and I saw the biwul snake there.", "Ngaliny biwul yinyay nyajin.", "ngaliny biwul yinyay nyajin | 1du.incl.NOM biwul.snake there see.PST", "r344_1du_incl_there_reptile_c_saw_biwul", ["ngaliny", "biwul", "yinyay", "nyajin"]),
    row(17, "You and I saw the yirku-yirku frog there.", "Ngaliny yirku-yirku yinyay nyajin.", "ngaliny yirku-yirku yinyay nyajin | 1du.incl.NOM yirku-yirku.frog there see.PST", "r344_1du_incl_there_reptile_c_saw_yirku_yirku", ["ngaliny", "yirku-yirku", "yinyay", "nyajin"]),
    row(18, "You and I saw the jadi goanna there.", "Ngaliny jadi yinyay nyajin.", "ngaliny jadi yinyay nyajin | 1du.incl.NOM jadi.goanna there see.PST", "r344_1du_incl_there_reptile_c_saw_jadi", ["ngaliny", "jadi", "yinyay", "nyajin"]),
    row(19, "You and I saw the walkarr goanna there.", "Ngaliny walkarr yinyay nyajin.", "ngaliny walkarr yinyay nyajin | 1du.incl.NOM walkarr.goanna there see.PST", "r344_1du_incl_there_reptile_c_saw_walkarr", ["ngaliny", "walkarr", "yinyay", "nyajin"]),
    row(20, "You and I saw the bajabaja lizard there.", "Ngaliny bajabaja yinyay nyajin.", "ngaliny bajabaja yinyay nyajin | 1du.incl.NOM bajabaja.lizard there see.PST", "r344_1du_incl_there_reptile_c_saw_bajabaja", ["ngaliny", "bajabaja", "yinyay", "nyajin"]),
    row(21, "You two saw the kalkamuku snake here.", "Yubal kalkamuku yaluy nyajin.", "yubal kalkamuku yaluy nyajin | 2du.NOM kalkamuku.snake here see.PST", "r344_2du_here_reptile_c_saw_kalkamuku", ["yubal", "kalkamuku", "yaluy", "nyajin"]),
    row(22, "You two saw the karrkurr frog here.", "Yubal karrkurr yaluy nyajin.", "yubal karrkurr yaluy nyajin | 2du.NOM karrkurr.frog here see.PST", "r344_2du_here_reptile_c_saw_karrkurr", ["yubal", "karrkurr", "yaluy", "nyajin"]),
    row(23, "You two saw the jinkalmu snake here.", "Yubal jinkalmu yaluy nyajin.", "yubal jinkalmu yaluy nyajin | 2du.NOM jinkalmu.snake here see.PST", "r344_2du_here_reptile_c_saw_jinkalmu", ["yubal", "jinkalmu", "yaluy", "nyajin"]),
    row(24, "You two saw the barka snake here.", "Yubal barka yaluy nyajin.", "yubal barka yaluy nyajin | 2du.NOM barka.snake here see.PST", "r344_2du_here_reptile_c_saw_barka", ["yubal", "barka", "yaluy", "nyajin"]),
    row(25, "You two saw the yalnga turtle here.", "Yubal yalnga yaluy nyajin.", "yubal yalnga yaluy nyajin | 2du.NOM yalnga.turtle here see.PST", "r344_2du_here_reptile_c_saw_yalnga", ["yubal", "yalnga", "yaluy", "nyajin"]),
    row(26, "You two saw the biwul snake here.", "Yubal biwul yaluy nyajin.", "yubal biwul yaluy nyajin | 2du.NOM biwul.snake here see.PST", "r344_2du_here_reptile_c_saw_biwul", ["yubal", "biwul", "yaluy", "nyajin"]),
    row(27, "You two saw the yirku-yirku frog here.", "Yubal yirku-yirku yaluy nyajin.", "yubal yirku-yirku yaluy nyajin | 2du.NOM yirku-yirku.frog here see.PST", "r344_2du_here_reptile_c_saw_yirku_yirku", ["yubal", "yirku-yirku", "yaluy", "nyajin"]),
    row(28, "You two saw the jadi goanna here.", "Yubal jadi yaluy nyajin.", "yubal jadi yaluy nyajin | 2du.NOM jadi.goanna here see.PST", "r344_2du_here_reptile_c_saw_jadi", ["yubal", "jadi", "yaluy", "nyajin"]),
    row(29, "You two saw the walkarr goanna here.", "Yubal walkarr yaluy nyajin.", "yubal walkarr yaluy nyajin | 2du.NOM walkarr.goanna here see.PST", "r344_2du_here_reptile_c_saw_walkarr", ["yubal", "walkarr", "yaluy", "nyajin"]),
    row(30, "You two saw the bajabaja lizard here.", "Yubal bajabaja yaluy nyajin.", "yubal bajabaja yaluy nyajin | 2du.NOM bajabaja.lizard here see.PST", "r344_2du_here_reptile_c_saw_bajabaja", ["yubal", "bajabaja", "yaluy", "nyajin"]),
    row(31, "You all saw the kalkamuku snake there.", "Yurra kalkamuku yinyay nyajin.", "yurra kalkamuku yinyay nyajin | 2pl.NOM kalkamuku.snake there see.PST", "r344_2pl_there_reptile_c_saw_kalkamuku", ["yurra", "kalkamuku", "yinyay", "nyajin"]),
    row(32, "You all saw the karrkurr frog there.", "Yurra karrkurr yinyay nyajin.", "yurra karrkurr yinyay nyajin | 2pl.NOM karrkurr.frog there see.PST", "r344_2pl_there_reptile_c_saw_karrkurr", ["yurra", "karrkurr", "yinyay", "nyajin"]),
    row(33, "You all saw the jinkalmu snake there.", "Yurra jinkalmu yinyay nyajin.", "yurra jinkalmu yinyay nyajin | 2pl.NOM jinkalmu.snake there see.PST", "r344_2pl_there_reptile_c_saw_jinkalmu", ["yurra", "jinkalmu", "yinyay", "nyajin"]),
    row(34, "You all saw the barka snake there.", "Yurra barka yinyay nyajin.", "yurra barka yinyay nyajin | 2pl.NOM barka.snake there see.PST", "r344_2pl_there_reptile_c_saw_barka", ["yurra", "barka", "yinyay", "nyajin"]),
    row(35, "You all saw the yalnga turtle there.", "Yurra yalnga yinyay nyajin.", "yurra yalnga yinyay nyajin | 2pl.NOM yalnga.turtle there see.PST", "r344_2pl_there_reptile_c_saw_yalnga", ["yurra", "yalnga", "yinyay", "nyajin"]),
    row(36, "You all saw the biwul snake there.", "Yurra biwul yinyay nyajin.", "yurra biwul yinyay nyajin | 2pl.NOM biwul.snake there see.PST", "r344_2pl_there_reptile_c_saw_biwul", ["yurra", "biwul", "yinyay", "nyajin"]),
    row(37, "You all saw the yirku-yirku frog there.", "Yurra yirku-yirku yinyay nyajin.", "yurra yirku-yirku yinyay nyajin | 2pl.NOM yirku-yirku.frog there see.PST", "r344_2pl_there_reptile_c_saw_yirku_yirku", ["yurra", "yirku-yirku", "yinyay", "nyajin"]),
    row(38, "You all saw the jadi goanna there.", "Yurra jadi yinyay nyajin.", "yurra jadi yinyay nyajin | 2pl.NOM jadi.goanna there see.PST", "r344_2pl_there_reptile_c_saw_jadi", ["yurra", "jadi", "yinyay", "nyajin"]),
    row(39, "You all saw the walkarr goanna there.", "Yurra walkarr yinyay nyajin.", "yurra walkarr yinyay nyajin | 2pl.NOM walkarr.goanna there see.PST", "r344_2pl_there_reptile_c_saw_walkarr", ["yurra", "walkarr", "yinyay", "nyajin"]),
    row(40, "You all saw the bajabaja lizard there.", "Yurra bajabaja yinyay nyajin.", "yurra bajabaja yinyay nyajin | 2pl.NOM bajabaja.lizard there see.PST", "r344_2pl_there_reptile_c_saw_bajabaja", ["yurra", "bajabaja", "yinyay", "nyajin"]),
    row(41, "We, not you, saw the kalkamuku snake here.", "Nganjin kalkamuku yaluy nyajin.", "nganjin kalkamuku yaluy nyajin | 1pl.excl.NOM kalkamuku.snake here see.PST", "r344_1pl_excl_here_reptile_c_saw_kalkamuku", ["nganjin", "kalkamuku", "yaluy", "nyajin"]),
    row(42, "We, not you, saw the karrkurr frog here.", "Nganjin karrkurr yaluy nyajin.", "nganjin karrkurr yaluy nyajin | 1pl.excl.NOM karrkurr.frog here see.PST", "r344_1pl_excl_here_reptile_c_saw_karrkurr", ["nganjin", "karrkurr", "yaluy", "nyajin"]),
    row(43, "We, not you, saw the jinkalmu snake here.", "Nganjin jinkalmu yaluy nyajin.", "nganjin jinkalmu yaluy nyajin | 1pl.excl.NOM jinkalmu.snake here see.PST", "r344_1pl_excl_here_reptile_c_saw_jinkalmu", ["nganjin", "jinkalmu", "yaluy", "nyajin"]),
    row(44, "We, not you, saw the barka snake here.", "Nganjin barka yaluy nyajin.", "nganjin barka yaluy nyajin | 1pl.excl.NOM barka.snake here see.PST", "r344_1pl_excl_here_reptile_c_saw_barka", ["nganjin", "barka", "yaluy", "nyajin"]),
    row(45, "We, not you, saw the yalnga turtle here.", "Nganjin yalnga yaluy nyajin.", "nganjin yalnga yaluy nyajin | 1pl.excl.NOM yalnga.turtle here see.PST", "r344_1pl_excl_here_reptile_c_saw_yalnga", ["nganjin", "yalnga", "yaluy", "nyajin"]),
    row(46, "We, not you, saw the biwul snake here.", "Nganjin biwul yaluy nyajin.", "nganjin biwul yaluy nyajin | 1pl.excl.NOM biwul.snake here see.PST", "r344_1pl_excl_here_reptile_c_saw_biwul", ["nganjin", "biwul", "yaluy", "nyajin"]),
    row(47, "We, not you, saw the yirku-yirku frog here.", "Nganjin yirku-yirku yaluy nyajin.", "nganjin yirku-yirku yaluy nyajin | 1pl.excl.NOM yirku-yirku.frog here see.PST", "r344_1pl_excl_here_reptile_c_saw_yirku_yirku", ["nganjin", "yirku-yirku", "yaluy", "nyajin"]),
    row(48, "We, not you, saw the jadi goanna here.", "Nganjin jadi yaluy nyajin.", "nganjin jadi yaluy nyajin | 1pl.excl.NOM jadi.goanna here see.PST", "r344_1pl_excl_here_reptile_c_saw_jadi", ["nganjin", "jadi", "yaluy", "nyajin"]),
    row(49, "We, not you, saw the walkarr goanna here.", "Nganjin walkarr yaluy nyajin.", "nganjin walkarr yaluy nyajin | 1pl.excl.NOM walkarr.goanna here see.PST", "r344_1pl_excl_here_reptile_c_saw_walkarr", ["nganjin", "walkarr", "yaluy", "nyajin"]),
    row(50, "We, not you, saw the bajabaja lizard here.", "Nganjin bajabaja yaluy nyajin.", "nganjin bajabaja yaluy nyajin | 1pl.excl.NOM bajabaja.lizard here see.PST", "r344_1pl_excl_here_reptile_c_saw_bajabaja", ["nganjin", "bajabaja", "yaluy", "nyajin"]),
]


def source_audit() -> tuple[dict[str, int], dict[str, int]]:
    hashes = {path: hashlib.sha256(open(path, "rb").read()).hexdigest() for path in EXPECTED_HASHES}
    if hashes != EXPECTED_HASHES:
        raise SystemExit(f"source hash changed: {hashes}")
    dictionary = yaml.safe_load(open(DICTIONARY, encoding="utf-8"))["words"]
    target_counts = {target: sum(entry.get("word") == target for entry in dictionary) for target in TARGET_ORDER}
    support_counts = {word: sum(entry.get("word") == word for entry in dictionary) for word in SUPPORT_RECORD_COUNTS}
    if target_counts != RECORD_COUNTS:
        raise SystemExit(f"target record inventory changed: {target_counts}")
    if support_counts != SUPPORT_RECORD_COUNTS:
        raise SystemExit(f"support record inventory changed: {support_counts}")
    for target in TARGET_ORDER:
        records = [entry for entry in dictionary if entry.get("word") == target]
        if not any(entry.get("type") == "noun" and entry.get("semantic_domain") == "fauna-reptile" for entry in records):
            raise SystemExit(f"no fauna-reptile noun record for {target}")
    ngali = next(entry for entry in dictionary if entry.get("word") == "ngali")
    if "inclusive" not in f"{ngali.get('gloss')} {ngali.get('definitions')}".lower():
        raise SystemExit("ngali dictionary conflict changed; reassess before banking")
    grammar = open(GRAMMAR, encoding="utf-8").read()
    required = (
        "Du.1.exc.<br>inc.<br>2.<br>3.",
        "ngali<br>ngaliny<br>yubal<br>bula",
        "Pl.1.exc.<br>inc.<br>2.<br>3.",
        "nganjin<br>ngana<br>yurra<br>iana",
    )
    if not all(fragment in grammar for fragment in required):
        raise SystemExit("Patz pronoun paradigm fragment changed")
    return target_counts, support_counts


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"row count {len(ROWS)} != 50")
    if [item[0] for item in ROWS] != list(range(1, 51)):
        issues.append("sequence is not 1..50")
    if len({item[1] for item in ROWS}) != 50:
        issues.append("duplicate English surface")
    if len({item[2] for item in ROWS}) != 50:
        issues.append("duplicate Kuku surface")
    for seq, english, kuku, analysis, frame, _tier, words_json, loans_json, evidence, confidence in ROWS:
        band = (seq - 1) // 10
        target = TARGET_ORDER[(seq - 1) % 10]
        pronoun, demo, prefix, location, person = FRAME_SPECS[band]
        words = json.loads(words_json)
        loans = json.loads(loans_json)
        expected_kuku = f"{pronoun.capitalize()} {target} {demo} nyajin."
        expected_english = f"{prefix} the {TARGET_LABELS[target]} {location}."
        expected_analysis = f"{pronoun} {target} {demo} nyajin | {person}.NOM {TARGET_GLOSSES[target]} {location} see.PST"
        if loans:
            issues.append(f"{seq}: loans {loans}")
        denied = sorted(word for word in DENY_KUKU if re.search(rf"(^|\s){re.escape(word)}([\s\.]|$)", kuku.lower()))
        if denied:
            issues.append(f"{seq}: denied Kuku {denied}")
        drift = [term for term in DENY_ENGLISH if re.search(rf"\b{re.escape(term)}\b", english.lower())]
        if drift:
            issues.append(f"{seq}: English drift {drift}")
        if kuku != expected_kuku:
            issues.append(f"{seq}: Kuku mismatch")
        if english != expected_english:
            issues.append(f"{seq}: English mismatch")
        if analysis != expected_analysis:
            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("r344_"):
            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:
    target_records, support_records = source_audit()
    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")
    if db.execute("SELECT COUNT(*) FROM sentences WHERE status='under_review'").fetchone()[0]:
        raise SystemExit("another batch is under review")
    target_coverage = count_words(db, TARGET_ORDER)
    support_coverage = count_words(db, list(EXPECTED_SUPPORT_COVERAGE))
    if target_coverage != EXPECTED_TARGET_COVERAGE:
        raise SystemExit(f"target coverage changed: {target_coverage}")
    if support_coverage != EXPECTED_SUPPORT_COVERAGE:
        raise SystemExit(f"support coverage changed: {support_coverage}")

    collisions = []
    for pronoun in [spec[0] for spec in FRAME_SPECS]:
        for target in TARGET_ORDER:
            for demo in ("yaluy", "yinyay"):
                surface = f"{pronoun.capitalize()} {target} {demo} 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.id,s.seq""",
                    (surface,),
                ).fetchall()
                if hits:
                    collisions.append((surface, hits))
    if collisions:
        raise SystemExit(f"100-cell matrix changed: {collisions}")

    verified, words = db.execute(
        """SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1)
           FROM sentences WHERE status IN ('accepted','revised')"""
    ).fetchone()
    if (verified, words) != (19347, 132747):
        raise SystemExit(f"baseline changed {verified}/{words}")
    notes = (
        "SENSE-QUALIFIED REPTILE LEXEME-SET-C NON-SINGULAR PAST PERCEPTION AFTER RULE 343 (Rule 344). "
        "50 under-review rows over the ten Rule 342-343 heads and five number/clusivity-distinct pronouns. "
        "All 100 cells empty; selected three-here/two-there projects gap 172->162. Patz overrides the dictionary's "
        "ngali inclusive misgloss and exact-ngaliny omission. Deferred ngana remains grammatical. All prior target "
        "record/sense/cultural/property/source/event boundaries remain closed."
    )
    cur = db.execute(
        "INSERT INTO batches(label,cheatsheet_version,guide_version,notes,created_at) VALUES(?,?,?,?,?)",
        (LABEL, "Rule 344 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, english, kuku, analysis, frame, tier, words_json, loans_json, evidence,
                "under_review", confidence, "project_approved_synthetic_pending_elder_verification",
                "codex-main", NOW, NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_json, loans_json, evidence, confidence in ROWS
        ],
    )
    events = [
        ("source", "source-hash-pass", f"Dictionary and Patz hashes match fixed Rule 344 expectations: {EXPECTED_HASHES}."),
        ("source", "source-readback", f"Fresh parsed target records={target_records}; support records={support_records}; all source passages reread."),
        ("source", "pronoun-conflict-ruling", "Patz Table 3.6 controls: ngali=1du.excl and ngaliny=1du.incl. Dictionary ngali inclusive misgloss and exact-ngaliny omission recorded; no clusivity flattening."),
        ("planning", "sense-preservation-audit", "Rule 342-343 generic labels and all duplicate/polysemy/taxonomy/cultural/food/whole-head/property/habitat/case/source/event boundaries retained."),
        ("planning", "construction-gap-audit", "All 100 non-singular-pronoun x target x deictic cells absent; both 50-row orientations internally unique and collision-free."),
        ("planning", "deictic-balance-audit", "Before banking yaluy=4482 and yinyay=4654, gap 172; selected three-here/two-there projects 162; inverse 182."),
        ("preflight", "coverage-audit", f"Coverage before banking: targets={target_coverage}; support={support_coverage}."),
        ("preflight", "duplicate-preflight", "All 50 selected Kuku and English surfaces internally unique and absent; complete 100-cell matrix empty."),
        ("preflight", "static-guard-pass", "50 explicit Rule 344 rows; source, clusivity, sense, morphology, frame, words, gloss, evidence, confidence, loan and exclusion contracts passed."),
    ]
    for phase, event_type, detail in events:
        record_event(
            db, batch_label=LABEL, phase=phase, event_type=event_type, 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 344 non-singular rows under_review; verified remains {verified}; words {words}.",
        command="python3 batch_scripts/bank_386.py", output_excerpt="50 under_review",
        artifact_path=PROCESS_LOG, created_at=NOW,
    )
    db.commit()
    print("banked batch-386: 50 rows under_review")
    return 0


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