#!/usr/bin/env python3
"""Bank Batch 389: Rule 347 complementary bird-set-D singular/third perception."""

from __future__ import annotations

import datetime as dt
import json
import re
import sqlite3

from bank_388 import (
    DENY_ENGLISH,
    DENY_KUKU,
    EXPECTED_HASHES,
    SUPPORT_EVIDENCE as RULE346_SUPPORT_EVIDENCE,
    TARGET_CONFIDENCE,
    TARGET_EVIDENCE as RULE346_TARGET_EVIDENCE,
    TARGET_GLOSSES,
    TARGET_LABELS,
    TARGET_ORDER,
    count_words,
    source_audit,
)
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-389"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-389-complementary-bird-set-d-singular-third-past-perception-after-rule346-process.md"
EXPECTED_TARGET_COVERAGE = {
    "diwalngku": 10, "bujil-yiran": 10, "karrmbil": 10, "yiki": 10, "bajal": 11,
    "biru-biru": 11, "jangkan": 11, "kurranday": 11, "ngarkay": 11, "wakuka": 12,
}
EXPECTED_SUPPORT_COVERAGE = {
    "ngayu": 1491, "yundu": 1087, "nyulu": 762, "bula": 558, "jana": 1263,
    "yaluy": 4562, "yinyay": 4724, "nyajin": 2400,
}
FRAME_SPECS = [
    ("ngayu", "yinyay", "I saw", "there", "1sg"),
    ("yundu", "yaluy", "You saw", "here", "2sg"),
    ("nyulu", "yinyay", "He saw", "there", "3sg"),
    ("bula", "yaluy", "The two of them saw", "here", "3du"),
    ("jana", "yinyay", "They saw", "there", "3pl"),
]
OPPOSITE = {"yaluy": "yinyay", "yinyay": "yaluy"}
TARGET_EVIDENCE = {
    target: f"{RULE346_TARGET_EVIDENCE[target]} Rule 347 adds only the exact opposite deictic cell and preserves every Rule 346 card, sense and source boundary."
    for target in TARGET_ORDER
}
SUPPORT_EVIDENCE = (
    f"{RULE346_SUPPORT_EVIDENCE} Rule 347 reasserts the corrected three-hit wakuka profile and maps every "
    "opposite to Batch 388 before banking; no zero-hit or proper-name inference is introduced."
)


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, "I saw the diwalngku bird there.", "Ngayu diwalngku yinyay nyajin.", "ngayu diwalngku yinyay nyajin | 1sg.NOM diwalngku.bird there see.PST", "r347_1sg_there_bird_d_saw_diwalngku", ["ngayu", "diwalngku", "yinyay", "nyajin"]),
    row(2, "I saw the bujil-yiran ibis there.", "Ngayu bujil-yiran yinyay nyajin.", "ngayu bujil-yiran yinyay nyajin | 1sg.NOM bujil-yiran.ibis there see.PST", "r347_1sg_there_bird_d_saw_bujil_yiran", ["ngayu", "bujil-yiran", "yinyay", "nyajin"]),
    row(3, "I saw the karrmbil parakeet there.", "Ngayu karrmbil yinyay nyajin.", "ngayu karrmbil yinyay nyajin | 1sg.NOM karrmbil.parakeet there see.PST", "r347_1sg_there_bird_d_saw_karrmbil", ["ngayu", "karrmbil", "yinyay", "nyajin"]),
    row(4, "I saw the yiki owl there.", "Ngayu yiki yinyay nyajin.", "ngayu yiki yinyay nyajin | 1sg.NOM yiki.owl there see.PST", "r347_1sg_there_bird_d_saw_yiki", ["ngayu", "yiki", "yinyay", "nyajin"]),
    row(5, "I saw the bajal bower bird there.", "Ngayu bajal yinyay nyajin.", "ngayu bajal yinyay nyajin | 1sg.NOM bajal.bower-bird there see.PST", "r347_1sg_there_bird_d_saw_bajal", ["ngayu", "bajal", "yinyay", "nyajin"]),
    row(6, "I saw the biru-biru rainbow bird there.", "Ngayu biru-biru yinyay nyajin.", "ngayu biru-biru yinyay nyajin | 1sg.NOM biru-biru.rainbow-bird there see.PST", "r347_1sg_there_bird_d_saw_biru_biru", ["ngayu", "biru-biru", "yinyay", "nyajin"]),
    row(7, "I saw the jangkan swift there.", "Ngayu jangkan yinyay nyajin.", "ngayu jangkan yinyay nyajin | 1sg.NOM jangkan.swift there see.PST", "r347_1sg_there_bird_d_saw_jangkan", ["ngayu", "jangkan", "yinyay", "nyajin"]),
    row(8, "I saw the kurranday seagull there.", "Ngayu kurranday yinyay nyajin.", "ngayu kurranday yinyay nyajin | 1sg.NOM kurranday.seagull there see.PST", "r347_1sg_there_bird_d_saw_kurranday", ["ngayu", "kurranday", "yinyay", "nyajin"]),
    row(9, "I saw the ngarkay waterhen there.", "Ngayu ngarkay yinyay nyajin.", "ngayu ngarkay yinyay nyajin | 1sg.NOM ngarkay.waterhen there see.PST", "r347_1sg_there_bird_d_saw_ngarkay", ["ngayu", "ngarkay", "yinyay", "nyajin"]),
    row(10, "I saw the wakuka bird there.", "Ngayu wakuka yinyay nyajin.", "ngayu wakuka yinyay nyajin | 1sg.NOM wakuka.bird there see.PST", "r347_1sg_there_bird_d_saw_wakuka", ["ngayu", "wakuka", "yinyay", "nyajin"]),
    row(11, "You saw the diwalngku bird here.", "Yundu diwalngku yaluy nyajin.", "yundu diwalngku yaluy nyajin | 2sg.NOM diwalngku.bird here see.PST", "r347_2sg_here_bird_d_saw_diwalngku", ["yundu", "diwalngku", "yaluy", "nyajin"]),
    row(12, "You saw the bujil-yiran ibis here.", "Yundu bujil-yiran yaluy nyajin.", "yundu bujil-yiran yaluy nyajin | 2sg.NOM bujil-yiran.ibis here see.PST", "r347_2sg_here_bird_d_saw_bujil_yiran", ["yundu", "bujil-yiran", "yaluy", "nyajin"]),
    row(13, "You saw the karrmbil parakeet here.", "Yundu karrmbil yaluy nyajin.", "yundu karrmbil yaluy nyajin | 2sg.NOM karrmbil.parakeet here see.PST", "r347_2sg_here_bird_d_saw_karrmbil", ["yundu", "karrmbil", "yaluy", "nyajin"]),
    row(14, "You saw the yiki owl here.", "Yundu yiki yaluy nyajin.", "yundu yiki yaluy nyajin | 2sg.NOM yiki.owl here see.PST", "r347_2sg_here_bird_d_saw_yiki", ["yundu", "yiki", "yaluy", "nyajin"]),
    row(15, "You saw the bajal bower bird here.", "Yundu bajal yaluy nyajin.", "yundu bajal yaluy nyajin | 2sg.NOM bajal.bower-bird here see.PST", "r347_2sg_here_bird_d_saw_bajal", ["yundu", "bajal", "yaluy", "nyajin"]),
    row(16, "You saw the biru-biru rainbow bird here.", "Yundu biru-biru yaluy nyajin.", "yundu biru-biru yaluy nyajin | 2sg.NOM biru-biru.rainbow-bird here see.PST", "r347_2sg_here_bird_d_saw_biru_biru", ["yundu", "biru-biru", "yaluy", "nyajin"]),
    row(17, "You saw the jangkan swift here.", "Yundu jangkan yaluy nyajin.", "yundu jangkan yaluy nyajin | 2sg.NOM jangkan.swift here see.PST", "r347_2sg_here_bird_d_saw_jangkan", ["yundu", "jangkan", "yaluy", "nyajin"]),
    row(18, "You saw the kurranday seagull here.", "Yundu kurranday yaluy nyajin.", "yundu kurranday yaluy nyajin | 2sg.NOM kurranday.seagull here see.PST", "r347_2sg_here_bird_d_saw_kurranday", ["yundu", "kurranday", "yaluy", "nyajin"]),
    row(19, "You saw the ngarkay waterhen here.", "Yundu ngarkay yaluy nyajin.", "yundu ngarkay yaluy nyajin | 2sg.NOM ngarkay.waterhen here see.PST", "r347_2sg_here_bird_d_saw_ngarkay", ["yundu", "ngarkay", "yaluy", "nyajin"]),
    row(20, "You saw the wakuka bird here.", "Yundu wakuka yaluy nyajin.", "yundu wakuka yaluy nyajin | 2sg.NOM wakuka.bird here see.PST", "r347_2sg_here_bird_d_saw_wakuka", ["yundu", "wakuka", "yaluy", "nyajin"]),
    row(21, "He saw the diwalngku bird there.", "Nyulu diwalngku yinyay nyajin.", "nyulu diwalngku yinyay nyajin | 3sg.NOM diwalngku.bird there see.PST", "r347_3sg_there_bird_d_saw_diwalngku", ["nyulu", "diwalngku", "yinyay", "nyajin"]),
    row(22, "He saw the bujil-yiran ibis there.", "Nyulu bujil-yiran yinyay nyajin.", "nyulu bujil-yiran yinyay nyajin | 3sg.NOM bujil-yiran.ibis there see.PST", "r347_3sg_there_bird_d_saw_bujil_yiran", ["nyulu", "bujil-yiran", "yinyay", "nyajin"]),
    row(23, "He saw the karrmbil parakeet there.", "Nyulu karrmbil yinyay nyajin.", "nyulu karrmbil yinyay nyajin | 3sg.NOM karrmbil.parakeet there see.PST", "r347_3sg_there_bird_d_saw_karrmbil", ["nyulu", "karrmbil", "yinyay", "nyajin"]),
    row(24, "He saw the yiki owl there.", "Nyulu yiki yinyay nyajin.", "nyulu yiki yinyay nyajin | 3sg.NOM yiki.owl there see.PST", "r347_3sg_there_bird_d_saw_yiki", ["nyulu", "yiki", "yinyay", "nyajin"]),
    row(25, "He saw the bajal bower bird there.", "Nyulu bajal yinyay nyajin.", "nyulu bajal yinyay nyajin | 3sg.NOM bajal.bower-bird there see.PST", "r347_3sg_there_bird_d_saw_bajal", ["nyulu", "bajal", "yinyay", "nyajin"]),
    row(26, "He saw the biru-biru rainbow bird there.", "Nyulu biru-biru yinyay nyajin.", "nyulu biru-biru yinyay nyajin | 3sg.NOM biru-biru.rainbow-bird there see.PST", "r347_3sg_there_bird_d_saw_biru_biru", ["nyulu", "biru-biru", "yinyay", "nyajin"]),
    row(27, "He saw the jangkan swift there.", "Nyulu jangkan yinyay nyajin.", "nyulu jangkan yinyay nyajin | 3sg.NOM jangkan.swift there see.PST", "r347_3sg_there_bird_d_saw_jangkan", ["nyulu", "jangkan", "yinyay", "nyajin"]),
    row(28, "He saw the kurranday seagull there.", "Nyulu kurranday yinyay nyajin.", "nyulu kurranday yinyay nyajin | 3sg.NOM kurranday.seagull there see.PST", "r347_3sg_there_bird_d_saw_kurranday", ["nyulu", "kurranday", "yinyay", "nyajin"]),
    row(29, "He saw the ngarkay waterhen there.", "Nyulu ngarkay yinyay nyajin.", "nyulu ngarkay yinyay nyajin | 3sg.NOM ngarkay.waterhen there see.PST", "r347_3sg_there_bird_d_saw_ngarkay", ["nyulu", "ngarkay", "yinyay", "nyajin"]),
    row(30, "He saw the wakuka bird there.", "Nyulu wakuka yinyay nyajin.", "nyulu wakuka yinyay nyajin | 3sg.NOM wakuka.bird there see.PST", "r347_3sg_there_bird_d_saw_wakuka", ["nyulu", "wakuka", "yinyay", "nyajin"]),
    row(31, "The two of them saw the diwalngku bird here.", "Bula diwalngku yaluy nyajin.", "bula diwalngku yaluy nyajin | 3du.NOM diwalngku.bird here see.PST", "r347_3du_here_bird_d_saw_diwalngku", ["bula", "diwalngku", "yaluy", "nyajin"]),
    row(32, "The two of them saw the bujil-yiran ibis here.", "Bula bujil-yiran yaluy nyajin.", "bula bujil-yiran yaluy nyajin | 3du.NOM bujil-yiran.ibis here see.PST", "r347_3du_here_bird_d_saw_bujil_yiran", ["bula", "bujil-yiran", "yaluy", "nyajin"]),
    row(33, "The two of them saw the karrmbil parakeet here.", "Bula karrmbil yaluy nyajin.", "bula karrmbil yaluy nyajin | 3du.NOM karrmbil.parakeet here see.PST", "r347_3du_here_bird_d_saw_karrmbil", ["bula", "karrmbil", "yaluy", "nyajin"]),
    row(34, "The two of them saw the yiki owl here.", "Bula yiki yaluy nyajin.", "bula yiki yaluy nyajin | 3du.NOM yiki.owl here see.PST", "r347_3du_here_bird_d_saw_yiki", ["bula", "yiki", "yaluy", "nyajin"]),
    row(35, "The two of them saw the bajal bower bird here.", "Bula bajal yaluy nyajin.", "bula bajal yaluy nyajin | 3du.NOM bajal.bower-bird here see.PST", "r347_3du_here_bird_d_saw_bajal", ["bula", "bajal", "yaluy", "nyajin"]),
    row(36, "The two of them saw the biru-biru rainbow bird here.", "Bula biru-biru yaluy nyajin.", "bula biru-biru yaluy nyajin | 3du.NOM biru-biru.rainbow-bird here see.PST", "r347_3du_here_bird_d_saw_biru_biru", ["bula", "biru-biru", "yaluy", "nyajin"]),
    row(37, "The two of them saw the jangkan swift here.", "Bula jangkan yaluy nyajin.", "bula jangkan yaluy nyajin | 3du.NOM jangkan.swift here see.PST", "r347_3du_here_bird_d_saw_jangkan", ["bula", "jangkan", "yaluy", "nyajin"]),
    row(38, "The two of them saw the kurranday seagull here.", "Bula kurranday yaluy nyajin.", "bula kurranday yaluy nyajin | 3du.NOM kurranday.seagull here see.PST", "r347_3du_here_bird_d_saw_kurranday", ["bula", "kurranday", "yaluy", "nyajin"]),
    row(39, "The two of them saw the ngarkay waterhen here.", "Bula ngarkay yaluy nyajin.", "bula ngarkay yaluy nyajin | 3du.NOM ngarkay.waterhen here see.PST", "r347_3du_here_bird_d_saw_ngarkay", ["bula", "ngarkay", "yaluy", "nyajin"]),
    row(40, "The two of them saw the wakuka bird here.", "Bula wakuka yaluy nyajin.", "bula wakuka yaluy nyajin | 3du.NOM wakuka.bird here see.PST", "r347_3du_here_bird_d_saw_wakuka", ["bula", "wakuka", "yaluy", "nyajin"]),
    row(41, "They saw the diwalngku bird there.", "Jana diwalngku yinyay nyajin.", "jana diwalngku yinyay nyajin | 3pl.NOM diwalngku.bird there see.PST", "r347_3pl_there_bird_d_saw_diwalngku", ["jana", "diwalngku", "yinyay", "nyajin"]),
    row(42, "They saw the bujil-yiran ibis there.", "Jana bujil-yiran yinyay nyajin.", "jana bujil-yiran yinyay nyajin | 3pl.NOM bujil-yiran.ibis there see.PST", "r347_3pl_there_bird_d_saw_bujil_yiran", ["jana", "bujil-yiran", "yinyay", "nyajin"]),
    row(43, "They saw the karrmbil parakeet there.", "Jana karrmbil yinyay nyajin.", "jana karrmbil yinyay nyajin | 3pl.NOM karrmbil.parakeet there see.PST", "r347_3pl_there_bird_d_saw_karrmbil", ["jana", "karrmbil", "yinyay", "nyajin"]),
    row(44, "They saw the yiki owl there.", "Jana yiki yinyay nyajin.", "jana yiki yinyay nyajin | 3pl.NOM yiki.owl there see.PST", "r347_3pl_there_bird_d_saw_yiki", ["jana", "yiki", "yinyay", "nyajin"]),
    row(45, "They saw the bajal bower bird there.", "Jana bajal yinyay nyajin.", "jana bajal yinyay nyajin | 3pl.NOM bajal.bower-bird there see.PST", "r347_3pl_there_bird_d_saw_bajal", ["jana", "bajal", "yinyay", "nyajin"]),
    row(46, "They saw the biru-biru rainbow bird there.", "Jana biru-biru yinyay nyajin.", "jana biru-biru yinyay nyajin | 3pl.NOM biru-biru.rainbow-bird there see.PST", "r347_3pl_there_bird_d_saw_biru_biru", ["jana", "biru-biru", "yinyay", "nyajin"]),
    row(47, "They saw the jangkan swift there.", "Jana jangkan yinyay nyajin.", "jana jangkan yinyay nyajin | 3pl.NOM jangkan.swift there see.PST", "r347_3pl_there_bird_d_saw_jangkan", ["jana", "jangkan", "yinyay", "nyajin"]),
    row(48, "They saw the kurranday seagull there.", "Jana kurranday yinyay nyajin.", "jana kurranday yinyay nyajin | 3pl.NOM kurranday.seagull there see.PST", "r347_3pl_there_bird_d_saw_kurranday", ["jana", "kurranday", "yinyay", "nyajin"]),
    row(49, "They saw the ngarkay waterhen there.", "Jana ngarkay yinyay nyajin.", "jana ngarkay yinyay nyajin | 3pl.NOM ngarkay.waterhen there see.PST", "r347_3pl_there_bird_d_saw_ngarkay", ["jana", "ngarkay", "yinyay", "nyajin"]),
    row(50, "They saw the wakuka bird there.", "Jana wakuka yinyay nyajin.", "jana wakuka yinyay nyajin | 3pl.NOM wakuka.bird there see.PST", "r347_3pl_there_bird_d_saw_wakuka", ["jana", "wakuka", "yinyay", "nyajin"]),
]


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 or len({item[2] for item in ROWS}) != 50:
        issues.append("duplicate authored 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)
        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 json.loads(loans_json):
            issues.append(f"{seq}: 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("r347_"):
            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:
    record_counts, patz_hits = 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}")
    for seq, _english, kuku, _analysis, _frame, _tier, words_json, _loans, _evidence, _confidence in ROWS:
        words = json.loads(words_json)
        candidate_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""", (kuku,),
        ).fetchall()
        if candidate_hits:
            raise SystemExit(f"candidate collision {seq}: {candidate_hits}")
        opposite_surface = f"{words[0].capitalize()} {words[1]} {OPPOSITE[words[2]]} nyajin."
        opposite_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""", (opposite_surface,),
        ).fetchall()
        if opposite_hits != [("batch-388", seq)]:
            raise SystemExit(f"opposite mismatch {seq}: {opposite_hits}")
    verified, words = db.execute(
        """SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1)
           FROM sentences WHERE status IN ('accepted','revised')"""
    ).fetchone()
    if (verified, words) != (19497, 133347):
        raise SystemExit(f"baseline changed {verified}/{words}")
    notes = (
        "COMPLEMENTARY BIRD SET-D SINGULAR/THIRD PAST PERCEPTION AFTER RULE 346 (Rule 347). 50 under-review "
        "exact deictic complements; candidates empty and opposites exactly Batch 388 seq 1-50. Two-card inventory, "
        "corrected wakuka three-hit profile and all unknown/conflicting taxonomy, card/domain, whole-head, cultural, "
        "property/habitat/place/source/event boundaries unchanged. Completion necessarily changes gap 162->172."
    )
    cur = db.execute(
        "INSERT INTO batches(label,cheatsheet_version,guide_version,notes,created_at) VALUES(?,?,?,?,?)",
        (LABEL, "Rule 347 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 Rule 347 expectations: {EXPECTED_HASHES}."),
        ("source", "source-readback", f"Fresh two-card target inventory passed: {record_counts}."),
        ("source", "patz-target-hit-audit", f"Corrected exact Patz profile unchanged: {patz_hits}; Wakuka Station remains proper-name context."),
        ("planning", "sense-preservation-audit", "Rule 346 generic labels and all taxonomy/card-domain/whole-head/cultural/property/habitat/place/source/event boundaries retained."),
        ("planning", "complement-map-audit", "All 50 candidate cells absent; every opposite exactly Batch 388 sequences 1-50; full 100-cell matrix accounted for."),
        ("planning", "deictic-balance-audit", "Before banking yaluy=4562 and yinyay=4724, gap 162; completion projects 172. Apparent 152-gap inverse is Batch 388 duplication."),
        ("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; opposites proved one-for-one."),
        ("preflight", "static-guard-pass", "50 explicit Rule 347 rows; source, card/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 347 complementary rows under_review; verified remains {verified}; words {words}.",
        command="python3 batch_scripts/bank_389.py", output_excerpt="50 under_review", artifact_path=PROCESS_LOG, created_at=NOW,
    )
    db.commit()
    print("banked batch-389: 50 rows under_review")
    return 0


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