#!/usr/bin/env python3
"""Bank batch-148: manner adverbs, adverbial -ku, and quality-to-manner frames."""

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-148"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")

DENY = {
    "wunbu-kari", "wawu-mumbu", "bikirr", "ngawuya", "wajan",
    "jinbi", "jibin", "nyawa", "respect", "permission", "authority",
}


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


ROWS = [
    (1, "He killed the wallaby thoroughly.",
     "Nyulu kadar kunin nguymal-ku.",
     "nyulu kadar kuni-ny nguymal-ku | he wallaby hit/kill-PAST thoroughly-ADV",
     "adverbial_particle_optional_ku", 5, js(["nyulu", "kadar", "kunil", "nguymal"]), js([]),
     "Patz ex.192 has nguymal(-ku) after a kill verb; this row uses the explicit -ku variant.", "0.88"),
    (2, "The man came discreetly.",
     "Dingkar kadan nganjarr-nganjarr.",
     "dingkar kada-ny nganjarr-nganjarr | man come-PAST discreetly",
     "adverbial_particle_manner", 5, js(["dingkar", "kaday", "nganjarr-nganjarr"]), js([]),
     "Patz ex.193 has nganjarrnganjarr with kada-ny; subject varied from police to common noun.", "0.86"),
    (3, "You fit well staying in the house.",
     "Yundu ngambuy bundanday bayan-ba.",
     "yundu ngambuy bundanda-y bayan-ba | you fittingly stay.REDUP-NONPAST house-LOC",
     "adverbial_particle_manner", 5, js(["yundu", "ngambuy", "bunday", "bayan"]), js([]),
     "Patz ex.196: ngambuy with bundanda-y bayan-ba; grammar sense is fittingly/appropriately.", "0.88"),
    (4, "I did not quite hit the tree with the stone.",
     "Ngayu juku kulji-bu nguyarr kunin.",
     "ngayu juku kulji-bu nguyarr kuni-ny | I tree stone-INST not.quite hit-PAST",
     "adverbial_particle_partial_action", 5, js(["ngayu", "juku", "kulji", "nguyarr", "kunil"]), js([]),
     "Patz ex.195: nguyarr marks not-quite action; source-shaped with juku kulji-bu.", "0.90"),
    (5, "It will tip over with you without quite falling.",
     "Yunu-nji nguyarr daray.",
     "yunu-nji nguyarr dara-y | you-COMIT not.quite fall-NONPAST",
     "adverbial_particle_partial_action", 5, js(["yunu", "nguyarr", "daray"]), js([]),
     "Patz ex.194: yunu-nji nguyarr dara-y in the unsafe-bench warning.", "0.90"),
    (6, "I hit myself on the foot by mistake.",
     "Ngayu kanban jina-nga kunijin.",
     "ngayu kanban jina-nga kuni-ji-ny | I by.mistake foot-LOC hit-REFL-PAST",
     "adverbial_particle_accidental", 5, js(["ngayu", "kanban", "jina", "kunil"]), js([]),
     "Grammar §4.2.4.3 treats kanban as the accidental-action adverb; reflexive body-hit frame is already controlled.", "0.84"),
    (7, "He dropped the egg by mistake.",
     "Nyulu kanban diburr dara-y-manin.",
     "nyulu kanban diburr dara-y-mani-ny | he by.mistake egg fall-CAUS-PAST",
     "adverbial_particle_accidental", 5, js(["nyulu", "kanban", "diburr", "dara-y-manil"]), js([]),
     "Combines kanban with Patz-listed dara-y-mani-l 'drop' and the diburr egg row.", "0.84"),
    (8, "The children went one after another to the creek.",
     "Karrkay-karrkay jururr dungan wawubaja-nga.",
     "karrkay-karrkay jururr dunga-ny wawubaja-nga | child-REDUP one.after.another go-PAST creek-LOC",
     "adverbial_particle_sequence", 5, js(["karrkay", "jururr", "dungay", "wawubaja"]), js([]),
     "Patz lists jururr(jururr) as repeated/one-after-another with motion verbs.", "0.84"),
    (9, "I sat briefly by the fire.",
     "Ngayu nganga bundan baya-nga.",
     "ngayu nganga bunda-ny baya-nga | I briefly sit-PAST fire-LOC",
     "adverbial_particle_brief", 4, js(["ngayu", "nganga", "bunday", "baya"]), js([]),
     "Patz lists nganga as briefly/for just a little bit; dictionary also has just/only.", "0.82"),
    (10, "The woman wove the dillybag absorbed in the work.",
     "Jalbu-ngku wudu balji wukurrin.",
     "jalbu-ngku wudu balji wukurri-ny | woman-ERG absorbed dillybag weave-PAST",
     "adverbial_particle_absorbed", 5, js(["jalbu", "wudu", "balji", "wukurril"]), js([]),
     "Grammar lists wudu as absorbed in some activity; wukurril covers weave/follow.", "0.84"),
    (11, "The dog went past.",
     "Kaya walku dungan.",
     "kaya walku dunga-ny | dog past go-PAST",
     "direction_particle_motion", 5, js(["kaya", "walku", "dungay"]), js([]),
     "Patz §4.1.2 gives walku dunga-y 'walk past' as a verb-complex modifier.", "0.88"),
    (12, "The dog came straight towards us.",
     "Kaya maku kadan.",
     "kaya maku kada-ny | dog straight.towards come-PAST",
     "direction_particle_motion", 4, js(["kaya", "maku", "kaday"]), js([]),
     "Patz lists maku as straight towards; dictionary has towards.", "0.82"),
    (13, "The people went off course.",
     "Bama wadu-wadu dungan.",
     "bama wadu-wadu dunga-ny | people astray-REDUP go-PAST",
     "direction_particle_motion", 4, js(["bama", "wadu", "dungay"]), js([]),
     "Patz lists wadu(wadu) as astray/off course; dictionary wadu is wrong/incorrect.", "0.82"),
    (14, "The child sat crouching.",
     "Karrkay mingki bundan.",
     "karrkay mingki bunda-ny | child crouching sit-PAST",
     "posture_particle_rest", 4, js(["karrkay", "mingki", "bunday"]), js([]),
     "Patz posture particle list: mingki crouching/squatting.", "0.82"),
    (15, "The dog lay prone.",
     "Kaya jarrbarr wunan.",
     "kaya jarrbarr wuna-ny | dog prone lie-PAST",
     "posture_particle_rest", 4, js(["kaya", "jarrbarr", "wunay"]), js([]),
     "Patz posture particle list: jarrbarr prone/on stomach.", "0.82"),
    (16, "The man stood upright.",
     "Dingkar jangka janan.",
     "dingkar jangka jana-ny | man upright stand-PAST",
     "posture_particle_rest", 4, js(["dingkar", "jangka", "janay"]), js([]),
     "Patz posture particle list: jangka upright/straight; dictionary has a homographic noun, so grammar sense controls.", "0.82"),
    (17, "The child went too soon.",
     "Karrkay ngura dungan.",
     "karrkay ngura dunga-ny | child too.soon go-PAST",
     "adverbial_particle_manner", 4, js(["karrkay", "ngura", "dungay"]), js([]),
     "Patz lists ngura as too fast; dictionary gloss narrows to too soon for others/impatient.", "0.82"),
    (18, "The young man went secretly.",
     "Warru yarngkay dungan.",
     "warru yarngkay dunga-ny | young.man secretly go-PAST",
     "adverbial_particle_manner", 4, js(["warru", "yarngkay", "dungay"]), js([]),
     "Patz lists yarngkay as secretly; dictionary marks it as grammar-sourced manner particle.", "0.84"),
    (19, "Grandmother cannot go.",
     "Babi murruji dungay.",
     "babi murruji dunga-y | grandmother cannot go-NONPAST",
     "adverbial_particle_cannot", 4, js(["babi", "murruji", "dungay"]), js([]),
     "Patz lists murruji as cannot among adverbial particles; dictionary agrees.", "0.82"),
    (20, "The children laugh briefly.",
     "Karrkay-karrkay nganga yajarril.",
     "karrkay-karrkay nganga yajarri-l | child-REDUP briefly laugh-NONPAST",
     "adverbial_particle_brief", 4, js(["karrkay", "nganga", "yajarril"]), js([]),
     "Second nganga row keeps it near the verb and away from English-only 'briefly' vocabulary.", "0.80"),

    (21, "My dog runs fast.",
     "Ngayku kaya jinbal-ku wanarri.",
     "ngayku kaya jinbal-ku wanarri-y | my dog fast-ADV run.REDUP-NONPAST",
     "derived_adverb_intransitive", 5, js(["ngayku", "kaya", "jinbal", "wanarri-y"]), js([]),
     "Patz ex.512 has possessive dog + jinbal-ku wanarri-y; possessor varied.", "0.88"),
    (22, "The dog runs quickly and quickly.",
     "Kaya jinbal-jinbal wanarri.",
     "kaya jinbal-jinbal wanarri-y | dog quick-REDUP run.REDUP-NONPAST",
     "derived_adverb_redup", 5, js(["kaya", "jinbal", "wanarri-y"]), js([]),
     "Patz table licenses jinbal-jinbal as quick reduplicated adverb form.", "0.84"),
    (23, "The children went very fast.",
     "Karrkay-karrkay jinbal-jinbal-ku dungan.",
     "karrkay-karrkay jinbal-jinbal-ku dunga-ny | child-REDUP quick-REDUP-ADV go-PAST",
     "derived_adverb_redup_ku", 4, js(["karrkay", "jinbal", "dungay"]), js([]),
     "Patz says reduplicated form plus -ku is possible but rare; one controlled row only.", "0.80"),
    (24, "The small horse walks slowly.",
     "Yarraman buban waymbul-ku dungay.",
     "yarraman buban waymbul-ku dunga-y | horse small slow-ADV go-NONPAST",
     "derived_adverb_intransitive", 5, js(["yarraman", "buban", "waymbul", "dungay"]), js([]),
     "Patz ex.198 has old horse waymbul-ku; adjective varied from mukul to buban.", "0.86"),
    (25, "Grandmother walked slowly.",
     "Babi waymbul-ku dungan.",
     "babi waymbul-ku dunga-ny | grandmother slow-ADV go-PAST",
     "derived_adverb_intransitive", 4, js(["babi", "waymbul", "dungay"]), js([]),
     "Productive adjective + -ku manner from waymbul slow/gently.", "0.82"),
    (26, "The other rope is lying neatly.",
     "Karra yindu mumbar-ku wunanay.",
     "karra yindu mumbar-ku wunana-y | rope other neat-ADV lie.REDUP-NONPAST",
     "derived_adverb_intransitive", 5, js(["karra", "yindu", "mumbar", "wunay"]), js([]),
     "Patz ex.197 has karra mumbar-ku wunana-y; yindu avoids exact source duplicate.", "0.88"),
    (27, "The dog runs wildly.",
     "Kaya kuli-ji-ku wanarri.",
     "kaya kuli-ji-ku wanarri-y | dog rage-COMIT-ADV run.REDUP-NONPAST",
     "derived_adverb_comitative", 5, js(["kaya", "kuli-ji", "wanarri-y"]), js([]),
     "Patz table gives kuli-ji-ku 'angrily'; dog/wild sense is also source-backed in Rule 105.", "0.84"),
    (28, "The man went foolishly.",
     "Dingkar kiru-kari-ku dungan.",
     "dingkar kiru-kari-ku dunga-ny | man brain-PRIV-ADV go-PAST",
     "derived_adverb_privative", 5, js(["dingkar", "kiru-kari", "dungay"]), js([]),
     "Patz table gives kiru-kari-ku 'stupidly'; privative stems do not favor reduplication.", "0.84"),
    (29, "He acted badly.",
     "Nyulu buyun-ku yalaman.",
     "nyulu buyun-ku yalama-ny | he bad-ADV do/say-PAST",
     "derived_adverb_intransitive", 4, js(["nyulu", "buyun", "yalama-l"]), js([]),
     "Patz table gives buyun-ku 'badly'; yalama-l do/say is grammar-sourced.", "0.80"),
    (30, "The person acts well.",
     "Bama ngulkurr-ku bunday.",
     "bama ngulkurr-ku bunda-y | person good-ADV sit/do-NONPAST",
     "derived_adverb_intransitive", 4, js(["bama", "ngulkurr", "bunday"]), js([]),
     "Dictionary source has ngulkurrku bunday for doing good; included as a bounded good-ADV row.", "0.82"),
    (31, "The man stands firmly.",
     "Dingkar dandi-ku janay.",
     "dingkar dandi-ku jana-y | man firm/hard-ADV stand-NONPAST",
     "productive_quality_to_manner", 3, js(["dingkar", "dandi", "janay"]), js([]),
     "Careful productive use of adjective + -ku from Patz process (a); dandi also means strong/healthy.", "0.76"),
    (32, "The child laughs well.",
     "Karrkay ngulkurr-ku yajarril.",
     "karrkay ngulkurr-ku yajarri-l | child good-ADV laugh-NONPAST",
     "productive_quality_to_manner", 3, js(["karrkay", "ngulkurr", "yajarril"]), js([]),
     "Controlled productive good-ADV row; avoids broad English 'happily' not licensed by source.", "0.76"),
    (33, "He sits quietly.",
     "Nyulu minday-ku bunday.",
     "nyulu minday-ku bunda-y | he quiet/tame-ADV sit-NONPAST",
     "productive_quality_to_manner", 3, js(["nyulu", "minday", "bunday"]), js([]),
     "Controlled productive row from minday quiet/tame/confident; marked lower confidence.", "0.74"),

    (34, "He quickly followed the stranger.",
     "Nyulu ngarrbal jinbal-da-ku wukurrin.",
     "nyulu ngarrbal jinbal-da-ku wukurri-ny | he stranger quick-ERG/INST-ADV follow-PAST",
     "transitive_inflected_adverb", 5, js(["nyulu", "ngarrbal", "jinbal", "wukurril"]), js([]),
     "Patz ex.199: transitive clauses use adjective + neutral ERG/INST + -ku.", "0.90"),
    (35, "I quickly killed the snake before it might bite me.",
     "Ngayu jarba jinbal-da-ku kunin, nganya bayka-nyjiku.",
     "ngayu jarba jinbal-da-ku kuni-ny, nganya bayka-nyjiku | I snake quick-ERG/INST-ADV hit/kill-PAST, me bite-SUCC",
     "transitive_inflected_adverb_successive", 5, js(["ngayu", "jarba", "jinbal", "kunil", "baykal"]), js([]),
     "Patz ex.445 preserves jinbal-da-ku in a transitive clause with successive -nyjiku.", "0.90"),
    (36, "He quickly followed the kangaroo.",
     "Nyulu mayarriji jinbal-da-ku wukurrin.",
     "nyulu mayarriji jinbal-da-ku wukurri-ny | he kangaroo quick-ERG/INST-ADV follow-PAST",
     "transitive_inflected_adverb", 5, js(["nyulu", "mayarriji", "jinbal", "wukurril"]), js([]),
     "Participant/object variant of Patz ex.199 retaining the transitive inflected adverb.", "0.84"),
    (37, "I cut the fish carefully.",
     "Ngayu kuyu mumbar-abu-ku yakan.",
     "ngayu kuyu mumbar-abu-ku yaka-ny | I fish careful-ERG/INST-ADV cut-PAST",
     "transitive_inflected_adverb", 5, js(["ngayu", "kuyu", "mumbar", "yakal"]), js([]),
     "Patz ex.200 licenses mumbar-abu-ku in transitive clauses; object varied.", "0.86"),
    (38, "Put the food carefully on the plate.",
     "Mayi mumbar-abu-ku bulin-ba yijarri-ka!",
     "mayi mumbar-abu-ku bulin-ba yijarri-ka | food careful-ERG/INST-ADV plate-LOC put-IMP",
     "transitive_inflected_adverb_imperative", 5, js(["mayi", "mumbar", "bulin", "yijarril"]), js([]),
     "Patz ex.200 has eggs on plate; object varied to mayi after duplicate preflight.", "0.84"),
    (39, "He made the spear carefully.",
     "Nyulu kalka mumbar-abu-ku bungan.",
     "nyulu kalka mumbar-abu-ku bunga-ny | he spear careful-ERG/INST-ADV make-PAST",
     "transitive_inflected_adverb", 5, js(["nyulu", "kalka", "mumbar", "bungal"]), js([]),
     "Keeps mumbar-abu-ku in transitive A/O/V frame with a common make verb.", "0.82"),
    (40, "He hit the dog viciously.",
     "Nyulu kaya kuli-ji-ku kunin.",
     "nyulu kaya kuli-ji-ku kuni-ny | he dog rage-COMIT-ADV hit-PAST",
     "transitive_comitative_adverb", 5, js(["nyulu", "kaya", "kuli-ji", "kunil"]), js([]),
     "Patz §4.1.2 gives kuli-ji-ku kuni-l 'hit viciously'.", "0.86"),
    (41, "I spoke foolish words.",
     "Ngayu kuku kiru-kari-ku balkan.",
     "ngayu kuku kiru-kari-ku balka-ny | I word/language brain-PRIV-ADV speak-PAST",
     "transitive_privative_adverb", 4, js(["ngayu", "kuku", "kiru-kari", "balkal"]), js([]),
     "Uses the Patz kiru-kari-ku adverb with the stable kuku balka-l speech frame.", "0.80"),
    (42, "I quickly speared the sea turtle.",
     "Ngayu ngawiya jinbal-da-ku daman.",
     "ngayu ngawiya jinbal-da-ku dama-ny | I sea.turtle quick-ERG/INST-ADV spear-PAST",
     "transitive_inflected_adverb", 5, js(["ngayu", "ngawiya", "jinbal", "damal"]), js([]),
     "Transitive jinbal-da-ku with an already controlled turtle-spear frame; corpus spelling ngawiya.", "0.84"),
    (43, "Grandmother cooked the food carefully.",
     "Babi-ngka mayi mumbar-abu-ku wajun.",
     "babi-ngka mayi mumbar-abu-ku waju-ny | grandmother-ERG food careful-ERG/INST-ADV cook-PAST",
     "transitive_inflected_adverb", 5, js(["babi", "mayi", "mumbar", "wajul"]), js([]),
     "Transitive mumbar-abu-ku with cook/burn past wajun, not wajan.", "0.84"),

    (44, "Sit like this.",
     "Yala-ku bunday!",
     "yala-ku bunda-y | this-ADV sit-IMP",
     "yala_ku_like_this", 5, js(["yala", "bunday"]), js([]),
     "Patz ex.201 and narrative lines license yala-ku 'like this'; not the already accepted Yala-ku balka.", "0.86"),
    (45, "You sit like this.",
     "Yala-ku yundu bunday!",
     "yala-ku yundu bunda-y | this-ADV you sit-IMP",
     "yala_ku_like_this", 5, js(["yala", "yundu", "bunday"]), js([]),
     "Patz text line 105 has yala-ku yundu bunda-y.", "0.88"),
    (46, "The dog stayed like this.",
     "Nyulu kaya, yala-ku nyulu bundan.",
     "nyulu kaya, yala-ku nyulu bunda-ny | he dog this-ADV he sit-PAST",
     "yala_ku_like_this", 5, js(["nyulu", "kaya", "yala", "bunday"]), js([]),
     "Patz text line 106: yala-ku with a dog/staying predicate.", "0.88"),
    (47, "The road went straight up like this.",
     "Baral yala-ku junkay-ku wangkar.",
     "baral yala-ku junkay-ku wangkar | road this-ADV straight-ADV up",
     "yala_ku_plus_derived_adverb", 5, js(["baral", "yala", "junkay", "wangkar"]), js([]),
     "Patz text line 34: Baral yala-ku junkay-ku wangkar.", "0.90"),
    (48, "As a young man I stayed by the sea.",
     "Ngayu warru-ku jalun-bu bundandan.",
     "ngayu warru-ku jalun-bu bundanda-ny | I young.man-ADV sea-LOC stay.REDUP-PAST",
     "temporal_nominal_ku", 4, js(["ngayu", "warru", "jalun", "bunday"]), js([]),
     "Patz ex.202 licenses warru-ku 'as a young man'; location changed from Palm Island to jalun-bu.", "0.80"),
    (49, "As a child I used to eat wallaby meat.",
     "Ngayu karrkay-nyja-ku minya kadar nuka-l-nuka-n.",
     "ngayu karrkay-nyja-ku minya kadar nuka-l-nuka-ny | I child-ERG-ADV meat wallaby eat-l-REDUP-PAST",
     "temporal_nominal_ku_transitive", 4, js(["ngayu", "karrkay", "minya", "kadar", "nukal"]), js([]),
     "Patz ex.203 licenses karrkay-nyja-ku in a transitive habitual eating row.", "0.82"),
    (50, "How do you make a spear?",
     "Wanjarr-ku yundu kalka bungal?",
     "wanjarr-ku yundu kalka bunga-l | how-ADV you spear make-NONPAST",
     "interrogative_adverbial_ku", 4, js(["wanjarr", "yundu", "kalka", "bungal"]), js([]),
     "Patz notes wanjarr 'how' usually takes adverbial -ku; ordinary spear-making frame.", "0.82"),
]


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"expected 50 rows, got {len(ROWS)}")
    seqs = [r[0] for r in ROWS]
    if seqs != list(range(1, 51)):
        issues.append(f"seqs must be 1..50, got {seqs[:5]}...{seqs[-5:]}")
    seen_ku: set[str] = set()
    for row in ROWS:
        seq, english, kuku, analysis, _frame, _tier, words_used, loans_used, _evidence, _confidence = row
        label = f"{LABEL}.{seq:02d}"
        e_start = english.lstrip("'\"")
        e_end = english.rstrip("'\"").rstrip()
        if not e_start[:1].isupper() or e_end[-1:] not in ".!?":
            issues.append(f"{label}: English must be capitalized and final-punctuated")
        if " | " not in analysis:
            issues.append(f"{label}: analysis missing gloss separator")
        k_start = kuku.lstrip("'\"")
        if not k_start[:1].isupper():
            issues.append(f"{label}: Kuku surface must start uppercase or with a quote before uppercase")
        for field_name, payload in (("words_used", words_used), ("loans_used", loans_used)):
            try:
                json.loads(payload)
            except Exception as exc:
                issues.append(f"{label}: {field_name} is not JSON: {exc}")
        low = f" {kuku.lower()} {analysis.lower()} "
        for word in DENY:
            if re.search(rf"(?<![a-z]){re.escape(word)}(?![a-z])", low):
                issues.append(f"{label}: denied phantom/wrong-sense/blocked token {word!r}")
        if kuku.lower() in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(kuku.lower())
    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 106 draft: manner adverbs and adverbial -ku", "operator-guide-2026-07-07", "under_review", NOW),
        )
        batch_id = cur.lastrowid

    dupes = []
    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 106 source pass checked Patz §3.8.6.1 adverbial particles, §3.8.6.2 derived adverbs, "
            "§4.1.2 verb-complex modifier limits, exx.192-203, ex.445, ex.512, and narrative yala-ku/junkay-ku rows. "
            "Dictionary cross-checks covered content nouns/verbs plus grammar-only particles and homograph warnings."
        ),
        command="rg/sed over grammar.md and dictionary.yaml, plus sqlite duplicate scan",
        artifact_path="process_logs/batch-148-manner-adverbs-process.md",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="scope",
        event_type="risk-controls",
        actor="claude-main",
        detail=(
            "Guarded against English adverb overreach: no invented quick/slow/careful words, no more than one verbal "
            "modifier per verb complex, simple particles kept uninflected unless Patz licenses optional -ku, transitive "
            "simple-adjective adverbs use jinbal-da-ku or mumbar-abu-ku, and productive quality-to-manner rows are few "
            "and low-confidence."
        ),
        artifact_path="process_logs/batch-148-manner-adverbs-process.md",
        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 106 manner-adverb rows under_review after static and duplicate checks.",
        command="python3 batch_scripts/bank_148.py",
        artifact_path="batch_scripts/bank_148.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="row-inventory",
        actor="claude-main",
        detail=(
            "Rows 1-20 train Patz adverbial particles; 21-33 train intransitive/reduplicated/productive adjective "
            "+ -ku adverbs; 34-43 train transitive inflected adverbs and comitative/privative adverbs; 44-50 train "
            "yala-ku, temporal nominal -ku, and wanjarr-ku."
        ),
        artifact_path="batch_scripts/bank_148.py",
        created_at=NOW,
    )
    db.commit()
    avg_words = sum(len(row[2].replace("!", " !").replace("?", " ?").split()) for row in ROWS) / len(ROWS)
    print(f"banked {LABEL}: 50 rows under_review; avg Kuku words/row={avg_words:.2f}")
    return 0


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