#!/usr/bin/env python3
"""Close Batch 392 after explicit 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-392"
NOW=dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00","Z")
LOG="process_logs/batch-392-low-coverage-bird-set-e-singular-third-past-perception-after-rule349-process.md"; LENSES=("morphology","lexicon","syntax","fidelity","register")
NOTES="LOW-COVERAGE BIRD SET-E SINGULAR/THIRD PAST PERCEPTION AFTER RULE 349 (Rule 350). 50 accepted; source/card/Patz and empty 200-cell audits passed; labels conservative; cultural/taxonomic/property/event boundaries closed; gap 172->162."
LESSONS=[
("source","Dictionary and Patz hashes were exact at Rule 350 preflight.","Rule 350: hashes."),
("source","Corrected set-E exact record counts are 2/1/2/1/1/1/1/1/2/2.","Rule 350: cards."),
("source","All ten set-E heads have zero exact Patz hits; dictionary attestation prevents negative inference.","Rule 350: Patz coverage."),
("process","All 200 future singular/non-singular set-E deictic cells were empty.","Rule 350: matrix."),
("process","Three here bands and two there bands reduce the deictic gap 172 to 162.","Rule 350: balance."),
("lexicon","Dakwurr-dakwun remains the exact hammer-bird head; totem/moiety and productive-allomorph claims closed.","Rule 350: dakwurr-dakwun."),
("lexicon","Kurkunbay remains butcherbird; stockwhip-bird alternate label, call and hunting prose closed.","Rule 350: kurkunbay."),
("lexicon","Kurrujuwa remains sentry bird; crocodile warning, riverbank and totem prose closed.","Rule 350: kurrujuwa."),
("lexicon","Mirrku remains kidney bird; anatomical inference and dabu-moiety totem prose closed.","Rule 350: mirrku."),
("lexicon","Baringkan remains an unidentified small bird; guessed species and properties closed.","Rule 350: baringkan."),
("lexicon","Barinkan remains scrub bird; dark-grey/white-breast description closed.","Rule 350: barinkan."),
("lexicon","Bilu-warra remains an exact spoonbill head; component and habitat readings closed.","Rule 350: bilu-warra."),
("lexicon","Dukunjulu remains golden whistler; song, sex, color and habitat closed.","Rule 350: dukunjulu."),
("lexicon","Jiwurrmal remains pheasant cuckoo; alternate-name and record-collapse claims closed.","Rule 350: jiwurrmal."),
("lexicon","Kujuway remains generic pigeon; brown/brown-green descriptions and record identity closed.","Rule 350: kujuway."),
("morphology","All targets remain bare absolutive objects without case or derivation.","Rule 350: target form."),
("morphology","Hyphenated and reduplicated dictionary heads remain indivisible lexical forms.","Rule 350: whole heads."),
("morphology","Ngayu, yundu, nyulu, bula and jana remain bare nominative agents.","Rule 350: pronouns."),
("morphology","Yaluy, yinyay and past nyajin remain fixed and uninflected.","Rule 350: support."),
("syntax","Rows 1-10 use Ngayu TARGET yaluy nyajin.","Rule 350: band 1."),
("syntax","Rows 11-20 use Yundu TARGET yinyay nyajin.","Rule 350: band 2."),
("syntax","Rows 21-30 use Nyulu TARGET yaluy nyajin.","Rule 350: band 3."),
("syntax","Rows 31-40 use Bula TARGET yinyay nyajin.","Rule 350: band 4."),
("syntax","Rows 41-50 use Jana TARGET yaluy nyajin.","Rule 350: band 5."),
("fidelity","English preserves participant, dictionary bird label, location and past seeing only.","Rule 350: fidelity."),
("fidelity","No totem, omen, color, song, behavior, habitat, source, possession or event proposition enters English.","Rule 350: exclusions."),
("register","All rows are neutral elicitation clauses without imported cultural narrative.","Rule 350: register."),
("process","AST proves all 50 rows are direct calls with literal arguments.","Rule 350: authorship."),
("process","Exact field readback precedes acceptance.","Rule 350: readback."),
("process","Whole-corpus lint, duplicate scan and integrity check remain acceptance gates.","Rule 350: integrity."),
("process","Five lenses produce 250 stored decisions rather than a batch-level assertion.","Rule 350: review."),
("process","Thirty-five lessons and eighteen source logs preserve the audit trail.","Rule 350: process storage."),
("process","Export and four living books must be refreshed and live-verified after close.","Rule 350: publication."),
("process","Source and audit artifacts remain on the mounted research drive.","Rule 350: mounted storage."),
("handoff","Rule 351 may use only the proved deictic complement after a fresh re-audit.","Rule 350: handoff."),]
LEX=[
("dakwurr-dakwun","noun",None,"NA","hammer bird","batch-392: exact whole bird head; totem/moiety boundaries."),("kurkunbay","noun",None,"NA","butcherbird","batch-392: alternate-label/call boundaries."),("kurrujuwa","noun",None,"NA","sentry bird","batch-392: warning/crocodile/river/totem boundaries."),("mirrku","noun",None,"NA","kidney bird","batch-392: anatomy/totem boundaries."),("baringkan","noun",None,"NA","small bird","batch-392: unknown species; generic small-bird label."),("barinkan","noun",None,"NA","scrub bird","batch-392: color/anatomy boundaries."),("bilu-warra","noun",None,"NA","spoonbill","batch-392: exact hyphenated bird head."),("dukunjulu","noun",None,"NA","golden whistler","batch-392: bird label only; song/color boundaries."),("jiwurrmal","noun",None,"NA","pheasant cuckoo","batch-392: normalized common label; alternate-name boundary."),("kujuway","noun",None,"NA","pigeon","batch-392: generic pigeon; color boundaries."),
("ngayu","pronoun",None,"NA","first singular nominative","batch-392: bare A."),("yundu","pronoun",None,"NA","second singular nominative","batch-392: bare A."),("nyulu","pronoun",None,"NA","third singular nominative","batch-392: bare A."),("bula","pronoun",None,"NA","third dual nominative","batch-392: bare A."),("jana","pronoun",None,"NA","third plural nominative","batch-392: bare A."),("yaluy","demonstrative",None,"NA","here","batch-392: Yalanji local."),("yinyay","demonstrative",None,"NA","there","batch-392: Yalanji local."),("nyajin","verb-tr","l","NA","see; see.PST","batch-392: 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-392-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()
    if (total,before,wb)!=(19697,19647,133947): raise SystemExit(f"baseline {total}/{before}/{wb}")
    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-392-verify",n,NOW))
    for ph,et,d in [("lint","under-review-lint-green",f"Lint {total}/0; verified {before}."),("review","five-lens-pass","250/250 passed; all bird/source boundaries retained."),("review","review-detail","No totem/omen/color/song/behavior/habitat/source/event or morphological drift remained."),("review","lesson-inventory","35 lessons."),("review","lexeme-source-log","18 logs.")]: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 350 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]
    if (after,wa,ac,rv)!=(19697,134147,50,250): raise SystemExit(f"postcheck {after}/{wa}/{ac}/{rv}")
    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_392.py",artifact_path=LOG,created_at=NOW);db.commit();print(f"closed batch-392: accepted={ac} reviews={rv} lessons=35 logs=18 verified {before}->{after} words {wb}->{wa}");return 0
if __name__=="__main__":raise SystemExit(main())
