feat: add BLIF Flow v2 authoritative operational adapter
This commit is contained in:
95
scripts/simulate_blif_flow_v2_authoritative.py
Normal file
95
scripts/simulate_blif_flow_v2_authoritative.py
Normal file
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read-only audit of the exact Operations boundary in V1 and authoritative V2."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.config import settings
|
||||
from app.operations_service import get_operations_summary
|
||||
|
||||
JSON_PATH = Path("/tmp/blif_flow_v2_authoritative_simulation.json")
|
||||
TEXT_PATH = Path("/tmp/blif_flow_v2_authoritative_operations.txt")
|
||||
CURRENT = {"do_now", "review", "blocked", "exception"}
|
||||
|
||||
|
||||
def _all(summary):
|
||||
return list(summary.get("work_items") or []) + list(summary.get("waiting_items") or []) + list(summary.get("backlog_items") or []) + list(summary.get("not_current_items") or [])
|
||||
|
||||
|
||||
def _metrics(summary):
|
||||
items = _all(summary)
|
||||
queues = [str(item.get("operational_queue") or "") for item in items]
|
||||
return {
|
||||
"current": sum(queue in CURRENT for queue in queues),
|
||||
"do_now": queues.count("do_now"), "review": queues.count("review"),
|
||||
"waiting": queues.count("waiting"), "backlog": queues.count("backlog"),
|
||||
"not_current": queues.count("not_current"),
|
||||
}
|
||||
|
||||
|
||||
def _identity(item):
|
||||
return str(item.get("canonical_opportunity_id") or item.get("opportunity_id") or item.get("process_key") or item.get("work_item_key"))
|
||||
|
||||
|
||||
def _classification(old, new):
|
||||
if new is not None:
|
||||
return "REPLACED_BY_CORRECT_ACTION"
|
||||
decision = old.get("decision") or {}
|
||||
code = str(decision.get("reason_code") or old.get("eligibility_reason_code") or "").upper()
|
||||
if "DUPLICATE" in code:
|
||||
return "DUPLICATE_SUPPRESSED"
|
||||
if "SATISF" in code or "ANSWERED" in code:
|
||||
return "SATISFIED"
|
||||
if "SUPERSE" in code or "TERMINAL" in code:
|
||||
return "SUPERSEDED"
|
||||
if "PREMATURE" in code:
|
||||
return "PREMATURE_REMOVED"
|
||||
if old.get("source") in {"task", "opportunity"}:
|
||||
return "LEGACY_ONLY"
|
||||
return "UNSAFE_FALSE_NEGATIVE"
|
||||
|
||||
|
||||
def simulate():
|
||||
original = settings.blif_flow_v2_mode
|
||||
try:
|
||||
settings.blif_flow_v2_mode = "off"
|
||||
v1 = get_operations_summary(limit=200)
|
||||
settings.blif_flow_v2_mode = "authoritative"
|
||||
v2 = get_operations_summary(limit=200)
|
||||
finally:
|
||||
settings.blif_flow_v2_mode = original
|
||||
v1_current = {_identity(item): item for item in _all(v1) if item.get("operational_queue") in CURRENT}
|
||||
v2_current = {_identity(item): item for item in _all(v2) if item.get("operational_queue") in CURRENT}
|
||||
removed = []
|
||||
for key, old in v1_current.items():
|
||||
new = v2_current.get(key)
|
||||
if new is None or new.get("action_code") != old.get("action_code"):
|
||||
removed.append({"identity": key, "v1_action": old.get("action_code"),
|
||||
"v2_action": (new or {}).get("action_code"),
|
||||
"classification": _classification(old, new)})
|
||||
added = [{"identity": key, "action": item.get("action_code")}
|
||||
for key, item in v2_current.items()
|
||||
if key not in v1_current or v1_current[key].get("action_code") != item.get("action_code")]
|
||||
result = {"v1": _metrics(v1), "authoritative_v2": _metrics(v2),
|
||||
"cards_removed": removed, "cards_added_or_replaced": added,
|
||||
"unsafe_false_negatives": sum(row["classification"] == "UNSAFE_FALSE_NEGATIVE" for row in removed)}
|
||||
JSON_PATH.write_text(json.dumps(result, indent=2, ensure_ascii=False, default=str) + "\n")
|
||||
lines = ["BLIF Flow v2 authoritative Operations simulation", "", f"V1: {result['v1']}",
|
||||
f"Authoritative V2: {result['authoritative_v2']}",
|
||||
f"Cards removed: {len(removed)}", f"Cards added/replaced: {len(added)}",
|
||||
f"UNSAFE_FALSE_NEGATIVE: {result['unsafe_false_negatives']}"]
|
||||
TEXT_PATH.write_text("\n".join(lines) + "\n")
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
audit = simulate()
|
||||
print(json.dumps(audit, indent=2, ensure_ascii=False, default=str))
|
||||
if audit["unsafe_false_negatives"]:
|
||||
raise SystemExit(2)
|
||||
Reference in New Issue
Block a user