ソースを参照

主机:让 Mission 生命周期适配新合同与检查点

Mission Service 贯通新的 Phase2 planning、候选校验和 debug checkpoint 状态,确保 Phase1 Direction、Phase2 Portfolio 与恢复推进使用一致的冻结输入。
SamLee 6 時間 前
コミット
82bc504f6d

+ 28 - 5
script_build_host/src/script_build_host/application/mission_service.py

@@ -628,6 +628,9 @@ class ScriptMissionService:
             binding,
             snapshot,
             direction_artifact_version_id=direction_version_id,
+            direction_goal_ids=await self._direction_goal_ids(
+                script_build_id, direction_version_id
+            ),
         )
         events = await self.runner.trace_store.get_events(binding.root_trace_id, 0)
         migrations = [
@@ -664,6 +667,19 @@ class ScriptMissionService:
             )
         return binding, snapshot, direction_version_id
 
+    async def _direction_goal_ids(
+        self, script_build_id: int, artifact_version_id: int
+    ) -> tuple[str, ...]:
+        artifacts = getattr(self.direction_reconciler, "artifacts", None)
+        if artifacts is None:
+            return ()
+        version = await artifacts.get_by_id(
+            artifact_version_id, script_build_id=script_build_id
+        )
+        if not isinstance(version.artifact, DirectionArtifact):
+            raise PhaseTwoBoundaryNotReady("active Direction artifact has the wrong type")
+        return tuple(goal.goal_id for goal in version.artifact.goals)
+
     async def run_phase_two(
         self,
         script_build_id: int,
@@ -685,6 +701,9 @@ class ScriptMissionService:
             binding,
             snapshot,
             direction_artifact_version_id=direction_artifact_version_id,
+            direction_goal_ids=await self._direction_goal_ids(
+                script_build_id, direction_artifact_version_id
+            ),
         )
         policy_digest = _canonical_digest(policy)
         toolset_digest = _canonical_digest(
@@ -1163,14 +1182,18 @@ class ScriptMissionService:
         )
         if phase == 2:
             policy = self.factory.build_phase_two_policy(snapshot)
+            direction_version_id = (
+                binding.active_direction_artifact_version_id
+                or await self.direction_reconciler.reconcile(
+                    script_build_id, binding.root_trace_id
+                )
+            )
             continuation = self.factory.build_phase_two_message(
                 binding,
                 snapshot,
-                direction_artifact_version_id=(
-                    binding.active_direction_artifact_version_id
-                    or await self.direction_reconciler.reconcile(
-                        script_build_id, binding.root_trace_id
-                    )
+                direction_artifact_version_id=direction_version_id,
+                direction_goal_ids=await self._direction_goal_ids(
+                    script_build_id, direction_version_id
                 ),
             )
             migrations = [

+ 10 - 23
script_build_host/tests/test_phase_one_e2e.py

@@ -77,21 +77,14 @@ def _contract(
     *,
     scope: str,
 ) -> dict[str, object]:
-    output_schema = {
-        "direction": "script-direction/v1",
-        "decode-retrieval": "evidence-record/v1",
-    }[kind]
     return {
-        "schema_version": "script-task-contract/v1",
         "task_kind": kind,
         "scope_ref": scope,
         "intent_class": "explore",
         "objective": objective,
-        "input_decision_refs": [],
-        "base_artifact_ref": None,
-        "write_scope": [scope],
+        "input_decision_ids": [],
+        "base_decision_id": None,
         "gap_ref": None,
-        "output_schema": output_schema,
         "criteria": [
             {
                 "criterion_id": criterion_id,
@@ -99,20 +92,9 @@ def _contract(
                 "hard": True,
             }
         ],
-        "budget": {
-            "max_attempts": 4,
-            "max_tokens": 32000,
-            "max_seconds": 900,
-            "max_external_queries": 40,
-            "max_no_improvement": 3,
-        },
         "goal_ids": [],
         "supersedes_decision_ids": [],
-        "candidate_closure_decision_refs": [],
-        "adopted_decision_ids": [],
-        "held_or_rejected_decision_ids": [],
-        "compose_order": [],
-        "comparison_decision_refs": [],
+        "comparison_decision_ids": [],
     }
 
 
@@ -306,7 +288,7 @@ class _ScriptedLLM:
                 {
                     "goals": [
                         {
-                            "goal_id": "g1",
+                            "client_key": "grounded-direction",
                             "statement": "make one grounded direction",
                             "rationale": "accepted evidence",
                             "success_criteria": ["the direction remains grounded"],
@@ -348,6 +330,9 @@ class _ScriptedLLM:
         self.validated_artifact_kinds.append(kind)
         failed = spec["objective"] == "decode evidence" and spec["version"] == 1
         verdict = "failed" if failed else "passed"
+        snapshot = prompt["artifact_snapshot"]
+        refs = [*snapshot["artifact_refs"], *snapshot["evidence_refs"]]
+        artifact_ref = refs[0]
         return self.call(
             "submit_validation",
             {
@@ -357,6 +342,7 @@ class _ScriptedLLM:
                         "criterion_id": item["criterion_id"],
                         "verdict": verdict,
                         "reason": "checked frozen snapshot",
+                        "evidence_refs": [] if failed else [artifact_ref],
                     }
                     for item in spec["acceptance_criteria"]
                 ],
@@ -585,7 +571,8 @@ async def test_real_runner_phase_one_fail_revise_pass_direction_accept_then_root
     assert llm.validated_artifact_kinds == ["evidence", "evidence", "direction"]
     assert legacy.status == BuildStatus.PARTIAL
     assert legacy.direction.startswith("# Direction\n\n## Goals")
-    assert "### g1: make one grounded direction" in legacy.direction
+    assert "### goal-" in legacy.direction
+    assert ": make one grounded direction" in legacy.direction
     assert "## Constraints" in legacy.direction
     assert publications.state == PublicationState.PUBLISHED
     assert bindings.active is not None