#!/usr/bin/env python3
"""Bank batch-303: Rule 261 complementary reptile/amphibian perception sweep."""

from __future__ import annotations

import datetime as dt
import json
import sqlite3

from bank_294 import (
    DENY_ENGLISH,
    DENY_KUKU,
    PRONOUN_SUPPORT,
    SUPPORT,
    TARGET_HEADS,
    english_has_denied,
    has_target_morphology,
    has_token,
    js,
    row,
)
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-303"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-303-reptile-amphibian-complementary-perception-after-rule260-process.md"

EVIDENCE = {
    "bajuy": "Dictionary lines 559-567 give bajuy as noun 'lizard', definition 'slippery lizard', semantic domain fauna-reptile; Rule 261 selects lizard only. Patz grammar lines 2553-2603 source bare nominative pronouns, lines 2838-2845 source yaluy/yinyay, and lines 3043-3049 source nyaji-l 'see'.",
    "bilngkumu": "Dictionary lines 2575-2585 give bilngkumu as noun 'crocodile' with salt-water source prose; Rule 261 selects crocodile only and leaves saltwater/habitat/danger rows closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source the support forms.",
    "binanjal": "Dictionary lines 2726-2734 give binanjal as noun 'frill necked lizard', semantic domain fauna-reptile. Rule 261 selects the animal label only. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source the frame.",
    "bindimu": "Dictionary lines 2884-2901 give bindimu as black snake and a red-bellied black-snake alternate; Rule 261 selects black snake only and leaves alternate/colour-predicate expansion closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "bubal": "Dictionary lines 3397-3417 give bubal as species of tree snake and tree snake; Rule 261 selects tree snake only and leaves tree/habitat/biting prose closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "jijirrbay": "Dictionary lines 8955-8972 give jijirrbay as noun 'whip snake', semantic domain fauna-reptile. Rule 261 selects whip snake only. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "kabul": "Dictionary lines 11816-11833 give kabul as noun 'rock python', semantic domain fauna-reptile. Rule 261 selects rock python only and leaves rock/place/action prose closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "kalajarr": "Dictionary lines 12243-12251 give kalajarr as noun 'scrub goanna', semantic domain fauna-reptile. Rule 261 selects scrub goanna only and leaves scrub/habitat/food/action prose closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "kundurr": "Dictionary lines 15704-15720 give kundurr as noun 'death adder', semantic domain fauna-reptile. Rule 261 selects death adder as a lexical label only and leaves danger/poison/biting prose closed; kundurr-kundurr is not imported. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
    "yuday": "Dictionary lines 30662-30670 give yuday as noun 'frog' with large/edible source prose; Rule 261 selects frog only and leaves edible/rain/swamp/event rows closed. Patz grammar lines 2553-2603, 2838-2845, and 3043-3049 source support.",
}

ROWS = [
    row(1, "I see the lizard here.", "Ngayu bajuy yaluy nyajil.", "ngayu bajuy yaluy nyajil | 1sg.NOM lizard here see.NPST", "r261_1sg_target_here_see_bajuy", ["ngayu", "bajuy", "yaluy", "nyajil"], EVIDENCE["bajuy"]),
    row(2, "I see the crocodile here.", "Ngayu bilngkumu yaluy nyajil.", "ngayu bilngkumu yaluy nyajil | 1sg.NOM crocodile here see.NPST", "r261_1sg_target_here_see_bilngkumu", ["ngayu", "bilngkumu", "yaluy", "nyajil"], EVIDENCE["bilngkumu"]),
    row(3, "I see the frill necked lizard here.", "Ngayu binanjal yaluy nyajil.", "ngayu binanjal yaluy nyajil | 1sg.NOM frill.necked.lizard here see.NPST", "r261_1sg_target_here_see_binanjal", ["ngayu", "binanjal", "yaluy", "nyajil"], EVIDENCE["binanjal"]),
    row(4, "I see the black snake here.", "Ngayu bindimu yaluy nyajil.", "ngayu bindimu yaluy nyajil | 1sg.NOM black.snake here see.NPST", "r261_1sg_target_here_see_bindimu", ["ngayu", "bindimu", "yaluy", "nyajil"], EVIDENCE["bindimu"]),
    row(5, "I see the tree snake here.", "Ngayu bubal yaluy nyajil.", "ngayu bubal yaluy nyajil | 1sg.NOM tree.snake here see.NPST", "r261_1sg_target_here_see_bubal", ["ngayu", "bubal", "yaluy", "nyajil"], EVIDENCE["bubal"]),
    row(6, "I see the whip snake here.", "Ngayu jijirrbay yaluy nyajil.", "ngayu jijirrbay yaluy nyajil | 1sg.NOM whip.snake here see.NPST", "r261_1sg_target_here_see_jijirrbay", ["ngayu", "jijirrbay", "yaluy", "nyajil"], EVIDENCE["jijirrbay"]),
    row(7, "I see the rock python here.", "Ngayu kabul yaluy nyajil.", "ngayu kabul yaluy nyajil | 1sg.NOM rock.python here see.NPST", "r261_1sg_target_here_see_kabul", ["ngayu", "kabul", "yaluy", "nyajil"], EVIDENCE["kabul"]),
    row(8, "I see the scrub goanna here.", "Ngayu kalajarr yaluy nyajil.", "ngayu kalajarr yaluy nyajil | 1sg.NOM scrub.goanna here see.NPST", "r261_1sg_target_here_see_kalajarr", ["ngayu", "kalajarr", "yaluy", "nyajil"], EVIDENCE["kalajarr"]),
    row(9, "I see the death adder here.", "Ngayu kundurr yaluy nyajil.", "ngayu kundurr yaluy nyajil | 1sg.NOM death.adder here see.NPST", "r261_1sg_target_here_see_kundurr", ["ngayu", "kundurr", "yaluy", "nyajil"], EVIDENCE["kundurr"]),
    row(10, "I see the frog here.", "Ngayu yuday yaluy nyajil.", "ngayu yuday yaluy nyajil | 1sg.NOM frog here see.NPST", "r261_1sg_target_here_see_yuday", ["ngayu", "yuday", "yaluy", "nyajil"], EVIDENCE["yuday"]),
    row(11, "He sees the lizard there.", "Nyulu bajuy yinyay nyajil.", "nyulu bajuy yinyay nyajil | 3sg.NOM lizard there see.NPST", "r261_3sg_target_there_see_bajuy", ["nyulu", "bajuy", "yinyay", "nyajil"], EVIDENCE["bajuy"]),
    row(12, "He sees the crocodile there.", "Nyulu bilngkumu yinyay nyajil.", "nyulu bilngkumu yinyay nyajil | 3sg.NOM crocodile there see.NPST", "r261_3sg_target_there_see_bilngkumu", ["nyulu", "bilngkumu", "yinyay", "nyajil"], EVIDENCE["bilngkumu"]),
    row(13, "He sees the frill necked lizard there.", "Nyulu binanjal yinyay nyajil.", "nyulu binanjal yinyay nyajil | 3sg.NOM frill.necked.lizard there see.NPST", "r261_3sg_target_there_see_binanjal", ["nyulu", "binanjal", "yinyay", "nyajil"], EVIDENCE["binanjal"]),
    row(14, "He sees the black snake there.", "Nyulu bindimu yinyay nyajil.", "nyulu bindimu yinyay nyajil | 3sg.NOM black.snake there see.NPST", "r261_3sg_target_there_see_bindimu", ["nyulu", "bindimu", "yinyay", "nyajil"], EVIDENCE["bindimu"]),
    row(15, "He sees the tree snake there.", "Nyulu bubal yinyay nyajil.", "nyulu bubal yinyay nyajil | 3sg.NOM tree.snake there see.NPST", "r261_3sg_target_there_see_bubal", ["nyulu", "bubal", "yinyay", "nyajil"], EVIDENCE["bubal"]),
    row(16, "He sees the whip snake there.", "Nyulu jijirrbay yinyay nyajil.", "nyulu jijirrbay yinyay nyajil | 3sg.NOM whip.snake there see.NPST", "r261_3sg_target_there_see_jijirrbay", ["nyulu", "jijirrbay", "yinyay", "nyajil"], EVIDENCE["jijirrbay"]),
    row(17, "He sees the rock python there.", "Nyulu kabul yinyay nyajil.", "nyulu kabul yinyay nyajil | 3sg.NOM rock.python there see.NPST", "r261_3sg_target_there_see_kabul", ["nyulu", "kabul", "yinyay", "nyajil"], EVIDENCE["kabul"]),
    row(18, "He sees the scrub goanna there.", "Nyulu kalajarr yinyay nyajil.", "nyulu kalajarr yinyay nyajil | 3sg.NOM scrub.goanna there see.NPST", "r261_3sg_target_there_see_kalajarr", ["nyulu", "kalajarr", "yinyay", "nyajil"], EVIDENCE["kalajarr"]),
    row(19, "He sees the death adder there.", "Nyulu kundurr yinyay nyajil.", "nyulu kundurr yinyay nyajil | 3sg.NOM death.adder there see.NPST", "r261_3sg_target_there_see_kundurr", ["nyulu", "kundurr", "yinyay", "nyajil"], EVIDENCE["kundurr"]),
    row(20, "He sees the frog there.", "Nyulu yuday yinyay nyajil.", "nyulu yuday yinyay nyajil | 3sg.NOM frog there see.NPST", "r261_3sg_target_there_see_yuday", ["nyulu", "yuday", "yinyay", "nyajil"], EVIDENCE["yuday"]),
    row(21, "You all see the lizard here.", "Yurra bajuy yaluy nyajil.", "yurra bajuy yaluy nyajil | 2pl.NOM lizard here see.NPST", "r261_2pl_target_here_see_bajuy", ["yurra", "bajuy", "yaluy", "nyajil"], EVIDENCE["bajuy"]),
    row(22, "You all see the crocodile here.", "Yurra bilngkumu yaluy nyajil.", "yurra bilngkumu yaluy nyajil | 2pl.NOM crocodile here see.NPST", "r261_2pl_target_here_see_bilngkumu", ["yurra", "bilngkumu", "yaluy", "nyajil"], EVIDENCE["bilngkumu"]),
    row(23, "You all see the frill necked lizard here.", "Yurra binanjal yaluy nyajil.", "yurra binanjal yaluy nyajil | 2pl.NOM frill.necked.lizard here see.NPST", "r261_2pl_target_here_see_binanjal", ["yurra", "binanjal", "yaluy", "nyajil"], EVIDENCE["binanjal"]),
    row(24, "You all see the black snake here.", "Yurra bindimu yaluy nyajil.", "yurra bindimu yaluy nyajil | 2pl.NOM black.snake here see.NPST", "r261_2pl_target_here_see_bindimu", ["yurra", "bindimu", "yaluy", "nyajil"], EVIDENCE["bindimu"]),
    row(25, "You all see the tree snake here.", "Yurra bubal yaluy nyajil.", "yurra bubal yaluy nyajil | 2pl.NOM tree.snake here see.NPST", "r261_2pl_target_here_see_bubal", ["yurra", "bubal", "yaluy", "nyajil"], EVIDENCE["bubal"]),
    row(26, "You all see the whip snake here.", "Yurra jijirrbay yaluy nyajil.", "yurra jijirrbay yaluy nyajil | 2pl.NOM whip.snake here see.NPST", "r261_2pl_target_here_see_jijirrbay", ["yurra", "jijirrbay", "yaluy", "nyajil"], EVIDENCE["jijirrbay"]),
    row(27, "You all see the rock python here.", "Yurra kabul yaluy nyajil.", "yurra kabul yaluy nyajil | 2pl.NOM rock.python here see.NPST", "r261_2pl_target_here_see_kabul", ["yurra", "kabul", "yaluy", "nyajil"], EVIDENCE["kabul"]),
    row(28, "You all see the scrub goanna here.", "Yurra kalajarr yaluy nyajil.", "yurra kalajarr yaluy nyajil | 2pl.NOM scrub.goanna here see.NPST", "r261_2pl_target_here_see_kalajarr", ["yurra", "kalajarr", "yaluy", "nyajil"], EVIDENCE["kalajarr"]),
    row(29, "You all see the death adder here.", "Yurra kundurr yaluy nyajil.", "yurra kundurr yaluy nyajil | 2pl.NOM death.adder here see.NPST", "r261_2pl_target_here_see_kundurr", ["yurra", "kundurr", "yaluy", "nyajil"], EVIDENCE["kundurr"]),
    row(30, "You all see the frog here.", "Yurra yuday yaluy nyajil.", "yurra yuday yaluy nyajil | 2pl.NOM frog here see.NPST", "r261_2pl_target_here_see_yuday", ["yurra", "yuday", "yaluy", "nyajil"], EVIDENCE["yuday"]),
    row(31, "The two of them see the lizard there.", "Bula bajuy yinyay nyajil.", "bula bajuy yinyay nyajil | 3du.NOM lizard there see.NPST", "r261_3du_target_there_see_bajuy", ["bula", "bajuy", "yinyay", "nyajil"], EVIDENCE["bajuy"]),
    row(32, "The two of them see the crocodile there.", "Bula bilngkumu yinyay nyajil.", "bula bilngkumu yinyay nyajil | 3du.NOM crocodile there see.NPST", "r261_3du_target_there_see_bilngkumu", ["bula", "bilngkumu", "yinyay", "nyajil"], EVIDENCE["bilngkumu"]),
    row(33, "The two of them see the frill necked lizard there.", "Bula binanjal yinyay nyajil.", "bula binanjal yinyay nyajil | 3du.NOM frill.necked.lizard there see.NPST", "r261_3du_target_there_see_binanjal", ["bula", "binanjal", "yinyay", "nyajil"], EVIDENCE["binanjal"]),
    row(34, "The two of them see the black snake there.", "Bula bindimu yinyay nyajil.", "bula bindimu yinyay nyajil | 3du.NOM black.snake there see.NPST", "r261_3du_target_there_see_bindimu", ["bula", "bindimu", "yinyay", "nyajil"], EVIDENCE["bindimu"]),
    row(35, "The two of them see the tree snake there.", "Bula bubal yinyay nyajil.", "bula bubal yinyay nyajil | 3du.NOM tree.snake there see.NPST", "r261_3du_target_there_see_bubal", ["bula", "bubal", "yinyay", "nyajil"], EVIDENCE["bubal"]),
    row(36, "The two of them see the whip snake there.", "Bula jijirrbay yinyay nyajil.", "bula jijirrbay yinyay nyajil | 3du.NOM whip.snake there see.NPST", "r261_3du_target_there_see_jijirrbay", ["bula", "jijirrbay", "yinyay", "nyajil"], EVIDENCE["jijirrbay"]),
    row(37, "The two of them see the rock python there.", "Bula kabul yinyay nyajil.", "bula kabul yinyay nyajil | 3du.NOM rock.python there see.NPST", "r261_3du_target_there_see_kabul", ["bula", "kabul", "yinyay", "nyajil"], EVIDENCE["kabul"]),
    row(38, "The two of them see the scrub goanna there.", "Bula kalajarr yinyay nyajil.", "bula kalajarr yinyay nyajil | 3du.NOM scrub.goanna there see.NPST", "r261_3du_target_there_see_kalajarr", ["bula", "kalajarr", "yinyay", "nyajil"], EVIDENCE["kalajarr"]),
    row(39, "The two of them see the death adder there.", "Bula kundurr yinyay nyajil.", "bula kundurr yinyay nyajil | 3du.NOM death.adder there see.NPST", "r261_3du_target_there_see_kundurr", ["bula", "kundurr", "yinyay", "nyajil"], EVIDENCE["kundurr"]),
    row(40, "The two of them see the frog there.", "Bula yuday yinyay nyajil.", "bula yuday yinyay nyajil | 3du.NOM frog there see.NPST", "r261_3du_target_there_see_yuday", ["bula", "yuday", "yinyay", "nyajil"], EVIDENCE["yuday"]),
    row(41, "They see the lizard here.", "Jana bajuy yaluy nyajil.", "jana bajuy yaluy nyajil | 3pl.NOM lizard here see.NPST", "r261_3pl_target_here_see_bajuy", ["jana", "bajuy", "yaluy", "nyajil"], EVIDENCE["bajuy"]),
    row(42, "They see the crocodile here.", "Jana bilngkumu yaluy nyajil.", "jana bilngkumu yaluy nyajil | 3pl.NOM crocodile here see.NPST", "r261_3pl_target_here_see_bilngkumu", ["jana", "bilngkumu", "yaluy", "nyajil"], EVIDENCE["bilngkumu"]),
    row(43, "They see the frill necked lizard here.", "Jana binanjal yaluy nyajil.", "jana binanjal yaluy nyajil | 3pl.NOM frill.necked.lizard here see.NPST", "r261_3pl_target_here_see_binanjal", ["jana", "binanjal", "yaluy", "nyajil"], EVIDENCE["binanjal"]),
    row(44, "They see the black snake here.", "Jana bindimu yaluy nyajil.", "jana bindimu yaluy nyajil | 3pl.NOM black.snake here see.NPST", "r261_3pl_target_here_see_bindimu", ["jana", "bindimu", "yaluy", "nyajil"], EVIDENCE["bindimu"]),
    row(45, "They see the tree snake here.", "Jana bubal yaluy nyajil.", "jana bubal yaluy nyajil | 3pl.NOM tree.snake here see.NPST", "r261_3pl_target_here_see_bubal", ["jana", "bubal", "yaluy", "nyajil"], EVIDENCE["bubal"]),
    row(46, "They see the whip snake here.", "Jana jijirrbay yaluy nyajil.", "jana jijirrbay yaluy nyajil | 3pl.NOM whip.snake here see.NPST", "r261_3pl_target_here_see_jijirrbay", ["jana", "jijirrbay", "yaluy", "nyajil"], EVIDENCE["jijirrbay"]),
    row(47, "They see the rock python here.", "Jana kabul yaluy nyajil.", "jana kabul yaluy nyajil | 3pl.NOM rock.python here see.NPST", "r261_3pl_target_here_see_kabul", ["jana", "kabul", "yaluy", "nyajil"], EVIDENCE["kabul"]),
    row(48, "They see the scrub goanna here.", "Jana kalajarr yaluy nyajil.", "jana kalajarr yaluy nyajil | 3pl.NOM scrub.goanna here see.NPST", "r261_3pl_target_here_see_kalajarr", ["jana", "kalajarr", "yaluy", "nyajil"], EVIDENCE["kalajarr"]),
    row(49, "They see the death adder here.", "Jana kundurr yaluy nyajil.", "jana kundurr yaluy nyajil | 3pl.NOM death.adder here see.NPST", "r261_3pl_target_here_see_kundurr", ["jana", "kundurr", "yaluy", "nyajil"], EVIDENCE["kundurr"]),
    row(50, "They see the frog here.", "Jana yuday yaluy nyajil.", "jana yuday yaluy nyajil | 3pl.NOM frog here see.NPST", "r261_3pl_target_here_see_yuday", ["jana", "yuday", "yaluy", "nyajil"], EVIDENCE["yuday"]),
]


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("r261_"):
            issues.append(f"{label}: Rule 261 frame must start with r261_")
        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 261")
        for denied in DENY_KUKU:
            if has_token(low, denied):
                issues.append(f"{label}: denied carry-forward 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 261 target reptile/amphibian noun, found {targets}")
        for head in targets:
            selected = TARGET_HEADS[head]
            dotted = selected.replace("-", ".").replace(" ", ".")
            if head not in words:
                issues.append(f"{label}: target head {head} missing from words_used")
            if selected not in english_low:
                issues.append(f"{label}: English must include selected sense {selected!r}")
            if selected not in analysis and dotted not in analysis:
                issues.append(f"{label}: analysis must gloss {head} as {selected}")
            if has_target_morphology(low, head):
                issues.append(f"{label}: target head {head} has forbidden morphology")
        for support in SUPPORT:
            if has_token(low, support) and support not in words:
                issues.append(f"{label}: support {support} missing from words_used")
        pronouns = [pronoun for pronoun in PRONOUN_SUPPORT if has_token(low, pronoun)]
        if len(pronouns) != 1:
            issues.append(f"{label}: rows require exactly one bare nominative pronoun, found {pronouns}")
        if 1 <= seq <= 10 and (not low.startswith("ngayu ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 1-10 require Ngayu TARGET yaluy nyajil")
        if 11 <= seq <= 20 and (not low.startswith("nyulu ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 11-20 require Nyulu TARGET yinyay nyajil")
        if 21 <= seq <= 30 and (not low.startswith("yurra ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 21-30 require Yurra TARGET yaluy nyajil")
        if 31 <= seq <= 40 and (not low.startswith("bula ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 31-40 require Bula TARGET yinyay nyajil")
        if 41 <= seq <= 50 and (not low.startswith("jana ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 41-50 require Jana TARGET yaluy nyajil")
        if any(english_has_denied(english_low, bad) for bad in DENY_ENGLISH):
            issues.append(f"{label}: denied reptile/amphibian behavior/use/habitat/story/body-part/source-prose 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 lessons 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:
        db.execute(
            "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
            (LABEL, "Rule 261 draft", "operator-guide-2026-07-09", "draft under review", NOW),
        )
        batch_id = db.execute("SELECT id FROM batches WHERE label=?", (LABEL,)).fetchone()[0]

    collisions: list[str] = []
    for seq, _english, kuku, *_rest in ROWS:
        found = db.execute(
            """
            SELECT b.label, s.seq, s.status
            FROM sentences s JOIN batches b ON b.id=s.batch_id
            WHERE lower(s.kuku)=lower(?) AND s.status IN ('accepted','revised','under_review')
            """,
            (kuku,),
        ).fetchall()
        if found:
            collisions.append(f"{seq}: {kuku} -> {found}")
    if collisions:
        raise SystemExit("collision scan failed:\n" + "\n".join(collisions))

    db.executemany(
        """
        INSERT INTO sentences(
          batch_id, seq, english, kuku, analysis, frame, tier, words_used,
          loans_used, evidence, status, confidence, created_at
        )
        VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)
        """,
        [
            (
                batch_id,
                seq,
                english,
                kuku,
                analysis,
                frame,
                tier,
                words_used,
                loans_used,
                evidence,
                "under_review",
                confidence,
                NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_used, loans_used, evidence, confidence in ROWS
        ],
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="source",
        event_type="source-readback",
        actor="claude-main",
        detail="Rule 261 source pass checked dictionary lines for bajuy lizard, bilngkumu crocodile, binanjal frill necked lizard, bindimu black snake, bubal tree snake, jijirrbay whip snake, kabul rock python, kalajarr scrub goanna, kundurr death adder, and yuday frog; Patz grammar lines 2553-2603 for bare nominative ngayu, nyulu, yurra, bula, and jana; lines 2838-2845 for yaluy/yinyay; and lines 3043-3049 for nyaji-l.",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="planning",
        event_type="debt-sweep-scope",
        actor="claude-main",
        detail="Selected Rule 261 complementary reptile/amphibian perception debt sweep after Rule 260: same ten checked Rule 252 reptile/amphibian labels in zero-collision nominative-pronoun perception bands using the opposite yaluy/yinyay pairing for ngayu, nyulu, yurra, bula, and jana. All reptile behavior/use/habitat/story/body-part/source-prose, target-case, pronoun-case, demonstrative-case, and perception-compound semantics remain closed.",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="preflight",
        event_type="coverage-audit",
        actor="claude-main",
        detail="Pre-batch token-aware target coverage among accepted/revised rows after Rule 260: bajuy 18, bilngkumu 64, binanjal 14, bindimu 15, bubal 12, jijirrbay 11, kabul 17, kalajarr 14, kundurr 13, and yuday 16. Support coverage: ngayu 1303, nyulu 1784, yurra 233, bula 213, jana 1506, yaluy 2373, yinyay 2626, nyajil 2867.",
        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 over the final 50 Rule 261 Kuku surfaces returned zero accepted, revised, or under-review collisions before banking.",
        command="sqlite3 synthetic.db planned surface collision query",
        output_excerpt="0 rows",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="preflight",
        event_type="static-guard-pass",
        actor="claude-main",
        detail="Static guard passed: 50 rows, one checked target per row, one bare nominative pronoun per row, correct complementary yaluy/yinyay band, no loans, no denied Kuku tokens, no denied English behavior/use/habitat/story/body-part/source-prose fields, no target morphology, no support inflection.",
        command="python3 batch_scripts/bank_303.py scan_static",
        output_excerpt="static guard passed before insert",
        artifact_path="batch_scripts/bank_303.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="bank-success",
        actor="claude-main",
        detail="Banked 50 hand-authored Rule 261 complementary reptile/amphibian perception rows as under_review.",
        command="python3 batch_scripts/bank_303.py",
        artifact_path="batch_scripts/bank_303.py",
        created_at=NOW,
    )
    db.commit()
    count = db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=?", (batch_id,)).fetchone()[0]
    print(f"banked {LABEL}: {count} rows under_review")
    return 0


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