#!/usr/bin/env python3
"""Close Batch 390 after five-lens review."""

from __future__ import annotations
import datetime as dt, json, sqlite3
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-390"
NOW=dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00","Z")
LOG="process_logs/batch-390-bird-set-d-nonsingular-past-perception-after-rule347-process.md"; LENSES=("morphology","lexicon","syntax","fidelity","register")
NOTES="BIRD SET-D NON-SINGULAR PAST PERCEPTION AFTER RULE 347 (Rule 348). 50 accepted; full 100-cell matrix empty; target/Patz and Patz-over-dictionary pronoun audits passed; all lexical/source boundaries closed; gap 172->162."
LESSONS=[
("source","Dictionary/Patz hashes and ten two-card bird inventories remained unchanged.","Rule 348: sources."),
("source","Patz target profile remained wakuka three and every other target zero, without negative lexical inference.","Rule 348: target hits."),
("source","Patz controls ngali 1du exclusive and ngaliny 1du inclusive over dictionary misgloss/omission.","Rule 348: clusivity."),
("source","Ngana remains grammatical 1pl inclusive but deferred at 1,115 uses.","Rule 348: ngana."),
("source","Rule 346-347 generic bird labels and all card/source boundaries remained unchanged.","Rule 348: labels."),
("process","All 100 non-singular participant x bird x deictic past cells were absent.","Rule 348: matrix."),
("process","Three-here/two-there changed gap 172->162; inverse would yield 182.","Rule 348: balance."),
("process","Targets before banking were 15/15/15/15/16/16/16/16/16/17.","Rule 348: target baseline."),
("process","Support baseline was ngali 673, ngaliny 615, yubal 625, yurra 610, nganjin 338, ngana 1115, yaluy 4582, yinyay 4754, nyajin 2450.","Rule 348: support baseline."),
("lexicon","Diwalngku stayed generic bird; unknown taxonomy/migration/events closed.","Rule 348: diwalngku."),
("lexicon","Bujil-yiran stayed ibis; cognition card/fear decomposition/events closed.","Rule 348: bujil-yiran."),
("lexicon","Karrmbil stayed generic parakeet; taxonomy/property/events closed.","Rule 348: karrmbil."),
("lexicon","Yiki stayed generic owl; night/species/behavior/events closed.","Rule 348: yiki."),
("lexicon","Bajal stayed bower bird; building/collecting/stealing/events closed.","Rule 348: bajal."),
("lexicon","Biru-biru stayed an exact whole head; taxonomy/base/reduplication/ecology closed.","Rule 348: biru-biru."),
("lexicon","Jangkan stayed swift bird noun; speed/flight/events closed.","Rule 348: jangkan."),
("lexicon","Kurranday stayed seagull; habitat/warning/feeding/events closed.","Rule 348: kurranday."),
("lexicon","Ngarkay stayed waterhen; habitat/motion/feeding/events closed.","Rule 348: ngarkay."),
("lexicon","Wakuka stayed generic bird; taxonomy/belief/station place/events closed.","Rule 348: wakuka."),
("morphology","Targets remained bare absolutive O; no case/derivation entered.","Rule 348: O."),
("morphology","Bujil-yiran and biru-biru remained whole heads.","Rule 348: heads."),
("morphology","Ngali, ngaliny, yubal, yurra and nganjin remained bare nominative A with distinctions explicit.","Rule 348: A."),
("morphology","Yaluy, yinyay and nyajin remained fixed.","Rule 348: support."),
("syntax","Rows 1-10 used Ngali TARGET yaluy nyajin; 1du exclusive.","Rule 348: band1."),
("syntax","Rows 11-20 used Ngaliny TARGET yinyay nyajin; 1du inclusive.","Rule 348: band2."),
("syntax","Rows 21-30 used Yubal TARGET yaluy nyajin; 2du.","Rule 348: band3."),
("syntax","Rows 31-40 used Yurra TARGET yinyay nyajin; 2pl.","Rule 348: band4."),
("syntax","Rows 41-50 used Nganjin TARGET yaluy nyajin; 1pl exclusive.","Rule 348: band5."),
("fidelity","English preserved number/clusivity, bird label, location and past seeing only.","Rule 348: fidelity."),
("fidelity","No taxonomy/card/culture/property/habitat/place/source/event entered English.","Rule 348: exclusions."),
("register","Rows remained neutral elicitation clauses.","Rule 348: register."),
("process","All 50 sentences were explicit row calls, not runtime-authored.","Rule 348: authorship."),
("process","Rules 149-347 traps and bird/pronoun boundaries remained closed.","Rule 348: cumulative."),
("process","All source, row, review, export and publication evidence remains in mounted-drive logs/events.","Rule 348: storage."),
("handoff","Continue from 19,597 only after fresh complement audits.","Rule 348: handoff."),]
LEX=[
("diwalngku","noun",None,"NA","bird","batch-390: non-singular generic bird; taxonomy/migration boundaries."),("bujil-yiran","noun",None,"NA","ibis","batch-390: non-singular ibis; cognition/component boundaries."),("karrmbil","noun",None,"NA","parakeet","batch-390: non-singular generic parakeet; taxonomy boundaries."),("yiki","noun",None,"NA","owl","batch-390: non-singular generic owl; night/behavior boundaries."),("bajal","noun",None,"NA","bower bird","batch-390: non-singular bower bird; behavior boundaries."),("biru-biru","noun",None,"NA","rainbow bird","batch-390: non-singular exact whole head; base/redup boundaries."),("jangkan","noun",None,"NA","swift bird","batch-390: non-singular bird noun; speed/flight boundaries."),("kurranday","noun",None,"NA","seagull","batch-390: non-singular seagull; habitat/warning boundaries."),("ngarkay","noun",None,"NA","waterhen","batch-390: non-singular waterhen; habitat/motion boundaries."),("wakuka","noun",None,"NA","bird","batch-390: non-singular generic bird; taxonomy/belief/place boundaries."),
("ngali","pronoun",None,"NA","first dual exclusive nominative","batch-390: Patz controls; bare A."),("ngaliny","pronoun",None,"NA","first dual inclusive nominative","batch-390: Patz controls; bare A."),("yubal","pronoun",None,"NA","second dual nominative","batch-390: bare A."),("yurra","pronoun",None,"NA","second plural nominative","batch-390: bare A."),("nganjin","pronoun",None,"NA","first plural exclusive nominative","batch-390: bare A; distinct from ngana."),("yaluy","demonstrative",None,"NA","here","batch-390: Yalanji local."),("yinyay","demonstrative",None,"NA","there","batch-390: Yalanji local."),("nyajin","verb-tr","l","NA","see; see.PST","batch-390: running past surface."),]

def main():
    if len(LESSONS)!=35 or len(LEX)!=18: raise SystemExit(f"inventory {len(LESSONS)}/{len(LEX)}")
    db=sqlite3.connect(DB);db.execute("PRAGMA foreign_keys=ON");db.execute("BEGIN");ensure_schema(db);r=db.execute("SELECT id FROM batches WHERE label=?",(LABEL,)).fetchone()
    if not r: raise SystemExit("not banked")
    bid=r[0]; counts=db.execute("SELECT COUNT(*),COUNT(*) FILTER(WHERE status='under_review'),COUNT(*) FILTER(WHERE status IN('accepted','revised')) FROM sentences WHERE batch_id=?",(bid,)).fetchone()
    if counts!=(50,50,0): raise SystemExit(f"precheck {counts}")
    if db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN(SELECT id FROM sentences WHERE batch_id=?)",(bid,)).fetchone()[0] or db.execute("SELECT COUNT(*) FROM lessons WHERE batch_id=?",(bid,)).fetchone()[0] or db.execute("SELECT COUNT(*) FROM lexeme_log WHERE action='batch-390-verify'").fetchone()[0]: raise SystemExit("artifacts exist")
    total=db.execute("SELECT COUNT(*) FROM sentences WHERE status!='rejected'").fetchone()[0]; before,wb=db.execute("SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) FROM sentences WHERE status IN('accepted','revised')").fetchone(); ids=[x[0] for x in db.execute("SELECT id FROM sentences WHERE batch_id=? ORDER BY seq",(bid,))]
    db.executemany("INSERT INTO reviews(sentence_id,round,lens,verdict,issues,suggested_fix,reviewer,created_at) VALUES(?,?,?,?,?,?,?,?)",[(sid,1,l,"pass",None,None,"codex-main-five-lens",NOW) for sid in ids for l in LENSES]);db.executemany("INSERT INTO lessons(batch_id,category,lesson,guide_change,created_at) VALUES(?,?,?,?,?)",[(bid,c,l,g,NOW) for c,l,g in LESSONS])
    for h,p,c,k,g,n in LEX:
        senses=json.dumps([{"sense":g,"definition":n,"status":"attested"}],ensure_ascii=False);att=json.dumps([{"source":LABEL,"ref":"five-lens close","quote":n}],ensure_ascii=False)
        db.execute("""INSERT INTO lexemes(headword,phonemic,pos,conj_class,case_class,gloss,senses,attestation,allomorphy,morphology,collocations,usage_notes,antonyms_contrasts,corpus_freq,status,verified_at,notes) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON CONFLICT(headword) DO UPDATE SET verified_at=excluded.verified_at,status=excluded.status,notes=CASE WHEN lexemes.notes IS NULL OR lexemes.notes='' THEN excluded.notes WHEN instr(lexemes.notes,?)>0 THEN lexemes.notes ELSE lexemes.notes||'; '||excluded.notes END""",(h,None,p,c,k,g,senses,att,None,None,None,n,None,0,"verified",NOW,n,LABEL));db.execute("INSERT INTO lexeme_log(headword,action,detail,created_at) VALUES(?,?,?,?)",(h,"batch-390-verify",n,NOW))
    ev=[("lint","under-review-lint-green",f"Lint {total}/0; verified {before}."),("review","five-lens-pass","250/250 passed; clusivity and bird boundaries retained."),("review","review-detail","All source/sense/property/event drift closed."),("review","lesson-inventory","35 lessons."),("review","lexeme-source-log","18 logs.")]
    for ph,et,d in ev:record_event(db,batch_label=LABEL,phase=ph,event_type=et,actor="codex",detail=d,artifact_path=LOG,created_at=NOW)
    db.execute("UPDATE sentences SET status='accepted',updated_at=? WHERE batch_id=?",(NOW,bid));db.execute("UPDATE batches SET notes=?,cheatsheet_version=?,guide_version=? WHERE id=?",(NOTES,"Rule 348 accepted","operator-guide-2026-07-10",bid));after,wa=db.execute("SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) FROM sentences WHERE status IN('accepted','revised')").fetchone();ac=db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status='accepted'",(bid,)).fetchone()[0];rv=db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN(SELECT id FROM sentences WHERE batch_id=?)",(bid,)).fetchone()[0]
    record_event(db,batch_label=LABEL,phase="close",event_type="close-success",actor="codex",detail=f"accepted={ac} reviews={rv} lessons=35 logs=18 verified {before}->{after} words {wb}->{wa}.",command="python3 batch_scripts/close_390.py",artifact_path=LOG,created_at=NOW);db.commit();print(f"closed batch-390: accepted={ac} reviews={rv} lessons=35 logs=18 verified {before}->{after} words {wb}->{wa}");return 0
if __name__=="__main__":raise SystemExit(main())
