fix: 9.5-code reachable items (parse unification, goal branch, fail-open logs, M7 test, fingerprint)

Parse: agent_tools/tool_approvals column parsing unified into agent_def shared helpers (parse None = undeclared; server keeps None, worker maps to []/{} at _task_from_row, dispatch behaviour unchanged). Goal: _apply_resume_preamble goal branch proven reachable via _goal_convert_queued on a queued auto-resumed task, kept with a test and corrected docs (not dead code). Fail-open: audit write failure and preamble lookup failure now log warnings (best-effort unchanged); partial policy failure and shadow parse drops documented as known gaps. M7: shadow endpoint explicit_capability_failures_truncated test. Fingerprint: RoutingPolicy.content_fingerprint (canonical JSON sha256, version excluded) surfaced only in dry-run output, never in audit rows. M1 wording: legacy gap stated as fixed stock persisting per-task until completion, not converging.
This commit is contained in:
host committed 2026-11-04 18:02:21 +00:00
1 parent d95d678379
commit 1b8b6e525d
7 files changed
+498 -57

No files matched your search

+40
View File
@@ -414,6 +414,37 @@ class RoutingPolicy:
"""Effective mode for one tier: explicit tier mode, else global."""
return self.tier_modes.get(tier, self.mode)
def content_fingerprint(self):
"""Content fingerprint of the parsed policy (9.5-code).
sha256 of the canonical JSON (sorted keys, tight
separators) over the fields that decide routing and
capability checks: mode, tiers, tier_modes,
tier_requirements, model_capabilities, fallback_models.
The operator-declared ``version`` string is deliberately
NOT part of the fingerprint: it is hand-filled and may
stay unchanged while requires change, so it cannot be
trusted to detect a change. The fingerprint is only for
dry-run output and debugging; it is never written into
worker_routing audit rows, so the shadow parser (which
splits audit detail on whitespace) is untouched.
Two policies that parse to the same effective fields
share a fingerprint even if their raw documents differ
in key order or in dropped/invalid tiers.
"""
import hashlib
payload = {
"mode": self.mode,
"tiers": self.tiers,
"tier_modes": self.tier_modes,
"tier_requirements": self.tier_requirements,
"model_capabilities": self.model_capabilities,
"fallback_models": self.fallback_models,
}
canonical = json.dumps(payload, sort_keys=True,
separators=(",", ":"))
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
def parse_routing_policy(data):
"""Parse one policy document, or return None when it is unusable.
@@ -538,6 +569,15 @@ def load_routing_policy(env=None):
" worker default", "; ".join(failures))
return None
if failures:
# 9.5-code fail-open 清单:部分失败是 fail-open——
# 可用来源继续生效、失败来源被丢弃,派发不炸。
# 可见性只有这条警告日志:load_routing_policy 仍
# 返回一个 policy,worker 的
# routing_policy_load_failed 标志只在全失败时
# 置位,所以部分失败不会产生任务级审计;运营方
# 只能从 worker 日志看到它,不能从审计里查到。
# 这是已知缺口(非静默,但不如全失败可见),
# 改动返回类型会牵动全部调用方,本轨不改。
_logger.warning(
"routing policy partially failed (%s); continuing with"
" the usable source", "; ".join(failures))