#!/usr/bin/env python3
"""Bank Batch 399: Rule 357 complementary fish-set-F non-singular past perception."""
from __future__ import annotations
import ast,datetime as dt,json,re,sqlite3
from pathlib import Path
from bank_398 import DB,DENY_ENGLISH,DENY_KUKU,TARGET_EVIDENCE as BASE_EVIDENCE,SUPPORT as BASE_SUPPORT,TARGET_ORDER,LABELS,GLOSSES,CONF,count_words,fish_source_audit,pronoun_source_audit
from process_ledger import ensure_schema,record_event
LABEL="batch-399";NOW=dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00","Z");LOG="process_logs/batch-399-complementary-linter-clean-fish-set-f-nonsingular-past-perception-after-rule356-process.md"
EXPECTED_TARGET={"kalngkan":20,"mangkurr-mangkurr":20,"dijarr":21,"julaji":21,"mawul":23,"balarri":39,"bayil":36,"dalmbal":36,"barrbal":41,"jalkay":41}
EXPECTED_SUPPORT={"ngali":723,"ngaliny":665,"yubal":675,"yurra":660,"nganjin":388,"ngana":1115,"yaluy":4812,"yinyay":4974,"nyajin":2900}
FRAMES=[("ngali","yinyay","We two, not you, saw","there","1du.excl"),("ngaliny","yaluy","You and I saw","here","1du.incl"),("yubal","yinyay","You two saw","there","2du"),("yurra","yaluy","You all saw","here","2pl"),("nganjin","yinyay","We, not you, saw","there","1pl.excl")];OPP={"yaluy":"yinyay","yinyay":"yaluy"}
TARGET_EVIDENCE={t:f"{BASE_EVIDENCE[t]} Rule357 changes deictic location only and retains every Rule354-356 source, sense, and clusivity boundary." for t in TARGET_ORDER};SUPPORT=f"{BASE_SUPPORT} Rule357 exactly complements Rule356."
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"{TARGET_EVIDENCE[t]} {SUPPORT}",CONF[t])
ROWS=[
row(1,"We two, not you, saw the kalngkan small mullet there.","Ngali kalngkan yinyay nyajin.","ngali kalngkan yinyay nyajin | 1du.excl.NOM kalngkan.small-mullet there see.PST","r357_1du_excl_there_fish_f_kalngkan",["ngali","kalngkan","yinyay","nyajin"]),
row(2,"We two, not you, saw the mangkurr-mangkurr garfish there.","Ngali mangkurr-mangkurr yinyay nyajin.","ngali mangkurr-mangkurr yinyay nyajin | 1du.excl.NOM mangkurr-mangkurr.garfish there see.PST","r357_1du_excl_there_fish_f_mangkurr",["ngali","mangkurr-mangkurr","yinyay","nyajin"]),
row(3,"We two, not you, saw the dijarr freshwater perch there.","Ngali dijarr yinyay nyajin.","ngali dijarr yinyay nyajin | 1du.excl.NOM dijarr.freshwater-perch there see.PST","r357_1du_excl_there_fish_f_dijarr",["ngali","dijarr","yinyay","nyajin"]),
row(4,"We two, not you, saw the julaji freshwater catfish there.","Ngali julaji yinyay nyajin.","ngali julaji yinyay nyajin | 1du.excl.NOM julaji.freshwater-catfish there see.PST","r357_1du_excl_there_fish_f_julaji",["ngali","julaji","yinyay","nyajin"]),
row(5,"We two, not you, saw the mawul freshwater eel there.","Ngali mawul yinyay nyajin.","ngali mawul yinyay nyajin | 1du.excl.NOM mawul.freshwater-eel there see.PST","r357_1du_excl_there_fish_f_mawul",["ngali","mawul","yinyay","nyajin"]),
row(6,"We two, not you, saw the balarri queenfish there.","Ngali balarri yinyay nyajin.","ngali balarri yinyay nyajin | 1du.excl.NOM balarri.queenfish there see.PST","r357_1du_excl_there_fish_f_balarri",["ngali","balarri","yinyay","nyajin"]),
row(7,"We two, not you, saw the bayil jungle perch there.","Ngali bayil yinyay nyajin.","ngali bayil yinyay nyajin | 1du.excl.NOM bayil.jungle-perch there see.PST","r357_1du_excl_there_fish_f_bayil",["ngali","bayil","yinyay","nyajin"]),
row(8,"We two, not you, saw the dalmbal flathead there.","Ngali dalmbal yinyay nyajin.","ngali dalmbal yinyay nyajin | 1du.excl.NOM dalmbal.flathead there see.PST","r357_1du_excl_there_fish_f_dalmbal",["ngali","dalmbal","yinyay","nyajin"]),
row(9,"We two, not you, saw the barrbal black bream there.","Ngali barrbal yinyay nyajin.","ngali barrbal yinyay nyajin | 1du.excl.NOM barrbal.black-bream there see.PST","r357_1du_excl_there_fish_f_barrbal",["ngali","barrbal","yinyay","nyajin"]),
row(10,"We two, not you, saw the jalkay salmon there.","Ngali jalkay yinyay nyajin.","ngali jalkay yinyay nyajin | 1du.excl.NOM jalkay.salmon there see.PST","r357_1du_excl_there_fish_f_jalkay",["ngali","jalkay","yinyay","nyajin"]),
row(11,"You and I saw the kalngkan small mullet here.","Ngaliny kalngkan yaluy nyajin.","ngaliny kalngkan yaluy nyajin | 1du.incl.NOM kalngkan.small-mullet here see.PST","r357_1du_incl_here_fish_f_kalngkan",["ngaliny","kalngkan","yaluy","nyajin"]),
row(12,"You and I saw the mangkurr-mangkurr garfish here.","Ngaliny mangkurr-mangkurr yaluy nyajin.","ngaliny mangkurr-mangkurr yaluy nyajin | 1du.incl.NOM mangkurr-mangkurr.garfish here see.PST","r357_1du_incl_here_fish_f_mangkurr",["ngaliny","mangkurr-mangkurr","yaluy","nyajin"]),
row(13,"You and I saw the dijarr freshwater perch here.","Ngaliny dijarr yaluy nyajin.","ngaliny dijarr yaluy nyajin | 1du.incl.NOM dijarr.freshwater-perch here see.PST","r357_1du_incl_here_fish_f_dijarr",["ngaliny","dijarr","yaluy","nyajin"]),
row(14,"You and I saw the julaji freshwater catfish here.","Ngaliny julaji yaluy nyajin.","ngaliny julaji yaluy nyajin | 1du.incl.NOM julaji.freshwater-catfish here see.PST","r357_1du_incl_here_fish_f_julaji",["ngaliny","julaji","yaluy","nyajin"]),
row(15,"You and I saw the mawul freshwater eel here.","Ngaliny mawul yaluy nyajin.","ngaliny mawul yaluy nyajin | 1du.incl.NOM mawul.freshwater-eel here see.PST","r357_1du_incl_here_fish_f_mawul",["ngaliny","mawul","yaluy","nyajin"]),
row(16,"You and I saw the balarri queenfish here.","Ngaliny balarri yaluy nyajin.","ngaliny balarri yaluy nyajin | 1du.incl.NOM balarri.queenfish here see.PST","r357_1du_incl_here_fish_f_balarri",["ngaliny","balarri","yaluy","nyajin"]),
row(17,"You and I saw the bayil jungle perch here.","Ngaliny bayil yaluy nyajin.","ngaliny bayil yaluy nyajin | 1du.incl.NOM bayil.jungle-perch here see.PST","r357_1du_incl_here_fish_f_bayil",["ngaliny","bayil","yaluy","nyajin"]),
row(18,"You and I saw the dalmbal flathead here.","Ngaliny dalmbal yaluy nyajin.","ngaliny dalmbal yaluy nyajin | 1du.incl.NOM dalmbal.flathead here see.PST","r357_1du_incl_here_fish_f_dalmbal",["ngaliny","dalmbal","yaluy","nyajin"]),
row(19,"You and I saw the barrbal black bream here.","Ngaliny barrbal yaluy nyajin.","ngaliny barrbal yaluy nyajin | 1du.incl.NOM barrbal.black-bream here see.PST","r357_1du_incl_here_fish_f_barrbal",["ngaliny","barrbal","yaluy","nyajin"]),
row(20,"You and I saw the jalkay salmon here.","Ngaliny jalkay yaluy nyajin.","ngaliny jalkay yaluy nyajin | 1du.incl.NOM jalkay.salmon here see.PST","r357_1du_incl_here_fish_f_jalkay",["ngaliny","jalkay","yaluy","nyajin"]),
row(21,"You two saw the kalngkan small mullet there.","Yubal kalngkan yinyay nyajin.","yubal kalngkan yinyay nyajin | 2du.NOM kalngkan.small-mullet there see.PST","r357_2du_there_fish_f_kalngkan",["yubal","kalngkan","yinyay","nyajin"]),
row(22,"You two saw the mangkurr-mangkurr garfish there.","Yubal mangkurr-mangkurr yinyay nyajin.","yubal mangkurr-mangkurr yinyay nyajin | 2du.NOM mangkurr-mangkurr.garfish there see.PST","r357_2du_there_fish_f_mangkurr",["yubal","mangkurr-mangkurr","yinyay","nyajin"]),
row(23,"You two saw the dijarr freshwater perch there.","Yubal dijarr yinyay nyajin.","yubal dijarr yinyay nyajin | 2du.NOM dijarr.freshwater-perch there see.PST","r357_2du_there_fish_f_dijarr",["yubal","dijarr","yinyay","nyajin"]),
row(24,"You two saw the julaji freshwater catfish there.","Yubal julaji yinyay nyajin.","yubal julaji yinyay nyajin | 2du.NOM julaji.freshwater-catfish there see.PST","r357_2du_there_fish_f_julaji",["yubal","julaji","yinyay","nyajin"]),
row(25,"You two saw the mawul freshwater eel there.","Yubal mawul yinyay nyajin.","yubal mawul yinyay nyajin | 2du.NOM mawul.freshwater-eel there see.PST","r357_2du_there_fish_f_mawul",["yubal","mawul","yinyay","nyajin"]),
row(26,"You two saw the balarri queenfish there.","Yubal balarri yinyay nyajin.","yubal balarri yinyay nyajin | 2du.NOM balarri.queenfish there see.PST","r357_2du_there_fish_f_balarri",["yubal","balarri","yinyay","nyajin"]),
row(27,"You two saw the bayil jungle perch there.","Yubal bayil yinyay nyajin.","yubal bayil yinyay nyajin | 2du.NOM bayil.jungle-perch there see.PST","r357_2du_there_fish_f_bayil",["yubal","bayil","yinyay","nyajin"]),
row(28,"You two saw the dalmbal flathead there.","Yubal dalmbal yinyay nyajin.","yubal dalmbal yinyay nyajin | 2du.NOM dalmbal.flathead there see.PST","r357_2du_there_fish_f_dalmbal",["yubal","dalmbal","yinyay","nyajin"]),
row(29,"You two saw the barrbal black bream there.","Yubal barrbal yinyay nyajin.","yubal barrbal yinyay nyajin | 2du.NOM barrbal.black-bream there see.PST","r357_2du_there_fish_f_barrbal",["yubal","barrbal","yinyay","nyajin"]),
row(30,"You two saw the jalkay salmon there.","Yubal jalkay yinyay nyajin.","yubal jalkay yinyay nyajin | 2du.NOM jalkay.salmon there see.PST","r357_2du_there_fish_f_jalkay",["yubal","jalkay","yinyay","nyajin"]),
row(31,"You all saw the kalngkan small mullet here.","Yurra kalngkan yaluy nyajin.","yurra kalngkan yaluy nyajin | 2pl.NOM kalngkan.small-mullet here see.PST","r357_2pl_here_fish_f_kalngkan",["yurra","kalngkan","yaluy","nyajin"]),
row(32,"You all saw the mangkurr-mangkurr garfish here.","Yurra mangkurr-mangkurr yaluy nyajin.","yurra mangkurr-mangkurr yaluy nyajin | 2pl.NOM mangkurr-mangkurr.garfish here see.PST","r357_2pl_here_fish_f_mangkurr",["yurra","mangkurr-mangkurr","yaluy","nyajin"]),
row(33,"You all saw the dijarr freshwater perch here.","Yurra dijarr yaluy nyajin.","yurra dijarr yaluy nyajin | 2pl.NOM dijarr.freshwater-perch here see.PST","r357_2pl_here_fish_f_dijarr",["yurra","dijarr","yaluy","nyajin"]),
row(34,"You all saw the julaji freshwater catfish here.","Yurra julaji yaluy nyajin.","yurra julaji yaluy nyajin | 2pl.NOM julaji.freshwater-catfish here see.PST","r357_2pl_here_fish_f_julaji",["yurra","julaji","yaluy","nyajin"]),
row(35,"You all saw the mawul freshwater eel here.","Yurra mawul yaluy nyajin.","yurra mawul yaluy nyajin | 2pl.NOM mawul.freshwater-eel here see.PST","r357_2pl_here_fish_f_mawul",["yurra","mawul","yaluy","nyajin"]),
row(36,"You all saw the balarri queenfish here.","Yurra balarri yaluy nyajin.","yurra balarri yaluy nyajin | 2pl.NOM balarri.queenfish here see.PST","r357_2pl_here_fish_f_balarri",["yurra","balarri","yaluy","nyajin"]),
row(37,"You all saw the bayil jungle perch here.","Yurra bayil yaluy nyajin.","yurra bayil yaluy nyajin | 2pl.NOM bayil.jungle-perch here see.PST","r357_2pl_here_fish_f_bayil",["yurra","bayil","yaluy","nyajin"]),
row(38,"You all saw the dalmbal flathead here.","Yurra dalmbal yaluy nyajin.","yurra dalmbal yaluy nyajin | 2pl.NOM dalmbal.flathead here see.PST","r357_2pl_here_fish_f_dalmbal",["yurra","dalmbal","yaluy","nyajin"]),
row(39,"You all saw the barrbal black bream here.","Yurra barrbal yaluy nyajin.","yurra barrbal yaluy nyajin | 2pl.NOM barrbal.black-bream here see.PST","r357_2pl_here_fish_f_barrbal",["yurra","barrbal","yaluy","nyajin"]),
row(40,"You all saw the jalkay salmon here.","Yurra jalkay yaluy nyajin.","yurra jalkay yaluy nyajin | 2pl.NOM jalkay.salmon here see.PST","r357_2pl_here_fish_f_jalkay",["yurra","jalkay","yaluy","nyajin"]),
row(41,"We, not you, saw the kalngkan small mullet there.","Nganjin kalngkan yinyay nyajin.","nganjin kalngkan yinyay nyajin | 1pl.excl.NOM kalngkan.small-mullet there see.PST","r357_1pl_excl_there_fish_f_kalngkan",["nganjin","kalngkan","yinyay","nyajin"]),
row(42,"We, not you, saw the mangkurr-mangkurr garfish there.","Nganjin mangkurr-mangkurr yinyay nyajin.","nganjin mangkurr-mangkurr yinyay nyajin | 1pl.excl.NOM mangkurr-mangkurr.garfish there see.PST","r357_1pl_excl_there_fish_f_mangkurr",["nganjin","mangkurr-mangkurr","yinyay","nyajin"]),
row(43,"We, not you, saw the dijarr freshwater perch there.","Nganjin dijarr yinyay nyajin.","nganjin dijarr yinyay nyajin | 1pl.excl.NOM dijarr.freshwater-perch there see.PST","r357_1pl_excl_there_fish_f_dijarr",["nganjin","dijarr","yinyay","nyajin"]),
row(44,"We, not you, saw the julaji freshwater catfish there.","Nganjin julaji yinyay nyajin.","nganjin julaji yinyay nyajin | 1pl.excl.NOM julaji.freshwater-catfish there see.PST","r357_1pl_excl_there_fish_f_julaji",["nganjin","julaji","yinyay","nyajin"]),
row(45,"We, not you, saw the mawul freshwater eel there.","Nganjin mawul yinyay nyajin.","nganjin mawul yinyay nyajin | 1pl.excl.NOM mawul.freshwater-eel there see.PST","r357_1pl_excl_there_fish_f_mawul",["nganjin","mawul","yinyay","nyajin"]),
row(46,"We, not you, saw the balarri queenfish there.","Nganjin balarri yinyay nyajin.","nganjin balarri yinyay nyajin | 1pl.excl.NOM balarri.queenfish there see.PST","r357_1pl_excl_there_fish_f_balarri",["nganjin","balarri","yinyay","nyajin"]),
row(47,"We, not you, saw the bayil jungle perch there.","Nganjin bayil yinyay nyajin.","nganjin bayil yinyay nyajin | 1pl.excl.NOM bayil.jungle-perch there see.PST","r357_1pl_excl_there_fish_f_bayil",["nganjin","bayil","yinyay","nyajin"]),
row(48,"We, not you, saw the dalmbal flathead there.","Nganjin dalmbal yinyay nyajin.","nganjin dalmbal yinyay nyajin | 1pl.excl.NOM dalmbal.flathead there see.PST","r357_1pl_excl_there_fish_f_dalmbal",["nganjin","dalmbal","yinyay","nyajin"]),
row(49,"We, not you, saw the barrbal black bream there.","Nganjin barrbal yinyay nyajin.","nganjin barrbal yinyay nyajin | 1pl.excl.NOM barrbal.black-bream there see.PST","r357_1pl_excl_there_fish_f_barrbal",["nganjin","barrbal","yinyay","nyajin"]),
row(50,"We, not you, saw the jalkay salmon there.","Nganjin jalkay yinyay nyajin.","nganjin jalkay yinyay nyajin | 1pl.excl.NOM jalkay.salmon there see.PST","r357_1pl_excl_there_fish_f_jalkay",["nganjin","jalkay","yinyay","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("r357_") or TARGET_EVIDENCE[t] not in ev or SUPPORT not in ev or conf!=CONF[t]:issues.append(f"{seq}:meta")
 return issues
def main():
 fish,fh=fish_source_audit();_old,pronouns=pronoun_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}")
 for seq,_en,ku,_an,_fr,_tier,wjson,_l,_ev,_c in ROWS:
  w=json.loads(wjson);ch=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 IN('accepted','revised','under_review')",(ku,)).fetchall();opp=f"{w[0].capitalize()} {w[1]} {OPP[w[2]]} nyajin.";oh=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 IN('accepted','revised','under_review')",(opp,)).fetchall()
  if ch or oh!=[("batch-398",seq)]:raise SystemExit(f"map {seq} {ch}/{oh}")
 verified,words=db.execute("SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) FROM sentences WHERE status IN('accepted','revised')").fetchone()
 if (verified,words)!=(19997,135347):raise SystemExit(f"baseline {verified}/{words}")
 notes="COMPLEMENTARY LINTER-CLEAN FISH SET-F NON-SINGULAR PAST PERCEPTION AFTER RULE356 (Rule357). 50 exact complements; full 100-cell non-singular grid and 200-cell set-F grid complete; gap162->172."
 bid=db.execute("INSERT INTO batches(label,cheatsheet_version,guide_version,notes,created_at) VALUES(?,?,?,?,?)",(LABEL,"Rule357 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"Fish={fish};Patz={fh};pronouns={pronouns}; source hashes retained."),("planning","sense-preservation-audit","All fish, whole-head, linter and source-prose boundaries retained."),("planning","complement-map-audit","50 candidates absent; opposites exactly Batch398 seq1-50."),("planning","balance-audit","Gap162->172; inverse duplicate."),("preflight","coverage-audit",f"targets={tc};support={sc}."),("preflight","duplicate-preflight","50 selected unique/absent."),("preflight","static-guard-pass","AST 50 literal; source/sense/clusivity/complement/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 Rule357; verified {verified}; words {words}.",command="python3 batch_scripts/bank_399.py",artifact_path=LOG,created_at=NOW);db.commit();print("banked batch-399: 50 under_review");return 0
if __name__=="__main__":raise SystemExit(main())
