Audit batch 4: I-6 whitespace audit format, I-7 explicit non-sub2api capability gate, I-8 coord gate attribution, I-10 checked gates, I-11 gate-without-finding escalate, I-13 structured-first classification

This commit is contained in:
host committed 2026-11-04 18:02:23 +00:00
1 parent 06e4e16bbb
commit cdac8039b3
5 files changed
+959 -47

No files matched your search

+673
View File
@@ -0,0 +1,673 @@
"""审计修复批 4 测试:I-6、I-7、I-8、I-10、I-11、I-13。
只用注入的假 runner 与 init_db 的本地库,不跑真实模型回合、
不联网、不登录任何服务;env 里的值只是占位用的假名。
行为变更锁定(本批有意改语义,测试即契约):
- I-6:策略写入路径(能力目录键、档模型、fallback_models)
含空白的一律丢弃;影子行 chosen(行尾字段)含空白可解析;
persisted 行模型含空白按鍵提取、复核不再默认显式 bypass。
- I-7:显式 + 非 sub2api 网关在 active 下已知不满足时从
“直接派发”变为“拦”(同款审计 action、error 文案、
blocked 标志);unknown-only 与 shadow 仍只审计。
- I-8:缺 key / 仓库门审计 detail 带任务 id 前缀,补进门拒绝
查询集合;仓库解析失败子任务归 gate_rejected、父走升级。
- I-10:gate_results 只列本轮实际跑过的门;显式与政策任务
两轮往返均不误触发 gate_missing。
- I-11:失败门 + 空 findings 时 evaluate 返回 escalate
(gate_failed_without_finding),不再抛 ValueError。
- I-13:分类优先读结构化审计行,子串只作最后回落且回落
命中时记 worker_coord_gate_classified_by_marker 审计行。
"""
import json
import logging
import os
import sqlite3
import sys
import tempfile
import time
import unittest
from pathlib import Path
from types import SimpleNamespace
try:
from cloud.audit_loop import Finding, evaluate
from cloud.auth import AuthStore
from cloud.models import (
RoutingPolicy, evaluate_capability, parse_model_capabilities,
parse_routing_policy)
from cloud.routing_shadow import (
evaluate_shadow, parse_worker_routing_detail)
from cloud.server import init_db
from cloud.worker import Worker, _GATE_ERROR_MARKERS
except ImportError: # discover -s cloud/tests 时把项目根目录补进 sys.path
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.audit_loop import Finding, evaluate
from cloud.auth import AuthStore
from cloud.models import (
RoutingPolicy, evaluate_capability, parse_model_capabilities,
parse_routing_policy)
from cloud.routing_shadow import (
evaluate_shadow, parse_worker_routing_detail)
from cloud.server import init_db
from cloud.worker import Worker, _GATE_ERROR_MARKERS
def _insert_task(db_path, task_id, model=None, status="queued",
project_id="p1", audit_loop=0, parent=None, part=None,
error=None, result=None):
now = time.time()
conn = sqlite3.connect(db_path)
try:
conn.execute(
"INSERT INTO tasks(id, project_id, prompt, status,"
" idempotency_key, depends_on, priority, created_by,"
" created_at, updated_at, last_heartbeat, audit_loop,"
" parent_task_id, coordinator_part, result_text, error_text,"
" model)"
" VALUES(?, ?, 'do work', ?, NULL, '[]', 0, 'tester',"
" ?, ?, NULL, ?, ?, ?, ?, ?, ?)",
(task_id, project_id, status, now, now, audit_loop,
parent, part, result, error, model),
)
conn.commit()
finally:
conn.close()
def _set_started_at(db_path, task_id, started_at):
conn = sqlite3.connect(db_path)
try:
conn.execute(
"UPDATE tasks SET started_at = ? WHERE id = ?",
(started_at, task_id))
conn.commit()
finally:
conn.close()
def _insert_audit(db_path, project_id, action, detail, created_at=None):
conn = sqlite3.connect(db_path)
try:
conn.execute(
"INSERT INTO audit_log(user_id, project_id, action, detail,"
" created_at) VALUES(NULL, ?, ?, ?, ?)",
(project_id, action, detail,
created_at if created_at is not None else time.time()))
conn.commit()
finally:
conn.close()
def _audit_actions(db_path, project_id="p1"):
store = AuthStore(db_path)
try:
return [(e["action"], e["detail"]) for e in store.list_audit(
project_id)]
finally:
store.conn.close()
def _completed_runner(cmd):
name = cmd[cmd.index("--name") + 1] if "dispatch" in cmd else "t"
return SimpleNamespace(
returncode=0,
stdout=json.dumps({"task": name, "status": "completed",
"text": "done"}),
stderr="")
def _failing_runner(cmd):
name = cmd[cmd.index("--name") + 1] if "dispatch" in cmd else "t"
return SimpleNamespace(
returncode=0,
stdout=json.dumps({"task": name, "status": "failed",
"text": "boom"}),
stderr="")
def _worker(db_path, policy_doc=None, env=None, gateway=None,
runner=None):
calls = []
def recording(cmd):
calls.append(list(cmd))
return (runner or _completed_runner)(cmd)
worker = Worker(db_path, "/fake/host.py", runner=recording,
env=env or {"XCM_API_KEY": "test-placeholder"},
routing_policy=policy_doc)
if gateway is not None:
worker._ensure_gateway = lambda task: gateway
worker._sub2api_default_model = lambda e: "gateway-model"
worker._calls = calls
return worker
def _state_row(db_path, task_id):
conn = sqlite3.connect(db_path)
try:
return conn.execute(
"SELECT round, passed_gates, last_decision"
" FROM task_audit_loop_state WHERE task_id = ?",
(task_id,)).fetchone()
finally:
conn.close()
def _task_row(db_path, task_id):
conn = sqlite3.connect(db_path)
try:
return conn.execute(
"SELECT status, error_text FROM tasks WHERE id = ?",
(task_id,)).fetchone()
finally:
conn.close()
class WhitespacePolicyTestCase(unittest.TestCase):
"""I-6:策略写入路径白名单。"""
def test_whitespace_tier_model_dropped_and_named(self):
with self.assertLogs("cloud.models", level="WARNING") as logs:
policy = parse_routing_policy({
"mode": "active",
"tiers": {"plan": "my model", "default": "ok-model"},
})
self.assertIsNotNone(policy)
self.assertEqual(policy.tiers, {"default": "ok-model"})
self.assertEqual(policy.dropped_tiers, ["plan"])
self.assertTrue(any("plan" in line for line in logs.output))
def test_whitespace_capability_key_dropped_is_unknown(self):
caps = parse_model_capabilities({
"my model": {"tool_calling": True},
"ok": {"tool_calling": True},
})
self.assertEqual(caps, {"ok": {"tool_calling": True}})
verdict = evaluate_capability(caps, "my model",
{"tool_calling": True})
self.assertFalse(verdict.ok)
self.assertTrue(verdict.unknown)
self.assertEqual(verdict.unknown_missing, ["tool_calling"])
self.assertEqual(verdict.known_missing, [])
def test_whitespace_fallback_and_direct_tiers_dropped(self):
policy = RoutingPolicy(
tiers={"default": "my model", "plan": "ok"},
fallback_models=["my model", "ok"])
self.assertEqual(policy.tiers, {"plan": "ok"})
self.assertEqual(policy.fallback_models, ["ok"])
def test_shadow_row_with_whitespace_chosen_parses(self):
parsed = parse_worker_routing_detail(
"T9 tier=default source=policy applied=true"
" suggested=- chosen=my model")
self.assertIsNotNone(parsed)
self.assertEqual(parsed["chosen_model"], "my model")
# 进样本计数,不再整行丢弃。
report = evaluate_shadow([
{"detail": "T9 tier=default source=policy applied=true"
" suggested=- chosen=my model",
"created_at": 1000.0}], {}, {})
self.assertEqual(report["routed_task_n"], 1)
self.assertEqual(report["dropped_rows"]["unparseable"], 0)
def test_legacy_suggested_whitespace_still_counted_not_lost(self):
# suggested 是中间字段,仍要求无空白:legacy 含空白
# suggested 的行解析失败,但按批 2 计入丢行桶,不静默消失。
parsed = parse_worker_routing_detail(
"T9 tier=default source=policy applied=true"
" suggested=my model chosen=ok")
self.assertIsNone(parsed)
report = evaluate_shadow([
{"detail": "T9 tier=default source=policy applied=true"
" suggested=my model chosen=ok",
"created_at": 1000.0}], {}, {})
self.assertEqual(report["routed_task_n"], 0)
self.assertEqual(report["dropped_rows"]["unparseable"], 1)
class PersistedWhitespaceModelTestCase(unittest.TestCase):
"""I-6:含空白写回模型的复核不再被 bypass。"""
def setUp(self):
self._old = os.environ.get("CODEX_PUSH_ALLOW_PRIVATE_TARGETS")
os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = "1"
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
init_db(self.db_path)
store = AuthStore(self.db_path)
store.conn.close()
def tearDown(self):
if self._old is None:
os.environ.pop("CODEX_PUSH_ALLOW_PRIVATE_TARGETS", None)
else:
os.environ[CODEX_PUSH_ALLOW_PRIVATE_TARGETS] = self._old
self.tmp.cleanup()
def _policy(self):
return {
"mode": "active",
"tiers": {"default": {"model": "policy-model",
"requires": {"tool_calling": True}}},
"model_capabilities": {
"my model": {"tool_calling": False},
},
}
def test_persisted_whitespace_model_matches_and_is_rechecked(self):
_insert_audit(self.db_path, "p1", "worker_model_persisted",
"T1 tier=default model=my model")
worker = _worker(self.db_path, self._policy())
audits = []
worker._audit = lambda p, a, d="": audits.append((p, a, d))
# 旧 split()[2] 定位置取在这里永远匹配不上(model
# 含空白被切成两段);新按键提取必须认出写回模型。
self.assertTrue(worker._is_persisted_model(
"T1", "p1", "my model"))
# 复核必须真正跑起来、不再默认显式 bypass:含空白的
# 模型名进不了能力目录(I-6 白名单已丢弃该键),故
# 复核结果是 unknown——按 fix5 分档只审计不拦;断言
# 点是复核确实跑了(有 recheck_unknown 审计行),
# 而不是悄悄按显式放行(那时连审计行都没有)。
# active 下 known 非空才拦,空白模型永远到不了
# known(目录里没有它的条目),这是白名单的直接
# 推论,不是复核 bypass。
error = worker._recheck_persisted_model({
"id": "T1", "project_id": "p1", "model": "my model"})
self.assertIsNone(error)
self.assertTrue([a for _p, a, _d in audits
if a == "worker_capability_recheck_unknown"])
self.assertTrue(any("my model" in d for _p, a, d in audits
if a == "worker_capability_recheck_unknown"))
def test_persisted_whitespace_model_mismatch_is_not_persisted(self):
_insert_audit(self.db_path, "p1", "worker_model_persisted",
"T1 tier=default model=my model")
worker = _worker(self.db_path, self._policy())
self.assertFalse(worker._is_persisted_model(
"T1", "p1", "other model"))
self.assertFalse(worker._is_persisted_model(
"T2", "p1", "my model"))
class ExplicitNonSub2apiGateTestCase(unittest.TestCase):
"""I-7:显式 + 非 sub2api 网关的能力门。"""
def setUp(self):
self._old = os.environ.get("CODEX_PUSH_ALLOW_PRIVATE_TARGETS")
os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = "1"
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
init_db(self.db_path)
def tearDown(self):
if self._old is None:
os.environ.pop("CODEX_PUSH_ALLOW_PRIVATE_TARGETS", None)
else:
os.environ[CODEX_PUSH_ALLOW_PRIVATE_TARGETS] = self._old
self.tmp.cleanup()
def _policy(self, mode, capabilities):
return {
"mode": mode,
"tiers": {"default": {"model": "policy-model",
"requires": {"tool_calling": True}}},
"model_capabilities": capabilities,
}
def test_known_missing_explicit_non_sub2api_is_blocked(self):
# 行为变更:旧行为是直接派发(能力门只在 sub2api
# 时跑),新行为是 active 下已知不满足即拦。
_insert_task(self.db_path, "E1", model="explicit-model")
worker = _worker(self.db_path, self._policy("active", {
"explicit-model": {"tool_calling": False},
}), gateway=("other-gw", {}))
result = worker.run_one()
self.assertEqual(result["status"], "failed")
self.assertFalse(worker._calls)
self.assertIn("lacks capability", result["error"])
actions = _audit_actions(self.db_path)
explicit = [d for a, d in actions
if a == "worker_capability_gateway_explicit"]
self.assertTrue(explicit)
self.assertIn("blocked=True", explicit[0])
self.assertTrue([a for a, _ in actions
if a == "worker_capability_gate_rejected"])
def test_unknown_explicit_non_sub2api_is_audited_not_blocked(self):
_insert_task(self.db_path, "E1", model="explicit-model")
worker = _worker(self.db_path, self._policy("active", {}),
gateway=("other-gw", {}))
result = worker.run_one()
self.assertEqual(result["status"], "completed")
self.assertTrue(worker._calls)
actions = _audit_actions(self.db_path)
explicit = [d for a, d in actions
if a == "worker_capability_gateway_explicit"]
self.assertTrue(explicit)
self.assertIn("blocked=False", explicit[0])
self.assertFalse([a for a, _ in actions
if a == "worker_capability_gate_rejected"])
def test_shadow_explicit_non_sub2api_is_audited_not_blocked(self):
_insert_task(self.db_path, "E1", model="explicit-model")
worker = _worker(self.db_path, self._policy("shadow", {
"explicit-model": {"tool_calling": False},
}), gateway=("other-gw", {}))
result = worker.run_one()
self.assertEqual(result["status"], "completed")
self.assertTrue(worker._calls)
actions = _audit_actions(self.db_path)
explicit = [d for a, d in actions
if a == "worker_capability_gateway_explicit"]
self.assertTrue(explicit)
self.assertIn("blocked=False", explicit[0])
def test_nonexplicit_non_sub2api_has_no_gateway_gate(self):
# 非显式 + 非 sub2api 根本不进网关能力块:模型为空,
# 后续走 decide_routing,不存在独立门误拦。
_insert_task(self.db_path, "E1")
worker = _worker(self.db_path, self._policy("active", {
"policy-model": {"tool_calling": True},
}), gateway=("other-gw", {}))
result = worker.run_one()
self.assertEqual(result["status"], "completed")
actions = _audit_actions(self.db_path)
self.assertFalse([a for a, _ in actions
if a == "worker_capability_gateway_explicit"])
def test_explicit_no_gateway_keeps_explicit_semantics(self):
# 边界:显式 + 无网关(provider 为 None)不进网关
# 能力块——它根本没走网关,显式语义不变:已知不满足
# 也不在这里拦(写回模型仍走后续复核,用户显式仍
# 不被复核,fixes5 契约)。无网关显式任务被网关块
# 拦住会是语义倒退,不是 I-7 的意图。
_insert_task(self.db_path, "E1", model="explicit-model")
worker = _worker(self.db_path, self._policy("active", {
"explicit-model": {"tool_calling": False},
}), gateway=(None, {}))
result = worker.run_one()
self.assertEqual(result["status"], "completed")
self.assertTrue(worker._calls)
actions = _audit_actions(self.db_path)
self.assertFalse([a for a, _ in actions
if a == "worker_capability_gateway_explicit"])
self.assertFalse([a for a, _ in actions
if a == "worker_capability_gate_rejected"])
def test_explicit_sub2api_path_unchanged(self):
# sub2api 显式路径与原来逐字一致:同一审计 action、
# 同一 error 文案、同一 blocked 标志。
_insert_task(self.db_path, "E1", model="explicit-model")
worker = _worker(self.db_path, self._policy("active", {
"explicit-model": {"tool_calling": False},
}), gateway=("sub2api", {}))
result = worker.run_one()
self.assertEqual(result["status"], "failed")
self.assertFalse(worker._calls)
self.assertIn("lacks capability", result["error"])
actions = _audit_actions(self.db_path)
explicit = [d for a, d in actions
if a == "worker_capability_gateway_explicit"]
self.assertTrue(explicit)
self.assertIn("blocked=True", explicit[0])
class CoordGateAttributionTestCase(unittest.TestCase):
"""I-8 + I-13:协调员门拒绝归因与分类回落可见性。"""
def setUp(self):
self._old = os.environ.get("CODEX_PUSH_ALLOW_PRIVATE_TARGETS")
os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = "1"
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
init_db(self.db_path)
store = AuthStore(self.db_path)
store.conn.close()
self.audits = []
self.worker = Worker(self.db_path, "/fake/host.py",
runner=_completed_runner,
env={"XCM_API_KEY": "test-placeholder"},
max_retries=0, retry_backoff_seconds=0)
self.worker._audit = lambda p, a, d="": self.audits.append(
(p, a, d))
conn = sqlite3.connect(self.db_path)
try:
self.worker._ensure_result_columns(conn)
conn.commit()
finally:
conn.close()
def tearDown(self):
if self._old is None:
os.environ.pop("CODEX_PUSH_ALLOW_PRIVATE_TARGETS", None)
else:
os.environ[CODEX_PUSH_ALLOW_PRIVATE_TARGETS] = self._old
self.tmp.cleanup()
def _merge(self, child_error, audit_rows=()):
now = time.time()
_insert_task(self.db_path, "P1", status="running", audit_loop=1,
parent=None)
_insert_task(self.db_path, "C1", status="failed", parent="P1",
part="a", error=child_error)
_set_started_at(self.db_path, "C1", now - 100)
for action, detail in audit_rows:
_insert_audit(self.db_path, "p1", action, detail,
created_at=now)
out = self.worker._coord_audit_loop_merge(
{"id": "P1", "project_id": "p1", "audit_loop": 1},
[{"name": "a", "prompt": "do a"}],
[{"id": "C1", "name": "a"}])
return out
def test_repo_failure_child_is_gate_rejected_not_execution(self):
out = self._merge(
"仓库解析失败: bad repo",
audit_rows=[("worker_repo_failed",
"C1: 仓库解析失败: bad repo")])
self.assertEqual(out["decision"], "escalate")
self.assertIn("a", out.get("gate_rejected", []))
self.assertEqual(_task_row(self.db_path, "C1")[0], "failed")
# 结构化证据已命中,不走子串回落、无回落审计行。
self.assertFalse([a for _p, a, _d in self.audits
if a == "worker_coord_gate_classified_by_marker"])
def test_missing_key_child_is_gate_rejected_not_execution(self):
out = self._merge(
"Missing environment variable: OPENKUN_API_KEY",
audit_rows=[("worker_missing_key",
"C1: Missing environment variable:"
" OPENKUN_API_KEY")])
self.assertEqual(out["decision"], "escalate")
self.assertIn("a", out.get("gate_rejected", []))
self.assertEqual(_task_row(self.db_path, "C1")[0], "failed")
def test_recheck_rejected_child_is_gate_rejected(self):
out = self._merge(
"default tier persisted model m lacks capability",
audit_rows=[("worker_capability_recheck_rejected",
"C1: default tier persisted model m lacks"
" capability")])
self.assertEqual(out["decision"], "escalate")
self.assertIn("a", out.get("gate_rejected", []))
def test_structured_evidence_wins_over_reworded_error(self):
# error_text 措辞改掉(不含任何 marker 子串),结构化
# 审计行不变时分类仍为 gate_rejected:分类不再依赖文案。
out = self._merge(
"repo exploded unexpectedly",
audit_rows=[("worker_repo_failed",
"C1: 仓库解析失败: bad repo")])
self.assertEqual(out["decision"], "escalate")
self.assertIn("a", out.get("gate_rejected", []))
self.assertFalse([a for _p, a, _d in self.audits
if a == "worker_coord_gate_classified_by_marker"])
def test_marker_fallback_is_audited(self):
# 删结构化审计行、仅留 error_text 时分类走回落,
# 且合流里有回落审计行点名 marker。
out = self._merge("必需工具缺失: playwright")
self.assertEqual(out["decision"], "escalate")
self.assertIn("a", out.get("gate_rejected", []))
marker_rows = [d for _p, a, d in self.audits
if a == "worker_coord_gate_classified_by_marker"]
self.assertTrue(marker_rows)
self.assertIn("C1", marker_rows[0])
self.assertIn("marker=必需工具缺失", marker_rows[0])
def test_gate_lookup_failure_still_escalates(self):
# I-5 回归:审计表缺失导致查询失败时合流走 escalate,
# 不改回空集推回(与批 2 测试同款口径,本夹具先删表)。
_insert_task(self.db_path, "P1", status="running", audit_loop=1)
_insert_task(self.db_path, "C1", status="failed", parent="P1",
part="a", error="boom")
conn = sqlite3.connect(self.db_path)
try:
conn.execute("DROP TABLE IF EXISTS audit_log")
conn.commit()
finally:
conn.close()
out = self.worker._coord_audit_loop_merge(
{"id": "P1", "project_id": "p1", "audit_loop": 1},
[{"name": "a", "prompt": "do a"}],
[{"id": "C1", "name": "a"}])
self.assertEqual(out["decision"], "escalate")
self.assertTrue(out.get("gate_lookup_failed"))
self.assertEqual(_task_row(self.db_path, "C1")[0], "failed")
class GateResultsCheckedTestCase(unittest.TestCase):
"""I-10:gate_results 只列实际跑过的门 + 两轮不误升级。"""
def setUp(self):
self._old = os.environ.get("CODEX_PUSH_ALLOW_PRIVATE_TARGETS")
os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = "1"
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
init_db(self.db_path)
def tearDown(self):
if self._old is None:
os.environ.pop("CODEX_PUSH_ALLOW_PRIVATE_TARGETS", None)
else:
os.environ[CODEX_PUSH_ALLOW_PRIVATE_TARGETS] = self._old
self.tmp.cleanup()
def test_direct_call_checked_subset_and_legacy(self):
worker = _worker(self.db_path)
task = {"id": "T1", "project_id": "p1"}
gates, _ = worker._audit_loop_gate_results(
task, "failed", {"error": "boom"}, gates_checked=["gateway"])
self.assertEqual(gates, {"execution": False, "gateway": True})
gates, _ = worker._audit_loop_gate_results(
task, "failed", {"error": "boom"}, gates_checked=[])
self.assertEqual(gates, {"execution": False})
# 未传 gates_checked 的旧直接调用保持旧行为。
gates, _ = worker._audit_loop_gate_results(
task, "failed", {"error": "boom"})
self.assertEqual(gates["capability"], True)
self.assertEqual(gates["required_tools"], True)
self.assertEqual(gates["gateway"], True)
def test_policy_task_two_rounds_pushback_without_gate_missing(self):
_insert_task(self.db_path, "T2", audit_loop=1)
worker = _worker(self.db_path, {
"mode": "active",
"tiers": {"default": {"model": "policy-model",
"requires": {"tool_calling": True}}},
"model_capabilities": {
"policy-model": {"tool_calling": True},
},
}, gateway=(None, {}), runner=_failing_runner)
for _ in range(2):
out = worker.run_one()
self.assertEqual(out["status"], "queued")
row = _state_row(self.db_path, "T2")
self.assertIsNotNone(row)
self.assertEqual(row[0], 2)
# 政策任务本轮实际跑过的门只有 capability(网关桩
# 回 None、无 agent_tools 故工具门未跑),状态里
# passed_gates 不含没跑过的门。
self.assertEqual(json.loads(row[1]), ["capability"])
self.assertEqual(row[2], "pushback")
def test_explicit_task_two_rounds_pushback_without_gate_missing(self):
_insert_task(self.db_path, "T3", model="explicit-model",
audit_loop=1)
worker = _worker(self.db_path, gateway=(None, {}),
runner=_failing_runner)
for _ in range(2):
out = worker.run_one()
self.assertEqual(out["status"], "queued")
row = _state_row(self.db_path, "T3")
self.assertIsNotNone(row)
self.assertEqual(row[0], 2)
self.assertEqual(row[2], "pushback")
class EvaluateNoFindingTestCase(unittest.TestCase):
"""I-11:失败门 + 空 findings 返回 escalate,不抛异常。"""
def test_failed_gate_without_finding_escalates(self):
out = evaluate(None, "T1", {"execution": False}, findings=[])
self.assertEqual(out["decision"], "escalate")
self.assertEqual(out["reason"], "gate_failed_without_finding")
self.assertEqual(out["round"], 0)
self.assertIsNone(out["packet"])
self.assertEqual(out["new_state"].round, 0)
self.assertEqual(out["new_state"].last_decision, "escalate")
def test_failed_gate_without_finding_keeps_round(self):
from cloud.audit_loop import LoopState
state = LoopState("T1", round=2, passed_gates=["execution"],
criteria={}, last_decision="pushback")
# 注意:上一轮通过的门本轮变失败会生成 regression
# finding(all_findings 非空),走不到本支;所以
# 本例让失败的是上一轮未通过过的门(capability),
# 且上一轮通过的 execution 本轮仍通过——无回归、
# 无缺门、无弱化、无 finding,只有失败门。
out = evaluate(state, "T1",
{"execution": True, "capability": False},
findings=[])
self.assertEqual(out["decision"], "escalate")
self.assertEqual(out["reason"], "gate_failed_without_finding")
self.assertEqual(out["new_state"].round, 2)
def test_finding_still_pushbacks_and_max_rounds_unchanged(self):
finding = Finding("execution_failed", "high", "boom", "re-run",
"task completes", kind="deterministic",
gate="execution")
out = evaluate(None, "T1", {"execution": False},
findings=[finding])
self.assertEqual(out["decision"], "pushback")
self.assertEqual(out["round"], 1)
from cloud.audit_loop import LoopState
state = LoopState("T1", round=3, passed_gates=[],
criteria={}, last_decision="pushback")
out = evaluate(state, "T1", {"execution": False},
findings=[finding])
self.assertEqual(out["decision"], "escalate")
self.assertEqual(out["reason"], "max_rounds_exceeded")
class GateErrorMarkersTestCase(unittest.TestCase):
"""I-13:分类子串表抽成模块常量。"""
def test_marker_constant_covers_known_markers(self):
self.assertIn("Missing environment variable:",
_GATE_ERROR_MARKERS)
self.assertIn("网关准备失败", _GATE_ERROR_MARKERS)
self.assertIn("必需工具缺失", _GATE_ERROR_MARKERS)
self.assertIn("工具审批声明矛盾", _GATE_ERROR_MARKERS)
self.assertIn("refusing to dispatch an incapable or"
" unverifiable model", _GATE_ERROR_MARKERS)
self.assertIn("fails capability", _GATE_ERROR_MARKERS)
if __name__ == "__main__":
unittest.main()