feat(v2): T07-b 专项 disabled 路径与工具策略守卫
T07 第二块。本批**只做 disabled 路径**(T07 卡片要求"先 disabled 专项路径"), 真正的 Jev/本地适配器属 T08,不预造。 新增契约:JudgmentAnswer(可辨识联合,score 必须有限数值)、NativeMetric、 Judgment、JudgmentConfig。契约层硬约束:disabled 不得产出 completed; 关闭专项必须记录原因且不得冻结任何配置修订。 ErrorCode 补齐 E-06/E-13/E-14/E-15/E-08/E-10,并**区分两类编码**: - 请求级(有 HTTP 语义,进 S1_ERROR_HTTP):含新增 JUDGMENT_CONFIG_INVALID 422、 EGRESS_NOT_APPROVED 403 - 运行级(RUN_LEVEL_CODES,只出现在运行体 degradations/judgment 里): JUDGMENT_UNAVAILABLE / CAPABILITY_UNSUPPORTED / OUTPUT_INVALID / RUN_BUDGET_EXCEEDED 不再把假的 HTTP 200 塞进错误映射;两者互斥且并集等于枚举,有测试锁定。 新增 services/judgment_provider.py: - resolve_config():关闭时记原因、不冻结配置;开关 false 却传 ID 属 E-07; 无可用配置时明确报 E-13 而非悄悄关闭;未验证/提供方不符均拒绝 - judge():disabled → skipped+disabled;缺证**不发起调用**直接 completed+unknown; 超时/异常 → failed+E-06;题型不符 → skipped+unsupported(E-14); 题型对但内容非法 → failed+E-15。失败一律 answer 为空,**不补默认分值** - allowed_tools():工具 = Skill 声明 ∩ 注册表白名单,**取交不取并**;材料正文里 写什么都无法扩展权限(AC-10,有反例测试) 测试:新增 test_judgment_provider.py 22 项。全量 390 项 OK(368 → 390); verify.py 10/10 PASS。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ from datetime import datetime, timezone
|
||||
from pydantic import ValidationError
|
||||
|
||||
from contracts.prescreen import (
|
||||
RUN_LEVEL_CODES,
|
||||
MAX_BATCH_BYTES,
|
||||
MAX_BATCH_FILES,
|
||||
MAX_ITEM_BYTES,
|
||||
@@ -288,8 +289,10 @@ class FactConflictTests(unittest.TestCase):
|
||||
|
||||
|
||||
class ErrorContractTests(unittest.TestCase):
|
||||
def test_every_code_has_an_http_status(self):
|
||||
self.assertEqual(set(S1_ERROR_HTTP), set(ErrorCode))
|
||||
def test_request_level_and_run_level_codes_partition_the_enum(self):
|
||||
# 请求级编码有 HTTP 语义;运行级编码只出现在运行体里,不得混入错误信封
|
||||
self.assertEqual(set(S1_ERROR_HTTP) | set(RUN_LEVEL_CODES), set(ErrorCode))
|
||||
self.assertEqual(set(S1_ERROR_HTTP) & set(RUN_LEVEL_CODES), set())
|
||||
|
||||
def test_status_semantics_match_existing_translate(self):
|
||||
# 既有 translate() 的语义:KeyError→404, Conflict→409, PermissionError→403, ValueError→422
|
||||
@@ -304,9 +307,10 @@ class ErrorContractTests(unittest.TestCase):
|
||||
# 运行级结果 = HTTP 200 且冲突保存在 degradations(那个面不走错误信封)。
|
||||
self.assertEqual(S1_ERROR_HTTP[ErrorCode.FACT_CONFLICT_UNRESOLVED], 422)
|
||||
|
||||
def test_all_codes_are_non_retryable(self):
|
||||
# 03 §5:403/422 不自动重试;409 需客户端改载荷或刷新
|
||||
self.assertFalse(any(S1_ERROR_RETRYABLE.values()))
|
||||
def test_only_provider_and_budget_errors_are_retryable(self):
|
||||
# 03 §5:403/422 不自动重试;仅提供方不可用与预算类可按预算重试一次
|
||||
retryable = {code for code, flag in S1_ERROR_RETRYABLE.items() if flag}
|
||||
self.assertEqual(retryable, {ErrorCode.JUDGMENT_UNAVAILABLE, ErrorCode.RUN_BUDGET_EXCEEDED})
|
||||
|
||||
def test_envelope_shape(self):
|
||||
body = ErrorResponse.model_validate({"error": {
|
||||
|
||||
Reference in New Issue
Block a user