#!/usr/bin/env python3
"""Bank batch-313: Rule 271 complementary insect non-singular pronoun perception."""

from __future__ import annotations

import datetime as dt
import json
import sqlite3

from bank_297 import (
    DENY_ENGLISH,
    DENY_KUKU as BASE_DENY_KUKU,
    TARGET_HEADS,
    english_has_denied,
    has_target_morphology,
    has_token,
    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-313"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-313-insect-complementary-nonsingular-pronoun-perception-after-rule270-process.md"
SUPPORT = {"ngali", "ngaliny", "yubal", "nganjin", "ngana", "yaluy", "yinyay", "nyajil"}
PRONOUN_SUPPORT = {"ngali", "ngaliny", "yubal", "nganjin", "ngana"}
EXTRA_DENY_KUKU = {
    "ngalingan", "ngalinyan", "yubalan", "nganjinan", "nganangan",
    "ngalininy", "ngalinyiny", "yubaliny", "nganjininy", "ngananiny",
    "ngalinka", "ngalinyanka", "yubalanka", "nganjinanka", "ngananka",
    "ngalinda", "ngalinyanda", "yubalanda", "nganjinanda", "ngananda",
    "ngalindamuny", "ngalinyandamuny", "yubalandamuny", "nganjinandamuny", "nganandamuny",
    "ngalinji", "ngalinyanji", "yubalanji", "nganjinanji", "ngananji",
    "ngalinga", "ngalinyanga", "yubalanga", "nganjinanga", "ngananga",
}
DENY_KUKU = set(BASE_DENY_KUKU) | EXTRA_DENY_KUKU

EVIDENCE = {
    "balarr": "Dictionary lines 833-841 give balarr as noun 'flea', semantic domain fauna-insect; the human-body wording is source prose only. Patz grammar lines 2577-2603 source bare nominative ngali, ngaliny, yubal, nganjin, and ngana; lines 2682-2684 discuss inclusive/exclusive and second-person non-singular forms; lines 2838-2845 source yaluy/yinyay; and lines 3043-3049 source nyaji-l 'see'.",
    "dijaraban": "Dictionary lines 5542-5551 give dijaraban as noun 'mayfly', definition/translations mayfly/dragonfly, semantic domain fauna-insect; Rule 271 selects mayfly only. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "kalburiji": "Dictionary lines 12474-12484 give kalburiji as noun 'bee', definition 'species of bee', semantic domain fauna-insect; Rule 271 selects kalburiji bee only and leaves honey/nest/egg/larvae readings closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "mawal": "Dictionary lines 18743-18753 give mawal as noun 'bee', definition 'species of bee', semantic domain fauna-insect; Rule 271 selects mawal bee only and leaves honey/nest/egg/larvae readings closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "munudumbun": "Dictionary lines 20816-20826 give munudumbun as noun 'scorpion', semantic domain fauna-insect, synonym munukurru; Rule 271 selects scorpion only and leaves synonym/sting/sickness/fear/danger rows closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "munyurri": "Dictionary lines 20868-20889 give duplicate munyurri noun entries as 'sandfly', semantic domain fauna-insect, synonym diri-diri; Rule 271 selects sandfly only and leaves synonym/biting/sickness rows closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "nakawarra": "Dictionary lines 21461-21470 give nakawarra as noun 'species of bee', translations species of bee/bee, semantic domain fauna-insect; Rule 271 selects nakawarra bee only and leaves honey/nest/egg/larvae readings closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "ngalmnbu": "Dictionary lines 21972-21980 give ngalmnbu as noun 'jumper ant', semantic domain fauna-insect; Rule 271 selects jumper ant only and leaves jumping, biting, sting, antbed, and event readings closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "yarrka": "Dictionary lines 29551-29559 give yarrka as noun 'bee', definition 'bee, the smallest bee in the bush', semantic domain fauna-insect; Rule 271 selects yarrka bee only and leaves smallest/bush/ecology readings closed. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
    "yiljun": "Dictionary lines 30019-30029 give yiljun as noun 'grasshopper', definition 'type of grasshopper', with a singing/yam-readiness source usage; Rule 271 selects grasshopper only. Patz grammar lines 2577-2603, 2682-2684, 2838-2845, and 3043-3049 source support.",
}

ROWS = [
    row(1, "We two, not you, see the flea here.", "Ngali balarr yaluy nyajil.", "ngali balarr yaluy nyajil | 1du.EXCL.NOM flea here see.NPST", "r271_1du_excl_target_here_see_balarr", ["ngali", "balarr", "yaluy", "nyajil"], EVIDENCE["balarr"]),
    row(2, "We two, not you, see the mayfly here.", "Ngali dijaraban yaluy nyajil.", "ngali dijaraban yaluy nyajil | 1du.EXCL.NOM mayfly here see.NPST", "r271_1du_excl_target_here_see_dijaraban", ["ngali", "dijaraban", "yaluy", "nyajil"], EVIDENCE["dijaraban"]),
    row(3, "We two, not you, see the kalburiji bee here.", "Ngali kalburiji yaluy nyajil.", "ngali kalburiji yaluy nyajil | 1du.EXCL.NOM kalburiji.bee here see.NPST", "r271_1du_excl_target_here_see_kalburiji", ["ngali", "kalburiji", "yaluy", "nyajil"], EVIDENCE["kalburiji"]),
    row(4, "We two, not you, see the mawal bee here.", "Ngali mawal yaluy nyajil.", "ngali mawal yaluy nyajil | 1du.EXCL.NOM mawal.bee here see.NPST", "r271_1du_excl_target_here_see_mawal", ["ngali", "mawal", "yaluy", "nyajil"], EVIDENCE["mawal"]),
    row(5, "We two, not you, see the scorpion here.", "Ngali munudumbun yaluy nyajil.", "ngali munudumbun yaluy nyajil | 1du.EXCL.NOM scorpion here see.NPST", "r271_1du_excl_target_here_see_munudumbun", ["ngali", "munudumbun", "yaluy", "nyajil"], EVIDENCE["munudumbun"]),
    row(6, "We two, not you, see the sandfly here.", "Ngali munyurri yaluy nyajil.", "ngali munyurri yaluy nyajil | 1du.EXCL.NOM sandfly here see.NPST", "r271_1du_excl_target_here_see_munyurri", ["ngali", "munyurri", "yaluy", "nyajil"], EVIDENCE["munyurri"]),
    row(7, "We two, not you, see the nakawarra bee here.", "Ngali nakawarra yaluy nyajil.", "ngali nakawarra yaluy nyajil | 1du.EXCL.NOM nakawarra.bee here see.NPST", "r271_1du_excl_target_here_see_nakawarra", ["ngali", "nakawarra", "yaluy", "nyajil"], EVIDENCE["nakawarra"]),
    row(8, "We two, not you, see the jumper ant here.", "Ngali ngalmnbu yaluy nyajil.", "ngali ngalmnbu yaluy nyajil | 1du.EXCL.NOM jumper.ant here see.NPST", "r271_1du_excl_target_here_see_ngalmnbu", ["ngali", "ngalmnbu", "yaluy", "nyajil"], EVIDENCE["ngalmnbu"]),
    row(9, "We two, not you, see the yarrka bee here.", "Ngali yarrka yaluy nyajil.", "ngali yarrka yaluy nyajil | 1du.EXCL.NOM yarrka.bee here see.NPST", "r271_1du_excl_target_here_see_yarrka", ["ngali", "yarrka", "yaluy", "nyajil"], EVIDENCE["yarrka"]),
    row(10, "We two, not you, see the grasshopper here.", "Ngali yiljun yaluy nyajil.", "ngali yiljun yaluy nyajil | 1du.EXCL.NOM grasshopper here see.NPST", "r271_1du_excl_target_here_see_yiljun", ["ngali", "yiljun", "yaluy", "nyajil"], EVIDENCE["yiljun"]),
    row(11, "You and I see the flea there.", "Ngaliny balarr yinyay nyajil.", "ngaliny balarr yinyay nyajil | 1du.INCL.NOM flea there see.NPST", "r271_1du_incl_target_there_see_balarr", ["ngaliny", "balarr", "yinyay", "nyajil"], EVIDENCE["balarr"]),
    row(12, "You and I see the mayfly there.", "Ngaliny dijaraban yinyay nyajil.", "ngaliny dijaraban yinyay nyajil | 1du.INCL.NOM mayfly there see.NPST", "r271_1du_incl_target_there_see_dijaraban", ["ngaliny", "dijaraban", "yinyay", "nyajil"], EVIDENCE["dijaraban"]),
    row(13, "You and I see the kalburiji bee there.", "Ngaliny kalburiji yinyay nyajil.", "ngaliny kalburiji yinyay nyajil | 1du.INCL.NOM kalburiji.bee there see.NPST", "r271_1du_incl_target_there_see_kalburiji", ["ngaliny", "kalburiji", "yinyay", "nyajil"], EVIDENCE["kalburiji"]),
    row(14, "You and I see the mawal bee there.", "Ngaliny mawal yinyay nyajil.", "ngaliny mawal yinyay nyajil | 1du.INCL.NOM mawal.bee there see.NPST", "r271_1du_incl_target_there_see_mawal", ["ngaliny", "mawal", "yinyay", "nyajil"], EVIDENCE["mawal"]),
    row(15, "You and I see the scorpion there.", "Ngaliny munudumbun yinyay nyajil.", "ngaliny munudumbun yinyay nyajil | 1du.INCL.NOM scorpion there see.NPST", "r271_1du_incl_target_there_see_munudumbun", ["ngaliny", "munudumbun", "yinyay", "nyajil"], EVIDENCE["munudumbun"]),
    row(16, "You and I see the sandfly there.", "Ngaliny munyurri yinyay nyajil.", "ngaliny munyurri yinyay nyajil | 1du.INCL.NOM sandfly there see.NPST", "r271_1du_incl_target_there_see_munyurri", ["ngaliny", "munyurri", "yinyay", "nyajil"], EVIDENCE["munyurri"]),
    row(17, "You and I see the nakawarra bee there.", "Ngaliny nakawarra yinyay nyajil.", "ngaliny nakawarra yinyay nyajil | 1du.INCL.NOM nakawarra.bee there see.NPST", "r271_1du_incl_target_there_see_nakawarra", ["ngaliny", "nakawarra", "yinyay", "nyajil"], EVIDENCE["nakawarra"]),
    row(18, "You and I see the jumper ant there.", "Ngaliny ngalmnbu yinyay nyajil.", "ngaliny ngalmnbu yinyay nyajil | 1du.INCL.NOM jumper.ant there see.NPST", "r271_1du_incl_target_there_see_ngalmnbu", ["ngaliny", "ngalmnbu", "yinyay", "nyajil"], EVIDENCE["ngalmnbu"]),
    row(19, "You and I see the yarrka bee there.", "Ngaliny yarrka yinyay nyajil.", "ngaliny yarrka yinyay nyajil | 1du.INCL.NOM yarrka.bee there see.NPST", "r271_1du_incl_target_there_see_yarrka", ["ngaliny", "yarrka", "yinyay", "nyajil"], EVIDENCE["yarrka"]),
    row(20, "You and I see the grasshopper there.", "Ngaliny yiljun yinyay nyajil.", "ngaliny yiljun yinyay nyajil | 1du.INCL.NOM grasshopper there see.NPST", "r271_1du_incl_target_there_see_yiljun", ["ngaliny", "yiljun", "yinyay", "nyajil"], EVIDENCE["yiljun"]),
    row(21, "You two see the flea here.", "Yubal balarr yaluy nyajil.", "yubal balarr yaluy nyajil | 2du.NOM flea here see.NPST", "r271_2du_target_here_see_balarr", ["yubal", "balarr", "yaluy", "nyajil"], EVIDENCE["balarr"]),
    row(22, "You two see the mayfly here.", "Yubal dijaraban yaluy nyajil.", "yubal dijaraban yaluy nyajil | 2du.NOM mayfly here see.NPST", "r271_2du_target_here_see_dijaraban", ["yubal", "dijaraban", "yaluy", "nyajil"], EVIDENCE["dijaraban"]),
    row(23, "You two see the kalburiji bee here.", "Yubal kalburiji yaluy nyajil.", "yubal kalburiji yaluy nyajil | 2du.NOM kalburiji.bee here see.NPST", "r271_2du_target_here_see_kalburiji", ["yubal", "kalburiji", "yaluy", "nyajil"], EVIDENCE["kalburiji"]),
    row(24, "You two see the mawal bee here.", "Yubal mawal yaluy nyajil.", "yubal mawal yaluy nyajil | 2du.NOM mawal.bee here see.NPST", "r271_2du_target_here_see_mawal", ["yubal", "mawal", "yaluy", "nyajil"], EVIDENCE["mawal"]),
    row(25, "You two see the scorpion here.", "Yubal munudumbun yaluy nyajil.", "yubal munudumbun yaluy nyajil | 2du.NOM scorpion here see.NPST", "r271_2du_target_here_see_munudumbun", ["yubal", "munudumbun", "yaluy", "nyajil"], EVIDENCE["munudumbun"]),
    row(26, "You two see the sandfly here.", "Yubal munyurri yaluy nyajil.", "yubal munyurri yaluy nyajil | 2du.NOM sandfly here see.NPST", "r271_2du_target_here_see_munyurri", ["yubal", "munyurri", "yaluy", "nyajil"], EVIDENCE["munyurri"]),
    row(27, "You two see the nakawarra bee here.", "Yubal nakawarra yaluy nyajil.", "yubal nakawarra yaluy nyajil | 2du.NOM nakawarra.bee here see.NPST", "r271_2du_target_here_see_nakawarra", ["yubal", "nakawarra", "yaluy", "nyajil"], EVIDENCE["nakawarra"]),
    row(28, "You two see the jumper ant here.", "Yubal ngalmnbu yaluy nyajil.", "yubal ngalmnbu yaluy nyajil | 2du.NOM jumper.ant here see.NPST", "r271_2du_target_here_see_ngalmnbu", ["yubal", "ngalmnbu", "yaluy", "nyajil"], EVIDENCE["ngalmnbu"]),
    row(29, "You two see the yarrka bee here.", "Yubal yarrka yaluy nyajil.", "yubal yarrka yaluy nyajil | 2du.NOM yarrka.bee here see.NPST", "r271_2du_target_here_see_yarrka", ["yubal", "yarrka", "yaluy", "nyajil"], EVIDENCE["yarrka"]),
    row(30, "You two see the grasshopper here.", "Yubal yiljun yaluy nyajil.", "yubal yiljun yaluy nyajil | 2du.NOM grasshopper here see.NPST", "r271_2du_target_here_see_yiljun", ["yubal", "yiljun", "yaluy", "nyajil"], EVIDENCE["yiljun"]),
    row(31, "We all, not you, see the flea there.", "Nganjin balarr yinyay nyajil.", "nganjin balarr yinyay nyajil | 1pl.EXCL.NOM flea there see.NPST", "r271_1pl_excl_target_there_see_balarr", ["nganjin", "balarr", "yinyay", "nyajil"], EVIDENCE["balarr"]),
    row(32, "We all, not you, see the mayfly there.", "Nganjin dijaraban yinyay nyajil.", "nganjin dijaraban yinyay nyajil | 1pl.EXCL.NOM mayfly there see.NPST", "r271_1pl_excl_target_there_see_dijaraban", ["nganjin", "dijaraban", "yinyay", "nyajil"], EVIDENCE["dijaraban"]),
    row(33, "We all, not you, see the kalburiji bee there.", "Nganjin kalburiji yinyay nyajil.", "nganjin kalburiji yinyay nyajil | 1pl.EXCL.NOM kalburiji.bee there see.NPST", "r271_1pl_excl_target_there_see_kalburiji", ["nganjin", "kalburiji", "yinyay", "nyajil"], EVIDENCE["kalburiji"]),
    row(34, "We all, not you, see the mawal bee there.", "Nganjin mawal yinyay nyajil.", "nganjin mawal yinyay nyajil | 1pl.EXCL.NOM mawal.bee there see.NPST", "r271_1pl_excl_target_there_see_mawal", ["nganjin", "mawal", "yinyay", "nyajil"], EVIDENCE["mawal"]),
    row(35, "We all, not you, see the scorpion there.", "Nganjin munudumbun yinyay nyajil.", "nganjin munudumbun yinyay nyajil | 1pl.EXCL.NOM scorpion there see.NPST", "r271_1pl_excl_target_there_see_munudumbun", ["nganjin", "munudumbun", "yinyay", "nyajil"], EVIDENCE["munudumbun"]),
    row(36, "We all, not you, see the sandfly there.", "Nganjin munyurri yinyay nyajil.", "nganjin munyurri yinyay nyajil | 1pl.EXCL.NOM sandfly there see.NPST", "r271_1pl_excl_target_there_see_munyurri", ["nganjin", "munyurri", "yinyay", "nyajil"], EVIDENCE["munyurri"]),
    row(37, "We all, not you, see the nakawarra bee there.", "Nganjin nakawarra yinyay nyajil.", "nganjin nakawarra yinyay nyajil | 1pl.EXCL.NOM nakawarra.bee there see.NPST", "r271_1pl_excl_target_there_see_nakawarra", ["nganjin", "nakawarra", "yinyay", "nyajil"], EVIDENCE["nakawarra"]),
    row(38, "We all, not you, see the jumper ant there.", "Nganjin ngalmnbu yinyay nyajil.", "nganjin ngalmnbu yinyay nyajil | 1pl.EXCL.NOM jumper.ant there see.NPST", "r271_1pl_excl_target_there_see_ngalmnbu", ["nganjin", "ngalmnbu", "yinyay", "nyajil"], EVIDENCE["ngalmnbu"]),
    row(39, "We all, not you, see the yarrka bee there.", "Nganjin yarrka yinyay nyajil.", "nganjin yarrka yinyay nyajil | 1pl.EXCL.NOM yarrka.bee there see.NPST", "r271_1pl_excl_target_there_see_yarrka", ["nganjin", "yarrka", "yinyay", "nyajil"], EVIDENCE["yarrka"]),
    row(40, "We all, not you, see the grasshopper there.", "Nganjin yiljun yinyay nyajil.", "nganjin yiljun yinyay nyajil | 1pl.EXCL.NOM grasshopper there see.NPST", "r271_1pl_excl_target_there_see_yiljun", ["nganjin", "yiljun", "yinyay", "nyajil"], EVIDENCE["yiljun"]),
    row(41, "We see the flea here.", "Ngana balarr yaluy nyajil.", "ngana balarr yaluy nyajil | 1pl.INC.NOM flea here see.NPST", "r271_1pl_incl_target_here_see_balarr", ["ngana", "balarr", "yaluy", "nyajil"], EVIDENCE["balarr"]),
    row(42, "We see the mayfly here.", "Ngana dijaraban yaluy nyajil.", "ngana dijaraban yaluy nyajil | 1pl.INC.NOM mayfly here see.NPST", "r271_1pl_incl_target_here_see_dijaraban", ["ngana", "dijaraban", "yaluy", "nyajil"], EVIDENCE["dijaraban"]),
    row(43, "We see the kalburiji bee here.", "Ngana kalburiji yaluy nyajil.", "ngana kalburiji yaluy nyajil | 1pl.INC.NOM kalburiji.bee here see.NPST", "r271_1pl_incl_target_here_see_kalburiji", ["ngana", "kalburiji", "yaluy", "nyajil"], EVIDENCE["kalburiji"]),
    row(44, "We see the mawal bee here.", "Ngana mawal yaluy nyajil.", "ngana mawal yaluy nyajil | 1pl.INC.NOM mawal.bee here see.NPST", "r271_1pl_incl_target_here_see_mawal", ["ngana", "mawal", "yaluy", "nyajil"], EVIDENCE["mawal"]),
    row(45, "We see the scorpion here.", "Ngana munudumbun yaluy nyajil.", "ngana munudumbun yaluy nyajil | 1pl.INC.NOM scorpion here see.NPST", "r271_1pl_incl_target_here_see_munudumbun", ["ngana", "munudumbun", "yaluy", "nyajil"], EVIDENCE["munudumbun"]),
    row(46, "We see the sandfly here.", "Ngana munyurri yaluy nyajil.", "ngana munyurri yaluy nyajil | 1pl.INC.NOM sandfly here see.NPST", "r271_1pl_incl_target_here_see_munyurri", ["ngana", "munyurri", "yaluy", "nyajil"], EVIDENCE["munyurri"]),
    row(47, "We see the nakawarra bee here.", "Ngana nakawarra yaluy nyajil.", "ngana nakawarra yaluy nyajil | 1pl.INC.NOM nakawarra.bee here see.NPST", "r271_1pl_incl_target_here_see_nakawarra", ["ngana", "nakawarra", "yaluy", "nyajil"], EVIDENCE["nakawarra"]),
    row(48, "We see the jumper ant here.", "Ngana ngalmnbu yaluy nyajil.", "ngana ngalmnbu yaluy nyajil | 1pl.INC.NOM jumper.ant here see.NPST", "r271_1pl_incl_target_here_see_ngalmnbu", ["ngana", "ngalmnbu", "yaluy", "nyajil"], EVIDENCE["ngalmnbu"]),
    row(49, "We see the yarrka bee here.", "Ngana yarrka yaluy nyajil.", "ngana yarrka yaluy nyajil | 1pl.INC.NOM yarrka.bee here see.NPST", "r271_1pl_incl_target_here_see_yarrka", ["ngana", "yarrka", "yaluy", "nyajil"], EVIDENCE["yarrka"]),
    row(50, "We see the grasshopper here.", "Ngana yiljun yaluy nyajil.", "ngana yiljun yaluy nyajil | 1pl.INC.NOM grasshopper here see.NPST", "r271_1pl_incl_target_here_see_yiljun", ["ngana", "yiljun", "yaluy", "nyajil"], EVIDENCE["yiljun"]),
]


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("r271_"):
            issues.append(f"{label}: Rule 271 frame must start with r271_")
        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 271")
        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 271 target insect 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 non-singular pronoun, found {pronouns}")
        if 1 <= seq <= 10 and (not low.startswith("ngali ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 1-10 require Ngali TARGET yaluy nyajil")
        if 11 <= seq <= 20 and (not low.startswith("ngaliny ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 11-20 require Ngaliny TARGET yinyay nyajil")
        if 21 <= seq <= 30 and (not low.startswith("yubal ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 21-30 require Yubal TARGET yaluy nyajil")
        if 31 <= seq <= 40 and (not low.startswith("nganjin ") or not low.endswith(" yinyay nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 31-40 require Nganjin TARGET yinyay nyajil")
        if 41 <= seq <= 50 and (not low.startswith("ngana ") or not low.endswith(" yaluy nyajil.") or len(low.split()) != 4):
            issues.append(f"{label}: rows 41-50 require Ngana TARGET yaluy nyajil")
        if any(english_has_denied(english_low, bad) for bad in DENY_ENGLISH):
            issues.append(f"{label}: denied insect behavior/use/body/ecology/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 271 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 271 source pass checked dictionary lines for balarr flea, dijaraban mayfly, kalburiji bee, mawal bee, munudumbun scorpion, munyurri sandfly, nakawarra bee, ngalmnbu jumper ant, yarrka bee, and yiljun grasshopper; Patz grammar lines 2577-2603 for bare nominative ngali, ngaliny, yubal, nganjin, and ngana; lines 2682-2684 for inclusive/exclusive and second-person non-singular discussion; 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 271 complementary insect non-singular pronoun perception sweep after Rule 270: ten checked Rule 212/255/256/270 insect labels in complementary zero-collision nominative-pronoun perception bands using ngali, ngaliny, yubal, nganjin, and ngana. All insect behavior/use/ecology/body/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="Coverage after Rule 270: all ten insect targets had 20 accepted rows; support ngali 268, ngaliny 203, yubal 214, nganjin 218, ngana 968, yaluy 2617, yinyay 2871, nyajil 3328.",
        command="sqlite3 synthetic.db token-aware coverage query",
        output_excerpt="Targets: balarr 20, dijaraban 20, kalburiji 20, mawal 20, munudumbun 20, munyurri 20, nakawarra 20, ngalmnbu 20, yarrka 20, yiljun 20. Support: ngali 268, ngaliny 203, yubal 214, nganjin 218, ngana 968, yaluy 2617, yinyay 2871, nyajil 3328.",
        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 50 final Rule 271 complementary insect non-singular-pronoun perception surfaces found no accepted, revised, or under-review collisions.",
        command="sqlite3 synthetic.db planned-surface duplicate query",
        output_excerpt="0 rows returned.",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="preflight",
        event_type="static-guard-pass",
        actor="claude-main",
        detail="bank_313.py static guards passed: 50 rows, r271 frame prefix, one checked insect target per row, exactly one bare non-singular nominative pronoun per row, expected complementary here/there bands, no denied insect behavior/use/ecology/body/source-prose/case/perception-compound tokens, and no loans.",
        command="python3 -m py_compile batch_scripts/bank_313.py; import bank_313; bank_313.scan_static()",
        output_excerpt="issues 0",
        artifact_path="batch_scripts/bank_313.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="bank",
        event_type="bank-success",
        actor="claude-main",
        detail="Inserted 50 explicit Batch 313 / Rule 271 rows as under_review.",
        command="python3 batch_scripts/bank_313.py",
        output_excerpt="banked batch-313: 50 rows under_review",
        artifact_path="synthetic.db",
        created_at=NOW,
    )
    db.commit()
    print(f"banked {LABEL}: 50 rows under_review")
    return 0


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