#!/usr/bin/env python3 """ Slice 21 emit: deterministic execution-frame guard — data files for the Go harness ================================================================================== Slice 18 showed the sparse lexical gate owns the aggregate boundary (PR-AUC 0.838, strict operating point at threshold 0.715 with P>=0.95 | R=0.264) and slice 19/20 showed learning heads collapse on capability-question LOFO. Slice 21 tests the deterministic alternative: a rule engine over existing parsers that decides execution eligibility as a three-way gate (permissive / blocked / ambiguous), never itself routing. This script only repackages the frozen dev pool for the Go harness. It reuses slice 18's feature builders and grouped-CV and slice 19's pair builder verbatim, so the numbers the Go side reports are the same populations the accepts measured. It writes: /tmp/mvn-s21/pool.json dev rows: idx, text, n_text, route, y, tags, cv_fold, split_group, source_id, family /tmp/mvn-s21/pairs.json capability-vs-action pairs (slice-19 builder) /tmp/mvn-s21/sparse_oof.json slice-18 "both" grouped-CV OOF proba per row (for the §17 guard+sparse composition) No training happens here and no label is changed. The guard itself is Go. """ import json import os import re import sys import numpy as np HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) import slice18_sparse # noqa: E402 import slice19_main # noqa: E402 OUT_DIR = "/tmp/mvn-s21" SPARSE_THRESHOLD = 0.715 # slice-18 §4 strict-operating-point (P>=0.95 best recall) def main(): # Population = the exact slice-20 dev pool (s19.load_dev): every dev_pool # row, fast-path included. The guard is evaluated on what slice 20 measured. meta, examples = slice18_sparse.load_data() dev = slice18_sparse.filter_dev_pool(examples) print(f"dev pool (all dev_pool rows): {len(dev)} rows") print(f"corpus meta: {meta.get('dev_count', '?')} dev rows declared, " f"{meta.get('route_counts', {}).get('action', '?')} action declared") n_texts = [slice18_sparse.normalize_match_text(e["text"]) for e in dev] rows = [] by_route = {} by_family = {} for i, (e, nt) in enumerate(zip(dev, n_texts)): tags = sorted(set(e.get("tags", []))) route = e["route"] fam = slice19_main.family_of(set(tags)) by_route[route] = by_route.get(route, 0) + 1 by_family[fam] = by_family.get(fam, 0) + 1 rows.append({ "idx": i, "text": e["text"], "n_text": nt, "route": route, "y": 1 if route == "action" else 0, "tags": tags, "cv_fold": e["cv_fold"], "split_group": e["split_group"], "source_id": e["source_id"], }) print("routes:", by_route) print("families:", by_family) # ── pairs (slice-19 builder, exact population) ───────────────────────── ldev = [{ "text_orig": nt, "route": r["route"], "y": r["y"], "cv_fold": r["cv_fold"], "tags": set(r["tags"]), "source_id": r["source_id"], } for r, nt in zip(rows, n_texts)] pairs = slice19_main.build_pairs(ldev, n_texts) print(f"pairs: {len(pairs)}") # ── slice-18 "both" grouped-CV OOF proba, aligned to row index ──────── y = [1 if r["route"] == "action" else 0 for r in rows] folds = [r["cv_fold"] for r in rows] X, _vec = slice18_sparse.build_features(n_texts, "both") print(f"sparse 'both' X: {X.shape}") yb = np.array(y) folds_arr = np.array(folds) idx_proba = {} for te_fold in sorted(set(folds)): tr = folds_arr != te_fold te = folds_arr == te_fold clf = slice18_sparse.LogisticRegression( C=1.0, max_iter=2000, solver="lbfgs", random_state=42) clf.fit(X[tr], yb[tr]) p = clf.predict_proba(X[te])[:, 1] te_idx = np.where(te)[0] for k, i in enumerate(te_idx): idx_proba[int(i)] = float(p[k]) assert len(idx_proba) == len(rows) sparse_oof = [{"idx": i, "proba": idx_proba[i]} for i in range(len(rows))] pred = [1 if idx_proba[i] >= 0.5 else 0 for i in range(len(rows))] tp = sum(1 for i in range(len(rows)) if y[i] == 1 and pred[i] == 1) fp = sum(1 for i in range(len(rows)) if y[i] == 0 and pred[i] == 1) fn = sum(1 for i in range(len(rows)) if y[i] == 1 and pred[i] == 0) print(f"sparse both OOF @0.5: P={tp/max(tp+fp,1):.3f} R={tp/max(tp+fn,1):.3f} " f"FA={fp} ({fp/len(rows):.4f})") pred21 = [1 if idx_proba[i] >= SPARSE_THRESHOLD else 0 for i in range(len(rows))] tp = sum(1 for i in range(len(rows)) if y[i] == 1 and pred21[i] == 1) fp = sum(1 for i in range(len(rows)) if y[i] == 0 and pred21[i] == 1) fn = sum(1 for i in range(len(rows)) if y[i] == 1 and pred21[i] == 0) print(f"sparse both OOF @{SPARSE_THRESHOLD}: P={tp/max(tp+fp,1):.3f} " f"R={tp/max(tp+fn,1):.3f} FA={fp} ({fp/len(rows):.4f})") os.makedirs(OUT_DIR, exist_ok=True) with open(os.path.join(OUT_DIR, "pool.json"), "w") as f: json.dump(rows, f, ensure_ascii=False, indent=1) with open(os.path.join(OUT_DIR, "pairs.json"), "w") as f: json.dump([[c, a] for c, a in pairs], f) with open(os.path.join(OUT_DIR, "sparse_oof.json"), "w") as f: json.dump(sparse_oof, f) print(f"wrote {OUT_DIR}/{{pool,pairs,sparse_oof}}.json") if __name__ == "__main__": main()