#!/usr/bin/env python3
"""Bank batch-164: Rule 122 case-frame consolidation."""

from __future__ import annotations

import datetime as dt
import json
import re
import sqlite3
import sys

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-164"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-164-place-source-object-participant-frames-process.md"

DENY = {"yah", "ngawuya", "walnga", "maja-nga", "yunga", "bada-bada", "wangkar-wangkar"}
CONSTRUCTION_CLITIC_MARKERS = ("-kuda", "-ngarr", "-ngurr", "-l-urr", "wawu-")
PRONOUN_A = {"ngayu", "ngana", "nganjin", "yundu", "yurra", "jana", "nyulu"}
SOURCED_COMITATIVES = ("ngawa-nji", "nganjan-anji", "jawun-unji", "juku-ji", "kambi-ji")
LAUGH_TARGET_SUFFIXES = ("-anka", "-anda", "-nda", "-ndu", "-nka")


def js(values: list[str]) -> str:
    return json.dumps(values, ensure_ascii=False)


ROWS = [
    (1, "Old people stayed at the sea.",
     "Mukul-bama jalun-bu bundandan.",
     "mukul-bama jalun-bu bundanda-ny | old-people sea-LOC stay-PAST",
     "r122_place_sea_stay", 3, js(["mukul", "bama", "jalun", "bunday"]), js([]),
     "Dictionary mukul/bama/jalun; Patz-backed jalun-bu sea-LOC; bundanda-y stay/posture frame.", "0.84"),
    (2, "People came from the sea.",
     "Bama jalun-muny kadan.",
     "bama jalun-muny kada-ny | people sea-ABL come-PAST",
     "r122_source_sea_come", 3, js(["bama", "jalun", "kaday"]), js([]),
     "Patz ex.299/Rule source table backs jalun-muny sea-ABL; bama and kada-y dictionary checked.", "0.84"),
    (3, "Grandmother sat on the road.",
     "Babi baral-ba bundan.",
     "babi baral-ba bunda-ny | grandmother road-LOC sit-PAST",
     "r122_place_road_sit", 3, js(["babi", "baral", "bunday"]), js([]),
     "Dictionary babi/baral; baral-ba road-LOC and bunda-y posture frame are source-backed.", "0.82"),
    (4, "The young man came to the road.",
     "Warru baral-ba kadan.",
     "warru baral-ba kada-ny | young.man road-LOC come-PAST",
     "r122_goal_road_come", 3, js(["warru", "baral", "kaday"]), js([]),
     "Warru dictionary entry; baral-ba used as place/goal with kada-y in the established motion frame.", "0.82"),
    (5, "Mother lay outside.",
     "Ngamu ngalkal-ba wunan.",
     "ngamu ngalkal-ba wuna-ny | mother outside-LOC lie-PAST",
     "r122_place_outside_lie", 3, js(["ngamu", "ngalkal", "wunay"]), js([]),
     "Ngamu dictionary entry; ngalkal-ba outside-LOC and wuna-y posture frame are source-backed.", "0.82"),
    (6, "The children sat in the shade.",
     "Karrkay-karrkay wabarr-ba bundan.",
     "karrkay-karrkay wabarr-ba bunda-ny | children shade-LOC sit-PAST",
     "r122_place_shade_sit", 3, js(["karrkay", "wabarr", "bunday"]), js([]),
     "Karrkay reduplication for children; wabarr-ba shade-LOC and bunda-y checked.", "0.82"),
    (7, "The man sat on the ground.",
     "Dingkar bubu-ngu bundan.",
     "dingkar bubu-ngu bunda-ny | man ground-LOC sit-PAST",
     "r122_place_ground_sit", 3, js(["dingkar", "bubu", "bunday"]), js([]),
     "Dingkar and bubu dictionary checked; bubu-ngu neutral LOC sourced in Patz/corpus lexemes.", "0.82"),
    (8, "The snake lay in the grass.",
     "Jarba jikan-ba wunan.",
     "jarba jikan-ba wuna-ny | snake grass-LOC lie-PAST",
     "r122_place_grass_lie", 3, js(["jarba", "jikan", "wunay"]), js([]),
     "Jarba and jikan dictionary checked; jikan-ba grass-LOC and wuna-y are source-backed.", "0.84"),
    (9, "The child came from the scrub.",
     "Karrkay madja-muny kadan.",
     "karrkay madja-muny kada-ny | child scrub-ABL come-PAST",
     "r122_source_scrub_come", 3, js(["karrkay", "madja", "kaday"]), js([]),
     "Karrkay/madja/kada-y checked; madja-muny scrub ABL retained from prior source logging.", "0.84"),
    (10, "The baby came from the house.",
     "Ngawa bayan-muny kadan.",
     "ngawa bayan-muny kada-ny | baby house-ABL come-PAST",
     "r122_source_house_baby_come", 3, js(["ngawa", "bayan", "kaday"]), js([]),
     "Ngawa and bayan dictionary checked; bayan-muny house/camp ABL backed by Patz examples.", "0.84"),

    (11, "The man put the bag on the road.",
     "Dingkar-angka balji baral-ba yijarrin.",
     "dingkar-angka balji baral-ba yijarri-ny | man-ERG bag road-LOC put-PAST",
     "r122_object_place_bag_road", 4, js(["dingkar", "balji", "baral", "yijarril"]), js([]),
     "Balji/baral/yijarri-l checked; common-noun A dingkar-angka carries ERG; object remains overt.", "0.84"),
    (12, "Grandmother left the billycan outside.",
     "Babi-ngka jungkalu ngalkal-ba bawan.",
     "babi-ngka jungkalu ngalkal-ba bawa-ny | grandmother-ERG billycan outside-LOC leave-PAST",
     "r122_object_place_billycan_outside", 4, js(["babi", "jungkalu", "ngalkal", "bawal"]), js([]),
     "Jungkalu and ngalkal-ba checked; bawa-l leave frame keeps object overt and A is ERG-marked.", "0.84"),
    (13, "The child put the spear in the shade.",
     "Karrkay-ngku kalka wabarr-ba yijarrin.",
     "karrkay-ngku kalka wabarr-ba yijarri-ny | child-ERG spear shade-LOC put-PAST",
     "r122_object_place_spear_shade", 4, js(["karrkay", "kalka", "wabarr", "yijarril"]), js([]),
     "Kalka/wabarr/yijarri-l checked; karrkay-ngku marks transitive A.", "0.84"),
    (14, "The woman put food on the ground.",
     "Jalbu-ngku mayi bubu-ngu yijarrin.",
     "jalbu-ngku mayi bubu-ngu yijarri-ny | woman-ERG food ground-LOC put-PAST",
     "r122_object_place_food_ground", 4, js(["jalbu", "mayi", "bubu", "yijarril"]), js([]),
     "Mayi/bubu/yijarri-l checked; bubu-ngu neutral LOC retained; common-noun A marked ERG.", "0.84"),
    (15, "The young man got fish at the sea.",
     "Warru-ngku kuyu jalun-bu manin.",
     "warru-ngku kuyu jalun-bu mani-ny | young.man-ERG fish sea-LOC get-PAST",
     "r122_object_place_fish_sea", 4, js(["warru", "kuyu", "jalun", "manil"]), js([]),
     "Kuyu/jalun/mani-l checked; jalun-bu place contrasts with jalun-muny source rows.", "0.84"),
    (16, "The man got mussels at the river.",
     "Dingkar-angka mukirr wawubaja-nga manin.",
     "dingkar-angka mukirr wawubaja-nga mani-ny | man-ERG mussels river-LOC get-PAST",
     "r122_object_place_mussels_river", 4, js(["dingkar", "mukirr", "wawubaja", "manil"]), js([]),
     "Mukirr/wawubaja/mani-l checked; wawubaja-nga LOC kept distinct from wawubaja-muny ABL.", "0.84"),
    (17, "Father ate meat in the grass.",
     "Nganjan-angka minya jikan-ba nukan.",
     "nganjan-angka minya jikan-ba nuka-ny | father-ERG meat grass-LOC eat-PAST",
     "r122_object_place_meat_grass", 4, js(["nganjan", "minya", "jikan", "nukal"]), js([]),
     "Nganjan/minya/jikan/nuka-l checked; common-noun A marked ERG.", "0.82"),
    (18, "Mother cooked food in the shade.",
     "Ngamu-ngku mayi wabarr-ba wajun.",
     "ngamu-ngku mayi wabarr-ba waju-ny | mother-ERG food shade-LOC cook-PAST",
     "r122_object_place_cook_shade", 4, js(["ngamu", "mayi", "wabarr", "waju-l"]), js([]),
     "Mayi/wabarr/waju-l checked; ngamu-ngku marks transitive A in the cooking frame.", "0.82"),
    (19, "The old woman brought firewood from the scrub.",
     "Wulbuman-angka baya madja-muny wundin.",
     "wulbuman-angka baya madja-muny wundi-ny | old.woman-ERG firewood scrub-ABL bring-PAST",
     "r122_object_source_firewood_scrub", 4, js(["wulbuman", "baya", "madja", "wundil"]), js([]),
     "Baya fire/firewood sense, madja-muny ABL, and wundi-l bring/take are source-backed.", "0.84"),
    (20, "Grandfather brought clothes from the house.",
     "Kami-ngka kambi bayan-muny wundin.",
     "kami-ngka kambi bayan-muny wundi-ny | grandfather-ERG clothes house-ABL bring-PAST",
     "r122_object_source_clothes_house", 4, js(["kami", "kambi", "bayan", "wundil"]), js([]),
     "Kambi clothing sense and bayan-muny ABL checked; kami-ngka marks common-noun A.", "0.84"),

    (21, "The child saw the crocodile in the river.",
     "Karrkay-ngku bilngkumu wawubaja-nga nyajin.",
     "karrkay-ngku bilngkumu wawubaja-nga nyaji-ny | child-ERG crocodile river-LOC see-PAST",
     "r122_perception_common_child_crocodile", 4, js(["karrkay", "bilngkumu", "wawubaja", "nyajil"]), js([]),
     "Bilngkumu/wawubaja/nyaji-l checked; common-noun perceiver is ERG-marked.", "0.84"),
    (22, "I saw the crocodile in the river.",
     "Ngayu bilngkumu wawubaja-nga nyajin.",
     "ngayu bilngkumu wawubaja-nga nyaji-ny | I crocodile river-LOC see-PAST",
     "r122_perception_pronoun_crocodile", 4, js(["ngayu", "bilngkumu", "wawubaja", "nyajil"]), js([]),
     "Pronoun A remains unmarked; object and location match row 21 for contrast.", "0.84"),
    (23, "The girl saw the snake in the grass.",
     "Maral-angka jarba jikan-ba nyajin.",
     "maral-angka jarba jikan-ba nyaji-ny | girl-ERG snake grass-LOC see-PAST",
     "r122_perception_common_girl_snake", 4, js(["maral", "jarba", "jikan", "nyajil"]), js([]),
     "Maral/jarba/jikan/nyaji-l checked; common-noun perceiver is ERG-marked.", "0.84"),
    (24, "We saw the snake in the grass.",
     "Ngana jarba jikan-ba nyajin.",
     "ngana jarba jikan-ba nyaji-ny | we snake grass-LOC see-PAST",
     "r122_perception_pronoun_snake", 4, js(["ngana", "jarba", "jikan", "nyajil"]), js([]),
     "Pronoun A remains unmarked; object and location match row 23 for contrast.", "0.84"),
    (25, "The man heard barking from the house.",
     "Dingkar-angka ngangkurr bayan-muny nyajin.",
     "dingkar-angka ngangkurr bayan-muny nyaji-ny | man-ERG barking.noise house-ABL hear-PAST",
     "r122_perception_common_barking_source", 4, js(["dingkar", "ngangkurr", "bayan", "nyajil"]), js([]),
     "Ngangkurr barking sound and bayan-muny source checked; nyaji-l hear sense retained.", "0.84"),
    (26, "I heard barking outside.",
     "Ngayu ngangkurr ngalkal-ba nyajin.",
     "ngayu ngangkurr ngalkal-ba nyaji-ny | I barking.noise outside-LOC hear-PAST",
     "r122_perception_pronoun_barking_outside", 4, js(["ngayu", "ngangkurr", "ngalkal", "nyajil"]), js([]),
     "Pronoun A unmarked; ngangkurr and ngalkal-ba checked.", "0.84"),
    (27, "Grandmother heard a song from the sea.",
     "Babi-ngka wulngku jalun-muny nyajin.",
     "babi-ngka wulngku jalun-muny nyaji-ny | grandmother-ERG song sea-ABL hear-PAST",
     "r122_perception_common_song_sea", 4, js(["babi", "wulngku", "jalun", "nyajil"]), js([]),
     "Wulngku song and jalun-muny source checked; common-noun perceiver is ERG-marked.", "0.84"),
    (28, "We heard a song from the house.",
     "Ngana wulngku bayan-muny nyajin.",
     "ngana wulngku bayan-muny nyaji-ny | we song house-ABL hear-PAST",
     "r122_perception_pronoun_song_house", 4, js(["ngana", "wulngku", "bayan", "nyajil"]), js([]),
     "Pronoun A unmarked; wulngku and bayan-muny checked.", "0.84"),
    (29, "The child saw a star at night.",
     "Karrkay-ngku dawar wujurr-bu nyajin.",
     "karrkay-ngku dawar wujurr-bu nyaji-ny | child-ERG star night-LOC see-PAST",
     "r122_perception_star_night", 4, js(["karrkay", "dawar", "wujurr", "nyajil"]), js([]),
     "Dawar is the safe star lexeme; wujurr-bu night LOC and common-noun ERG perceiver checked.", "0.84"),
    (30, "I saw the moon at night.",
     "Ngayu kija wujurr-bu nyajin.",
     "ngayu kija wujurr-bu nyaji-ny | I moon night-LOC see-PAST",
     "r122_perception_moon_night", 4, js(["ngayu", "kija", "wujurr", "nyajil"]), js([]),
     "Kija is restricted here to moon/month; pronoun A unmarked; wujurr-bu checked.", "0.86"),

    (31, "The girl asked grandmother about food.",
     "Maral babi-nda mayi-ka babajin.",
     "maral babi-nda mayi-ka babaji-ny | girl grandmother-LOC:pt food-DAT ask-PAST",
     "r122_ask_grandmother_food", 4, js(["maral", "babi", "mayi", "babaji"]), js([]),
     "Babaji-y fixed intransitive ask-about frame; no ERG on asker; LOC:pt person and DAT topic.", "0.86"),
    (32, "The young man asked grandfather about water.",
     "Warru kami-nda bana-ka babajin.",
     "warru kami-nda bana-ka babaji-ny | young.man grandfather-LOC:pt water-DAT ask-PAST",
     "r122_ask_grandfather_water", 4, js(["warru", "kami", "bana", "babaji"]), js([]),
     "Ask-about frame keeps ABS/S asker, kami-nda person asked, and bana-ka DAT topic.", "0.86"),
    (33, "The woman asked father about the spear.",
     "Jalbu nganjan-anda kalka-ka babajin.",
     "jalbu nganjan-anda kalka-ka babaji-ny | woman father-LOC:pt spear-DAT ask-PAST",
     "r122_ask_father_spear", 4, js(["jalbu", "nganjan", "kalka", "babaji"]), js([]),
     "Babaji-y ask-about frame; kalka-ka DAT topic; asker remains ABS/S.", "0.86"),
    (34, "The friend asked mother about the house.",
     "Jawun ngamu-ndu bayan-ka babajin.",
     "jawun ngamu-ndu bayan-ka babaji-ny | friend mother-LOC:pt house-DAT ask-PAST",
     "r122_ask_mother_house", 4, js(["jawun", "ngamu", "bayan", "babaji"]), js([]),
     "Ngamu-ndu and bayan-ka are sourced case forms; no ergative drift on asker.", "0.86"),
    (35, "The child asked the girl about the bag.",
     "Karrkay maral-anda balji-ka babajin.",
     "karrkay maral-anda balji-ka babaji-ny | child girl-LOC:pt bag-DAT ask-PAST",
     "r122_ask_girl_bag", 4, js(["karrkay", "maral", "balji", "babaji"]), js([]),
     "Maral-anda person asked plus balji-ka DAT topic; ABS/S asker retained.", "0.86"),

    (36, "The young man laughed at the child.",
     "Warru karrkay-anka yajarrin.",
     "warru karrkay-anka yajarri-ny | young.man child-DAT:pt laugh-PAST",
     "r122_laugh_child", 3, js(["warru", "karrkay", "yajarril"]), js([]),
     "Yajarri-l target frame; karrkay-anka DAT:pt target and intransitive S laugher.", "0.84"),
    (37, "Grandmother laughed at grandfather.",
     "Babi kami-nda yajarrin.",
     "babi kami-nda yajarri-ny | grandmother grandfather-DAT:pt laugh-PAST",
     "r122_laugh_grandfather", 3, js(["babi", "kami", "yajarril"]), js([]),
     "Kami-nda target follows sourced potent-person target pattern; subject remains ABS/S.", "0.84"),
    (38, "The woman laughed at father.",
     "Jalbu nganjan-anka yajarrin.",
     "jalbu nganjan-anka yajarri-ny | woman father-DAT:pt laugh-PAST",
     "r122_laugh_father", 3, js(["jalbu", "nganjan", "yajarril"]), js([]),
     "Nganjan-anka laugh target retained from sourced Rule 120/121 pattern.", "0.84"),
    (39, "The friend laughed at the young man.",
     "Jawun warru-ndu yajarrin.",
     "jawun warru-ndu yajarri-ny | friend young.man-DAT:pt laugh-PAST",
     "r122_laugh_young_man", 3, js(["jawun", "warru", "yajarril"]), js([]),
     "Patz example supports warru-ndu with laugh/ask target frames; subject is intransitive S.", "0.84"),
    (40, "The child laughed at the woman.",
     "Karrkay jalbu-ndu yajarrin.",
     "karrkay jalbu-ndu yajarri-ny | child woman-DAT:pt laugh-PAST",
     "r122_laugh_woman", 3, js(["karrkay", "jalbu", "yajarril"]), js([]),
     "Jalbu-ndu DAT:pt target follows sourced potent-person allomorphy; no ERG on laugher.", "0.84"),

    (41, "Grandmother is staying in the house with the baby.",
     "Babi bayan-ba bundanday ngawa-nji.",
     "babi bayan-ba bunda-n-bunda-y ngawa-nji | grandmother house-LOC stay-REDUP-NONPAST baby-COMIT:pt",
     "r122_comit_baby_house_stay", 4, js(["babi", "bayan", "bunday", "ngawa"]), js([]),
     "Patz ex.549 directly sources house-LOC stay with ngawa-nji; subject and English changed.", "0.88"),
    (42, "Mother is staying in the shade with the baby.",
     "Ngamu wabarr-ba bundanday ngawa-nji.",
     "ngamu wabarr-ba bunda-n-bunda-y ngawa-nji | mother shade-LOC stay-REDUP-NONPAST baby-COMIT:pt",
     "r122_comit_baby_shade_stay", 4, js(["ngamu", "wabarr", "bunday", "ngawa"]), js([]),
     "Ngawa-nji is the same sourced baby comitative; wabarr-ba shade LOC checked.", "0.84"),
    (43, "The young man went to the road with a friend.",
     "Warru jawun-unji baral-ba dungan.",
     "warru jawun-unji baral-ba dunga-ny | young.man friend-COMIT:pt road-LOC go-PAST",
     "r122_comit_friend_road_go", 4, js(["warru", "jawun", "baral", "dungay"]), js([]),
     "Jawun-unji is a sourced comitative; baral-ba and dunga-y checked.", "0.84"),
    (44, "The child went to the sea with father.",
     "Karrkay nganjan-anji jalun-bu dungan.",
     "karrkay nganjan-anji jalun-bu dunga-ny | child father-COMIT:pt sea-LOC go-PAST",
     "r122_comit_father_sea_go", 4, js(["karrkay", "nganjan", "jalun", "dungay"]), js([]),
     "Nganjan-anji is a sourced comitative; jalun-bu and dunga-y checked.", "0.84"),
    (45, "The woman came from the scrub with a stick.",
     "Jalbu juku-ji madja-muny kadan.",
     "jalbu juku-ji madja-muny kada-ny | woman stick-COMIT scrub-ABL come-PAST",
     "r122_comit_stick_scrub_come", 4, js(["jalbu", "juku", "madja", "kaday"]), js([]),
     "Juku-ji is the sourced neutral comitative; madja-muny ABL and kada-y checked.", "0.84"),
    (46, "Mother came from the house with clothes.",
     "Ngamu kambi-ji bayan-muny kadan.",
     "ngamu kambi-ji bayan-muny kada-ny | mother clothes-COMIT house-ABL come-PAST",
     "r122_comit_clothes_house_come", 4, js(["ngamu", "kambi", "bayan", "kaday"]), js([]),
     "Kambi-ji is the sourced clothes comitative; bayan-muny source and kada-y checked.", "0.84"),

    (47, "Mother left the dog's food outside.",
     "Ngamu-ngku mayi kaya-nga ngalkal-ba bawan.",
     "ngamu-ngku mayi kaya-nga ngalkal-ba bawa-ny | mother-ERG food dog-POSS.ABS outside-LOC leave-PAST",
     "r122_poss_food_leave_outside", 5, js(["ngamu", "mayi", "kaya", "ngalkal", "bawal"]), js([]),
     "Patz-backed mayi kaya-nga possessive object; ngalkal-ba and bawa-l frame checked.", "0.84"),
    (48, "Grandmother put the dog's food in the bag.",
     "Babi-ngka mayi kaya-nga balji-nga yijarrin.",
     "babi-ngka mayi kaya-nga balji-nga yijarri-ny | grandmother-ERG food dog-POSS.ABS bag-LOC put-PAST",
     "r122_poss_food_put_bag", 5, js(["babi", "mayi", "kaya", "balji", "yijarril"]), js([]),
     "Patz-backed possessive object plus balji-nga LOC and yijarri-l put frame.", "0.84"),
    (49, "The woman took mother's clothes from the house.",
     "Jalbu-ngku kambi ngamu-ngu bayan-muny wundin.",
     "jalbu-ngku kambi ngamu-ngu bayan-muny wundi-ny | woman-ERG clothes mother-POSS.ABS house-ABL take-PAST",
     "r122_poss_clothes_take_source", 5, js(["jalbu", "kambi", "ngamu", "bayan", "wundil"]), js([]),
     "Patz-backed kambi ngamu-ngu possessive object; bayan-muny source and wundi-l checked.", "0.84"),
    (50, "We took mother's clothes to camp.",
     "Ngana kambi ngamu-ngu bayan-ka wundin.",
     "ngana kambi ngamu-ngu bayan-ka wundi-ny | we clothes mother-POSS.ABS camp-DAT take-PAST",
     "r122_poss_clothes_take_goal", 5, js(["ngana", "kambi", "ngamu", "bayan", "wundil"]), js([]),
     "Pronoun A unmarked; kambi ngamu-ngu and bayan-ka goal are sourced; moved object remains overt.", "0.84"),
]


def has_denied_token(low: str, token: str) -> bool:
    return re.search(rf"(?<![a-z-]){re.escape(token)}(?![a-z-])", 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 in enumerate(ROWS, start=1):
        seq, english, kuku, analysis, frame, _tier, words_used, loans_used, evidence, confidence = row
        label = f"{LABEL}.{seq:02d}"
        low = kuku.lower()
        analysis_low = analysis.lower()
        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("r122_"):
            issues.append(f"{label}: Rule 122 batch frame must start with r122_")
        if low in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(low)
        for marker in CONSTRUCTION_CLITIC_MARKERS:
            if marker in low:
                issues.append(f"{label}: construction consolidation batch must not reuse temporal clitic marker {marker}")
        for denied in DENY:
            if has_denied_token(low, denied):
                issues.append(f"{label}: denied or out-of-scope token {denied!r}")
        if "baral-muny" in low:
            issues.append(f"{label}: baral-muny is not source-logged for this batch")
        if "kadar-anji" in low:
            issues.append(f"{label}: kadar-anji is outside the sourced comitative set")
        if "kija" in low and not re.search(r"\b(moon|month)\b", analysis_low):
            issues.append(f"{label}: kija is moon/month only; analysis must say moon/month")
        if "star" in english.lower() and "dawar" not in low:
            issues.append(f"{label}: star row must use dawar")
        if "babajin" in low and re.match(r"^[A-Za-z-]+-(?:ngka|angka|ngku)\b", kuku):
            issues.append(f"{label}: babaji-y asker drifted into ergative A marking")
        if "nyaji" in analysis_low:
            first = low.split()[0].strip(".,!?")
            if first not in PRONOUN_A and not re.search(r"-(?:ngka|angka|ngku)\b", first):
                issues.append(f"{label}: common-noun perceiver with nyaji-l should be ERG-marked")
        if "yajarrin" in low and not any(suffix in low for suffix in LAUGH_TARGET_SUFFIXES):
            issues.append(f"{label}: laugh target should show sourced DAT:pt target suffix")
        if "COMIT" in analysis and not any(form in low for form in SOURCED_COMITATIVES):
            issues.append(f"{label}: comitative form is not in the sourced Rule 122 set")
        if any(form in low for form in ("kaya-nga", "ngamu-ngu")) and "POSS" not in analysis:
            issues.append(f"{label}: possessive form needs explicit POSS analysis")
        try:
            json.loads(words_used)
            json.loads(loans_used)
        except json.JSONDecodeError as exc:
            issues.append(f"{label}: JSON field failed: {exc}")
        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 sentences WHERE batch_id=?", (batch_id,))
        db.execute("UPDATE batches SET notes=?, created_at=? WHERE id=?", ("rebanked under_review", NOW, batch_id))
    else:
        cur = db.execute(
            "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
            (LABEL, "Rule 122 draft: place/source/object/participant case-frame consolidation", "operator-guide-2026-07-07", "under_review", NOW),
        )
        batch_id = cur.lastrowid

    dupes: list[str] = []
    for seq, _english, kuku, *_rest in ROWS:
        hit = db.execute(
            """
            SELECT b.label, s.seq
            FROM sentences s JOIN batches b ON b.id=s.batch_id
            WHERE lower(trim(s.kuku))=lower(trim(?))
              AND s.status!='rejected'
              AND s.batch_id!=?
            """,
            (kuku, batch_id),
        ).fetchone()
        if hit:
            dupes.append(f"{LABEL}.{seq:02d} duplicates {hit[0]}.{hit[1]:02d}: {kuku}")
    if dupes:
        db.rollback()
        print("cross-corpus duplicate check failed:")
        for dupe in dupes:
            print(f"  - {dupe}")
        return 1

    record_event(
        db,
        batch_label=LABEL,
        phase="source",
        event_type="source-pass",
        actor="claude-main",
        detail=(
            "Rule 122 source pass checked Patz grammar, dictionary.yaml, and the corpus lexeme table before authoring. "
            "The batch consolidates already sourced place/source/object/participant case frames rather than adding "
            "new lexical or morphological material."
        ),
        command="rg over grammar.md/dictionary.yaml; sqlite readback from lexemes",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="source",
        event_type="dictionary-source-check",
        actor="claude-main",
        detail=(
            "Exact dictionary readback found all planned base heads: babi, bama, balji, bana, baral, baya, bayan, "
            "bilngkumu, bubu, dawar, dingkar, jalbu, jalun, jarba, jawun, jikan, juku, jungkalu, kalka, kami, "
            "kambi, karrkay, kaya, kija, kuyu, madja, maral, mayi, minya, mukirr, mukul, ngalkal, ngamu, ngana, "
            "ngangkurr, nganjan, ngawa, ngayu, wabarr, warru, wawubaja, wujurr, wulbuman, wulngku, plus verb heads "
            "babaji, bawal, bunday, dungay, kaday, manil, nukal, nyajil, waju-l, wunay, wundil, yajarril, yijarril."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="source",
        event_type="grammar-source-check",
        actor="claude-main",
        detail=(
            "Grammar controls used: jalun-bu/jalun-muny; bayan-muny/bayan-ka; wawubaja-nga/wawubaja-muny; "
            "madja-muny; baral-ba; ngalkal-ba; wabarr-ba; bubu-ngu; jikan-ba; balji-nga; wujurr-bu; ngawa-nji; "
            "nganjan-anji; jawun-unji; juku-ji; kambi-ji; mayi kaya-nga; kambi ngamu-ngu; babaji-y ask-about; "
            "yajarri-l laugh target; nyaji-l perception; kija moon-only; dawar star."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="scope",
        event_type="risk-controls",
        actor="claude-main",
        detail=(
            "Controls: no temporal clitics; no free possessive templates; no unsourced comitatives; no baral-muny; "
            "no kadar-anji; no maja-nga or yunga-l; no ergative babaji-y askers; kija moon-only; dawar for star; "
            "common-noun nyaji-l perceivers ERG-marked; pronoun A unmarked; laugh targets DAT:pt."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="preflight",
        event_type="existing-surface-scan",
        actor="claude-main",
        detail="Checked all 50 planned Kuku surfaces against the full non-rejected corpus; no exact duplicates found before insert.",
        command="sqlite exact-surface query over planned Batch 164 rows",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="process",
        event_type="process-capture-expanded",
        actor="claude-main",
        detail=(
            "Batch 164 stores source inventory, guard design, row grouping, duplicate scan, static checks, bank outcome, "
            "and later lint/review/close/export/docs evidence as structured process_events plus the process log."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    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_used,
                loans_used, evidence, "under_review", confidence,
                "project_approved_synthetic_pending_elder_verification",
                "claude-main", NOW, NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_used, loans_used, evidence, confidence in ROWS
        ],
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="bank-success",
        actor="claude-main",
        detail="Banked 50 hand-authored Rule 122 case-frame consolidation rows under_review after static guards and duplicate checks.",
        command="python3 batch_scripts/bank_164.py",
        artifact_path="batch_scripts/bank_164.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="row-inventory",
        actor="claude-main",
        detail=(
            "Rows 1-10 place/source/posture/motion; 11-20 object plus place/source/goal; 21-30 perception contrasts; "
            "31-35 ask-about; 36-40 laugh targets; 41-46 sourced comitatives; 47-50 Patz-backed possessive objects."
        ),
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    db.commit()

    count = db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=?", (batch_id,)).fetchone()[0]
    avg_words = db.execute(
        "SELECT AVG(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1.0) FROM sentences WHERE batch_id=?",
        (batch_id,),
    ).fetchone()[0]
    process_events = db.execute("SELECT COUNT(*) FROM process_events WHERE batch_label=?", (LABEL,)).fetchone()[0]
    print(f"banked {LABEL}: {count} rows under_review, avg_words={avg_words:.2f}, process_events={process_events}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
