fix: audit batch 2 visibility (I-1 shadow dropped rows, I-2 partial policy failure, I-5 failure policy table)
- I-1: evaluate_shadow counts dropped rows by cause (unparseable / unknown_tier / explicit / duplicate) with the identity audit_rows_total == routed_task_n + dropped_total; the shadow endpoint passes created_at/id through and reports audit totals next to sample counts. - I-2 (option b): parse logs a warning naming each tier dropped for an invalid mode/requires; load attaches partial_failures to the policy; the worker records a task-level worker_routing_policy_load_failed audit for partial failures (same action as full failure, detail says failure=partial); the shadow report carries the policy fingerprint, dropped tiers and partial failures (no task context on that path, so no task-level audit there). - I-5: coordinator gate audit lookup failure now raises _CoordGateLookupFailed and the merge converts it to escalate (no requeue, no round consumed); persisted-model lookup failure and required-tools check failure each write a named audit row before falling through to dispatch/allow; audit-loop evaluate failure stays escalate (regression test). - Test fixtures: coordinator suites build audit_log via AuthStore as production's first real audit does, since their stubbed _audit never creates the table.
This commit is contained in:
1 parent
44e0f4f1d6
commit
a7f77c8a98
7 files changed
+648
-40
No files matched your search
+31
-2
@@ -4212,7 +4212,7 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
conn = self._db()
|
||||
try:
|
||||
audit_rows = conn.execute(
|
||||
"SELECT detail FROM audit_log"
|
||||
"SELECT detail, created_at, id FROM audit_log"
|
||||
" WHERE project_id = ? AND action = 'worker_routing'"
|
||||
" ORDER BY created_at, id LIMIT 10001",
|
||||
(project_id,),
|
||||
@@ -4262,8 +4262,16 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
entry["unpriced_tokens"] += int(row["total_tokens"] or 0)
|
||||
else:
|
||||
entry["usd_micros"] += int(row["usd_micros"])
|
||||
# Audit batch 2 (I-1): pass created_at/id through instead
|
||||
# of stripping them — evaluate_shadow sorts by them for
|
||||
# dedup, and the fetched rows were already ordered this
|
||||
# way; stripping forced the sort to rely on input order.
|
||||
# The report now carries audit_rows_total (from evaluate)
|
||||
# next to routed_task_n, reconciled by dropped_rows.
|
||||
report = _evaluate_shadow(
|
||||
[{"detail": row["detail"]} for row in audit_rows],
|
||||
[{"detail": row["detail"],
|
||||
"created_at": row["created_at"],
|
||||
"id": row["id"]} for row in audit_rows],
|
||||
tasks_by_id, costs_by_task, explicit_n=explicit_n)
|
||||
report["project_id"] = project_id
|
||||
report["truncated"] = truncated
|
||||
@@ -4360,6 +4368,27 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
report["recommendation"] += (
|
||||
" Note: audit rows were truncated to 10000, so"
|
||||
" this report is based on a partial sample.")
|
||||
# Audit batch 2 (I-2): the currently loaded policy, as the
|
||||
# report sees it — fingerprint plus any tiers dropped by
|
||||
# fail-closed parsing and any source-level partial
|
||||
# failures. This endpoint has no task context, so it
|
||||
# cannot emit a task-level audit; that visibility lives
|
||||
# on the worker dispatch path (see worker.py run_one).
|
||||
# When no usable policy loads, status is "unavailable"
|
||||
# and dropped tiers are unknown (not empty-by-claim).
|
||||
if _policy is None:
|
||||
report["routing_policy"] = {
|
||||
"status": "unavailable", "fingerprint": None,
|
||||
"dropped_tiers": [], "partial_failures": []}
|
||||
else:
|
||||
_dropped = list(getattr(_policy, "dropped_tiers", []) or [])
|
||||
_partial = list(getattr(_policy, "partial_failures", [])
|
||||
or [])
|
||||
report["routing_policy"] = {
|
||||
"status": "partial" if (_dropped or _partial) else "ok",
|
||||
"fingerprint": _policy.content_fingerprint(),
|
||||
"dropped_tiers": _dropped,
|
||||
"partial_failures": _partial}
|
||||
self._send_json(200, report)
|
||||
|
||||
def _task_cost(self, user_id, task_id):
|
||||
|
||||
Reference in new issue
Block a user