From 83beb2bf35e278bfdc8c35d1113dd16bde7a9d43 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sat, 12 Sep 2026 04:47:15 -0400 Subject: [PATCH] admin: surface corr_gate_veto on the correlation-debug endpoint (#115) corr_gate_veto was written to corr_debug but the admin endpoint's whitelist (_call_summary + the summary tally) never surfaced it, so the last commit's whole point -- measuring window #4 instead of guessing -- would have produced nothing to read. Add it to both. Also softened the docstring's remaining overclaim: whether the active-only ctx[recent] limitation explains the 2/24 window-3 misses is unanswered, not confirmed -- read corr_gate_veto next window instead of asserting a guess again. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Tbknwttzou4s46PAykmtix --- drb-c2-core/app/routers/admin.py | 9 +++++++++ drb-c2-core/app/routers/upload.py | 11 ++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drb-c2-core/app/routers/admin.py b/drb-c2-core/app/routers/admin.py index 8c533ae..815b6ee 100644 --- a/drb-c2-core/app/routers/admin.py +++ b/drb-c2-core/app/routers/admin.py @@ -135,6 +135,12 @@ async def debug_correlation( "corr_llm_reasoning": call.get("corr_llm_reasoning"), "corr_llm_action": call.get("corr_llm_action"), "corr_rules_action": call.get("corr_rules_action"), + # server-26#115 — why an llm=orphan/rules=new disagreement escalated + # to tiebreak instead of being gated (see upload.py's + # _call_is_substanceless). Present only on that disagreement shape; + # written here specifically so a live measurement window can read + # the reason instead of reconstructing it by hand from the dump. + "corr_gate_veto": call.get("corr_gate_veto"), } # ── Determine which systems have AI active ──────────────────────────────── @@ -293,6 +299,9 @@ async def debug_correlation( "corr_fit_signal": _tally(c.get("corr_fit_signal") for c in linked), "corr_consensus": _tally(c.get("corr_consensus") for c in linked), "corr_llm_action": _tally(c.get("corr_llm_action") for c in linked), + # server-26#115 — this IS the number the escape-hatch fix exists to + # produce: why each llm=orphan/rules=new call escaped the gate. + "corr_gate_veto": _tally(c.get("corr_gate_veto") for c in linked), # STT coverage: correlation quality is capped by this, so it belongs in # the same view rather than a separate investigation. "linked_calls_with_transcript": with_transcript, diff --git a/drb-c2-core/app/routers/upload.py b/drb-c2-core/app/routers/upload.py index b40a697..a7b2d32 100644 --- a/drb-c2-core/app/routers/upload.py +++ b/drb-c2-core/app/routers/upload.py @@ -150,11 +150,12 @@ def _recent_incident_on_same_talkgroup(ctx: dict) -> bool: fix needs a dedicated Firestore query that is not status/capacity filtered — a new read, out of scope for this pass. - This does NOT explain the 2/24 unexplained gate misses in the window #3 - measurement — re-review found a code-level explanation for both instead - (see `_call_is_substanceless`'s `incident_type`/`reassignment` branch and - the scene-level severity/tags fields the call-doc alone doesn't show), so - this limitation is believed inactive so far, not a live loose end. + Whether this limitation explains the 2/24 unexplained gate misses in the + window #3 measurement is UNANSWERED, not confirmed either way — a prior + pass here claimed a "confirmed explanation" for both that turned out to + be self-contradictory. Read `corr_gate_veto` (written to corr_debug on + every escalation of this exact disagreement shape — see the caller) in + the next measurement window instead of guessing from the raw dump again. # TODO(server-26#115): add a talkgroup-scoped incident lookup (any # status, no capacity filter) if a future measurement window pins a real # gate miss on a resolved/capped same-talkgroup incident.