Przeglądaj źródła

测试(脚本构建): 对齐 Goal 驱动的 Phase2 脚本夹具

统一 Direction Goal ID,确保 Phase2 创作合同显式携带 Direction DecisionRef,Phase2 Retrieval 携带有效 Goal ID。\n\n按 task_kind 筛选 Compare 与 Portfolio 候选,并让 Structure scope 真正覆盖 Paragraph,避免固定输入数量和旧作用域假设。
SamLee 16 godzin temu
rodzic
commit
b2bae3bc15

+ 49 - 36
script_build_host/tests/test_phase_two_real_runner_e2e.py

@@ -55,6 +55,7 @@ from script_build_host.tools.gateway import RetrievalResult
 
 ROOT = "phase-two-real-runner-root"
 FULL_SCOPE = "script-build://scopes/full"
+DIRECTION_GOAL_ID = "direction-goal"
 
 
 def _tool(call_id: str, name: str, arguments: Mapping[str, Any]) -> dict[str, Any]:
@@ -135,6 +136,7 @@ def _contract(
     compose_order: Sequence[str] = (),
     comparison_decision_refs: Sequence[Mapping[str, Any]] = (),
     supersedes_decision_ids: Sequence[str] = (),
+    goal_ids: Sequence[str] | None = None,
 ) -> dict[str, Any]:
     output_schema = {
         "direction": "script-direction/v1",
@@ -177,9 +179,9 @@ def _contract(
             "max_external_queries": 40,
             "max_no_improvement": 3,
         },
-        "goal_ids": (
-            [] if task_kind in {"direction", "decode-retrieval"} else ["goal-1"]
-        ),
+        "goal_ids": list(goal_ids)
+        if goal_ids is not None
+        else ([] if task_kind in {"direction", "decode-retrieval"} else [DIRECTION_GOAL_ID]),
         "supersedes_decision_ids": list(supersedes_decision_ids),
         "candidate_closure_decision_refs": list(closure),
         "adopted_decision_ids": list(adopted),
@@ -305,6 +307,14 @@ class _PhaseTwoScriptedLLM:
                 },
             )
 
+        direction_decision, direction_ref = _accepted_ref(ledger, direction)
+        direction_pin = _decision_ref(
+            direction_decision,
+            direction_ref,
+            scope_ref=FULL_SCOPE,
+            task_kind="direction",
+        )
+
         portfolio = next(iter(by_kind("candidate-portfolio")), None)
         if portfolio is None:
             return self._call(
@@ -315,6 +325,7 @@ class _PhaseTwoScriptedLLM:
                             "candidate-portfolio",
                             scope_ref=FULL_SCOPE,
                             write_scope=("script-build://writes",),
+                            input_decision_refs=(direction_pin,),
                         )
                     ]
                 },
@@ -330,6 +341,7 @@ class _PhaseTwoScriptedLLM:
                             "compose",
                             scope_ref=FULL_SCOPE,
                             write_scope=("script-build://writes",),
+                            input_decision_refs=(direction_pin,),
                         )
                     ],
                 },
@@ -345,6 +357,7 @@ class _PhaseTwoScriptedLLM:
                             "structure",
                             scope_ref="script-build://scopes/full/opening",
                             write_scope=("script-build://writes/paragraphs/opening",),
+                            input_decision_refs=(direction_pin,),
                         )
                     ],
                 },
@@ -375,7 +388,7 @@ class _PhaseTwoScriptedLLM:
                             scope_ref="script-build://scopes/full/opening",
                             write_scope=("script-build://writes/paragraphs/opening",),
                             objective=first_paragraph_objective,
-                            input_decision_refs=(structure_pin,),
+                            input_decision_refs=(direction_pin, structure_pin),
                             base_artifact_ref=_artifact_ref_payload(structure_ref),
                         )
                     ],
@@ -386,7 +399,6 @@ class _PhaseTwoScriptedLLM:
         if paragraph.status is TaskStatus.AWAITING_DECISION:
             return self._accept(paragraph, ledger)
 
-        direction_decision, direction_ref = _accepted_ref(ledger, direction)
         paragraph_decision, paragraph_ref = _accepted_ref(ledger, paragraph)
         if compose.current_spec_version == 1:
             structure_pin = _decision_ref(
@@ -411,14 +423,7 @@ class _PhaseTwoScriptedLLM:
                         "compose",
                         scope_ref=FULL_SCOPE,
                         write_scope=("script-build://writes",),
-                        input_decision_refs=(
-                            _decision_ref(
-                                direction_decision,
-                                direction_ref,
-                                scope_ref=FULL_SCOPE,
-                                task_kind="direction",
-                            ),
-                        ),
+                        input_decision_refs=(direction_pin,),
                         closure=(structure_pin, paragraph_pin),
                         adopted=(structure_decision, paragraph_decision),
                         compose_order=(structure_decision, paragraph_decision),
@@ -445,6 +450,7 @@ class _PhaseTwoScriptedLLM:
                             write_scope=("script-build://writes/paragraphs/opening",),
                             objective=replacement_objective,
                             input_decision_refs=(
+                                direction_pin,
                                 _decision_ref(
                                     structure_decision,
                                     structure_ref,
@@ -487,6 +493,7 @@ class _PhaseTwoScriptedLLM:
                             scope_ref="script-build://scopes/full/opening/evidence",
                             write_scope=(),
                             objective="retrieve evidence for the replacement opening",
+                            goal_ids=(DIRECTION_GOAL_ID,),
                         )
                     ],
                 },
@@ -513,6 +520,7 @@ class _PhaseTwoScriptedLLM:
                             scope_ref="script-build://scopes/full/opening",
                             write_scope=(),
                             objective="compare opening candidates A and B symmetrically",
+                            input_decision_refs=(direction_pin,),
                             comparison_decision_refs=(
                                 _decision_ref(
                                     paragraph_decision,
@@ -566,14 +574,7 @@ class _PhaseTwoScriptedLLM:
                         "compose",
                         scope_ref=FULL_SCOPE,
                         write_scope=("script-build://writes",),
-                        input_decision_refs=(
-                            _decision_ref(
-                                direction_decision,
-                                direction_ref,
-                                scope_ref=FULL_SCOPE,
-                                task_kind="direction",
-                            ),
-                        ),
+                        input_decision_refs=(direction_pin,),
                         closure=(structure_pin, paragraph_pin, replacement_pin),
                         adopted=(structure_decision, replacement_decision),
                         held=(paragraph_decision,),
@@ -612,6 +613,7 @@ class _PhaseTwoScriptedLLM:
                         "candidate-portfolio",
                         scope_ref=FULL_SCOPE,
                         write_scope=("script-build://writes",),
+                        input_decision_refs=(direction_pin,),
                         closure=(compose_pin,),
                         adopted=(compose_decision,),
                         compose_order=(compose_decision,),
@@ -673,7 +675,7 @@ class _PhaseTwoScriptedLLM:
                     {
                         "goals": [
                             {
-                                "goal_id": "direction-goal",
+                                "goal_id": DIRECTION_GOAL_ID,
                                 "statement": "open with one observable reversal",
                                 "rationale": "the frozen topic benefits from a concrete hook",
                                 "success_criteria": [
@@ -767,8 +769,12 @@ class _PhaseTwoScriptedLLM:
                 return self._call("submit_attempt", {})
             if not isinstance(last, list):
                 return self._call("read_pinned_candidates", {})
-            assert len(last) == 2
-            candidates = [str(item["artifact_ref"]["uri"]) for item in last]
+            candidates = [
+                str(item["artifact_ref"]["uri"])
+                for item in last
+                if item["task_kind"] == "paragraph"
+            ]
+            assert len(candidates) == 2
             return self._call(
                 "save_comparison_candidate",
                 {
@@ -811,7 +817,7 @@ class _PhaseTwoScriptedLLM:
                 return self._call("submit_attempt", {})
             if not isinstance(last, list):
                 return self._call("read_pinned_candidates", {})
-            assert len(last) == 1
+            assert sorted(item["task_kind"] for item in last) == ["compose", "direction"]
             return self._call(
                 "save_candidate_portfolio",
                 {
@@ -956,6 +962,13 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
             return await super()._planner()
 
         root = ledger.tasks[ledger.root_task_id]
+        direction_decision, direction_ref = _accepted_ref(ledger, direction)
+        direction_pin = _decision_ref(
+            direction_decision,
+            direction_ref,
+            scope_ref=FULL_SCOPE,
+            task_kind="direction",
+        )
 
         def by_objective(value: str) -> Any | None:
             return next((task for task in tasks if task.current_spec.objective == value), None)
@@ -972,6 +985,7 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
                             "candidate-portfolio",
                             scope_ref=FULL_SCOPE,
                             write_scope=("script-build://writes",),
+                            input_decision_refs=(direction_pin,),
                         )
                     ]
                 },
@@ -987,6 +1001,7 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
                             "compose",
                             scope_ref=FULL_SCOPE,
                             write_scope=("script-build://writes",),
+                            input_decision_refs=(direction_pin,),
                         )
                     ],
                 },
@@ -994,7 +1009,7 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
 
         exploratory: dict[str, Any] = {}
         scopes = {
-            "structure": "script-build://scopes/full/opening/structure-sketch",
+            "structure": "script-build://scopes/full/opening",
             "paragraph": "script-build://scopes/full/opening",
             "element-set": "script-build://scopes/full/opening/elements",
         }
@@ -1007,13 +1022,14 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
             objective = f"explore {kind} before formal adoption"
             task = by_objective(objective)
             if task is None:
-                inputs: tuple[dict[str, Any], ...] = ()
+                inputs: tuple[dict[str, Any], ...] = (direction_pin,)
                 base: dict[str, Any] | None = None
                 if kind == "paragraph":
                     structure_decision, structure_ref = _accepted_ref(
                         ledger, exploratory["structure"]
                     )
                     inputs = (
+                        direction_pin,
                         _decision_ref(
                             structure_decision,
                             structure_ref,
@@ -1062,6 +1078,7 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
                             write_scope=("script-build://writes/paragraphs/opening",),
                             objective=paragraph_objective,
                             input_decision_refs=(
+                                direction_pin,
                                 _decision_ref(
                                     structure_decision,
                                     structure_ref,
@@ -1105,6 +1122,7 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
                             write_scope=("script-build://writes/elements",),
                             objective=element_objective,
                             input_decision_refs=(
+                                direction_pin,
                                 _decision_ref(
                                     element_decision,
                                     element_ref,
@@ -1130,7 +1148,6 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
             return self._accept(element_replacement, ledger)
         adopted_element_decision, adopted_element_ref = _accepted_ref(ledger, element_replacement)
 
-        direction_decision, direction_ref = _accepted_ref(ledger, direction)
         pins = (
             _decision_ref(
                 structure_decision,
@@ -1174,14 +1191,7 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
                         "compose",
                         scope_ref=FULL_SCOPE,
                         write_scope=("script-build://writes",),
-                        input_decision_refs=(
-                            _decision_ref(
-                                direction_decision,
-                                direction_ref,
-                                scope_ref=FULL_SCOPE,
-                                task_kind="direction",
-                            ),
-                        ),
+                        input_decision_refs=(direction_pin,),
                         closure=pins,
                         adopted=(
                             structure_decision,
@@ -1214,6 +1224,7 @@ class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
                         "candidate-portfolio",
                         scope_ref=FULL_SCOPE,
                         write_scope=("script-build://writes",),
+                        input_decision_refs=(direction_pin,),
                         closure=(
                             _decision_ref(
                                 compose_decision,
@@ -1958,6 +1969,8 @@ async def test_historical_partial_advances_via_http_and_reenters_after_filestore
     assert phase_one_root.status is TaskStatus.BLOCKED
     assert phase_one_root.blocked_reason == PHASE_ONE_CAPABILITY_BOUNDARY
     assert await build_states.get_status(script_build_id) is BuildStatus.PARTIAL
+    phase_one_traces = FileSystemTraceStore(str(tmp_path / "trace"))
+    assert await phase_one_traces.get_goal_tree(ROOT) is None
 
     # Reopen every file-backed component to represent the first Phase2 process.
     transition_store = FileSystemTaskStore(str(tmp_path / "ledger"))