#!/usr/bin/env python3
"""Bank batch-207: Rule 165 flora reference lexical expansion after Rule 164."""

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-207"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-207-flora-reference-lexical-expansion-after-rule164-process.md"

TARGET_HEADS = {
    "babajaka": "bloodwood tree",
    "balmbalka": "balmbalka tree",
    "baway": "black bean tree",
    "bijin": "tea tree",
    "dalban": "oak tree",
    "dingki": "dingki tree",
    "dulmbil": "umbrella palm",
    "duwar": "black palm",
    "kalal": "vine",
    "kumul": "grass",
}
PRONOUN_A = {"ngayu", "yundu", "jana", "ngana"}
DENY_KUKU = {
    "mayi", "nuka", "nukan", "waju", "wajun", "warrmba", "warrmban", "mani", "manin", "daji", "dajin",
    "dinda", "dindan", "duda", "dudan", "wukurri", "wukurrin", "ngara", "ngaran", "kuni", "kunin",
    "yaka", "yakan", "balka", "balkan", "kida", "kidan", "dunga", "dungan", "wandi", "wandin",
    "baya", "kubu", "kurrma", "medicine", "juku", "bark", "spear", "storm", "bayan", "bubu",
    "buyku", "bijan",
}
DENY_ENGLISH = (
    "eat", "eats", "ate", "eaten", "food", "fruit", "nut", "medicine", "heal", "healed", "sore",
    "bark", "burn", "burned", "burnt", "fire", "smoke", "storm", "spear", "woomera", "timber",
    "wood", "stick", "weave", "woven", "tie", "tied", "basket", "place", "camp", "beach", "fruit",
    "gather", "gathered", "harvest", "harvested", "dig", "dug", "cook", "cooked", "roast", "roasted",
    "pod", "pods", "seed", "seeds",
)


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


def row(seq: int, english: str, kuku: str, analysis: str, frame: str, words: list[str], evidence: str) -> tuple:
    return (seq, english, kuku, analysis, frame, 4, js(words), js([]), evidence, "0.84")


ROWS = [
    row(1, "The bloodwood tree is here.", "Babajaka yaluy.", "babajaka yaluy | bloodwood.tree here", "r165_deictic_babajaka_here", ["babajaka", "yaluy"], "Rule 165 admits dictionary-attested babajaka as bloodwood tree; reference only."),
    row(2, "The balmbalka tree is here.", "Balmbalka yaluy.", "balmbalka yaluy | balmbalka.tree here", "r165_deictic_balmbalka_here", ["balmbalka", "yaluy"], "Rule 165 admits balmbalka as tree with English name unknown; bare yaluy deictic frame."),
    row(3, "The black bean tree is here.", "Baway yaluy.", "baway yaluy | black.bean.tree here", "r165_deictic_baway_here", ["baway", "yaluy"], "Rule 165 admits baway as black bean tree; no seed/food-processing claim."),
    row(4, "The tea tree is here.", "Bijin yaluy.", "bijin yaluy | tea.tree here", "r165_deictic_bijin_here", ["bijin", "yaluy"], "Rule 165 admits bijin as tea tree; no medicine/bark-use claim."),
    row(5, "The oak tree is here.", "Dalban yaluy.", "dalban yaluy | oak.tree here", "r165_deictic_dalban_here", ["dalban", "yaluy"], "Rule 165 admits dalban as oak tree; no timber/tool claim."),
    row(6, "The dingki tree is here.", "Dingki yaluy.", "dingki yaluy | dingki.tree here", "r165_deictic_dingki_here", ["dingki", "yaluy"], "Rule 165 admits dingki as a type of tree; dictionary-only target with no conflicting Patz use found."),
    row(7, "The umbrella palm is here.", "Dulmbil yaluy.", "dulmbil yaluy | umbrella.palm here", "r165_deictic_dulmbil_here", ["dulmbil", "yaluy"], "Rule 165 admits dulmbil as umbrella palm; no place-name or weaving/material claim."),
    row(8, "The black palm is here.", "Duwar yaluy.", "duwar yaluy | black.palm here", "r165_deictic_duwar_here", ["duwar", "yaluy"], "Rule 165 admits duwar as black palm; spear/material-source sense remains closed."),
    row(9, "The vine is here.", "Kalal yaluy.", "kalal yaluy | vine here", "r165_deictic_kalal_here", ["kalal", "yaluy"], "Rule 165 admits kalal as vine; Patz confirms kalal 'vine'."),
    row(10, "The grass is here.", "Kumul yaluy.", "kumul yaluy | grass here", "r165_deictic_kumul_here", ["kumul", "yaluy"], "Rule 165 admits kumul as a species of grass; no fire/fuel or ground-cover claim."),
    row(11, "I see the bloodwood tree.", "Ngayu babajaka nyajil.", "ngayu babajaka nyaji-l | I bloodwood.tree see-NONPAST", "r165_present_i_babajaka", ["ngayu", "babajaka", "nyaji-l"], "Pronoun A bare; babajaka object bare; no bloodwood-use claim."),
    row(12, "I see the balmbalka tree.", "Ngayu balmbalka nyajil.", "ngayu balmbalka nyaji-l | I balmbalka.tree see-NONPAST", "r165_present_i_balmbalka", ["ngayu", "balmbalka", "nyaji-l"], "Pronoun A bare; balmbalka object bare."),
    row(13, "I see the black bean tree.", "Ngayu baway nyajil.", "ngayu baway nyaji-l | I black.bean.tree see-NONPAST", "r165_present_i_baway", ["ngayu", "baway", "nyaji-l"], "Pronoun A bare; baway object bare; no seeds/food row."),
    row(14, "I see the tea tree.", "Ngayu bijin nyajil.", "ngayu bijin nyaji-l | I tea.tree see-NONPAST", "r165_present_i_bijin", ["ngayu", "bijin", "nyaji-l"], "Pronoun A bare; bijin object bare; no medicine row."),
    row(15, "I see the oak tree.", "Ngayu dalban nyajil.", "ngayu dalban nyaji-l | I oak.tree see-NONPAST", "r165_present_i_dalban", ["ngayu", "dalban", "nyaji-l"], "Pronoun A bare; dalban object bare."),
    row(16, "I see the dingki tree.", "Ngayu dingki nyajil.", "ngayu dingki nyaji-l | I dingki.tree see-NONPAST", "r165_present_i_dingki", ["ngayu", "dingki", "nyaji-l"], "Pronoun A bare; dingki object bare."),
    row(17, "I see the umbrella palm.", "Ngayu dulmbil nyajil.", "ngayu dulmbil nyaji-l | I umbrella.palm see-NONPAST", "r165_present_i_dulmbil", ["ngayu", "dulmbil", "nyaji-l"], "Pronoun A bare; dulmbil object bare; no place-name row."),
    row(18, "I see the black palm.", "Ngayu duwar nyajil.", "ngayu duwar nyaji-l | I black.palm see-NONPAST", "r165_present_i_duwar", ["ngayu", "duwar", "nyaji-l"], "Pronoun A bare; duwar object bare; no spear/material-source row."),
    row(19, "I see the vine.", "Ngayu kalal nyajil.", "ngayu kalal nyaji-l | I vine see-NONPAST", "r165_present_i_kalal", ["ngayu", "kalal", "nyaji-l"], "Pronoun A bare; kalal object bare; no tying or rope claim."),
    row(20, "I see the grass.", "Ngayu kumul nyajil.", "ngayu kumul nyaji-l | I grass see-NONPAST", "r165_present_i_kumul", ["ngayu", "kumul", "nyaji-l"], "Pronoun A bare; kumul object bare."),
    row(21, "You see the bloodwood tree there.", "Yundu babajaka yinyay nyajil.", "yundu babajaka yinyay nyaji-l | you bloodwood.tree there see-NONPAST", "r165_present_you_babajaka_there", ["yundu", "babajaka", "yinyay", "nyaji-l"], "Second-person pronoun A bare; yinyay support only."),
    row(22, "You see the balmbalka tree there.", "Yundu balmbalka yinyay nyajil.", "yundu balmbalka yinyay nyaji-l | you balmbalka.tree there see-NONPAST", "r165_present_you_balmbalka_there", ["yundu", "balmbalka", "yinyay", "nyaji-l"], "Second-person pronoun A bare; target object bare."),
    row(23, "You see the black bean tree there.", "Yundu baway yinyay nyajil.", "yundu baway yinyay nyaji-l | you black.bean.tree there see-NONPAST", "r165_present_you_baway_there", ["yundu", "baway", "yinyay", "nyaji-l"], "Second-person pronoun A bare; no fruit/seed/food row."),
    row(24, "You see the tea tree there.", "Yundu bijin yinyay nyajil.", "yundu bijin yinyay nyaji-l | you tea.tree there see-NONPAST", "r165_present_you_bijin_there", ["yundu", "bijin", "yinyay", "nyaji-l"], "Second-person pronoun A bare; no medicine/bark row."),
    row(25, "You see the oak tree there.", "Yundu dalban yinyay nyajil.", "yundu dalban yinyay nyaji-l | you oak.tree there see-NONPAST", "r165_present_you_dalban_there", ["yundu", "dalban", "yinyay", "nyaji-l"], "Second-person pronoun A bare; target object bare."),
    row(26, "You see the dingki tree there.", "Yundu dingki yinyay nyajil.", "yundu dingki yinyay nyaji-l | you dingki.tree there see-NONPAST", "r165_present_you_dingki_there", ["yundu", "dingki", "yinyay", "nyaji-l"], "Second-person pronoun A bare; target object bare."),
    row(27, "You see the umbrella palm there.", "Yundu dulmbil yinyay nyajil.", "yundu dulmbil yinyay nyaji-l | you umbrella.palm there see-NONPAST", "r165_present_you_dulmbil_there", ["yundu", "dulmbil", "yinyay", "nyaji-l"], "Second-person pronoun A bare; no place/material row."),
    row(28, "You see the black palm there.", "Yundu duwar yinyay nyajil.", "yundu duwar yinyay nyaji-l | you black.palm there see-NONPAST", "r165_present_you_duwar_there", ["yundu", "duwar", "yinyay", "nyaji-l"], "Second-person pronoun A bare; no spear/material-source row."),
    row(29, "You see the vine there.", "Yundu kalal yinyay nyajil.", "yundu kalal yinyay nyaji-l | you vine there see-NONPAST", "r165_present_you_kalal_there", ["yundu", "kalal", "yinyay", "nyaji-l"], "Second-person pronoun A bare; no tying/rope claim."),
    row(30, "You see the grass there.", "Yundu kumul yinyay nyajil.", "yundu kumul yinyay nyaji-l | you grass there see-NONPAST", "r165_present_you_kumul_there", ["yundu", "kumul", "yinyay", "nyaji-l"], "Second-person pronoun A bare; target object bare."),
    row(31, "They saw the bloodwood tree there.", "Jana babajaka yinyay nyajin.", "jana babajaka yinyay nyaji-ny | they bloodwood.tree there see-PAST", "r165_past_they_babajaka_there", ["jana", "babajaka", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; past perception; target bare."),
    row(32, "They saw the balmbalka tree there.", "Jana balmbalka yinyay nyajin.", "jana balmbalka yinyay nyaji-ny | they balmbalka.tree there see-PAST", "r165_past_they_balmbalka_there", ["jana", "balmbalka", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; past perception."),
    row(33, "They saw the black bean tree there.", "Jana baway yinyay nyajin.", "jana baway yinyay nyaji-ny | they black.bean.tree there see-PAST", "r165_past_they_baway_there", ["jana", "baway", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; no food/seed row."),
    row(34, "They saw the tea tree there.", "Jana bijin yinyay nyajin.", "jana bijin yinyay nyaji-ny | they tea.tree there see-PAST", "r165_past_they_bijin_there", ["jana", "bijin", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; no medicine/bark row."),
    row(35, "They saw the oak tree there.", "Jana dalban yinyay nyajin.", "jana dalban yinyay nyaji-ny | they oak.tree there see-PAST", "r165_past_they_dalban_there", ["jana", "dalban", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; target object bare."),
    row(36, "They saw the dingki tree there.", "Jana dingki yinyay nyajin.", "jana dingki yinyay nyaji-ny | they dingki.tree there see-PAST", "r165_past_they_dingki_there", ["jana", "dingki", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; target object bare."),
    row(37, "They saw the umbrella palm there.", "Jana dulmbil yinyay nyajin.", "jana dulmbil yinyay nyaji-ny | they umbrella.palm there see-PAST", "r165_past_they_dulmbil_there", ["jana", "dulmbil", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; target object bare."),
    row(38, "They saw the black palm there.", "Jana duwar yinyay nyajin.", "jana duwar yinyay nyaji-ny | they black.palm there see-PAST", "r165_past_they_duwar_there", ["jana", "duwar", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; no spear/material-source row."),
    row(39, "They saw the vine there.", "Jana kalal yinyay nyajin.", "jana kalal yinyay nyaji-ny | they vine there see-PAST", "r165_past_they_kalal_there", ["jana", "kalal", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; no tying/rope claim."),
    row(40, "They saw the grass there.", "Jana kumul yinyay nyajin.", "jana kumul yinyay nyaji-ny | they grass there see-PAST", "r165_past_they_kumul_there", ["jana", "kumul", "yinyay", "nyaji-l"], "Third-person plural pronoun A bare; target object bare."),
    row(41, "We see the bloodwood tree here.", "Ngana babajaka yaluy nyajil.", "ngana babajaka yaluy nyaji-l | we bloodwood.tree here see-NONPAST", "r165_present_we_babajaka_here", ["ngana", "babajaka", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; yaluy support only."),
    row(42, "We see the balmbalka tree here.", "Ngana balmbalka yaluy nyajil.", "ngana balmbalka yaluy nyaji-l | we balmbalka.tree here see-NONPAST", "r165_present_we_balmbalka_here", ["ngana", "balmbalka", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; target object bare."),
    row(43, "We see the black bean tree here.", "Ngana baway yaluy nyajil.", "ngana baway yaluy nyaji-l | we black.bean.tree here see-NONPAST", "r165_present_we_baway_here", ["ngana", "baway", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; no fruit/seed/food row."),
    row(44, "We see the tea tree here.", "Ngana bijin yaluy nyajil.", "ngana bijin yaluy nyaji-l | we tea.tree here see-NONPAST", "r165_present_we_bijin_here", ["ngana", "bijin", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; no medicine/bark row."),
    row(45, "We see the oak tree here.", "Ngana dalban yaluy nyajil.", "ngana dalban yaluy nyaji-l | we oak.tree here see-NONPAST", "r165_present_we_dalban_here", ["ngana", "dalban", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; target object bare."),
    row(46, "We see the dingki tree here.", "Ngana dingki yaluy nyajil.", "ngana dingki yaluy nyaji-l | we dingki.tree here see-NONPAST", "r165_present_we_dingki_here", ["ngana", "dingki", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; target object bare."),
    row(47, "We see the umbrella palm here.", "Ngana dulmbil yaluy nyajil.", "ngana dulmbil yaluy nyaji-l | we umbrella.palm here see-NONPAST", "r165_present_we_dulmbil_here", ["ngana", "dulmbil", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; no place/material row."),
    row(48, "We see the black palm here.", "Ngana duwar yaluy nyajil.", "ngana duwar yaluy nyaji-l | we black.palm here see-NONPAST", "r165_present_we_duwar_here", ["ngana", "duwar", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; no spear/material-source row."),
    row(49, "We see the vine here.", "Ngana kalal yaluy nyajil.", "ngana kalal yaluy nyaji-l | we vine here see-NONPAST", "r165_present_we_kalal_here", ["ngana", "kalal", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; no tying/rope claim."),
    row(50, "We see the grass here.", "Ngana kumul yaluy nyajil.", "ngana kumul yaluy nyaji-l | we grass here see-NONPAST", "r165_present_we_kumul_here", ["ngana", "kumul", "yaluy", "nyaji-l"], "Inclusive pronoun A bare; target object bare."),
]


def has_token(low: str, token: str) -> bool:
    return re.search(rf"(?<![a-z-]){re.escape(token)}(?![a-z-])", low) is not None


def first_word(low: str) -> str:
    return low.split()[0].strip(".,!?;:\"")


def has_target_case(low: str, head: str) -> bool:
    return re.search(rf"(?<![a-z-]){re.escape(head)}-(?:ngka|ngku|angka|nga|ngu|ba|mba|mbu|muny|darr|burr|bu|ka|nka|nku|ji|jirr|ku|mundu|amu|mu|iji|nda|ndu|anda|undu|unji|nji|kari|baka)\b", low) is not None


def english_has_denied(english_low: str, denied: str) -> bool:
    return denied in english_low if " " in denied else re.search(rf"\b{re.escape(denied)}\b", english_low) is not None


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"{LABEL}: expected 50 rows, found {len(ROWS)}")
    seen_ku: set[str] = set()
    for expected, row_data in enumerate(ROWS, start=1):
        seq, english, kuku, analysis, frame, _tier, words_used, loans_used, evidence, confidence = row_data
        label = f"{LABEL}.{seq:02d}"
        low = kuku.lower()
        english_low = english.lower()
        words = json.loads(words_used)
        if seq != expected:
            issues.append(f"{label}: sequence mismatch; expected {expected}")
        if not english[:1].isupper() or english[-1:] not in ".!?":
            issues.append(f"{label}: English punctuation/capitalization failure")
        if " | " not in analysis:
            issues.append(f"{label}: analysis missing gloss separator")
        if not frame.startswith("r165_"):
            issues.append(f"{label}: Rule 165 frame must start with r165_")
        if low in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(low)
        if json.loads(loans_used):
            issues.append(f"{label}: no loans are admitted in Rule 165")
        for denied in DENY_KUKU:
            if has_token(low, denied):
                issues.append(f"{label}: denied use/homograph token {denied!r}")
        targets = [head for head in TARGET_HEADS if has_token(low, head)]
        if len(targets) != 1:
            issues.append(f"{label}: expected exactly one Rule 165 target head, found {targets}")
        for head in targets:
            selected = TARGET_HEADS[head]
            if head not in words:
                issues.append(f"{label}: target head {head} missing from words_used")
            if selected.replace(" ", ".") not in analysis and selected not in analysis:
                issues.append(f"{label}: target head {head} must be glossed as {selected}")
            if selected not in english_low:
                issues.append(f"{label}: {head} must be translated with selected sense {selected!r}")
            if has_target_case(low, head):
                issues.append(f"{label}: target head {head} has forbidden case/compound morphology")
        if seq <= 10:
            if "nyajil" in low or "nyajin" in low or "nyaji-l" in words:
                issues.append(f"{label}: deictic rows must not use nyaji-l")
            if not has_token(low, "yaluy") or "yaluy" not in words or "here" not in english_low:
                issues.append(f"{label}: Rule 165 deictic rows require yaluy/here")
        else:
            if ("nyajin" not in low and "nyajil" not in low) or "nyaji-l" not in words:
                issues.append(f"{label}: Rule 165 perception rows must use nyaji-l")
            first = first_word(low)
            if first not in PRONOUN_A:
                issues.append(f"{label}: perception A should be an approved bare pronoun")
            if 11 <= seq <= 20 and first != "ngayu":
                issues.append(f"{label}: rows 11-20 require ngayu")
            if 21 <= seq <= 30 and (first != "yundu" or not has_token(low, "yinyay") or "there" not in english_low):
                issues.append(f"{label}: rows 21-30 require yundu + yinyay/there")
            if 31 <= seq <= 40 and (first != "jana" or not has_token(low, "yinyay") or "saw" not in english_low):
                issues.append(f"{label}: rows 31-40 require jana + yinyay + past saw")
            if 41 <= seq <= 50 and (first != "ngana" or not has_token(low, "yaluy") or "here" not in english_low):
                issues.append(f"{label}: rows 41-50 require ngana + yaluy/here")
            if "nyajin" in low and "see-PAST" not in analysis:
                issues.append(f"{label}: nyajin must be glossed as see-PAST")
            if "nyajil" in low and "see-NONPAST" not in analysis:
                issues.append(f"{label}: nyajil must be glossed as see-NONPAST")
        if any(english_has_denied(english_low, bad) for bad in DENY_ENGLISH):
            issues.append(f"{label}: denied use/event/homograph field in English")
        if not evidence or not confidence:
            issues.append(f"{label}: missing evidence/confidence")
    return issues


def main() -> int:
    issues = scan_static()
    if issues:
        print("pre-bank scan failed:")
        for issue in issues:
            print(f"  - {issue}")
        return 1

    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    db.execute("BEGIN")
    ensure_schema(db)
    existing = db.execute("SELECT id FROM batches WHERE label=?", (LABEL,)).fetchone()
    if existing:
        batch_id = existing[0]
        accepted = db.execute(
            "SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status IN ('accepted','revised')",
            (batch_id,),
        ).fetchone()[0]
        if accepted:
            raise SystemExit(f"{LABEL} already has accepted/revised rows; refusing to overwrite")
        db.execute("DELETE FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)", (batch_id,))
        db.execute("DELETE FROM 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 165 draft: flora reference lexical expansion after Rule 164", "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 165 source pass selected flora reference heads babajaka, balmbalka, baway, bijin, dalban, dingki, dulmbil, duwar, kalal, and kumul. Dictionary exact readback completed. Patz readback confirmed kalal 'vine' plus support forms yaluy/yinyay, nyaji-l/nyaji-ny, and bare pronoun A forms. Corpus readback excluded buyku due prior near-identical frames and bijan due homographic lick-form risk.",
                 command="rg dictionary exact readback; rg grammar readback; sqlite corpus surface readback",
                 artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="scope", event_type="lexical-scope", actor="claude-main",
                 detail="Rule 165 is a lexical flora-reference pass for ten bare target heads. It excludes food-processing, fruit, seeds, medicine, bark use, spear-making, timber/material-source, weaving, tying, cutting, burning, storm-control, place-name, target case, target compounds, possession, and ecological-event claims.",
                 artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="duplicate-preflight", actor="claude-main",
                 detail="Exact duplicate preflight found 50 planned Kuku surfaces, 50 unique within-batch surfaces, and no exact accepted/revised/under_review cross-corpus duplicates.",
                 command="sqlite exact lower(trim(kuku)) duplicate scan before banking", artifact_path="batch_scripts/bank_207.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="static-guard-pass", actor="claude-main",
                 detail="Static guards passed: exactly one Rule 165 flora target per row; all targets bare; no loans; no use/event/homograph tokens; no target case/compound forms; pronoun A bare; yinyay/yaluy support only.",
                 command="python3 batch_scripts/bank_207.py static scan", artifact_path="batch_scripts/bank_207.py", created_at=NOW)

    db.executemany(
        """
        INSERT INTO sentences(
          batch_id, seq, english, kuku, analysis, frame, tier, words_used,
          loans_used, evidence, status, confidence, author, created_at, updated_at
        )
        VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        """,
        [(batch_id, *row_data[:9], "under_review", row_data[9], "claude-main", NOW, NOW) for row_data in ROWS],
    )
    db.commit()

    under_review = db.execute(
        "SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status='under_review'",
        (batch_id,),
    ).fetchone()[0]
    verified = db.execute("SELECT COUNT(*) FROM sentences WHERE status IN ('accepted','revised')").fetchone()[0]
    print(f"banked {LABEL}: rows={len(ROWS)}, under_review={under_review}, verified_before_close={verified}")
    return 0


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