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:
host committed 2026-11-04 18:02:22 +00:00
1 parent 44e0f4f1d6
commit a7f77c8a98
7 files changed
+648 -40

No files matched your search

+16 -1
View File
@@ -28,11 +28,13 @@ from types import SimpleNamespace
try:
from cloud.audit_loop import Finding
from cloud.auth import AuthStore
from cloud.server import init_db
from cloud.worker import Worker
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.audit_loop import Finding
from cloud.auth import AuthStore
from cloud.server import init_db
from cloud.worker import Worker
@@ -100,6 +102,14 @@ class CoordAuditTestCase(unittest.TestCase):
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
init_db(self.db_path)
# Audit batch 2 (I-5): 合流的门拒绝审计查询失败时走
# escalate,不再回落空集。生产里 audit_log 由首次
# 真实 _audit 经 AuthStore 建好,合流时表一定存在;
# 本文件把 _audit 桩成列表,表永远不会出现,所以
# 在夹具里按生产实际补建表——否则所有合流测试都会
# 因缺表改走 escalate,与被测行为无关。
_store = AuthStore(self.db_path)
_store.conn.close()
self.env = {"XCM_API_KEY": "test-placeholder"}
self.audits = []
self.worker = self._worker()
@@ -399,8 +409,13 @@ class CoordAuditTestCase(unittest.TestCase):
"CREATE TABLE IF NOT EXISTS audit_log(user_id TEXT,"
" project_id TEXT, action TEXT, detail TEXT,"
" created_at DOUBLE PRECISION)")
# Audit batch 2 (I-5):setUp 已按生产实际经 AuthStore
# 建好含 id 列的 audit_log 表,INSERT 必须显式列名,
# 否则在 6 列的生产表上会因 5 个值报错;显式列名在
# 旧 5 列手工表上同样成立。
conn.execute(
"INSERT INTO audit_log VALUES('system', 'p1',"
"INSERT INTO audit_log(user_id, project_id, action,"
" detail, created_at) VALUES('system', 'p1',"
" 'worker_tool_gate_rejected',"
" 'C1: agent 声明必需工具缺失:websearch', ?)",
(time.time(),))