#!/usr/bin/env python3
"""Close Batch 393 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-393";NOW=dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00","Z");LOG="process_logs/batch-393-complementary-bird-set-e-singular-third-past-perception-after-rule350-process.md";LENSES=("morphology","lexicon","syntax","fidelity","register")
NOTES="COMPLEMENTARY BIRD SET-E SINGULAR/THIRD PAST PERCEPTION AFTER RULE 350 (Rule 351). 50 accepted; corrected target set; exact Batch 392 complements; all source/linter boundaries retained; gap 162->172."
LESSONS=[
("source","Corrected set-E hashes, cards and zero-Patz profile remained exact.","Rule 351: source."),("source","Kurkunbay, kurrujuwa and mirrku remain the linter-clean replacements.","Rule 351: correction."),("process","All 50 candidates were absent and every opposite was Batch 392 at the same sequence.","Rule 351: map."),("process","The singular/third 100-cell paradigm is complete; gap 162->172.","Rule 351: completion."),("process","Jukujuku, dawadawa and jurriyam remain excluded; no linter weakening.","Rule 351: rejected heads."),
("lexicon","Dakwurr-dakwun remains hammer bird; totem/moiety closed.","Rule 351: dakwurr."),("lexicon","Kurkunbay remains butcherbird; alternate label/call/hunting closed.","Rule 351: kurkunbay."),("lexicon","Kurrujuwa remains sentry bird; warning/crocodile/river/totem closed.","Rule 351: kurrujuwa."),("lexicon","Mirrku remains kidney bird; anatomy/totem closed.","Rule 351: mirrku."),("lexicon","Baringkan remains unidentified small bird; taxonomy closed.","Rule 351: baringkan."),("lexicon","Barinkan remains scrub bird; color/anatomy closed.","Rule 351: barinkan."),("lexicon","Bilu-warra remains spoonbill whole head; components/habitat closed.","Rule 351: bilu-warra."),("lexicon","Dukunjulu remains golden whistler; song/color closed.","Rule 351: dukunjulu."),("lexicon","Jiwurrmal remains pheasant cuckoo; alternate naming closed.","Rule 351: jiwurrmal."),("lexicon","Kujuway remains pigeon; color/record identity closed.","Rule 351: kujuway."),
("morphology","All targets remain bare absolutive objects.","Rule 351: O."),("morphology","Lexical hyphenation/reduplication remains exact and nonproductive.","Rule 351: heads."),("morphology","Selected pronouns remain bare nominative agents.","Rule 351: A."),("morphology","Yaluy/yinyay/nyajin remain fixed.","Rule 351: support."),
("syntax","Rows 1-10 use Ngayu TARGET yinyay nyajin.","Rule 351: band1."),("syntax","Rows 11-20 use Yundu TARGET yaluy nyajin.","Rule 351: band2."),("syntax","Rows 21-30 use Nyulu TARGET yinyay nyajin.","Rule 351: band3."),("syntax","Rows 31-40 use Bula TARGET yaluy nyajin.","Rule 351: band4."),("syntax","Rows 41-50 use Jana TARGET yinyay nyajin.","Rule 351: band5."),
("fidelity","English preserves participant, bird label, location and past seeing only.","Rule 351: fidelity."),("fidelity","No cultural/property/behavior/habitat/source/event proposition enters.","Rule 351: exclusions."),("register","All rows remain neutral elicitation clauses.","Rule 351: register."),("process","AST proves 50 direct literal row calls.","Rule 351: authorship."),("process","Exact readback and whole-corpus lint precede close.","Rule 351: verification."),("process","Five lenses produce 250 stored decisions.","Rule 351: reviews."),("process","Thirty-five lessons and eighteen logs persist process.","Rule 351: storage."),("process","Export and four books refresh after acceptance.","Rule 351: publication."),("process","All artifacts remain on mounted research storage.","Rule 351: mounted."),("handoff","Rule 352 requires fresh non-singular participant and matrix audit.","Rule 351: handoff."),("handoff","Do not reuse completed singular/third set-E cells.","Rule 351: no reuse."),]
LEX=[("dakwurr-dakwun","hammer bird","whole head; totem closed"),("kurkunbay","butcherbird","alternate/call closed"),("kurrujuwa","sentry bird","warning/totem closed"),("mirrku","kidney bird","anatomy/totem closed"),("baringkan","small bird","taxonomy closed"),("barinkan","scrub bird","color closed"),("bilu-warra","spoonbill","whole head"),("dukunjulu","golden whistler","song/color closed"),("jiwurrmal","pheasant cuckoo","alternate name closed"),("kujuway","pigeon","color closed"),("ngayu","first singular nominative","bare A"),("yundu","second singular nominative","bare A"),("nyulu","third singular nominative","bare A"),("bula","third dual nominative","bare A"),("jana","third plural nominative","bare A"),("yaluy","here","Yalanji local"),("yinyay","there","Yalanji local"),("nyajin","see; see.PST","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];c=db.execute("SELECT COUNT(*),COUNT(*) FILTER(WHERE status='under_review') FROM sentences WHERE batch_id=?",(bid,)).fetchone()
    if c!=(50,50) or db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN(SELECT id FROM sentences WHERE batch_id=?)",(bid,)).fetchone()[0]:raise SystemExit(f"precheck {c}")
    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)!=(19747,19697,134147):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,g,n in LEX:
        p="pronoun" if h in {"ngayu","yundu","nyulu","bula","jana"} else "demonstrative" if h in {"yaluy","yinyay"} else "verb-tr" if h=="nyajin" else "noun";conj="l" if h=="nyajin" else None;senses=json.dumps([{"sense":g,"definition":f"batch-393: {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,conj,"NA",g,senses,att,None,None,None,n,None,0,"verified",NOW,f"batch-393: {n}",LABEL));db.execute("INSERT INTO lexeme_log(headword,action,detail,created_at) VALUES(?,?,?,?)",(h,"batch-393-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; corrected target/linter boundaries retained."),("review","review-detail","No cultural/property/behavior/habitat/source/event drift."),("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 351 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();rv=db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN(SELECT id FROM sentences WHERE batch_id=?)",(bid,)).fetchone()[0]
    if (after,wa,rv)!=(19747,134347,250):raise SystemExit(f"post {after}/{wa}/{rv}")
    record_event(db,batch_label=LABEL,phase="close",event_type="close-success",actor="codex",detail=f"accepted=50 reviews=250 lessons=35 logs=18 verified {before}->{after} words {wb}->{wa}.",command="python3 batch_scripts/close_393.py",artifact_path=LOG,created_at=NOW);db.commit();print(f"closed batch-393: verified {before}->{after} words {wb}->{wa}");return 0
if __name__=="__main__":raise SystemExit(main())
