소스 검색

主机:要求 Validator 提交闭包内证据引用

把 criterion evidence_refs 纳入验证请求、受保护上下文和结果校验;passed criterion 必须引用当前 Artifact 或 accepted closure 中的精确证据,overall verdict 与缺陷严重度保持一致。
SamLee 21 시간 전
부모
커밋
bee87d4f1f
1개의 변경된 파일84개의 추가작업 그리고 14개의 파일을 삭제
  1. 84 14
      script_build_host/src/script_build_host/agents/validation.py

+ 84 - 14
script_build_host/src/script_build_host/agents/validation.py

@@ -88,22 +88,45 @@ class ScriptBuildValidationPolicy:
             preset = "script_candidate_validator"
         else:
             raise ValueError(f"Unsupported script-build task kind: {kind!r}")
+        preflight_rules = [
+            "immutable-reference-present",
+            "sha256-digest-shape",
+            (
+                "root-delivery-artifact-shape"
+                if kind == ROOT_DELIVERY_KIND
+                else (
+                    "phase-two-artifact-shape"
+                    if kind in PHASE_TWO_KINDS
+                    else "phase-one-artifact-shape"
+                )
+            ),
+        ]
+        if kind in PHASE_TWO_KINDS:
+            preflight_rules.extend(["business-artifact-owner", "realized-content"])
+            if kind in {"structure", "paragraph", "element-set", "compare"}:
+                preflight_rules.append("candidate-lineage")
+            if kind == "compare":
+                preflight_rules.append("comparison-fairness")
+            if kind == "compose":
+                preflight_rules.append("goal-coverage")
+            if kind == "candidate-portfolio":
+                preflight_rules.extend(["input-closure-digest", "goal-coverage"])
+        elif kind == ROOT_DELIVERY_KIND:
+            preflight_rules.extend(
+                [
+                    "root-manifest-shape",
+                    "root-blocking-defects-empty",
+                    "root-input-closure",
+                    "root-script-content",
+                    "root-goal-coverage",
+                    "root-direction-contract",
+                    "root-legacy-projection",
+                ]
+            )
         return ValidationPlan(
             mode=ValidationMode.AGENT,
             validator_preset=preset,
-            preflight_rule_ids=(
-                "immutable-reference-present",
-                "sha256-digest-shape",
-                (
-                    "root-delivery-artifact-shape"
-                    if kind == ROOT_DELIVERY_KIND
-                    else (
-                        "phase-two-artifact-shape"
-                        if kind in PHASE_TWO_KINDS
-                        else "phase-one-artifact-shape"
-                    )
-                ),
-            ),
+            preflight_rule_ids=tuple(preflight_rules),
             max_evidence_queries=8,
             max_evidence_items_per_query=25,
             evidence_timeout_seconds=8.0,
@@ -215,9 +238,24 @@ def validate_phase_two_defects(
     return defects
 
 
+class BusinessPreflight(Protocol):
+    async def deterministic_precheck(
+        self, *, context: Mapping[str, Any]
+    ) -> Sequence[Mapping[str, Any]]: ...
+
+
 class ScriptBuildDeterministicValidator:
     """Optional deterministic validator for explicitly selected rule plans."""
 
+    def __init__(
+        self,
+        *,
+        candidates: BusinessPreflight | None = None,
+        root_delivery: BusinessPreflight | None = None,
+    ) -> None:
+        self._candidates = candidates
+        self._root_delivery = root_delivery
+
     async def validate(
         self,
         context: ValidationContext,
@@ -225,7 +263,30 @@ class ScriptBuildDeterministicValidator:
     ) -> DeterministicValidationResult:
         if plan.mode != ValidationMode.DETERMINISTIC:
             raise ValueError("deterministic validator requires deterministic mode")
-        available = {item.rule_id: item for item in precheck_snapshot(context)}
+        values = list(precheck_snapshot(context))
+        kind = task_kind(context.task.current_spec.context_refs)
+        protected_context = {
+            "root_trace_id": context.root_trace_id,
+            "task_id": context.task.task_id,
+            "attempt_id": context.attempt.attempt_id,
+            "snapshot_id": context.snapshot.snapshot_id,
+            "spec_version": context.task.current_spec_version,
+        }
+        if kind in PHASE_TWO_KINDS and self._candidates is not None:
+            values.extend(
+                _rule_result(item)
+                for item in await self._candidates.deterministic_precheck(
+                    context=protected_context
+                )
+            )
+        elif kind == ROOT_DELIVERY_KIND and self._root_delivery is not None:
+            values.extend(
+                _rule_result(item)
+                for item in await self._root_delivery.deterministic_precheck(
+                    context=protected_context
+                )
+            )
+        available = {item.rule_id: item for item in values}
         selected = [
             available.get(
                 rule_id,
@@ -248,6 +309,15 @@ class ScriptBuildDeterministicValidator:
         return DeterministicValidationResult(verdict=verdict, rule_results=selected)
 
 
+def _rule_result(value: Mapping[str, Any]) -> DeterministicRuleResult:
+    return DeterministicRuleResult(
+        rule_id=str(value["rule_id"]),
+        verdict=ValidationVerdict(str(value["verdict"])),
+        reason=str(value.get("reason", "")),
+        error=(str(value["error"]) if value.get("error") is not None else None),
+    )
+
+
 class EvidenceContentReader(Protocol):
     async def read(
         self,