#!/usr/bin/env python3
"""Bank Batch 396: Rule 354 linter-clean fish-set-F singular/third past perception."""
from __future__ import annotations
import ast,datetime as dt,hashlib,json,re,sqlite3
from pathlib import Path
import yaml
from process_ledger import ensure_schema,record_event
ROOT="/mnt/donto-data/donto-resources/research/translation-training/kuku-yalanji-runpod-2026-06-30";DB=f"{ROOT}/synthetic/claude-synthetic-v1-2026-07-02/synthetic.db";DICT="/mnt/donto-data/workspace/mobtranslate.com/dictionaries/kuku_yalanji/dictionary.yaml";GRAMMAR="/mnt/donto-data/workspace/mobtranslate.com/dictionaries/kuku_yalanji/grammar.md";LABEL="batch-396";NOW=dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00","Z");LOG="process_logs/batch-396-linter-clean-fish-set-f-singular-third-past-perception-after-rule353-process.md"
HASHES={DICT:"b575ab035ecc60f09f7bfc5537116555f3d290869fe75b2edf0fd8db60fe1b37",GRAMMAR:"5f21305f1fca9ee2cb64668e18156d1f322e3bd9e59850fda662bf0673714153"}
TARGET_ORDER=["kalngkan","mangkurr-mangkurr","dijarr","julaji","mawul","balarri","bayil","dalmbal","barrbal","jalkay"]
SOURCE_TERMS={"kalngkan":"small mullet","mangkurr-mangkurr":"garfish","dijarr":"fresh water perch","julaji":"fresh water catfish","mawul":"fresh water eel","balarri":"queenfish","bayil":"jungle perch","dalmbal":"flathead","barrbal":"black bream","jalkay":"salmon"}
LABELS={"kalngkan":"kalngkan small mullet","mangkurr-mangkurr":"mangkurr-mangkurr garfish","dijarr":"dijarr freshwater perch","julaji":"julaji freshwater catfish","mawul":"mawul freshwater eel","balarri":"balarri queenfish","bayil":"bayil jungle perch","dalmbal":"dalmbal flathead","barrbal":"barrbal black bream","jalkay":"jalkay salmon"}
GLOSSES={"kalngkan":"kalngkan.small-mullet","mangkurr-mangkurr":"mangkurr-mangkurr.garfish","dijarr":"dijarr.freshwater-perch","julaji":"julaji.freshwater-catfish","mawul":"mawul.freshwater-eel","balarri":"balarri.queenfish","bayil":"bayil.jungle-perch","dalmbal":"dalmbal.flathead","barrbal":"barrbal.black-bream","jalkay":"jalkay.salmon"}
CONF={t:"0.92" for t in TARGET_ORDER};CONF["dijarr"]="0.88";CONF["mawul"]="0.90";CONF["bayil"]="0.90"
EXPECTED_TARGET={"kalngkan":5,"mangkurr-mangkurr":5,"dijarr":6,"julaji":6,"mawul":8,"balarri":24,"bayil":21,"dalmbal":21,"barrbal":26,"jalkay":26}
EXPECTED_SUPPORT={"ngayu":1521,"yundu":1117,"nyulu":792,"bula":588,"jana":1293,"yaluy":4732,"yinyay":4904,"nyajin":2750}
FRAMES=[("ngayu","yaluy","I saw","here","1sg"),("yundu","yinyay","You saw","there","2sg"),("nyulu","yaluy","He saw","here","3sg"),("bula","yinyay","The two of them saw","there","3du"),("jana","yaluy","They saw","here","3pl")]
EVIDENCE={
"kalngkan":"Dictionary lines 12850-12869 contain two small-mullet noun cards. Rule354 uses small mullet only; size comparison, schooling, habitat, capture, food, source, possession and events closed.",
"mangkurr-mangkurr":"Dictionary lines 17775-17807 contain two garfish noun cards. Rule354 preserves the exact reduplicated whole head; productive reduplication, shape, swimming, habitat, capture, food, source and events closed.",
"dijarr":"Dictionary lines 5552-5577 contain two freshwater-fish/perch cards and comparison/location prose. Rule354 uses freshwater perch only; dojarr variant, jungle-perch equivalence, Rossville/Shipton location, distribution, habitat, source and events closed.",
"julaji":"Dictionary lines 10380-10407 contain two Yalanji freshwater-catfish cards. Rule354 uses freshwater catfish only; dialect comparison, whiskers, habitat, capture, food, source and events closed.",
"mawul":"Dictionary lines 18775-18796 contain two small-freshwater-eel cards and old-people food-taboo prose. Rule354 uses freshwater eel only; size, age restriction, taboo, eating, habitat, capture, source and events closed.",
"balarri":"Dictionary lines 842-860 contain two agreeing queenfish noun cards. Rule354 uses queenfish only; taxonomy expansion, habitat, capture, food, source and events closed.",
"bayil":"Dictionary lines 1992-2010 contain freshwater-perch and jungle-perch cards. Rule354 uses jungle perch only; record equivalence, distribution, habitat, capture, food, source and events closed.",
"dalmbal":"Dictionary lines 4947-4970 contain two flathead noun cards and a dukul-barangka synonym note. Rule354 uses flathead only; synonym substitution, anatomy/shape, camouflage, habitat, capture, food, source and events closed.",
"barrbal":"Dictionary lines 1810-1828 contain two agreeing black-bream noun cards. Rule354 uses black bream only; color predication, taxonomy, habitat, capture, food, source and events closed.",
"jalkay":"Dictionary lines 7465-7483 contain two agreeing salmon noun cards. Rule354 uses salmon only; species resolution, migration, habitat, capture, food, source and events closed.",}
SUPPORT="Exact Patz scans return zero for all ten set-F heads; dictionary cards license them, so zero is not absence. Patz 2577-2584 licenses selected nominative pronouns; 2834-2847 Yalanji yaluy/yinyay; 2021-2024, 2568-2571 and 3160-3162 past nyaji-ny; 4256-4265 nominative-pronoun A with bare O in perception; dictionary 23876-23883/23968-23976 running nyajin."
DENY_KUKU={"ngali","ngaliny","yubal","yurra","nganjin","ngana","yulu","iana","yalay","yala","yanyu","yinya","nyajil","nyaji-l","nyaji-nka","kijukum","walarrji"}
DENY_ENGLISH=("schooling","swimming","dojarr","Rossville","Shipton","whisker","taboo","old people","eat","capture","caught","habitat","duk ul","synonym","camouflage","migration","source","possess","event")
def count_words(db,words):return{w:db.execute("SELECT COUNT(*) FROM sentences s WHERE s.status IN('accepted','revised') AND EXISTS(SELECT 1 FROM json_each(s.words_used) WHERE lower(value)=?)",(w,)).fetchone()[0] for w in words}
def source_audit():
 for p,h in HASHES.items():
  if hashlib.sha256(Path(p).read_bytes()).hexdigest()!=h:raise SystemExit(f"hash {p}")
 records=yaml.safe_load(Path(DICT).read_text())["words"];counts={}
 for t in TARGET_ORDER:
  rs=[x for x in records if x.get("word")==t];counts[t]=len(rs)
  if len(rs)!=2 or any(x.get("type")!="noun" or x.get("loanword") for x in rs) or any(x.get("semantic_domain")!="fauna-fish" for x in rs):raise SystemExit(f"cards {t}")
  if SOURCE_TERMS[t] not in json.dumps(rs,ensure_ascii=False).lower():raise SystemExit(f"term {t}")
 g=Path(GRAMMAR).read_text();hits={t:len(re.findall(rf"(?<![A-Za-z-]){re.escape(t)}(?![A-Za-z-])",g,re.I)) for t in TARGET_ORDER}
 if any(hits.values()):raise SystemExit(f"Patz {hits}")
 return counts,hits
def row(seq,en,ku,an,fr,words):
 t=words[1];return(seq,en,ku,an,fr,3,json.dumps(words,ensure_ascii=False),json.dumps([],ensure_ascii=False),f"{EVIDENCE[t]} {SUPPORT}",CONF[t])
ROWS=[
row(1,"I saw the kalngkan small mullet here.","Ngayu kalngkan yaluy nyajin.","ngayu kalngkan yaluy nyajin | 1sg.NOM kalngkan.small-mullet here see.PST","r354_1sg_here_fish_f_kalngkan",["ngayu","kalngkan","yaluy","nyajin"]),
row(2,"I saw the mangkurr-mangkurr garfish here.","Ngayu mangkurr-mangkurr yaluy nyajin.","ngayu mangkurr-mangkurr yaluy nyajin | 1sg.NOM mangkurr-mangkurr.garfish here see.PST","r354_1sg_here_fish_f_mangkurr",["ngayu","mangkurr-mangkurr","yaluy","nyajin"]),
row(3,"I saw the dijarr freshwater perch here.","Ngayu dijarr yaluy nyajin.","ngayu dijarr yaluy nyajin | 1sg.NOM dijarr.freshwater-perch here see.PST","r354_1sg_here_fish_f_dijarr",["ngayu","dijarr","yaluy","nyajin"]),
row(4,"I saw the julaji freshwater catfish here.","Ngayu julaji yaluy nyajin.","ngayu julaji yaluy nyajin | 1sg.NOM julaji.freshwater-catfish here see.PST","r354_1sg_here_fish_f_julaji",["ngayu","julaji","yaluy","nyajin"]),
row(5,"I saw the mawul freshwater eel here.","Ngayu mawul yaluy nyajin.","ngayu mawul yaluy nyajin | 1sg.NOM mawul.freshwater-eel here see.PST","r354_1sg_here_fish_f_mawul",["ngayu","mawul","yaluy","nyajin"]),
row(6,"I saw the balarri queenfish here.","Ngayu balarri yaluy nyajin.","ngayu balarri yaluy nyajin | 1sg.NOM balarri.queenfish here see.PST","r354_1sg_here_fish_f_balarri",["ngayu","balarri","yaluy","nyajin"]),
row(7,"I saw the bayil jungle perch here.","Ngayu bayil yaluy nyajin.","ngayu bayil yaluy nyajin | 1sg.NOM bayil.jungle-perch here see.PST","r354_1sg_here_fish_f_bayil",["ngayu","bayil","yaluy","nyajin"]),
row(8,"I saw the dalmbal flathead here.","Ngayu dalmbal yaluy nyajin.","ngayu dalmbal yaluy nyajin | 1sg.NOM dalmbal.flathead here see.PST","r354_1sg_here_fish_f_dalmbal",["ngayu","dalmbal","yaluy","nyajin"]),
row(9,"I saw the barrbal black bream here.","Ngayu barrbal yaluy nyajin.","ngayu barrbal yaluy nyajin | 1sg.NOM barrbal.black-bream here see.PST","r354_1sg_here_fish_f_barrbal",["ngayu","barrbal","yaluy","nyajin"]),
row(10,"I saw the jalkay salmon here.","Ngayu jalkay yaluy nyajin.","ngayu jalkay yaluy nyajin | 1sg.NOM jalkay.salmon here see.PST","r354_1sg_here_fish_f_jalkay",["ngayu","jalkay","yaluy","nyajin"]),
row(11,"You saw the kalngkan small mullet there.","Yundu kalngkan yinyay nyajin.","yundu kalngkan yinyay nyajin | 2sg.NOM kalngkan.small-mullet there see.PST","r354_2sg_there_fish_f_kalngkan",["yundu","kalngkan","yinyay","nyajin"]),
row(12,"You saw the mangkurr-mangkurr garfish there.","Yundu mangkurr-mangkurr yinyay nyajin.","yundu mangkurr-mangkurr yinyay nyajin | 2sg.NOM mangkurr-mangkurr.garfish there see.PST","r354_2sg_there_fish_f_mangkurr",["yundu","mangkurr-mangkurr","yinyay","nyajin"]),
row(13,"You saw the dijarr freshwater perch there.","Yundu dijarr yinyay nyajin.","yundu dijarr yinyay nyajin | 2sg.NOM dijarr.freshwater-perch there see.PST","r354_2sg_there_fish_f_dijarr",["yundu","dijarr","yinyay","nyajin"]),
row(14,"You saw the julaji freshwater catfish there.","Yundu julaji yinyay nyajin.","yundu julaji yinyay nyajin | 2sg.NOM julaji.freshwater-catfish there see.PST","r354_2sg_there_fish_f_julaji",["yundu","julaji","yinyay","nyajin"]),
row(15,"You saw the mawul freshwater eel there.","Yundu mawul yinyay nyajin.","yundu mawul yinyay nyajin | 2sg.NOM mawul.freshwater-eel there see.PST","r354_2sg_there_fish_f_mawul",["yundu","mawul","yinyay","nyajin"]),
row(16,"You saw the balarri queenfish there.","Yundu balarri yinyay nyajin.","yundu balarri yinyay nyajin | 2sg.NOM balarri.queenfish there see.PST","r354_2sg_there_fish_f_balarri",["yundu","balarri","yinyay","nyajin"]),
row(17,"You saw the bayil jungle perch there.","Yundu bayil yinyay nyajin.","yundu bayil yinyay nyajin | 2sg.NOM bayil.jungle-perch there see.PST","r354_2sg_there_fish_f_bayil",["yundu","bayil","yinyay","nyajin"]),
row(18,"You saw the dalmbal flathead there.","Yundu dalmbal yinyay nyajin.","yundu dalmbal yinyay nyajin | 2sg.NOM dalmbal.flathead there see.PST","r354_2sg_there_fish_f_dalmbal",["yundu","dalmbal","yinyay","nyajin"]),
row(19,"You saw the barrbal black bream there.","Yundu barrbal yinyay nyajin.","yundu barrbal yinyay nyajin | 2sg.NOM barrbal.black-bream there see.PST","r354_2sg_there_fish_f_barrbal",["yundu","barrbal","yinyay","nyajin"]),
row(20,"You saw the jalkay salmon there.","Yundu jalkay yinyay nyajin.","yundu jalkay yinyay nyajin | 2sg.NOM jalkay.salmon there see.PST","r354_2sg_there_fish_f_jalkay",["yundu","jalkay","yinyay","nyajin"]),
row(21,"He saw the kalngkan small mullet here.","Nyulu kalngkan yaluy nyajin.","nyulu kalngkan yaluy nyajin | 3sg.NOM kalngkan.small-mullet here see.PST","r354_3sg_here_fish_f_kalngkan",["nyulu","kalngkan","yaluy","nyajin"]),
row(22,"He saw the mangkurr-mangkurr garfish here.","Nyulu mangkurr-mangkurr yaluy nyajin.","nyulu mangkurr-mangkurr yaluy nyajin | 3sg.NOM mangkurr-mangkurr.garfish here see.PST","r354_3sg_here_fish_f_mangkurr",["nyulu","mangkurr-mangkurr","yaluy","nyajin"]),
row(23,"He saw the dijarr freshwater perch here.","Nyulu dijarr yaluy nyajin.","nyulu dijarr yaluy nyajin | 3sg.NOM dijarr.freshwater-perch here see.PST","r354_3sg_here_fish_f_dijarr",["nyulu","dijarr","yaluy","nyajin"]),
row(24,"He saw the julaji freshwater catfish here.","Nyulu julaji yaluy nyajin.","nyulu julaji yaluy nyajin | 3sg.NOM julaji.freshwater-catfish here see.PST","r354_3sg_here_fish_f_julaji",["nyulu","julaji","yaluy","nyajin"]),
row(25,"He saw the mawul freshwater eel here.","Nyulu mawul yaluy nyajin.","nyulu mawul yaluy nyajin | 3sg.NOM mawul.freshwater-eel here see.PST","r354_3sg_here_fish_f_mawul",["nyulu","mawul","yaluy","nyajin"]),
row(26,"He saw the balarri queenfish here.","Nyulu balarri yaluy nyajin.","nyulu balarri yaluy nyajin | 3sg.NOM balarri.queenfish here see.PST","r354_3sg_here_fish_f_balarri",["nyulu","balarri","yaluy","nyajin"]),
row(27,"He saw the bayil jungle perch here.","Nyulu bayil yaluy nyajin.","nyulu bayil yaluy nyajin | 3sg.NOM bayil.jungle-perch here see.PST","r354_3sg_here_fish_f_bayil",["nyulu","bayil","yaluy","nyajin"]),
row(28,"He saw the dalmbal flathead here.","Nyulu dalmbal yaluy nyajin.","nyulu dalmbal yaluy nyajin | 3sg.NOM dalmbal.flathead here see.PST","r354_3sg_here_fish_f_dalmbal",["nyulu","dalmbal","yaluy","nyajin"]),
row(29,"He saw the barrbal black bream here.","Nyulu barrbal yaluy nyajin.","nyulu barrbal yaluy nyajin | 3sg.NOM barrbal.black-bream here see.PST","r354_3sg_here_fish_f_barrbal",["nyulu","barrbal","yaluy","nyajin"]),
row(30,"He saw the jalkay salmon here.","Nyulu jalkay yaluy nyajin.","nyulu jalkay yaluy nyajin | 3sg.NOM jalkay.salmon here see.PST","r354_3sg_here_fish_f_jalkay",["nyulu","jalkay","yaluy","nyajin"]),
row(31,"The two of them saw the kalngkan small mullet there.","Bula kalngkan yinyay nyajin.","bula kalngkan yinyay nyajin | 3du.NOM kalngkan.small-mullet there see.PST","r354_3du_there_fish_f_kalngkan",["bula","kalngkan","yinyay","nyajin"]),
row(32,"The two of them saw the mangkurr-mangkurr garfish there.","Bula mangkurr-mangkurr yinyay nyajin.","bula mangkurr-mangkurr yinyay nyajin | 3du.NOM mangkurr-mangkurr.garfish there see.PST","r354_3du_there_fish_f_mangkurr",["bula","mangkurr-mangkurr","yinyay","nyajin"]),
row(33,"The two of them saw the dijarr freshwater perch there.","Bula dijarr yinyay nyajin.","bula dijarr yinyay nyajin | 3du.NOM dijarr.freshwater-perch there see.PST","r354_3du_there_fish_f_dijarr",["bula","dijarr","yinyay","nyajin"]),
row(34,"The two of them saw the julaji freshwater catfish there.","Bula julaji yinyay nyajin.","bula julaji yinyay nyajin | 3du.NOM julaji.freshwater-catfish there see.PST","r354_3du_there_fish_f_julaji",["bula","julaji","yinyay","nyajin"]),
row(35,"The two of them saw the mawul freshwater eel there.","Bula mawul yinyay nyajin.","bula mawul yinyay nyajin | 3du.NOM mawul.freshwater-eel there see.PST","r354_3du_there_fish_f_mawul",["bula","mawul","yinyay","nyajin"]),
row(36,"The two of them saw the balarri queenfish there.","Bula balarri yinyay nyajin.","bula balarri yinyay nyajin | 3du.NOM balarri.queenfish there see.PST","r354_3du_there_fish_f_balarri",["bula","balarri","yinyay","nyajin"]),
row(37,"The two of them saw the bayil jungle perch there.","Bula bayil yinyay nyajin.","bula bayil yinyay nyajin | 3du.NOM bayil.jungle-perch there see.PST","r354_3du_there_fish_f_bayil",["bula","bayil","yinyay","nyajin"]),
row(38,"The two of them saw the dalmbal flathead there.","Bula dalmbal yinyay nyajin.","bula dalmbal yinyay nyajin | 3du.NOM dalmbal.flathead there see.PST","r354_3du_there_fish_f_dalmbal",["bula","dalmbal","yinyay","nyajin"]),
row(39,"The two of them saw the barrbal black bream there.","Bula barrbal yinyay nyajin.","bula barrbal yinyay nyajin | 3du.NOM barrbal.black-bream there see.PST","r354_3du_there_fish_f_barrbal",["bula","barrbal","yinyay","nyajin"]),
row(40,"The two of them saw the jalkay salmon there.","Bula jalkay yinyay nyajin.","bula jalkay yinyay nyajin | 3du.NOM jalkay.salmon there see.PST","r354_3du_there_fish_f_jalkay",["bula","jalkay","yinyay","nyajin"]),
row(41,"They saw the kalngkan small mullet here.","Jana kalngkan yaluy nyajin.","jana kalngkan yaluy nyajin | 3pl.NOM kalngkan.small-mullet here see.PST","r354_3pl_here_fish_f_kalngkan",["jana","kalngkan","yaluy","nyajin"]),
row(42,"They saw the mangkurr-mangkurr garfish here.","Jana mangkurr-mangkurr yaluy nyajin.","jana mangkurr-mangkurr yaluy nyajin | 3pl.NOM mangkurr-mangkurr.garfish here see.PST","r354_3pl_here_fish_f_mangkurr",["jana","mangkurr-mangkurr","yaluy","nyajin"]),
row(43,"They saw the dijarr freshwater perch here.","Jana dijarr yaluy nyajin.","jana dijarr yaluy nyajin | 3pl.NOM dijarr.freshwater-perch here see.PST","r354_3pl_here_fish_f_dijarr",["jana","dijarr","yaluy","nyajin"]),
row(44,"They saw the julaji freshwater catfish here.","Jana julaji yaluy nyajin.","jana julaji yaluy nyajin | 3pl.NOM julaji.freshwater-catfish here see.PST","r354_3pl_here_fish_f_julaji",["jana","julaji","yaluy","nyajin"]),
row(45,"They saw the mawul freshwater eel here.","Jana mawul yaluy nyajin.","jana mawul yaluy nyajin | 3pl.NOM mawul.freshwater-eel here see.PST","r354_3pl_here_fish_f_mawul",["jana","mawul","yaluy","nyajin"]),
row(46,"They saw the balarri queenfish here.","Jana balarri yaluy nyajin.","jana balarri yaluy nyajin | 3pl.NOM balarri.queenfish here see.PST","r354_3pl_here_fish_f_balarri",["jana","balarri","yaluy","nyajin"]),
row(47,"They saw the bayil jungle perch here.","Jana bayil yaluy nyajin.","jana bayil yaluy nyajin | 3pl.NOM bayil.jungle-perch here see.PST","r354_3pl_here_fish_f_bayil",["jana","bayil","yaluy","nyajin"]),
row(48,"They saw the dalmbal flathead here.","Jana dalmbal yaluy nyajin.","jana dalmbal yaluy nyajin | 3pl.NOM dalmbal.flathead here see.PST","r354_3pl_here_fish_f_dalmbal",["jana","dalmbal","yaluy","nyajin"]),
row(49,"They saw the barrbal black bream here.","Jana barrbal yaluy nyajin.","jana barrbal yaluy nyajin | 3pl.NOM barrbal.black-bream here see.PST","r354_3pl_here_fish_f_barrbal",["jana","barrbal","yaluy","nyajin"]),
row(50,"They saw the jalkay salmon here.","Jana jalkay yaluy nyajin.","jana jalkay yaluy nyajin | 3pl.NOM jalkay.salmon here see.PST","r354_3pl_here_fish_f_jalkay",["jana","jalkay","yaluy","nyajin"]),]
def literal_guard():
 m=ast.parse(Path(__file__).read_text());a=[n for n in m.body if isinstance(n,ast.Assign) and any(isinstance(t,ast.Name) and t.id=="ROWS" for t in n.targets)];issues=[]
 if len(a)!=1 or not isinstance(a[0].value,ast.List):return["ROWS"]
 if len(a[0].value.elts)!=50:issues.append("count")
 for i,e in enumerate(a[0].value.elts,1):
  if not isinstance(e,ast.Call) or not isinstance(e.func,ast.Name) or e.func.id!="row":issues.append(f"{i}:call");continue
  try:v=[ast.literal_eval(x) for x in e.args]
  except Exception:issues.append(f"{i}:literal");continue
  if len(v)!=6 or v[0]!=i:issues.append(f"{i}:args")
 return issues
def scan_static():
 issues=literal_guard()
 if len(ROWS)!=50 or [r[0] for r in ROWS]!=list(range(1,51)) or len({r[1] for r in ROWS})!=50 or len({r[2] for r in ROWS})!=50:issues.append("inventory")
 for seq,en,ku,an,fr,_tier,wjson,ljson,ev,conf in ROWS:
  p,d,prefix,loc,person=FRAMES[(seq-1)//10];t=TARGET_ORDER[(seq-1)%10];w=json.loads(wjson);den=[x for x in DENY_KUKU if re.search(rf"(^|\s){re.escape(x)}([\s\.]|$)",ku.lower())];dr=[x for x in DENY_ENGLISH if re.search(rf"\b{re.escape(x)}\b",en.lower())]
  if json.loads(ljson) or den or dr:issues.append(f"{seq}:deny {den}/{dr}")
  if ku!=f"{p.capitalize()} {t} {d} nyajin." or en!=f"{prefix} the {LABELS[t]} {loc}." or an!=f"{p} {t} {d} nyajin | {person}.NOM {GLOSSES[t]} {loc} see.PST":issues.append(f"{seq}:fields")
  if w!=[p,t,d,"nyajin"] or not fr.startswith("r354_") or EVIDENCE[t] not in ev or SUPPORT not in ev or conf!=CONF[t]:issues.append(f"{seq}:meta")
 return issues
def main():
 rec,hits=source_audit();issues=scan_static()
 if issues:raise SystemExit("\n".join(issues))
 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() or db.execute("SELECT COUNT(*) FROM sentences WHERE status='under_review'").fetchone()[0]:raise SystemExit("open")
 tc=count_words(db,TARGET_ORDER);sc=count_words(db,list(EXPECTED_SUPPORT))
 if tc!=EXPECTED_TARGET or sc!=EXPECTED_SUPPORT:raise SystemExit(f"coverage {tc}/{sc}")
 ps=["ngayu","yundu","nyulu","bula","jana","ngali","ngaliny","yubal","yurra","nganjin"];coll=[]
 for p in ps:
  for t in TARGET_ORDER:
   for d in ("yaluy","yinyay"):
    s=f"{p.capitalize()} {t} {d} nyajin.";h=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'",(s,)).fetchall()
    if h:coll.append((s,h))
 if coll:raise SystemExit(f"matrix {coll}")
 verified,words=db.execute("SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) FROM sentences WHERE status IN('accepted','revised')").fetchone()
 if (verified,words)!=(19847,134747):raise SystemExit(f"baseline {verified}/{words}")
 notes="LINTER-CLEAN FISH SET-F SINGULAR/THIRD PAST PERCEPTION AFTER RULE353 (Rule354). Ten two-card heads; zero Patz hits; all 200 cells empty; source prose closed; gap172->162."
 bid=db.execute("INSERT INTO batches(label,cheatsheet_version,guide_version,notes,created_at) VALUES(?,?,?,?,?)",(LABEL,"Rule354 under review","operator-guide-2026-07-10",notes,NOW)).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(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",[(bid,seq,en,ku,an,fr,tier,w,l,ev,"under_review",conf,"project_approved_synthetic_pending_elder_verification","codex-main",NOW,NOW) for seq,en,ku,an,fr,tier,w,l,ev,conf in ROWS])
 events=[("source","source-audit-pass",f"Two cards each; Patz={hits}; hashes={HASHES}."),("planning","linter-aware-selection-audit","Excluded stop-final/rhotic-palatal/dialect-only/polysemous provisional heads; final ten pass linter and source clarity."),("planning","sense-boundary-audit","Location/taboo/synonym/habitat/capture/food/source/event prose closed."),("planning","construction-gap-audit","All 200 cells absent."),("planning","balance-audit","Gap172->162."),("preflight","coverage-audit",f"targets={tc};support={sc}."),("preflight","duplicate-preflight","50 selected unique/absent."),("preflight","static-guard-pass","AST 50 literal; source/sense/linter contracts passed.")]
 for ph,et,d in events:record_event(db,batch_label=LABEL,phase=ph,event_type=et,actor="codex",detail=d,artifact_path=LOG,created_at=NOW)
 record_event(db,batch_label=LABEL,phase="bank",event_type="bank-success",actor="codex",detail=f"Banked 50 Rule354; verified {verified}; words {words}.",command="python3 batch_scripts/bank_396.py",artifact_path=LOG,created_at=NOW);db.commit();print("banked batch-396: 50 under_review");return 0
if __name__=="__main__":raise SystemExit(main())
