Browse Source

测试:重写 Goal 驱动的 Phase2 SQL 流程

更新 SQLite/SQL 流程夹具以使用 PlannerTaskInput、动态 Goal、Host 派生 closure 和正式脚本内容合同,移除对旧七步固定流程的依赖。
SamLee 9 hours ago
parent
commit
f59a0e2d18
1 changed files with 66 additions and 149 deletions
  1. 66 149
      script_build_host/tests/test_phase_two_sql_flow.py

+ 66 - 149
script_build_host/tests/test_phase_two_sql_flow.py

@@ -86,18 +86,7 @@ def _contract(
     comparison_refs: Sequence[dict[str, Any]] = (),
     execution_ready: bool = True,
 ) -> dict[str, Any]:
-    output_schema = {
-        ScriptTaskKind.DIRECTION: "script-direction/v1",
-        ScriptTaskKind.DECODE_RETRIEVAL: "evidence-record/v1",
-        ScriptTaskKind.STRUCTURE: "structure-artifact/v1",
-        ScriptTaskKind.PARAGRAPH: "paragraph-patch/v1" if base_ref else "paragraph-artifact/v1",
-        ScriptTaskKind.ELEMENT_SET: (
-            "element-set-patch/v1" if base_ref else "element-set-artifact/v1"
-        ),
-        ScriptTaskKind.COMPARE: "comparison-artifact/v1",
-        ScriptTaskKind.COMPOSE: "structured-script/v1",
-        ScriptTaskKind.CANDIDATE_PORTFOLIO: "candidate-portfolio/v1",
-    }[kind]
+    del write_scope, closure, adopted, held, order, execution_ready
     if kind in {ScriptTaskKind.COMPOSE, ScriptTaskKind.CANDIDATE_PORTFOLIO}:
         intent = "compose" if kind is ScriptTaskKind.COMPOSE else "portfolio"
     elif supersedes:
@@ -105,16 +94,21 @@ def _contract(
     else:
         intent = "explore"
     payload: dict[str, Any] = {
-        "schema_version": "script-task-contract/v1",
         "task_kind": kind.value,
         "scope_ref": scope,
         "intent_class": intent,
         "objective": f"produce independently verifiable {kind.value} content",
-        "input_decision_refs": list(input_refs),
-        "base_artifact_ref": _ref_payload(base_ref) if base_ref else None,
-        "write_scope": list(write_scope),
+        "input_decision_ids": [str(item["decision_id"]) for item in input_refs],
+        "base_decision_id": next(
+            (
+                str(item["decision_id"])
+                for item in input_refs
+                if base_ref is not None
+                and cast(dict[str, Any], item["artifact_ref"])["uri"] == base_ref.uri
+            ),
+            None,
+        ),
         "gap_ref": None,
-        "output_schema": output_schema,
         "criteria": [
             {
                 "criterion_id": "closed",
@@ -122,35 +116,14 @@ def _contract(
                 "hard": True,
             }
         ],
-        "budget": {
-            "max_attempts": 4,
-            "max_tokens": 32_000,
-            "max_seconds": 900,
-            "max_external_queries": 40,
-            "max_no_improvement": 3,
-        },
         "goal_ids": (
             []
             if kind in {ScriptTaskKind.DIRECTION, ScriptTaskKind.DECODE_RETRIEVAL}
             else ["goal-1"]
         ),
         "supersedes_decision_ids": list(supersedes),
-        "candidate_closure_decision_refs": list(closure),
-        "adopted_decision_ids": list(adopted),
-        "held_or_rejected_decision_ids": list(held),
-        "compose_order": list(order),
-        "comparison_decision_refs": list(comparison_refs),
+        "comparison_decision_ids": [str(item["decision_id"]) for item in comparison_refs],
     }
-    if not execution_ready and kind in {
-        ScriptTaskKind.COMPOSE,
-        ScriptTaskKind.CANDIDATE_PORTFOLIO,
-    }:
-        payload.update(
-            candidate_closure_decision_refs=[],
-            adopted_decision_ids=[],
-            held_or_rejected_decision_ids=[],
-            compose_order=[],
-        )
     return payload
 
 
@@ -258,32 +231,46 @@ class _ScriptedExecutor:
             if workspace["paragraphs"]:
                 paragraph_id = workspace["paragraphs"][0]["paragraph_id"]
             else:
-                created = await self.candidates.create_script_paragraph(
-                    payload={
-                        "paragraph_index": 1,
-                        "name": f"{kind.value} opening",
-                        "content_range": {"scope": "opening"},
-                    },
+                atom = (
+                    {"原子点": "可观察的反转", "维度": "开场", "维度类型": "主维度"},
+                )
+                created = await self.candidates.create_script_paragraphs(
+                    paragraphs=(
+                        {
+                            "client_key": "opening",
+                            "paragraph_index": 1,
+                            "name": f"{kind.value} opening",
+                            "content_range": {"scope": "opening"},
+                            "level": 1,
+                            "theme_elements": atom,
+                            "form_elements": atom,
+                            "function_elements": atom,
+                            "feeling_elements": atom,
+                        },
+                    ),
+                    context=candidate_context,
+                )
+                paragraph_id = created["paragraph_ids_by_client_key"]["opening"]
+            if kind is ScriptTaskKind.PARAGRAPH:
+                await self.candidates.batch_update_script_paragraphs(
+                    updates=(
+                        {
+                            "paragraph_id": paragraph_id,
+                            "theme": "a specific reversal",
+                            "form": "contrast",
+                            "function": "hook",
+                            "feeling": "curiosity",
+                            "description": (
+                                "The observable detail changes the initial interpretation."
+                            ),
+                            "full_description": (
+                                "The opening states a familiar assumption, then overturns it "
+                                "with one visible and source-grounded detail."
+                            ),
+                        },
+                    ),
                     context=candidate_context,
                 )
-                paragraph_id = created["paragraph_id"]
-            await self.candidates.batch_update_script_paragraphs(
-                updates=(
-                    {
-                        "paragraph_id": paragraph_id,
-                        "theme": "a specific reversal",
-                        "form": "contrast",
-                        "function": "hook",
-                        "feeling": "curiosity",
-                        "description": "The observable detail changes the initial interpretation.",
-                        "full_description": (
-                            "The opening states a familiar assumption, then overturns it with "
-                            "one visible and source-grounded detail."
-                        ),
-                    },
-                ),
-                context=candidate_context,
-            )
         elif kind is ScriptTaskKind.ELEMENT_SET:
             created = await self.candidates.create_script_element(
                 payload={
@@ -308,7 +295,11 @@ class _ScriptedExecutor:
                 )
         elif kind is ScriptTaskKind.COMPARE:
             pinned = await self.candidates.read_pinned_candidates(context=candidate_context)
-            candidate_refs = [str(item["artifact_ref"]["uri"]) for item in pinned]
+            candidate_refs = [
+                str(item["artifact_ref"]["uri"])
+                for item in pinned
+                if item["artifact_ref"]["kind"] == ArtifactKind.PARAGRAPH.value
+            ]
             await self.candidates.save_comparison_candidate(
                 payload={
                     "criterion_results": [
@@ -554,7 +545,11 @@ async def test_sql_phase_two_dynamic_replacement_compose_portfolio_and_boundary(
         call_id="retrieval",
     )
     await _dispatch_accept(planning, coordinator, retrieval)
-    direction_decision, direction_ref = await _dispatch_accept(planning, coordinator, direction)
+    _, direction_ref = await _dispatch_accept(planning, coordinator, direction)
+    await bindings.set_active_direction(
+        script_build_id=script_build_id,
+        artifact_version_id=int(direction_ref.version),
+    )
 
     portfolio = await _plan_one(
         planning,
@@ -610,15 +605,6 @@ async def test_sql_phase_two_dynamic_replacement_compose_portfolio_and_boundary(
             ScriptTaskKind.STRUCTURE,
             scope="script-build://scopes/full/opening",
             write_scope=("script-build://writes/paragraphs/opening",),
-            input_refs=(
-                _decision_ref(
-                    old_paragraph_decision,
-                    old_paragraph_ref,
-                    scope="script-build://scopes/full/opening",
-                    kind=ScriptTaskKind.PARAGRAPH,
-                ),
-            ),
-            base_ref=old_paragraph_ref,
         ),
         parent_task_id=compose,
         call_id="structure-third",
@@ -680,7 +666,7 @@ async def test_sql_phase_two_dynamic_replacement_compose_portfolio_and_boundary(
         parent_task_id=compose,
         call_id="replace-element",
     )
-    element_decision, element_ref = await _dispatch_accept(
+    element_decision, _element_ref = await _dispatch_accept(
         planning, coordinator, element_replacement
     )
 
@@ -708,7 +694,7 @@ async def test_sql_phase_two_dynamic_replacement_compose_portfolio_and_boundary(
         parent_task_id=compose,
         call_id="compare-opening-candidates",
     )
-    comparison_decision, comparison_ref = await _dispatch_accept(planning, coordinator, comparison)
+    await _dispatch_accept(planning, coordinator, comparison)
 
     with pytest.raises(PhaseTwoBoundaryNotReady, match="CandidatePortfolio"):
         await planning.decide_script_task(
@@ -721,96 +707,27 @@ async def test_sql_phase_two_dynamic_replacement_compose_portfolio_and_boundary(
             context={"root_trace_id": ROOT, "tool_call_id": "early-boundary"},
         )
 
-    local_refs = (
-        _decision_ref(
-            old_element_decision,
-            old_element_ref,
-            scope="script-build://scopes/full/elements",
-            kind=ScriptTaskKind.ELEMENT_SET,
-        ),
-        _decision_ref(
-            old_paragraph_decision,
-            old_paragraph_ref,
-            scope="script-build://scopes/full/opening",
-            kind=ScriptTaskKind.PARAGRAPH,
-        ),
-        _decision_ref(
-            structure_decision,
-            structure_ref,
-            scope="script-build://scopes/full/opening",
-            kind=ScriptTaskKind.STRUCTURE,
-        ),
-        _decision_ref(
-            paragraph_decision,
-            paragraph_ref,
-            scope="script-build://scopes/full/opening",
-            kind=ScriptTaskKind.PARAGRAPH,
-        ),
-        _decision_ref(
-            element_decision,
-            element_ref,
-            scope="script-build://scopes/full/elements",
-            kind=ScriptTaskKind.ELEMENT_SET,
-        ),
-    )
     adopted = (structure_decision, paragraph_decision, element_decision)
-    held = (old_element_decision, old_paragraph_decision)
-    compose_contract = _contract(
-        ScriptTaskKind.COMPOSE,
-        write_scope=("script-build://writes",),
-        input_refs=(
-            _decision_ref(
-                direction_decision,
-                direction_ref,
-                scope=FULL_SCOPE,
-                kind=ScriptTaskKind.DIRECTION,
-            ),
-        ),
-        closure=local_refs,
-        adopted=adopted,
-        held=held,
-        order=adopted,
-        comparison_refs=(
-            _decision_ref(
-                comparison_decision,
-                comparison_ref,
-                scope="script-build://scopes/full/opening",
-                kind=ScriptTaskKind.COMPARE,
-            ),
-        ),
-    )
     await planning.decide_script_task(
         task_id=compose,
         action=DecisionAction.REVISE.value,
         reason="freeze the selected active frontier and formal compose order",
         validation_id=None,
-        replacement_contract=compose_contract,
+        replacement_contract=None,
         child_contracts=(),
+        selected_decision_ids=adopted,
         context={"root_trace_id": ROOT, "tool_call_id": "close-compose"},
     )
     compose_decision, compose_ref = await _dispatch_accept(planning, coordinator, compose)
 
-    portfolio_contract = _contract(
-        ScriptTaskKind.CANDIDATE_PORTFOLIO,
-        write_scope=("script-build://writes",),
-        closure=(
-            _decision_ref(
-                compose_decision,
-                compose_ref,
-                scope=FULL_SCOPE,
-                kind=ScriptTaskKind.COMPOSE,
-            ),
-        ),
-        adopted=(compose_decision,),
-        order=(compose_decision,),
-    )
     await planning.decide_script_task(
         task_id=portfolio,
         action=DecisionAction.REVISE.value,
         reason="freeze the uniquely adopted StructuredScript",
         validation_id=None,
-        replacement_contract=portfolio_contract,
+        replacement_contract=None,
         child_contracts=(),
+        selected_decision_ids=(compose_decision,),
         context={"root_trace_id": ROOT, "tool_call_id": "close-portfolio"},
     )
     portfolio_decision, _ = await _dispatch_accept(planning, coordinator, portfolio)