test: 9.5-code review minors (M1 conversion test, M5 no-fingerprint audit test, M2/M4 wording)

- M1: test_goal_branch_via_convert_queued_then_claim actually calls
  server._goal_convert_queued on a queued auto-resumed task before
  claiming, instead of only inserting the post-conversion state.
- M5: test_worker_routing_audit_rows_carry_no_fingerprint drives a
  real run_one with an active policy and asserts worker_routing
  audit rows exist and carry no fingerprint (negative control
  verified: injecting fingerprint into the detail fails it).
- M2: 'all callers' wording tightened to the real counts
  (load_routing_policy: 2 production call sites; evaluate_shadow: 1).
- M4: content_fingerprint docstring now states the fingerprint covers
  the full normalized payload including fallback_models, which the
  router deliberately does not apply (fail-safe direction).
No behaviour change; M3 DESIGN.md edits are outside the repo.
This commit is contained in:
host committed 2026-11-04 18:02:22 +00:00
1 parent b0347b374d
commit e3423865aa
3 files changed
+137 -6

No files matched your search

+9 -3
View File
@@ -418,8 +418,12 @@ class RoutingPolicy:
"""Content fingerprint of the parsed policy (9.5-code). """Content fingerprint of the parsed policy (9.5-code).
sha256 of the canonical JSON (sorted keys, tight sha256 of the canonical JSON (sorted keys, tight
separators) over the fields that decide routing and separators) over the full normalized policy payload
capability checks: mode, tiers, tier_modes, (not only the fields that decide routing and capability
checks: fallback_models is included even though the
router deliberately does not apply it, so the
fingerprint is fail-safe — it reports more changes,
never fewer): mode, tiers, tier_modes,
tier_requirements, model_capabilities, fallback_models. tier_requirements, model_capabilities, fallback_models.
The operator-declared ``version`` string is deliberately The operator-declared ``version`` string is deliberately
NOT part of the fingerprint: it is hand-filled and may NOT part of the fingerprint: it is hand-filled and may
@@ -577,7 +581,9 @@ def load_routing_policy(env=None):
# 置位,所以部分失败不会产生任务级审计;运营方 # 置位,所以部分失败不会产生任务级审计;运营方
# 只能从 worker 日志看到它,不能从审计里查到。 # 只能从 worker 日志看到它,不能从审计里查到。
# 这是已知缺口(非静默,但不如全失败可见), # 这是已知缺口(非静默,但不如全失败可见),
# 改动返回类型会牵动全部调用方,本轨不改。 # 本轨不改返回类型:生产调用点只有 2 处(server
# 影子端点经 _load_routing_policy、worker 派发),
# 真正的成本是同步改调用方与测试,而不是“牵动全部”。
_logger.warning( _logger.warning(
"routing policy partially failed (%s); continuing with" "routing policy partially failed (%s); continuing with"
" the usable source", "; ".join(failures)) " the usable source", "; ".join(failures))
+4 -3
View File
@@ -71,9 +71,10 @@ def parse_worker_routing_detail(detail):
9.5-code fail-open 清单:解析失败的行返回 None 后在 9.5-code fail-open 清单:解析失败的行返回 None 后在
evaluate_shadow 里直接跳过、不计数、不报错——样本无 evaluate_shadow 里直接跳过、不计数、不报错——样本无
声消失(例如模型名含空白会使整行匹配失败,见 M7)。 声消失(例如模型名含空白会使整行匹配失败,见 M7)。
这是已知缺口:改返回类型会牵动评估函数与端点,本轨 这是已知缺口:改返回类型只需同步评估函数的唯一生产
只在此处写明,不改语义;运营方无法从报告里看到丢 调用方(server 影子端点)与测试,本轨只在此处写明,
了多少行,只能靠审计行总数与样本数对不上来发现。 不改语义;运营方无法从报告里看到丢了多少行,只能靠
审计行总数与样本数对不上来发现。
""" """
if not isinstance(detail, str): if not isinstance(detail, str):
return None return None
+124
View File
@@ -40,6 +40,7 @@ from cloud.auth import AuthStore
from cloud.models import parse_routing_policy from cloud.models import parse_routing_policy
from cloud.routing_dryrun import dry_run_policy_document from cloud.routing_dryrun import dry_run_policy_document
from cloud.server import ( from cloud.server import (
CloudControlPlaneHandler,
_agent_tool_approvals_from_row, _agent_tool_approvals_from_row,
_agent_tools_from_row, _agent_tools_from_row,
create_server, create_server,
@@ -174,6 +175,64 @@ class GoalBranchTestCase(WorkerCase):
conn.close() conn.close()
self.assertEqual(row[0], "做目标") self.assertEqual(row[0], "做目标")
def test_goal_branch_via_convert_queued_then_claim(self):
# M1 strength (9.5-code minors): actually execute the
# conversion step itself, not just insert the
# post-conversion state. A queued non-goal task that was
# auto-resumed as platform_fresh is converted in place
# by server._goal_convert_queued, then claimed: the
# claim must reach the goal branch of
# _apply_resume_preamble (no _resume, no breakpoint).
_insert_task(self.db_path, "t2", prompt="做活",
auto_resume_count=1)
conn = sqlite3.connect(self.db_path)
try:
ensure_attempts_table(conn)
record_attempt(conn, "t2", 1, "platform_fresh",
"worker_shutdown", -1,
"做到一半:已建 a.py", "被平台杀掉")
conn.commit()
finally:
conn.close()
handler = CloudControlPlaneHandler.__new__(
CloudControlPlaneHandler)
handler.server = SimpleNamespace(db_path=self.db_path)
converted = handler._goal_convert_queued("t2", "做目标")
self.assertIsNotNone(converted)
self.assertEqual(converted["goal"], "做目标")
self.assertEqual(converted["goal_mode"], 1)
# Conversion is in-place on the queued row only: count
# and ledger untouched, no resume message planted.
conn = sqlite3.connect(self.db_path)
try:
row = conn.execute(
"SELECT status, goal_mode, auto_resume_count,"
" resume_message FROM tasks WHERE id = ?",
("t2",)).fetchone()
finally:
conn.close()
self.assertEqual(row[0], "queued")
self.assertEqual(row[1], 1)
self.assertEqual(row[2], 1)
self.assertIsNone(row[3])
worker = self._worker()
task = worker.get_task("t2")
self.assertFalse(task.get("_resume"))
self.assertFalse(task.get("_goal_resume_breakpoint"))
injected = worker._apply_resume_preamble(task)
self.assertIn("上次尝试说明", injected["goal"])
self.assertTrue(injected["goal"].endswith("做目标"))
self.assertIn("上次尝试说明", injected["prompt"])
self.assertTrue(injected["prompt"].endswith("做活"))
# Memory only: the stored goal is untouched.
conn = sqlite3.connect(self.db_path)
try:
row = conn.execute("SELECT goal FROM tasks WHERE id = ?",
("t2",)).fetchone()
finally:
conn.close()
self.assertEqual(row[0], "做目标")
class FailOpenLogTestCase(WorkerCase): class FailOpenLogTestCase(WorkerCase):
def test_audit_failure_logs_without_breaking_dispatch(self): def test_audit_failure_logs_without_breaking_dispatch(self):
@@ -234,6 +293,71 @@ class FingerprintTestCase(unittest.TestCase):
a.content_fingerprint()) a.content_fingerprint())
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="")
class RoutingAuditNoFingerprintTestCase(unittest.TestCase):
# M5 (9.5-code minors): "fingerprint never enters audit rows"
# must be asserted on real audit rows, not just on the dry-run
# report. Drive a real run_one with an active policy so the
# worker writes a worker_routing audit row, then read the
# audit_log table directly. The test is sensitive: if anyone
# writes the fingerprint into a worker_routing detail, the
# "fingerprint" substring assertion fails; if routing stops
# writing audit rows at all, the "at least one row" assertion
# fails instead of passing vacuously.
def setUp(self):
self._old_push_allow = 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)
self.env = {"XCM_API_KEY": "test-placeholder"}
def tearDown(self):
if self._old_push_allow is None:
os.environ.pop("CODEX_PUSH_ALLOW_PRIVATE_TARGETS", None)
else:
os.environ["CODEX_PUSH_ALLOW_PRIVATE_TARGETS"] = (
self._old_push_allow)
self.tmp.cleanup()
def test_worker_routing_audit_rows_carry_no_fingerprint(self):
_insert_task(self.db_path, "t9", prompt="做活")
worker = Worker(
self.db_path, "/fake/host.py", runner=_completed_runner,
env=self.env, max_retries=0, retry_backoff_seconds=0,
routing_policy={"mode": "active",
"tiers": {"default": "m1"}})
out = worker.run_one()
self.assertEqual(out["status"], "completed")
conn = sqlite3.connect(self.db_path)
try:
rows = conn.execute(
"SELECT action, detail FROM audit_log").fetchall()
finally:
conn.close()
routing = [d for (a, d) in rows if a == "worker_routing"]
self.assertGreaterEqual(
len(routing), 1,
"expected at least one worker_routing audit row; "
"without it the fingerprint assertion would be vacuous")
for detail in routing:
self.assertNotIn("fingerprint", detail)
# The written format is exactly tier/source/applied/
# suggested/chosen; nothing else may ride along.
self.assertTrue(detail.startswith("t9 tier=default "))
for (_action, detail) in rows:
self.assertNotIn("fingerprint", detail or "")
class _HttpServerCase(unittest.TestCase): class _HttpServerCase(unittest.TestCase):
def setUp(self): def setUp(self):
self._old = {k: os.environ.get(k) for k in ( self._old = {k: os.environ.get(k) for k in (