Преглед изворни кода

清理旧E2E旁路并完善公开观测

SamLee пре 10 часа
родитељ
комит
60eb68f346

+ 11 - 0
script_build_host/src/script_build_host/api/routes.py

@@ -775,6 +775,17 @@ def create_script_build_router(
             "messages": [item.to_dict() for item in messages if int(item.sequence) > after_sequence]
         }
 
+    @router.get("/api/pattern/script_builds/{script_build_id}/traces/{trace_id}/events")
+    async def trace_events(
+        script_build_id: int,
+        trace_id: str,
+        actor: PrincipalDependency,
+        since: int = Query(0, ge=0),
+    ) -> dict[str, Any]:
+        binding = await scope(script_build_id, actor)
+        await _owned_trace(trace_store, binding.root_trace_id, trace_id)
+        return {"events": await trace_store.get_events(trace_id, since)}
+
     @router.get("/api/pattern/script_builds/{script_build_id}/trace_messages")
     async def legacy_trace_messages(
         script_build_id: int, actor: PrincipalDependency

+ 7 - 12
script_build_host/src/script_build_host/application/phase_three.py

@@ -27,12 +27,11 @@ from script_build_host.domain.ports import (
     ScriptBusinessArtifactRepository,
 )
 from script_build_host.domain.records import BuildStatus, MissionOwnerToken, Principal
+from script_build_host.domain.task_contract_compiler import TaskContractCompiler
 from script_build_host.domain.task_contracts import (
     AcceptedDecisionRef,
     ScriptCriterion,
-    ScriptIntentClass,
     ScriptTaskBudget,
-    ScriptTaskContractV1,
     ScriptTaskKind,
 )
 from script_build_host.domain.task_refs import TASK_KIND_PREFIX, task_kind
@@ -529,16 +528,16 @@ class PhaseThreeContinuationService:
         )
         if not isinstance(direction_version.artifact, DirectionArtifact):
             raise PhaseThreeBoundaryNotReady("Root Direction has the wrong Artifact type")
-        contract = ScriptTaskContractV1(
+        if not snapshot_id:
+            raise PhaseThreeBoundaryNotReady("phase-three InputSnapshot is missing")
+        contract = TaskContractCompiler().compile_host_contract(
+            task_id=root.task_id,
+            parent_task_id=root.task_id,
             task_kind=ScriptTaskKind.ROOT_DELIVERY,
+            branch_ref="script-build://branches/root-delivery",
             scope_ref=closure.direction.scope_ref,
-            intent_class=ScriptIntentClass.DELIVER,
             objective=root.current_spec.objective,
             input_decision_refs=(closure.direction, closure.portfolio),
-            base_artifact_ref=None,
-            write_scope=(),
-            gap_ref=None,
-            output_schema="root-delivery-manifest/v1",
             criteria=tuple(
                 ScriptCriterion(item.criterion_id, item.description, item.hard)
                 for item in root.current_spec.acceptance_criteria
@@ -547,10 +546,6 @@ class PhaseThreeContinuationService:
             goal_ids=tuple(item.goal_id for item in direction_version.artifact.goals),
         )
         frozen = await self.contracts.freeze(root_trace_id, contract)
-        # Snapshot identity is present in TaskSpec; this guards accidental use of
-        # a contract prepared for a different continuation.
-        if not snapshot_id:
-            raise PhaseThreeBoundaryNotReady("phase-three InputSnapshot is missing")
         return frozen
 
     async def _prepare_policy_messages(

+ 150 - 88
script_build_host/src/script_build_host/internal_e2e.py

@@ -582,82 +582,123 @@ def _checkpoint_report(
 
 
 async def _collect_diagnostics(host: Any, script_build_id: int) -> dict[str, Any]:
-    binding = await host.composition.mission_service.bindings.get_by_build(script_build_id)
-    ledger = await host.composition.coordinator.task_store.load(binding.root_trace_id)
-    ledger_value = ledger.to_dict()
-    root = ledger.tasks[ledger.root_task_id]
+    transport = httpx.ASGITransport(
+        app=host.composition.app,
+        raise_app_exceptions=False,
+    )
+    async with httpx.AsyncClient(
+        transport=transport,
+        base_url="http://script-build-internal",
+        timeout=120,
+    ) as client:
+        mission = _successful_json(
+            await client.get(
+                f"/api/pattern/script_builds/{script_build_id}/mission"
+            ),
+            "mission diagnostics",
+        )
+        trace_page = _successful_json(
+            await client.get(
+                f"/api/pattern/script_builds/{script_build_id}/traces"
+            ),
+            "trace diagnostics",
+        )
+        traces = {
+            str(item.get("trace_id")): item
+            for item in trace_page.get("traces", ())
+            if isinstance(item, dict) and item.get("trace_id")
+        }
+        trace_messages: dict[str, list[dict[str, Any]]] = {}
+        trace_events: dict[str, list[dict[str, Any]]] = {}
+        for trace_id in traces:
+            messages = _successful_json(
+                await client.get(
+                    f"/api/pattern/script_builds/{script_build_id}"
+                    f"/traces/{trace_id}/messages"
+                ),
+                "trace messages",
+            )
+            events = _successful_json(
+                await client.get(
+                    f"/api/pattern/script_builds/{script_build_id}"
+                    f"/traces/{trace_id}/events"
+                ),
+                "trace events",
+            )
+            trace_messages[trace_id] = [
+                item for item in messages.get("messages", ()) if isinstance(item, dict)
+            ]
+            trace_events[trace_id] = [
+                item for item in events.get("events", ()) if isinstance(item, dict)
+            ]
+
+    tasks = {
+        str(item["task_id"]): item
+        for item in mission.get("tasks", ())
+        if isinstance(item, dict) and item.get("task_id")
+    }
+    attempts = [
+        item for item in mission.get("attempts", ()) if isinstance(item, dict)
+    ]
+    validations = [
+        item for item in mission.get("validations", ()) if isinstance(item, dict)
+    ]
+    operations = [
+        item for item in mission.get("operations", ()) if isinstance(item, dict)
+    ]
+    root = tasks[str(mission["root_task_id"])]
     active = [
-        ledger_value["operations"][item.operation_id]
-        for item in ledger.operations.values()
-        if item.status.value in {"pending", "running", "stop_requested"}
+        item
+        for item in operations
+        if item.get("status") in {"pending", "running", "stop_requested"}
     ]
-    attempts = list(ledger.attempts.values())
-    validations = list(ledger.validations.values())
     task_seconds: dict[str, float] = {}
     preset_usage: dict[str, dict[str, int | float]] = {}
     domain_errors: list[dict[str, str]] = []
     planner_tool_errors: list[dict[str, str]] = []
-    trace_store = host.composition.mission_service.runner.trace_store
-    trace_presets = {binding.root_trace_id: "script_planner"}
-    trace_presets.update({item.worker_trace_id: item.worker_preset for item in attempts})
-    trace_presets.update({item.validator_trace_id: item.validator_preset for item in validations})
-    traces_with_usage: set[str] = set()
-    usage_reader = getattr(
-        trace_store, "get_model_usage", None
+    root_trace_id = str(mission["root_trace_id"])
+    trace_presets = {root_trace_id: "script_planner"}
+    trace_presets.update(
+        {
+            str(item.get("worker_trace_id")): str(item.get("worker_preset"))
+            for item in attempts
+            if item.get("worker_trace_id")
+        }
     )
-    if callable(usage_reader):
-        for trace_id, preset in trace_presets.items():
-            model_usage = await usage_reader(trace_id)
-            models = model_usage.get("models", []) if isinstance(model_usage, dict) else []
-            agent_models = [
-                item for item in models if isinstance(item, dict) and item.get("source") == "agent"
-            ]
-            if agent_models:
-                traces_with_usage.add(trace_id)
-            usage = preset_usage.setdefault(str(preset), _empty_preset_usage())
-            usage["agent_runs"] += 1
-            for model in agent_models:
-                usage["calls"] += int(model.get("call_count") or 0)
-                usage["prompt_tokens"] += int(model.get("prompt_tokens") or 0)
-                usage["completion_tokens"] += int(model.get("completion_tokens") or 0)
-                usage["cache_read_tokens"] += int(model.get("cache_read_tokens") or 0)
-                usage["tokens"] += int(model.get("total_tokens") or 0)
-    planner_trace = await trace_store.get_trace(binding.root_trace_id)
-    preset_usage.setdefault("script_planner", _empty_preset_usage())["seconds"] += (
-        _trace_elapsed_seconds(planner_trace)
+    trace_presets.update(
+        {
+            str(item.get("validator_trace_id")): str(item.get("validator_preset"))
+            for item in validations
+            if item.get("validator_trace_id")
+        }
     )
+    for trace_id, preset in trace_presets.items():
+        usage = preset_usage.setdefault(preset, _empty_preset_usage())
+        usage["agent_runs"] += 1
+        usage["seconds"] += _trace_elapsed_seconds(traces.get(trace_id))
     for item in [*attempts, *validations]:
-        task = ledger.tasks[item.task_id]
+        task_id = str(item.get("task_id"))
+        task = tasks[task_id]
         kind = next(
             (
                 ref.rsplit("/", 1)[-1]
-                for ref in task.current_spec.context_refs
+                for ref in task.get("current_spec", {}).get("context_refs", ())
                 if "/task-kinds/" in ref
             ),
             "root",
         )
-        if item.duration_ms is not None:
-            task_seconds[kind] = task_seconds.get(kind, 0.0) + item.duration_ms / 1000
-        stats = item.execution_stats
-        if stats is not None:
-            key = getattr(item, "worker_preset", None) or getattr(
-                item, "validator_preset", "unknown"
-            )
+        duration_ms = item.get("duration_ms")
+        if duration_ms is not None:
+            task_seconds[kind] = task_seconds.get(kind, 0.0) + float(duration_ms) / 1000
+        stats = item.get("execution_stats")
+        if isinstance(stats, dict):
+            key = item.get("worker_preset") or item.get("validator_preset") or "unknown"
             usage = preset_usage.setdefault(str(key), _empty_preset_usage())
             usage["stage_runs"] += 1
-            trace_id = getattr(item, "worker_trace_id", None) or getattr(
-                item, "validator_trace_id", ""
-            )
-            if trace_id not in traces_with_usage:
-                usage["calls"] += 1
-                usage["tokens"] += int(stats.total_tokens or 0)
-            usage["seconds"] += (item.duration_ms or 0) / 1000
-            failure = getattr(item, "failure", None)
-            failure_code = str(
-                getattr(failure, "code", "")
-                or getattr(stats.failure_code, "value", stats.failure_code)
-                or ""
-            )
+            usage["calls"] += 1
+            usage["tokens"] += int(stats.get("total_tokens") or 0)
+            usage["seconds"] += float(duration_ms or 0) / 1000
+            failure_code = str(stats.get("failure_code") or "")
             if failure_code:
                 usage["failures"] += 1
                 if "timeout" in failure_code.lower():
@@ -665,57 +706,62 @@ async def _collect_diagnostics(host: Any, script_build_id: int) -> dict[str, Any
                 domain_errors.append(
                     {
                         "code": failure_code,
-                        "message": str(getattr(failure, "message", "") or item.error or ""),
-                        "task_id": item.task_id,
+                        "message": str(item.get("error") or ""),
+                        "task_id": task_id,
                     }
                 )
 
-    trace_ids = {binding.root_trace_id}
-    trace_ids.update(item.worker_trace_id for item in attempts)
-    trace_ids.update(item.validator_trace_id for item in validations)
     tool_calls: dict[str, int] = {}
     broker_events = [
         item
-        for item in await trace_store.get_events(binding.root_trace_id)
+        for item in trace_events.get(root_trace_id, ())
         if item.get("event") == "context_broker_receipt"
+        or item.get("event_type") == "context_broker_receipt"
     ]
-    for trace_id in trace_ids:
-        for message in await trace_store.get_trace_messages(trace_id):
-            content = message.content
+    for trace_id, message_rows in trace_messages.items():
+        for message in message_rows:
+            content = message.get("content")
             if not isinstance(content, dict):
                 continue
-            if trace_id == binding.root_trace_id:
+            if trace_id == root_trace_id:
                 failure = _tool_failure_from_message(message)
                 if failure is not None:
                     if not failure["task_id"]:
-                        failure["task_id"] = root.task_id
+                        failure["task_id"] = str(root["task_id"])
                     planner_tool_errors.append(failure)
             for call in content.get("tool_calls") or []:
                 name = str(call.get("function", {}).get("name") or "unknown")
                 tool_calls[name] = tool_calls.get(name, 0) + 1
     historical_errors = [*domain_errors, *planner_tool_errors]
-    last_task = max(ledger.tasks.values(), key=lambda item: item.updated_at)
-    last_attempt = max(attempts, key=lambda item: item.updated_at) if attempts else None
-    last_validation = max(validations, key=lambda item: item.updated_at) if validations else None
+    last_task = max(tasks.values(), key=lambda item: str(item.get("updated_at", "")))
+    last_attempt = (
+        max(attempts, key=lambda item: str(item.get("updated_at", "")))
+        if attempts
+        else None
+    )
+    last_validation = (
+        max(validations, key=lambda item: str(item.get("updated_at", "")))
+        if validations
+        else None
+    )
     return {
-        "checkpoint": root.blocked_reason or root.status.value,
-        "root": {"status": root.status.value, "blocked_reason": root.blocked_reason},
+        "checkpoint": root.get("blocked_reason") or root.get("status"),
+        "root": {
+            "status": root.get("status"),
+            "blocked_reason": root.get("blocked_reason"),
+        },
         "active_operations": active,
-        "last_task": ledger_value["tasks"][last_task.task_id],
-        "last_attempt": (
-            ledger_value["attempts"][last_attempt.attempt_id] if last_attempt else None
-        ),
-        "last_validation": (
-            ledger_value["validations"][last_validation.validation_id] if last_validation else None
-        ),
+        "last_task": last_task,
+        "last_attempt": last_attempt,
+        "last_validation": last_validation,
         "current_domain_error": (
             None
-            if root.status.value == "completed"
+            if root.get("status") == "completed"
             else next(
                 (
                     item
                     for item in reversed(domain_errors)
-                    if ledger.tasks[item["task_id"]].status.value
+                    if tasks[item["task_id"]].get("status")
                     not in {"completed", "cancelled", "superseded"}
                 ),
                 None,
@@ -725,7 +771,7 @@ async def _collect_diagnostics(host: Any, script_build_id: int) -> dict[str, Any
             (
                 item
                 for item in reversed(domain_errors)
-                if ledger.tasks[item["task_id"]].status.value
+                if tasks[item["task_id"]].get("status")
                 in {"completed", "cancelled", "superseded"}
             ),
             None,
@@ -743,15 +789,22 @@ async def _collect_diagnostics(host: Any, script_build_id: int) -> dict[str, Any
         },
         "context_broker": _context_broker_diagnostics(broker_events),
         "deterministic_preflight_rejection": any(
-            "Deterministic preflight rejected" in item.summary for item in validations
+            "Deterministic preflight rejected" in str(item.get("summary", ""))
+            for item in validations
         ),
     }
 
 
 def _tool_failure_from_message(message: Any) -> dict[str, str] | None:
-    if getattr(message, "role", None) != "tool" or not isinstance(message.content, dict):
+    role = message.get("role") if isinstance(message, dict) else getattr(message, "role", None)
+    content = (
+        message.get("content")
+        if isinstance(message, dict)
+        else getattr(message, "content", None)
+    )
+    if role != "tool" or not isinstance(content, dict):
         return None
-    result = message.content.get("result")
+    result = content.get("result")
     if isinstance(result, str):
         try:
             payload = json.loads(result)
@@ -779,7 +832,7 @@ def _tool_failure_from_message(message: Any) -> dict[str, str] | None:
         "message": str(failure.get("message") or ""),
         "task_id": task_id,
         "source_tool": str(
-            failure.get("source_tool") or message.content.get("tool_name") or ""
+            failure.get("source_tool") or content.get("tool_name") or ""
         ),
     }
 
@@ -787,6 +840,15 @@ def _tool_failure_from_message(message: Any) -> dict[str, str] | None:
 def _trace_elapsed_seconds(trace: Any) -> float:
     if trace is None:
         return 0.0
+    if isinstance(trace, dict):
+        started_at = trace.get("created_at")
+        completed_at = trace.get("completed_at") or trace.get("last_activity_at")
+        try:
+            start = datetime.fromisoformat(str(started_at).replace("Z", "+00:00"))
+            end = datetime.fromisoformat(str(completed_at).replace("Z", "+00:00"))
+        except (TypeError, ValueError):
+            return max(float(trace.get("total_duration_ms", 0) or 0) / 1000, 0.0)
+        return max((end - start).total_seconds(), 0.0)
     started_at = getattr(trace, "created_at", None)
     completed_at = getattr(trace, "completed_at", None) or getattr(
         trace, "last_activity_at", None

+ 0 - 596
script_build_host/tests/test_phase_one_e2e.py

@@ -1,596 +0,0 @@
-from __future__ import annotations
-
-import json
-from dataclasses import replace
-from datetime import UTC, datetime
-from types import SimpleNamespace
-
-import pytest
-from agent import AgentRunner, FileSystemArtifactStore, FileSystemTaskStore, FileSystemTraceStore
-from agent.orchestration import ArtifactRef, TaskStatus, ValidationVerdict
-
-from script_build_host.composition import HostDependencies, compose_host
-from script_build_host.domain.artifacts import (
-    ArtifactKind,
-    ArtifactState,
-    ArtifactVersion,
-    EvidenceRecordV1,
-)
-from script_build_host.domain.errors import ArtifactNotFound
-from script_build_host.domain.input_snapshot import ScriptBuildInputSnapshotV1
-from script_build_host.domain.records import (
-    BuildStatus,
-    MissionBinding,
-    Principal,
-    PublicationState,
-)
-from script_build_host.infrastructure.canonical_json import canonical_sha256
-from script_build_host.tools.gateway import RetrievalResult
-
-
-def _tool(call_id: str, name: str, arguments: dict[str, object]) -> dict[str, object]:
-    return {
-        "content": "",
-        "tool_calls": [
-            {
-                "id": call_id,
-                "type": "function",
-                "function": {"name": name, "arguments": json.dumps(arguments)},
-            }
-        ],
-        "finish_reason": "tool_calls",
-    }
-
-
-def _prompt(messages: list[dict[str, object]]) -> dict[str, object]:
-    for message in reversed(messages):
-        if message.get("role") != "user" or not isinstance(message.get("content"), str):
-            continue
-        try:
-            value = json.loads(message["content"])
-        except ValueError:
-            continue
-        if isinstance(value, dict) and "task_spec" in value:
-            return value
-    raise AssertionError("missing role prompt")
-
-
-def _last_tool_json(messages: list[dict[str, object]]) -> dict[str, object] | None:
-    for message in reversed(messages):
-        if message.get("role") != "tool":
-            continue
-        content = message.get("content")
-        if isinstance(content, str):
-            try:
-                value = json.loads(content)
-                return value if isinstance(value, dict) else None
-            except ValueError:
-                return None
-    return None
-
-
-def _contract(
-    kind: str,
-    objective: str,
-    criterion_id: str,
-    description: str,
-    *,
-    scope: str,
-) -> dict[str, object]:
-    criterion = (
-        {
-            "client_key": criterion_id,
-            "criterion_type": "evidence_nonempty",
-            "params": {"minimum_count": 1},
-            "hard": True,
-        }
-        if kind.endswith("-retrieval")
-        else {
-            "client_key": criterion_id,
-            "criterion_type": "custom",
-            "description": description,
-            "hard": True,
-        }
-    )
-    return {
-        "task_kind": kind,
-        "scope_selector": {
-            "anchor": "mission",
-            "path": scope.removeprefix("script-build://scopes/").split("/")[:4],
-        },
-        "intent_class": "explore",
-        "objective": objective,
-        "input_decision_ids": [],
-        "base_decision_id": None,
-        "gap_key": None,
-        "criteria": [criterion],
-        "goal_ids": [],
-        "supersedes_decision_ids": [],
-        "comparison_decision_ids": [],
-    }
-
-
-class _ScriptedLLM:
-    def __init__(self, store: FileSystemTaskStore, root: str) -> None:
-        self.store = store
-        self.root = root
-        self.calls = 0
-        self.decode_versions: list[int] = []
-        self.validated_artifact_kinds: list[str] = []
-        self.replayed_dispatch = False
-
-    def call(self, name: str, arguments: dict[str, object]) -> dict[str, object]:
-        self.calls += 1
-        return _tool(f"call-{self.calls}", name, arguments)
-
-    def call_with_id(
-        self,
-        call_id: str,
-        name: str,
-        arguments: dict[str, object],
-    ) -> dict[str, object]:
-        self.calls += 1
-        return _tool(call_id, name, arguments)
-
-    async def __call__(self, messages, tools, **_kwargs):
-        names = {item["function"]["name"] for item in tools or []}
-        if "plan_script_tasks" in names:
-            return await self._planner()
-        if "submit_validation" in names:
-            return self._validator(messages)
-        if "submit_attempt" in names:
-            return self._worker(messages, names)
-        raise AssertionError(sorted(names))
-
-    async def _planner(self):
-        ledger = await self.store.load(self.root)
-        root = ledger.tasks[ledger.root_task_id]
-        direction = next(
-            (
-                item
-                for item in ledger.tasks.values()
-                if "script-build://task-kinds/direction" in item.current_spec.context_refs
-            ),
-            None,
-        )
-        if direction is None:
-            return self.call(
-                "plan_script_tasks",
-                {
-                    "contracts": [
-                        _contract(
-                            "direction",
-                            "build direction",
-                            "direction-ready",
-                            "direction is grounded",
-                            scope="script-build://scopes/direction",
-                        )
-                    ],
-                },
-            )
-        retrieval = next(
-            (
-                item
-                for item in ledger.tasks.values()
-                if "script-build://task-kinds/decode-retrieval" in item.current_spec.context_refs
-            ),
-            None,
-        )
-        if retrieval is None:
-            return self.call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": direction.task_id,
-                    "contracts": [
-                        _contract(
-                            "decode-retrieval",
-                            "decode evidence",
-                            "evidence-ready",
-                            "evidence is relevant",
-                            scope="script-build://scopes/direction/decode",
-                        )
-                    ],
-                },
-            )
-        if retrieval.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self.call_with_id(
-                f"decode-dispatch-v{retrieval.current_spec_version}",
-                "dispatch_script_tasks",
-                {"task_ids": [retrieval.task_id]},
-            )
-        if retrieval.status == TaskStatus.AWAITING_DECISION:
-            if retrieval.current_spec_version == 1 and not self.replayed_dispatch:
-                self.replayed_dispatch = True
-                return self.call_with_id(
-                    "decode-dispatch-v1",
-                    "dispatch_script_tasks",
-                    {"task_ids": [retrieval.task_id]},
-                )
-            validation = ledger.validations[retrieval.validation_ids[-1]]
-            if validation.verdict == ValidationVerdict.FAILED:
-                return self.call(
-                    "decide_script_task",
-                    {
-                        "task_id": retrieval.task_id,
-                        "validation_id": validation.validation_id,
-                        "action": "revise",
-                        "reason": "make decode query precise",
-                        "replacement_contract": _contract(
-                            "decode-retrieval",
-                            "decode evidence revised",
-                            "evidence-ready",
-                            "evidence is relevant",
-                            scope="script-build://scopes/direction/decode",
-                        ),
-                    },
-                )
-            return self.call(
-                "decide_script_task",
-                {
-                    "task_id": retrieval.task_id,
-                    "validation_id": validation.validation_id,
-                    "action": "accept",
-                    "reason": "decode evidence passed",
-                },
-            )
-        if direction.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self.call("dispatch_script_tasks", {"task_ids": [direction.task_id]})
-        if direction.status == TaskStatus.AWAITING_DECISION:
-            validation = ledger.validations[direction.validation_ids[-1]]
-            return self.call(
-                "decide_script_task",
-                {
-                    "task_id": direction.task_id,
-                    "validation_id": validation.validation_id,
-                    "action": "accept",
-                    "reason": "direction passed",
-                },
-            )
-        assert direction.status == TaskStatus.COMPLETED
-        if root.current_spec_version == 1:
-            return self.call(
-                "decide_script_task",
-                {
-                    "task_id": root.task_id,
-                    "action": "revise",
-                    "reason": "record accepted direction in root objective",
-                    "replacement_contract": {
-                        "objective": root.current_spec.objective + " using accepted direction",
-                    },
-                },
-            )
-        return self.call(
-            "decide_script_task",
-            {
-                "task_id": root.task_id,
-                "action": "block",
-                "reason": "PHASE_ONE_CAPABILITY_BOUNDARY",
-            },
-        )
-
-    def _worker(self, messages, names):
-        prompt = _prompt(messages)
-        spec = prompt["task_spec"]
-        last = _last_tool_json(messages)
-        if "search_script_decode_case" in names:
-            if last is None or not last.get("submitted"):
-                self.decode_versions.append(spec["version"])
-                return self.call(
-                    "search_script_decode_case",
-                    {"return_field": "主脉络", "keyword": "focus", "top_k": 3},
-                )
-            return self.call(
-                "submit_attempt",
-                {},
-            )
-        if last is None or "direction_handle" not in last:
-            accepted = prompt["role_context"]["accepted_inputs"]
-            return self.call(
-                "save_direction_candidate",
-                {
-                    "goals": [
-                        {
-                            "client_key": "grounded-direction",
-                            "statement": "make one grounded direction",
-                            "rationale": "accepted evidence",
-                            "success_criteria": ["the direction remains grounded"],
-                        }
-                    ],
-                    "constraints": [
-                        {
-                            "client_key": "c1",
-                            "statement": "remain grounded",
-                            "rationale": "accepted evidence is mandatory",
-                        }
-                    ],
-                    "preferences": [],
-                    "strategy_handles": [],
-                    "evidence_decision_ids": [accepted[0]["decision_id"]],
-                },
-            )
-        return self.call(
-            "submit_attempt",
-            {},
-        )
-
-    def _validator(self, messages):
-        prompt = _prompt(messages)
-        spec = prompt["task_spec"]
-        kind = (
-            "direction"
-            if "script-build://task-kinds/direction" in spec["context_refs"]
-            else "evidence"
-        )
-        role_context = prompt["role_context"]
-        assert role_context["artifact"]["items"], "validator must receive the frozen artifact body"
-        artifact = role_context["artifact"]["items"][0]["content"]
-        assert isinstance(artifact, dict)
-        self.validated_artifact_kinds.append(kind)
-        failed = spec["objective"] == "decode evidence" and spec["version"] == 1
-        verdict = "failed" if failed else "passed"
-        evidence_handle = role_context["evidence_handles"][0]["evidence_handle"]
-        return self.call(
-            "submit_validation",
-            {
-                "verdict": verdict,
-                "criterion_results": [
-                    {
-                        "criterion_id": item["criterion_id"],
-                        "verdict": verdict,
-                        "reason": "checked frozen snapshot",
-                        "evidence_handle_ids": [] if failed else [evidence_handle],
-                    }
-                    for item in spec["acceptance_criteria"]
-                ],
-                "defects": (
-                    [
-                        {
-                            "defect_code": "DECODE_QUERY_TOO_BROAD",
-                            "criterion_id": spec["acceptance_criteria"][0]["criterion_id"],
-                            "scope_selector": {
-                                "anchor": "mission",
-                                "path": ["direction", "decode"],
-                            },
-                            "observed_excerpt": "v1 decode evidence was too broad",
-                            "evidence_handle_ids": [],
-                            "severity": "hard",
-                            "invalidated_inputs": [],
-                            "recommended_action_class": "revise",
-                        }
-                    ]
-                    if failed
-                    else []
-                ),
-                "recommendation": "revise" if failed else "accept",
-            },
-        )
-
-
-class _Bindings:
-    def __init__(self, binding: MissionBinding) -> None:
-        self.binding = binding
-        self.active = None
-
-    async def get_by_build(self, _: int) -> MissionBinding:
-        return self.binding
-
-    async def get_by_root(self, _: str) -> MissionBinding:
-        return self.binding
-
-    async def set_active_direction(self, **values) -> MissionBinding:
-        self.active = values["artifact_version_id"]
-        return replace(self.binding, active_direction_artifact_version_id=self.active)
-
-
-class _Snapshots:
-    def __init__(self, snapshot: ScriptBuildInputSnapshotV1) -> None:
-        self.snapshot = snapshot
-
-    async def get(self, *_args, **_kwargs) -> ScriptBuildInputSnapshotV1:
-        return self.snapshot
-
-
-class _Artifacts:
-    def __init__(self) -> None:
-        self.values = {}
-        self.next_id = 1
-
-    async def freeze(self, *, artifact, script_build_id, task_id, attempt_id, spec_version):
-        payload = artifact.content_payload()
-        digest = canonical_sha256(payload).wire
-        if isinstance(artifact, EvidenceRecordV1):
-            artifact = replace(artifact, content_sha256=digest)
-            kind = ArtifactKind.EVIDENCE
-        else:
-            artifact = replace(artifact, canonical_sha256=digest)
-            kind = ArtifactKind.DIRECTION
-        value = ArtifactVersion(
-            self.next_id,
-            script_build_id,
-            task_id,
-            attempt_id,
-            spec_version,
-            kind,
-            digest,
-            ArtifactState.FROZEN,
-            artifact,
-            datetime.now(UTC),
-            datetime.now(UTC),
-        )
-        ref = ArtifactRef(
-            uri=f"script-build://artifact-versions/{self.next_id}",
-            kind=kind.value,
-            version=str(self.next_id),
-            digest=digest,
-            metadata={
-                "script_build_id": script_build_id,
-                "task_id": task_id,
-                "attempt_id": attempt_id,
-            },
-        )
-        self.values[self.next_id] = (value, ref)
-        self.next_id += 1
-        return value, ref
-
-    async def read_by_ref(self, ref, **_kwargs):
-        return self.values[int(ref.version)][0]
-
-    async def get_by_attempt(self, *, script_build_id, task_id, attempt_id):
-        try:
-            return next(
-                value
-                for value, _ref in self.values.values()
-                if value.script_build_id == script_build_id
-                and value.task_id == task_id
-                and value.attempt_id == attempt_id
-            )
-        except StopIteration as exc:
-            raise ArtifactNotFound() from exc
-
-
-class _Legacy:
-    def __init__(self) -> None:
-        self.status = BuildStatus.RUNNING
-        self.direction = None
-
-    async def get_status(self, _: int) -> BuildStatus:
-        return self.status
-
-    async def set_status(self, _id, status, **_kwargs):
-        self.status = status
-
-    async def project_direction(self, _id, value):
-        assert self.status != BuildStatus.STOPPING
-        self.direction = value
-
-
-class _Publications:
-    def __init__(self) -> None:
-        self.state = PublicationState.PENDING
-
-    async def prepare(self, **values):
-        return SimpleNamespace(publication_id=1, state=self.state, **values)
-
-    async def mark_published(self, _id):
-        self.state = PublicationState.PUBLISHED
-
-    async def mark_failed(self, _id, **_values):
-        self.state = PublicationState.FAILED
-
-
-class _Decode:
-    async def retrieve(self, *, query, snapshot):
-        del snapshot
-        return RetrievalResult(
-            source_refs=(f"decode:{query['keyword']}",),
-            summary="evidence",
-            supports=("direction",),
-            confidence="high",
-            metadata={"index_sha256": "sha256:" + "1" * 64, "ranks": [1]},
-        )
-
-
-class _PrincipalProvider:
-    async def current(self, request_context=None):
-        del request_context
-        return Principal("owner")
-
-
-class _Authorizer:
-    async def require_source_access(self, _principal, **_source):
-        return None
-
-    async def require_access(self, _principal, _build):
-        return None
-
-
-@pytest.mark.asyncio
-async def test_real_runner_phase_one_fail_revise_pass_direction_accept_then_root_block(tmp_path):
-    now = datetime.now(UTC)
-    binding = MissionBinding(1, 7, "phase-one-root", 11, None, None, "test", "v1", now, now)
-    model_presets = {
-        name: {"model": "fake-model", "temperature": 0, "max_iterations": 50}
-        for name in (
-            "script_decode_retrieval_worker",
-            "script_direction_worker",
-            "script_retrieval_validator",
-            "script_candidate_validator",
-        )
-    }
-    snapshot = ScriptBuildInputSnapshotV1(
-        "11",
-        7,
-        1,
-        2,
-        3,
-        {"topic": {"id": 3, "result": "topic"}},
-        {"account_name": "acct"},
-        (),
-        (),
-        (),
-        (),
-        {},
-        {"presets": model_presets},
-        "sha256:" + "a" * 64,
-        now,
-    )
-    task_store = FileSystemTaskStore(str(tmp_path / "ledger"))
-    llm = _ScriptedLLM(task_store, binding.root_trace_id)
-    runner = AgentRunner(trace_store=FileSystemTraceStore(str(tmp_path / "trace")), llm_call=llm)
-    bindings = _Bindings(binding)
-    snapshots = _Snapshots(snapshot)
-    artifacts = _Artifacts()
-    legacy = _Legacy()
-    publications = _Publications()
-    composition = compose_host(
-        HostDependencies(
-            runner=runner,
-            task_store=task_store,
-            framework_artifact_store=FileSystemArtifactStore(str(tmp_path / "artifacts")),
-            input_snapshot_service=SimpleNamespace(get=snapshots.get),
-            input_snapshots=snapshots,
-            bindings=bindings,
-            business_artifacts=artifacts,
-            publications=publications,
-            legacy_state=legacy,
-            principal_provider=_PrincipalProvider(),
-            build_authorizer=_Authorizer(),
-            retrieval_adapters={"decode": _Decode()},
-        )
-    )
-    await composition.mission_service.run(7)
-    ledger = await task_store.load(binding.root_trace_id)
-    root = ledger.tasks[ledger.root_task_id]
-    assert root.status == TaskStatus.BLOCKED
-    assert root.blocked_reason == "PHASE_ONE_CAPABILITY_BOUNDARY"
-    assert root.current_spec_version == 2
-    assert llm.decode_versions == [1, 2]
-    assert llm.replayed_dispatch
-    assert len(ledger.operations) == 3
-    assert llm.validated_artifact_kinds == ["evidence", "evidence", "direction"]
-    assert legacy.status == BuildStatus.PARTIAL
-    assert legacy.direction.startswith("# Direction\n\n## Goals")
-    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
-    assert not hasattr(bindings.binding, "branch_id")
-
-    model_calls = llm.calls
-    reloaded_ledger = await FileSystemTaskStore(str(tmp_path / "ledger")).load(
-        binding.root_trace_id
-    )
-    assert reloaded_ledger.tasks[reloaded_ledger.root_task_id].status == TaskStatus.BLOCKED
-    reloaded_traces = FileSystemTraceStore(str(tmp_path / "trace"))
-    reloaded_trace = await reloaded_traces.get_trace(binding.root_trace_id)
-    assert reloaded_trace is not None
-    assert await reloaded_traces.get_goal_tree(binding.root_trace_id) is None
-    submitted = next(
-        attempt for attempt in reloaded_ledger.attempts.values() if attempt.snapshot_id
-    )
-    assert await FileSystemArtifactStore(str(tmp_path / "artifacts")).get(
-        binding.root_trace_id,
-        submitted.snapshot_id,
-    )
-    assert llm.calls == model_calls

+ 0 - 2294
script_build_host/tests/test_phase_two_real_runner_e2e.py

@@ -1,2294 +0,0 @@
-from __future__ import annotations
-
-import asyncio
-import json
-from collections.abc import Mapping, Sequence
-from datetime import UTC, datetime, timedelta
-from pathlib import Path
-from types import SimpleNamespace
-from typing import Any, cast
-from uuid import uuid4
-
-import httpx
-import pytest
-from agent import (
-    AgentRunner,
-    FileSystemArtifactStore,
-    FileSystemTaskStore,
-    FileSystemTraceStore,
-    Message,
-)
-from agent.orchestration import ArtifactRef, DecisionAction, TaskStatus, ValidationVerdict
-from sqlalchemy import select
-
-from script_build_host.agents.prompts import (
-    phase_one_prompt_manifest,
-    phase_three_prompt_manifest,
-    script_build_prompt_manifest,
-)
-from script_build_host.application.input_snapshot_service import ScriptInputSnapshotService
-from script_build_host.application.legacy_projection import LegacyDetailProjectionService
-from script_build_host.application.mission_service import PHASE_ONE_CAPABILITY_BOUNDARY
-from script_build_host.application.phase_two_planning import (
-    PHASE_TWO_CANDIDATE_PORTFOLIO_READY,
-)
-from script_build_host.composition import HostDependencies, compose_host
-from script_build_host.domain.artifacts import ArtifactKind
-from script_build_host.domain.input_snapshot import ScriptBuildInput
-from script_build_host.domain.records import BuildStatus, Principal
-from script_build_host.infrastructure.canonical_json import canonical_sha256
-from script_build_host.infrastructure.final_publication import (
-    SqlAlchemyFinalPublicationUnitOfWork,
-)
-from script_build_host.infrastructure.legacy_tables import (
-    script_build_paragraph,
-    script_build_record,
-)
-from script_build_host.infrastructure.ownership import FencedCommandGate, OwnerLease
-from script_build_host.infrastructure.tables import (
-    artifact_version_table,
-    input_snapshot_table,
-    mission_binding_table,
-    publication_table,
-)
-from script_build_host.infrastructure.task_contract_store import FileScriptTaskContractStore
-from script_build_host.internal_e2e import _collect_diagnostics
-from script_build_host.repositories.legacy_state import SqlAlchemyLegacyBuildStateRepository
-from script_build_host.repositories.sqlalchemy import (
-    SqlAlchemyInputSnapshotRepository,
-    SqlAlchemyMissionBindingRepository,
-    SqlAlchemyPublicationRepository,
-    SqlAlchemyScriptBusinessArtifactRepository,
-)
-from script_build_host.repositories.workspace import SqlAlchemyCandidateWorkspaceRepository
-from script_build_host.tools.gateway import RetrievalResult
-
-ROOT = "phase-two-real-runner-root"
-FULL_SCOPE = "script-build://scopes/full"
-
-
-def _tool(call_id: str, name: str, arguments: Mapping[str, Any]) -> dict[str, Any]:
-    return {
-        "content": "",
-        "tool_calls": [
-            {
-                "id": call_id,
-                "type": "function",
-                "function": {"name": name, "arguments": json.dumps(arguments)},
-            }
-        ],
-        "finish_reason": "tool_calls",
-    }
-
-
-def _role_prompt(messages: Sequence[Mapping[str, Any]]) -> Mapping[str, Any]:
-    for message in reversed(messages):
-        if message.get("role") != "user" or not isinstance(message.get("content"), str):
-            continue
-        try:
-            value = json.loads(cast(str, message["content"]))
-        except ValueError:
-            continue
-        if isinstance(value, dict) and "task_spec" in value:
-            return value
-    raise AssertionError("role trace does not contain the protected Task prompt")
-
-
-def _last_tool_value(messages: Sequence[Mapping[str, Any]]) -> Any:
-    for message in reversed(messages):
-        if message.get("role") != "tool" or not isinstance(message.get("content"), str):
-            continue
-        try:
-            return json.loads(cast(str, message["content"]))
-        except ValueError:
-            return None
-    return None
-
-
-def _artifact_ref_payload(ref: ArtifactRef) -> dict[str, Any]:
-    return {
-        "uri": ref.uri,
-        "kind": ref.kind,
-        "version": ref.version,
-        "digest": ref.digest,
-        "summary": ref.summary,
-        "metadata": ref.metadata,
-    }
-
-
-def _decision_ref(
-    decision_id: str,
-    artifact_ref: ArtifactRef,
-    *,
-    scope_ref: str,
-    task_kind: str,
-) -> dict[str, Any]:
-    return {
-        "decision_id": decision_id,
-        "artifact_ref": _artifact_ref_payload(artifact_ref),
-        "scope_ref": scope_ref,
-        "expected_task_kind": task_kind,
-    }
-
-
-def _contract(
-    task_kind: str,
-    *,
-    scope_ref: str,
-    write_scope: Sequence[str],
-    objective: str | None = None,
-    input_decision_refs: Sequence[Mapping[str, Any]] = (),
-    base_artifact_ref: Mapping[str, Any] | None = None,
-    closure: Sequence[Mapping[str, Any]] = (),
-    adopted: Sequence[str] = (),
-    held: Sequence[str] = (),
-    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]:
-    del write_scope, closure, adopted, held, compose_order
-    input_decision_ids = [str(item["decision_id"]) for item in input_decision_refs]
-    base_decision_id = None
-    if base_artifact_ref is not None:
-        base_uri = str(base_artifact_ref["uri"])
-        base_decision_id = next(
-            (
-                str(item["decision_id"])
-                for item in input_decision_refs
-                if str(cast(Mapping[str, Any], item["artifact_ref"])["uri"]) == base_uri
-            ),
-            None,
-        )
-        assert base_decision_id is not None
-    criterion = (
-        {
-            "client_key": f"{task_kind}-closed",
-            "criterion_type": "evidence_nonempty",
-            "params": {"minimum_count": 1},
-            "hard": True,
-        }
-        if task_kind.endswith("-retrieval")
-        else {
-            "client_key": f"{task_kind}-closed",
-            "criterion_type": "custom",
-            "description": "the immutable output is concrete and causally closed",
-            "hard": True,
-        }
-    )
-    return {
-        "task_kind": task_kind,
-        "scope_selector": {
-            "anchor": "mission",
-            "path": scope_ref.removeprefix("script-build://scopes/").split("/")[:4],
-        },
-        "intent_class": (
-            "compose"
-            if task_kind == "compose"
-            else "portfolio"
-            if task_kind == "candidate-portfolio"
-            else "explore"
-        ),
-        "objective": objective or f"produce one concrete and independently verifiable {task_kind}",
-        "input_decision_ids": input_decision_ids,
-        "base_decision_id": base_decision_id,
-        "gap_key": None,
-        "criteria": [criterion],
-        "goal_ids": list(goal_ids or ()),
-        "supersedes_decision_ids": list(supersedes_decision_ids),
-        "comparison_decision_ids": [str(item["decision_id"]) for item in comparison_decision_refs],
-    }
-
-
-def _task_kind(task: Any) -> str:
-    values = [
-        item.rsplit("/", 1)[-1] for item in task.current_spec.context_refs if "/task-kinds/" in item
-    ]
-    assert len(values) == 1
-    return values[0]
-
-
-def _accepted_ref(ledger: Any, task: Any) -> tuple[str, ArtifactRef]:
-    decision_id = task.decision_ids[-1]
-    decision = ledger.decisions[decision_id]
-    attempt = ledger.attempts[decision.attempt_id]
-    refs = [*attempt.submission.artifact_refs, *attempt.submission.evidence_refs]
-    assert len(refs) == 1
-    return decision_id, refs[0]
-
-
-class _PhaseTwoScriptedLLM:
-    """Exercise phase-two through the actual preset/tool/LocalExecutor surface."""
-
-    def __init__(self, store: FileSystemTaskStore) -> None:
-        self.store = store
-        self.calls = 0
-        self.call_namespace = uuid4().hex
-        self.tool_calls_by_name: dict[str, int] = {}
-        self.submit_attempt_arguments: list[dict[str, Any]] = []
-        self.worker_kinds: list[str] = []
-        self.validator_kinds: list[str] = []
-        self.phase_two_active = False
-        self.compose_candidates_saved = 0
-        self.direction_goal_id: str | None = None
-
-    def _call(self, name: str, arguments: Mapping[str, Any]) -> dict[str, Any]:
-        self.calls += 1
-        self.tool_calls_by_name[name] = self.tool_calls_by_name.get(name, 0) + 1
-        if name == "submit_attempt":
-            self.submit_attempt_arguments.append(dict(arguments))
-        return _tool(f"phase2-{self.call_namespace}-{self.calls}", name, arguments)
-
-    async def __call__(self, messages, tools, **_kwargs):
-        if self.direction_goal_id is None:
-            for message in reversed(messages):
-                if not isinstance(message.get("content"), str):
-                    continue
-                try:
-                    value = json.loads(cast(str, message["content"]))
-                except ValueError:
-                    continue
-                if isinstance(value, dict) and isinstance(
-                    value.get("goal_ids_by_client_key"), dict
-                ):
-                    self.direction_goal_id = str(
-                        value["goal_ids_by_client_key"]["observable-opening"]
-                    )
-                    break
-                if isinstance(value, dict) and isinstance(
-                    value.get("active_direction_goal_ids"), list
-                ):
-                    goal_ids = cast(list[str], value["active_direction_goal_ids"])
-                    if goal_ids:
-                        self.direction_goal_id = goal_ids[0]
-                        break
-        self.phase_two_active = self.phase_two_active or any(
-            PHASE_TWO_CANDIDATE_PORTFOLIO_READY in str(item.get("content", "")) for item in messages
-        )
-        names = {item["function"]["name"] for item in tools or []}
-        if "plan_script_tasks" in names:
-            return await self._planner()
-        if "submit_validation" in names:
-            return self._validator(messages)
-        if "submit_attempt" in names:
-            return self._worker(messages, names)
-        raise AssertionError(f"unexpected tool surface: {sorted(names)}")
-
-    async def _planner(self) -> dict[str, Any]:
-        ledger = await self.store.load(ROOT)
-        root = ledger.tasks[ledger.root_task_id]
-        tasks = [task for task in ledger.tasks.values() if task.task_id != ledger.root_task_id]
-
-        def by_kind(kind: str) -> list[Any]:
-            return [task for task in tasks if _task_kind(task) == kind]
-
-        def by_objective(value: str) -> Any | None:
-            return next(
-                (task for task in tasks if task.current_spec.objective == value),
-                None,
-            )
-
-        direction = next(iter(by_kind("direction")), None)
-        if direction is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "contracts": [
-                        _contract(
-                            "direction",
-                            scope_ref=FULL_SCOPE,
-                            write_scope=("script-build://writes/direction",),
-                        )
-                    ]
-                },
-            )
-        retrieval = next(
-            (
-                task
-                for task in by_kind("decode-retrieval")
-                if task.parent_task_id == direction.task_id
-            ),
-            None,
-        )
-        if retrieval is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": direction.task_id,
-                    "contracts": [
-                        _contract(
-                            "decode-retrieval",
-                            scope_ref="script-build://scopes/full/direction-evidence",
-                            write_scope=(),
-                        )
-                    ],
-                },
-            )
-        if retrieval.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [retrieval.task_id]})
-        if retrieval.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(retrieval, ledger)
-        if direction.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [direction.task_id]})
-        if direction.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(direction, ledger)
-        if not self.phase_two_active:
-            return self._call(
-                "decide_script_task",
-                {
-                    "task_id": root.task_id,
-                    "action": "block",
-                    "reason": PHASE_ONE_CAPABILITY_BOUNDARY,
-                },
-            )
-
-        direction_decision, direction_ref = _accepted_ref(ledger, direction)
-        assert self.direction_goal_id is not None
-        goal_ids = (self.direction_goal_id,)
-        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(
-                "plan_script_tasks",
-                {
-                    "contracts": [
-                        _contract(
-                            "candidate-portfolio",
-                            scope_ref=FULL_SCOPE,
-                            write_scope=("script-build://writes",),
-                            input_decision_refs=(direction_pin,),
-                            goal_ids=goal_ids,
-                        )
-                    ]
-                },
-            )
-        compose = next(iter(by_kind("compose")), None)
-        if compose is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": portfolio.task_id,
-                    "contracts": [
-                        _contract(
-                            "compose",
-                            scope_ref=FULL_SCOPE,
-                            write_scope=("script-build://writes",),
-                            input_decision_refs=(direction_pin,),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-        structure = next(iter(by_kind("structure")), None)
-        if structure is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": compose.task_id,
-                    "contracts": [
-                        _contract(
-                            "structure",
-                            scope_ref="script-build://scopes/full/opening",
-                            write_scope=("script-build://writes/paragraphs/opening",),
-                            input_decision_refs=(direction_pin,),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-        if structure.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [structure.task_id]})
-        if structure.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(structure, ledger)
-
-        structure_decision, structure_ref = _accepted_ref(ledger, structure)
-        first_paragraph_objective = "draft opening candidate A"
-        replacement_objective = "replace the placeholder opening with retrieved evidence"
-        paragraph = by_objective(first_paragraph_objective)
-        if paragraph is None:
-            structure_pin = _decision_ref(
-                structure_decision,
-                structure_ref,
-                scope_ref="script-build://scopes/full/opening",
-                task_kind="structure",
-            )
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": compose.task_id,
-                    "contracts": [
-                        _contract(
-                            "paragraph",
-                            scope_ref="script-build://scopes/full/opening",
-                            write_scope=("script-build://writes/paragraphs/opening",),
-                            objective=first_paragraph_objective,
-                            input_decision_refs=(direction_pin, structure_pin),
-                            base_artifact_ref=_artifact_ref_payload(structure_ref),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-        if paragraph.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [paragraph.task_id]})
-        if paragraph.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(paragraph, ledger)
-
-        paragraph_decision, paragraph_ref = _accepted_ref(ledger, paragraph)
-        element_objective = "attach one independent element to the opening"
-        element = by_objective(element_objective)
-        if element is None:
-            paragraph_pin = _decision_ref(
-                paragraph_decision,
-                paragraph_ref,
-                scope_ref="script-build://scopes/full/opening",
-                task_kind="paragraph",
-            )
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": compose.task_id,
-                    "contracts": [
-                        _contract(
-                            "element-set",
-                            scope_ref="script-build://scopes/full/opening/elements",
-                            write_scope=("script-build://writes/elements",),
-                            objective=element_objective,
-                            input_decision_refs=(direction_pin, paragraph_pin),
-                            base_artifact_ref=_artifact_ref_payload(paragraph_ref),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-        if element.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [element.task_id]})
-        if element.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(element, ledger)
-        element_decision, _element_ref = _accepted_ref(ledger, element)
-        if compose.current_spec_version == 1:
-            return self._call(
-                "decide_script_task",
-                {
-                    "task_id": compose.task_id,
-                    "action": "revise",
-                    "reason": "pin the adopted paragraph and formal compose order",
-                    "selected_decision_ids": [
-                        structure_decision,
-                        paragraph_decision,
-                        element_decision,
-                    ],
-                },
-            )
-        if compose.status is TaskStatus.PENDING:
-            return self._call("dispatch_script_tasks", {"task_ids": [compose.task_id]})
-        if compose.status is TaskStatus.AWAITING_DECISION:
-            validation = ledger.validations[compose.validation_ids[-1]]
-            if validation.verdict is ValidationVerdict.PASSED:
-                return self._accept(compose, ledger)
-            return self._call(
-                "decide_script_task",
-                {
-                    "task_id": compose.task_id,
-                    "validation_id": validation.validation_id,
-                    "action": "split",
-                    "reason": "replace only the unrealized opening after targeted retrieval",
-                    "child_contracts": [
-                        _contract(
-                            "paragraph",
-                            scope_ref="script-build://scopes/full/opening",
-                            write_scope=("script-build://writes/paragraphs/opening",),
-                            objective=replacement_objective,
-                            input_decision_refs=(
-                                direction_pin,
-                                _decision_ref(
-                                    structure_decision,
-                                    structure_ref,
-                                    scope_ref="script-build://scopes/full/opening",
-                                    task_kind="structure",
-                                ),
-                                _decision_ref(
-                                    paragraph_decision,
-                                    paragraph_ref,
-                                    scope_ref="script-build://scopes/full/opening",
-                                    task_kind="paragraph",
-                                ),
-                            ),
-                            base_artifact_ref=_artifact_ref_payload(structure_ref),
-                            supersedes_decision_ids=(paragraph_decision,),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-
-        # Compose is now a deterministic Workbench Worker. A complete creative
-        # frontier produces one valid artifact instead of asking an LLM Worker
-        # to manufacture an invalid first candidate for this fixture.
-        if compose.status is TaskStatus.COMPLETED:
-            compose_decision, _compose_ref = _accepted_ref(ledger, compose)
-            if portfolio.current_spec_version == 1:
-                return self._call(
-                    "decide_script_task",
-                    {
-                        "task_id": portfolio.task_id,
-                        "action": "revise",
-                        "reason": "pin the uniquely adopted StructuredScript",
-                        "selected_decision_ids": [compose_decision],
-                    },
-                )
-            if portfolio.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-                return self._call("dispatch_script_tasks", {"task_ids": [portfolio.task_id]})
-            if portfolio.status is TaskStatus.AWAITING_DECISION:
-                return self._accept(portfolio, ledger)
-            assert portfolio.status is TaskStatus.COMPLETED
-            return self._call(
-                "decide_script_task",
-                {
-                    "task_id": root.task_id,
-                    "action": "block",
-                    "reason": PHASE_TWO_CANDIDATE_PORTFOLIO_READY,
-                },
-            )
-
-        replacement = by_objective(replacement_objective)
-        if replacement is None:
-            raise AssertionError("failed Compose must SPLIT one bounded replacement Task")
-        phase_two_retrieval = next(
-            (
-                task
-                for task in by_kind("decode-retrieval")
-                if task.parent_task_id == replacement.task_id
-            ),
-            None,
-        )
-        if phase_two_retrieval is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": replacement.task_id,
-                    "contracts": [
-                        _contract(
-                            "decode-retrieval",
-                            scope_ref="script-build://scopes/full/opening/evidence",
-                            write_scope=(),
-                            objective="retrieve evidence for the replacement opening",
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-        if phase_two_retrieval.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [phase_two_retrieval.task_id]})
-        if phase_two_retrieval.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(phase_two_retrieval, ledger)
-        if replacement.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [replacement.task_id]})
-        if replacement.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(replacement, ledger)
-
-        replacement_decision, replacement_ref = _accepted_ref(ledger, replacement)
-        comparison = next(iter(by_kind("compare")), None)
-        if comparison is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": compose.task_id,
-                    "contracts": [
-                        _contract(
-                            "compare",
-                            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,
-                                    paragraph_ref,
-                                    scope_ref="script-build://scopes/full/opening",
-                                    task_kind="paragraph",
-                                ),
-                                _decision_ref(
-                                    replacement_decision,
-                                    replacement_ref,
-                                    scope_ref="script-build://scopes/full/opening",
-                                    task_kind="paragraph",
-                                ),
-                            ),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-        if comparison.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [comparison.task_id]})
-        if comparison.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(comparison, ledger)
-
-        _comparison_decision, _comparison_ref = _accepted_ref(ledger, comparison)
-        if compose.status is TaskStatus.NEEDS_REPLAN:
-            return self._call(
-                "decide_script_task",
-                {
-                    "task_id": compose.task_id,
-                    "action": "revise",
-                    "reason": "pin the accepted replacement and the symmetric comparison",
-                    "selected_decision_ids": [
-                        structure_decision,
-                        replacement_decision,
-                        element_decision,
-                    ],
-                },
-            )
-        if compose.status is TaskStatus.PENDING:
-            return self._call("dispatch_script_tasks", {"task_ids": [compose.task_id]})
-        if compose.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(compose, ledger)
-
-        compose_decision, _compose_ref = _accepted_ref(ledger, compose)
-        if portfolio.current_spec_version == 1:
-            return self._call(
-                "decide_script_task",
-                {
-                    "task_id": portfolio.task_id,
-                    "action": "revise",
-                    "reason": "pin the uniquely adopted StructuredScript",
-                    "selected_decision_ids": [compose_decision],
-                },
-            )
-        if portfolio.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [portfolio.task_id]})
-        if portfolio.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(portfolio, ledger)
-
-        assert portfolio.status is TaskStatus.COMPLETED
-        return self._call(
-            "decide_script_task",
-            {
-                "task_id": root.task_id,
-                "action": "block",
-                "reason": PHASE_TWO_CANDIDATE_PORTFOLIO_READY,
-            },
-        )
-
-    def _accept(self, task: Any, ledger: Any) -> dict[str, Any]:
-        validation = ledger.validations[task.validation_ids[-1]]
-        assert validation.verdict is ValidationVerdict.PASSED
-        return self._call(
-            "decide_script_task",
-            {
-                "task_id": task.task_id,
-                "validation_id": validation.validation_id,
-                "action": "accept",
-                "reason": "the independent Validator passed the frozen artifact",
-            },
-        )
-
-    def _worker(self, messages, names: set[str]) -> dict[str, Any]:
-        prompt = _role_prompt(messages)
-        spec = cast(Mapping[str, Any], prompt["task_spec"])
-        refs = cast(Sequence[str], spec["context_refs"])
-        kind = next(item.rsplit("/", 1)[-1] for item in refs if "/task-kinds/" in item)
-        if kind not in self.worker_kinds:
-            self.worker_kinds.append(kind)
-        last = _last_tool_value(messages)
-        if kind == "decode-retrieval":
-            if not isinstance(last, dict) or not last.get("submitted"):
-                return self._call(
-                    "search_script_decode_case",
-                    {"return_field": "主脉络", "keyword": "observable reversal", "top_k": 3},
-                )
-            return self._call("submit_attempt", {})
-        if kind == "direction":
-            if not isinstance(last, dict) or "direction_handle" not in last:
-                role_context = cast(Mapping[str, Any], prompt["role_context"])
-                accepted = cast(Sequence[Mapping[str, Any]], role_context["accepted_inputs"])
-                return self._call(
-                    "save_direction_candidate",
-                    {
-                        "goals": [
-                            {
-                                "client_key": "observable-opening",
-                                "statement": "open with one observable reversal",
-                                "rationale": "the frozen topic benefits from a concrete hook",
-                                "success_criteria": [
-                                    "the opening contains one observable reversal"
-                                ],
-                            }
-                        ],
-                        "constraints": [
-                            {
-                                "client_key": "grounded-direction",
-                                "statement": "the direction is concrete and testable",
-                                "rationale": "accepted evidence must remain traceable",
-                            }
-                        ],
-                        "preferences": [],
-                        "strategy_handles": [],
-                        "evidence_decision_ids": [accepted[0]["decision_id"]],
-                    },
-                )
-            goal_map = cast(Mapping[str, str], last["goal_ids_by_client_key"])
-            self.direction_goal_id = goal_map["observable-opening"]
-            return self._call("submit_attempt", {})
-        if kind == "structure":
-            if not isinstance(last, dict) or last.get("status") != "saved":
-                role_context = cast(Mapping[str, Any], prompt["role_context"])
-                return self._call(
-                    "save_script_paragraphs",
-                    {
-                        "paragraphs": [
-                            {
-                                "client_key": "opening",
-                                "paragraph_index": 1,
-                                "name": "Opening structure",
-                                "content_range": {
-                                    "scope": "opening",
-                                    "beats": ["setup", "reversal"],
-                                },
-                                "level": 1,
-                                "theme_elements": [
-                                    {
-                                        "原子点": "assumption changes after evidence",
-                                        "维度": "claim",
-                                        "维度类型": "主维度",
-                                    }
-                                ],
-                                "form_elements": [
-                                    {
-                                        "原子点": "setup then concrete reversal",
-                                        "维度": "shape",
-                                        "维度类型": "主维度",
-                                    }
-                                ],
-                                "function_elements": [
-                                    {
-                                        "原子点": "create curiosity without abstraction",
-                                        "维度": "job",
-                                        "维度类型": "主维度",
-                                    }
-                                ],
-                                "feeling_elements": [
-                                    {"原子点": "measured surprise", "维度": "tone"}
-                                ],
-                            }
-                        ],
-                        "expected_state_revision": role_context["state_revision"],
-                    },
-                )
-            return self._call("submit_attempt", {})
-        if kind == "paragraph":
-            if isinstance(last, dict) and last.get("status") == "saved":
-                return self._call("submit_attempt", {})
-            role_context = cast(Mapping[str, Any], prompt["role_context"])
-            paragraphs = cast(Sequence[Mapping[str, Any]], role_context["paragraph_targets"])
-            replacement = "replace the placeholder" in str(spec["objective"])
-            paragraph: dict[str, Any] = {
-                "theme": (
-                    "The retrieved observable reversal changes the opening claim."
-                    if replacement
-                    else "A familiar expectation changes after a visible detail."
-                ),
-                "form": "A concrete setup followed by one concise reversal.",
-                "function": "Give the audience an immediate reason to continue.",
-                "feeling": "Measured surprise grounded in observation.",
-                "description": "The opening overturns one assumption with evidence.",
-                "full_description": (
-                    "Use the accepted retrieval evidence to replace the unrealized opening and "
-                    "show exactly how the observed detail changes the interpretation."
-                    if replacement
-                    else "Start from the familiar expectation, name the visible detail, and show "
-                    "exactly how that detail changes the interpretation."
-                ),
-            }
-            if paragraphs:
-                paragraph["paragraph_target_key"] = paragraphs[0]["paragraph_target_key"]
-            else:
-                paragraph.update(
-                    {
-                        "client_key": "opening",
-                        "paragraph_index": 1,
-                        "name": "Opening",
-                        "content_range": {"scope": "opening", "beats": ["setup", "reversal"]},
-                        "level": 1,
-                        "theme_elements": [
-                            {"原子点": "visible reversal", "维度": "claim", "维度类型": "主维度"}
-                        ],
-                        "form_elements": [
-                            {"原子点": "setup and reversal", "维度": "shape", "维度类型": "主维度"}
-                        ],
-                        "function_elements": [
-                            {"原子点": "create curiosity", "维度": "job", "维度类型": "主维度"}
-                        ],
-                        "feeling_elements": [{"原子点": "measured surprise", "维度": "tone"}],
-                    }
-                )
-            return self._call(
-                "save_script_paragraphs",
-                {
-                    "paragraphs": [paragraph],
-                    "expected_state_revision": role_context["state_revision"],
-                },
-            )
-        if kind == "element-set":
-            if isinstance(last, dict) and last.get("created") == 1 and last.get("linked") == 1:
-                return self._call("submit_attempt", {})
-            role_context = cast(Mapping[str, Any], prompt["role_context"])
-            paragraphs = cast(Sequence[Mapping[str, Any]], role_context["paragraph_targets"])
-            assert len(paragraphs) == 1
-            return self._call(
-                "save_script_elements",
-                {
-                    "elements": [
-                        {
-                            "client_key": "reversal",
-                            "name": "Observable reversal",
-                            "dimension_primary": "实质",
-                            "dimension_secondary": "opening-evidence",
-                            "commonality_analysis": {
-                                "summary": "a visible detail changes the claim"
-                            },
-                            "topic_support": {"strength": "high"},
-                            "weight_score": {"value": 0.9},
-                            "support_elements": [{"source_handle": "accepted-opening"}],
-                        }
-                    ],
-                    "links": [
-                        {
-                            "paragraph_target_key": paragraphs[0]["paragraph_target_key"],
-                            "element_client_keys": ["reversal"],
-                        }
-                    ],
-                    "expected_state_revision": role_context["state_revision"],
-                },
-            )
-        if kind == "compare":
-            if isinstance(last, dict) and "artifact_ref" in last:
-                return self._call("submit_attempt", {})
-            role_context = cast(Mapping[str, Any], prompt["role_context"])
-            candidates = list(role_context["task"]["comparison_decision_ids"])
-            assert len(candidates) == 2
-            return self._call(
-                "save_comparison_candidate",
-                {
-                    "criterion_results": [
-                        {
-                            "criterion_id": "compare-closed",
-                            "candidate_results": [
-                                {
-                                    "decision_id": candidate,
-                                    "reason": "reviewed against the same frozen criterion",
-                                }
-                                for candidate in candidates
-                            ],
-                        }
-                    ],
-                    "conflicts": [
-                        "candidate B realizes the retrieved evidence while candidate A does not"
-                    ],
-                    "recommended_decision_id": candidates[-1],
-                },
-            )
-        if kind in {"compose", "candidate-portfolio"}:
-            raise AssertionError(f"{kind} must be executed by DeterministicWorker")
-        raise AssertionError(f"unexpected Worker kind {kind}; tools={sorted(names)}")
-
-    def _validator(self, messages) -> dict[str, Any]:
-        prompt = _role_prompt(messages)
-        spec = cast(Mapping[str, Any], prompt["task_spec"])
-        refs = cast(Sequence[str], spec["context_refs"])
-        kind = next(item.rsplit("/", 1)[-1] for item in refs if "/task-kinds/" in item)
-        if kind not in self.validator_kinds:
-            self.validator_kinds.append(kind)
-        last = _last_tool_value(messages)
-        if not isinstance(last, dict) or "rule_results" not in last:
-            return self._call("deterministic_precheck", {})
-        criteria = cast(Sequence[Mapping[str, Any]], spec["acceptance_criteria"])
-        role_context = cast(Mapping[str, Any], prompt["role_context"])
-        evidence_handle = role_context["evidence_handles"][0]["evidence_handle"]
-        failed_rules = [item for item in last["rule_results"] if item["verdict"] != "passed"]
-        if failed_rules:
-            return self._call(
-                "submit_validation",
-                {
-                    "verdict": "failed",
-                    "criterion_results": [
-                        {
-                            "criterion_id": item["criterion_id"],
-                            "verdict": "failed",
-                            "reason": "the rendered candidate still contains a placeholder",
-                        }
-                        for item in criteria
-                    ],
-                    "defects": [
-                        {
-                            "defect_code": "REALIZATION_PLACEHOLDER",
-                            "criterion_id": criteria[0]["criterion_id"],
-                            "scope_selector": {
-                                "anchor": "mission",
-                                "path": ["full", "opening"],
-                            },
-                            "observed_excerpt": (
-                                "TODO replace this placeholder opening with retrieved evidence."
-                            ),
-                            "evidence_handle_ids": [evidence_handle],
-                            "severity": "hard",
-                            "invalidated_inputs": [],
-                            "recommended_action_class": "split",
-                        }
-                    ],
-                    "recommendation": "split",
-                },
-            )
-        assert all(item["verdict"] == "passed" for item in last["rule_results"])
-        return self._call(
-            "submit_validation",
-            {
-                "verdict": "passed",
-                "criterion_results": [
-                    {
-                        "criterion_id": item["criterion_id"],
-                        "verdict": "passed",
-                        "reason": "the immutable artifact passed deterministic and semantic review",
-                        "evidence_handle_ids": [evidence_handle],
-                    }
-                    for item in criteria
-                ],
-                "defects": [],
-                "recommendation": "accept",
-            },
-        )
-
-
-class _PhaseThreeScriptedLLM(_PhaseTwoScriptedLLM):
-    """Continue the real Phase2 runner through the protected Root delivery."""
-
-    def __init__(self, store: FileSystemTaskStore) -> None:
-        super().__init__(store)
-        self.phase_three_active = False
-
-    async def __call__(self, messages, tools, **kwargs):
-        self.phase_three_active = self.phase_three_active or any(
-            "script-build-phase-three/v1" in str(item.get("content", "")) for item in messages
-        )
-        names = {item["function"]["name"] for item in tools or []}
-        if self.phase_three_active and "plan_script_tasks" in names:
-            return await self._phase_three_planner()
-        return await super().__call__(messages, tools, **kwargs)
-
-    async def _phase_three_planner(self) -> dict[str, Any]:
-        ledger = await self.store.load(ROOT)
-        root = ledger.tasks[ledger.root_task_id]
-        assert _task_kind(root) == "root-delivery"
-        if root.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [root.task_id]})
-        if root.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(root, ledger)
-        if root.status is TaskStatus.COMPLETED:
-            return {"content": "Root delivery accepted", "finish_reason": "stop"}
-        raise AssertionError(f"unexpected Root delivery status {root.status}")
-
-    def _worker(self, messages, names: set[str]) -> dict[str, Any]:
-        prompt = _role_prompt(messages)
-        refs = cast(Sequence[str], cast(Mapping[str, Any], prompt["task_spec"])["context_refs"])
-        kind = next(item.rsplit("/", 1)[-1] for item in refs if "/task-kinds/" in item)
-        if kind != "root-delivery":
-            return super()._worker(messages, names)
-        if kind not in self.worker_kinds:
-            self.worker_kinds.append(kind)
-        last = _last_tool_value(messages)
-        if isinstance(last, dict) and "artifact_ref" in last:
-            return self._call("submit_attempt", {})
-        if isinstance(last, dict) and "legacy_projection_digest" in last:
-            return self._call(
-                "save_root_delivery_manifest",
-                {"build_summary": "final summary", "blocking_defects": []},
-            )
-        return self._call("legacy_projection_dry_run", {"build_summary": "final summary"})
-
-
-class _OrderedExplorationScriptedLLM(_PhaseTwoScriptedLLM):
-    """Close Phase2 after exercising each creative preset in a chosen order."""
-
-    def __init__(self, store: FileSystemTaskStore, order: tuple[str, str, str]) -> None:
-        super().__init__(store)
-        self.order = order
-        # This scenario checks ordering, not placeholder repair. The parent Worker
-        # increments this before saving, so its first Compose is fully realized.
-        self.compose_candidates_saved = 1
-
-    async def _planner(self) -> dict[str, Any]:
-        ledger = await self.store.load(ROOT)
-        tasks = [task for task in ledger.tasks.values() if task.task_id != ledger.root_task_id]
-        direction = next((task for task in tasks if _task_kind(task) == "direction"), None)
-        if (
-            direction is None
-            or direction.status is not TaskStatus.COMPLETED
-            or not self.phase_two_active
-        ):
-            return await super()._planner()
-
-        root = ledger.tasks[ledger.root_task_id]
-        assert self.direction_goal_id is not None
-        goal_ids = (self.direction_goal_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)
-
-        portfolio = next(
-            (task for task in tasks if _task_kind(task) == "candidate-portfolio"), None
-        )
-        if portfolio is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "contracts": [
-                        _contract(
-                            "candidate-portfolio",
-                            scope_ref=FULL_SCOPE,
-                            write_scope=("script-build://writes",),
-                            input_decision_refs=(direction_pin,),
-                            goal_ids=goal_ids,
-                        )
-                    ]
-                },
-            )
-        compose = next((task for task in tasks if _task_kind(task) == "compose"), None)
-        if compose is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": portfolio.task_id,
-                    "contracts": [
-                        _contract(
-                            "compose",
-                            scope_ref=FULL_SCOPE,
-                            write_scope=("script-build://writes",),
-                            input_decision_refs=(direction_pin,),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-
-        exploratory: dict[str, Any] = {}
-        scopes = {
-            "structure": "script-build://scopes/full/opening",
-            "paragraph": "script-build://scopes/full/opening",
-            "element-set": "script-build://scopes/full/opening/elements",
-        }
-        write_scopes = {
-            "structure": ("script-build://writes/paragraphs/opening",),
-            "paragraph": ("script-build://writes/paragraphs/opening",),
-            "element-set": ("script-build://writes/elements",),
-        }
-        for kind in self.order:
-            objective = f"explore {kind} before formal adoption"
-            task = by_objective(objective)
-            if task is None:
-                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,
-                            scope_ref=scopes["structure"],
-                            task_kind="structure",
-                        ),
-                    )
-                    base = _artifact_ref_payload(structure_ref)
-                elif kind == "element-set":
-                    structure_decision, structure_ref = _accepted_ref(
-                        ledger, exploratory["structure"]
-                    )
-                    inputs = (
-                        direction_pin,
-                        _decision_ref(
-                            structure_decision,
-                            structure_ref,
-                            scope_ref=scopes["structure"],
-                            task_kind="structure",
-                        ),
-                    )
-                    base = _artifact_ref_payload(structure_ref)
-                return self._call(
-                    "plan_script_tasks",
-                    {
-                        "parent_task_id": compose.task_id,
-                        "contracts": [
-                            _contract(
-                                kind,
-                                scope_ref=scopes[kind],
-                                write_scope=write_scopes[kind],
-                                objective=objective,
-                                input_decision_refs=inputs,
-                                base_artifact_ref=base,
-                                goal_ids=goal_ids,
-                            )
-                        ],
-                    },
-                )
-            exploratory[kind] = task
-            if task.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-                return self._call("dispatch_script_tasks", {"task_ids": [task.task_id]})
-            if task.status is TaskStatus.AWAITING_DECISION:
-                return self._accept(task, ledger)
-
-        structure_decision, structure_ref = _accepted_ref(ledger, exploratory["structure"])
-        paragraph_decision, paragraph_ref = _accepted_ref(ledger, exploratory["paragraph"])
-        element_decision, element_ref = _accepted_ref(ledger, exploratory["element-set"])
-
-        paragraph_objective = "adopt structure and paragraph as one logical opening"
-        paragraph_replacement = by_objective(paragraph_objective)
-        if paragraph_replacement is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": compose.task_id,
-                    "contracts": [
-                        _contract(
-                            "paragraph",
-                            scope_ref="script-build://scopes/full/opening",
-                            write_scope=("script-build://writes/paragraphs/opening",),
-                            objective=paragraph_objective,
-                            input_decision_refs=(
-                                direction_pin,
-                                _decision_ref(
-                                    structure_decision,
-                                    structure_ref,
-                                    scope_ref=scopes["structure"],
-                                    task_kind="structure",
-                                ),
-                                _decision_ref(
-                                    paragraph_decision,
-                                    paragraph_ref,
-                                    scope_ref="script-build://scopes/full/opening",
-                                    task_kind="paragraph",
-                                ),
-                            ),
-                            base_artifact_ref=_artifact_ref_payload(structure_ref),
-                            supersedes_decision_ids=(paragraph_decision,),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-        if paragraph_replacement.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call(
-                "dispatch_script_tasks", {"task_ids": [paragraph_replacement.task_id]}
-            )
-        if paragraph_replacement.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(paragraph_replacement, ledger)
-        adopted_paragraph_decision, adopted_paragraph_ref = _accepted_ref(
-            ledger, paragraph_replacement
-        )
-
-        element_objective = "place the explored element on the adopted opening"
-        element_replacement = by_objective(element_objective)
-        if element_replacement is None:
-            return self._call(
-                "plan_script_tasks",
-                {
-                    "parent_task_id": compose.task_id,
-                    "contracts": [
-                        _contract(
-                            "element-set",
-                            scope_ref=scopes["element-set"],
-                            write_scope=("script-build://writes/elements",),
-                            objective=element_objective,
-                            input_decision_refs=(
-                                direction_pin,
-                                _decision_ref(
-                                    element_decision,
-                                    element_ref,
-                                    scope_ref=scopes["element-set"],
-                                    task_kind="element-set",
-                                ),
-                                _decision_ref(
-                                    adopted_paragraph_decision,
-                                    adopted_paragraph_ref,
-                                    scope_ref="script-build://scopes/full/opening",
-                                    task_kind="paragraph",
-                                ),
-                            ),
-                            base_artifact_ref=_artifact_ref_payload(adopted_paragraph_ref),
-                            supersedes_decision_ids=(element_decision,),
-                            goal_ids=goal_ids,
-                        )
-                    ],
-                },
-            )
-        if element_replacement.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [element_replacement.task_id]})
-        if element_replacement.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(element_replacement, ledger)
-        adopted_element_decision, adopted_element_ref = _accepted_ref(ledger, element_replacement)
-
-        del adopted_paragraph_ref, adopted_element_ref
-        if compose.current_spec_version == 1:
-            return self._call(
-                "decide_script_task",
-                {
-                    "task_id": compose.task_id,
-                    "action": "revise",
-                    "reason": "formal adoption follows immutable lineage, not exploration order",
-                    "selected_decision_ids": [
-                        structure_decision,
-                        adopted_paragraph_decision,
-                        adopted_element_decision,
-                    ],
-                },
-            )
-        if compose.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [compose.task_id]})
-        if compose.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(compose, ledger)
-
-        compose_decision, _compose_ref = _accepted_ref(ledger, compose)
-        if portfolio.current_spec_version == 1:
-            return self._call(
-                "decide_script_task",
-                {
-                    "task_id": portfolio.task_id,
-                    "action": "revise",
-                    "reason": "pin the unique accepted StructuredScript",
-                    "selected_decision_ids": [compose_decision],
-                },
-            )
-        if portfolio.status in {TaskStatus.PENDING, TaskStatus.NEEDS_REPLAN}:
-            return self._call("dispatch_script_tasks", {"task_ids": [portfolio.task_id]})
-        if portfolio.status is TaskStatus.AWAITING_DECISION:
-            return self._accept(portfolio, ledger)
-        return self._call(
-            "decide_script_task",
-            {
-                "task_id": root.task_id,
-                "action": "block",
-                "reason": PHASE_TWO_CANDIDATE_PORTFOLIO_READY,
-            },
-        )
-
-
-class _PrincipalProvider:
-    async def current(self, request_context: Any = None) -> Principal:
-        del request_context
-        return Principal("phase-two-owner")
-
-
-class _Authorizer:
-    async def require_source_access(self, _principal: Principal, **_source: Any) -> None:
-        return None
-
-    async def require_access(self, _principal: Principal, _script_build_id: int) -> None:
-        return None
-
-
-class _DecodeAdapter:
-    async def retrieve(self, *, query: Mapping[str, Any], snapshot: Any) -> RetrievalResult:
-        del snapshot
-        return RetrievalResult(
-            source_refs=("decode-index://fixture/opening-reversal",),
-            summary=f"A grounded opening reverses one expectation: {query['keyword']}",
-            supports=("direction",),
-            confidence="high",
-            metadata={"rank": 1, "score": 0.93},
-        )
-
-    async def require_access(self, _principal: Principal, _script_build_id: int) -> None:
-        return None
-
-
-class _PromptCatalog:
-    def __init__(self) -> None:
-        self._entries = {
-            (str(item["preset"]), str(item["role"])): dict(item)
-            for item in (*script_build_prompt_manifest(), *phase_three_prompt_manifest())
-        }
-
-    async def load(self, requests: Sequence[Any]) -> tuple[dict[str, Any], ...]:
-        return tuple(dict(self._entries[(str(item.preset), str(item.role))]) for item in requests)
-
-
-class _BlockedAfterPolicyLLM(_PhaseTwoScriptedLLM):
-    """Hold the first Phase2 model call after policy persistence and UNBLOCK."""
-
-    def __init__(self, store: FileSystemTaskStore) -> None:
-        super().__init__(store)
-        self.phase_two_model_call = asyncio.Event()
-        self._never_release = asyncio.Event()
-
-    async def __call__(self, messages, tools, **kwargs):
-        del kwargs
-        if any("script-build-phase-two/v1" in str(item.get("content", "")) for item in messages):
-            self.phase_two_model_call.set()
-            await self._never_release.wait()
-        return await super().__call__(messages, tools)
-
-
-def _persisted_host(
-    *,
-    sessions: Any,
-    tmp_path: Path,
-    llm: Any,
-    enabled_phase: int,
-    model_manifest: Mapping[str, object],
-) -> Any:
-    snapshots = SqlAlchemyInputSnapshotRepository(sessions)
-    input_service = ScriptInputSnapshotService(
-        legacy_input=cast(Any, None),
-        persona_source=cast(Any, None),
-        strategy_source=cast(Any, None),
-        prompt_source=_PromptCatalog(),
-        snapshots=snapshots,
-    )
-    artifacts = SqlAlchemyScriptBusinessArtifactRepository(sessions)
-    task_store = FileSystemTaskStore(str(tmp_path / "ledger"))
-    runner = AgentRunner(
-        trace_store=FileSystemTraceStore(str(tmp_path / "trace")),
-        llm_call=llm,
-    )
-    return compose_host(
-        HostDependencies(
-            runner=runner,
-            task_store=task_store,
-            framework_artifact_store=FileSystemArtifactStore(str(tmp_path / "framework-artifacts")),
-            input_snapshot_service=input_service,
-            input_snapshots=snapshots,
-            bindings=SqlAlchemyMissionBindingRepository(sessions),
-            business_artifacts=artifacts,
-            publications=SqlAlchemyPublicationRepository(sessions),
-            legacy_state=SqlAlchemyLegacyBuildStateRepository(sessions),
-            principal_provider=_PrincipalProvider(),
-            build_authorizer=_Authorizer(),
-            retrieval_adapters={"decode": _DecodeAdapter()},
-            enabled_phase=enabled_phase,
-            agent_data_root=tmp_path / "agent-data",
-            candidate_workspaces=SqlAlchemyCandidateWorkspaceRepository(sessions, artifacts),
-            default_model_manifest=model_manifest,
-        )
-    )
-
-
-@pytest.mark.asyncio
-@pytest.mark.parametrize(
-    "exploration_order",
-    [
-        pytest.param(("structure", "element-set", "paragraph"), id="element-second"),
-        pytest.param(("structure", "paragraph", "element-set"), id="paragraph-second"),
-    ],
-)
-async def test_real_runner_creative_exploration_order_does_not_choose_adoption_order(
-    database: Any,
-    tmp_path: Path,
-    exploration_order: tuple[str, str, str],
-) -> None:
-    _, sessions = database
-    build_states = SqlAlchemyLegacyBuildStateRepository(sessions)
-    script_build_id = await build_states.create(
-        execution_id=711,
-        topic_build_id=712,
-        topic_id=713,
-        agent_type="script_planner",
-        agent_config={},
-        data_source_url=None,
-        strategies_config={},
-    )
-    prompt_manifest = script_build_prompt_manifest()
-    model_manifest = {
-        "presets": {
-            str(item["preset"]): {
-                "model": "scripted-fake-model",
-                "temperature": 0,
-                "max_iterations": 80,
-            }
-            for item in prompt_manifest
-        }
-    }
-    snapshot_repository = SqlAlchemyInputSnapshotRepository(sessions)
-    snapshot = await snapshot_repository.freeze(
-        ScriptBuildInput(
-            script_build_id=script_build_id,
-            execution_id=711,
-            topic_build_id=712,
-            topic_id=713,
-            topic={"topic": {"id": 713, "result": "Order must not determine adoption"}},
-            account={"account_name": "fixture-account"},
-            prompt_manifest=tuple(dict(item) for item in prompt_manifest),
-            model_manifest=model_manifest,
-        )
-    )
-    bindings = SqlAlchemyMissionBindingRepository(sessions)
-    await bindings.create(
-        script_build_id=script_build_id,
-        root_trace_id=ROOT,
-        input_snapshot_id=int(snapshot.snapshot_id),
-        engine_version="phase-two-ordered-real-runner-test",
-        schema_version="v2",
-    )
-    task_store = FileSystemTaskStore(str(tmp_path / "ledger"))
-    trace_store = FileSystemTraceStore(str(tmp_path / "trace"))
-    llm = _OrderedExplorationScriptedLLM(task_store, exploration_order)
-    runner = AgentRunner(trace_store=trace_store, llm_call=llm)
-    business_artifacts = SqlAlchemyScriptBusinessArtifactRepository(sessions)
-    composition = compose_host(
-        HostDependencies(
-            runner=runner,
-            task_store=task_store,
-            framework_artifact_store=FileSystemArtifactStore(str(tmp_path / "framework-artifacts")),
-            input_snapshot_service=cast(Any, snapshot_repository),
-            input_snapshots=snapshot_repository,
-            bindings=bindings,
-            business_artifacts=business_artifacts,
-            publications=SqlAlchemyPublicationRepository(sessions),
-            legacy_state=build_states,
-            principal_provider=_PrincipalProvider(),
-            build_authorizer=_Authorizer(),
-            retrieval_adapters={"decode": _DecodeAdapter()},
-            enabled_phase=2,
-            agent_data_root=tmp_path / "agent-data",
-            candidate_workspaces=SqlAlchemyCandidateWorkspaceRepository(
-                sessions, business_artifacts
-            ),
-            default_model_manifest=model_manifest,
-        )
-    )
-
-    await build_states.set_status(script_build_id, BuildStatus.RUNNING)
-    await composition.mission_service.run(script_build_id)
-
-    ledger = await task_store.load(ROOT)
-    root = ledger.tasks[ledger.root_task_id]
-    assert root.status is TaskStatus.BLOCKED
-    assert root.blocked_reason == PHASE_TWO_CANDIDATE_PORTFOLIO_READY
-    assert await build_states.get_status(script_build_id) is BuildStatus.PARTIAL
-    assert tuple(llm.worker_kinds[2:5]) == exploration_order
-    assert tuple(llm.validator_kinds[2:5]) == exploration_order
-
-    task_by_objective = {
-        task.current_spec.objective: task
-        for task in ledger.tasks.values()
-        if task.task_id != ledger.root_task_id
-    }
-    expected_presets = {
-        "structure": "script_structure_worker",
-        "paragraph": "script_paragraph_worker",
-        "element-set": "script_element_set_worker",
-    }
-    reopened_traces = FileSystemTraceStore(str(tmp_path / "trace"))
-    for kind in exploration_order:
-        task = task_by_objective[f"explore {kind} before formal adoption"]
-        attempt = ledger.attempts[task.attempt_ids[-1]]
-        assert attempt.worker_preset == expected_presets[kind]
-        worker_trace = await reopened_traces.get_trace(attempt.worker_trace_id)
-        assert worker_trace is not None and worker_trace.status == "completed"
-        messages = await reopened_traces.get_trace_messages(attempt.worker_trace_id)
-        if worker_trace.context.get("deterministic_worker"):
-            assert messages == []
-        else:
-            assert messages
-        validation = next(
-            ledger.validations[item]
-            for item in task.validation_ids
-            if ledger.validations[item].attempt_id == attempt.attempt_id
-        )
-        assert validation.verdict is ValidationVerdict.PASSED
-        validator_trace = await reopened_traces.get_trace(validation.validator_trace_id)
-        assert validator_trace is not None and validator_trace.status == "completed"
-        assert await reopened_traces.get_trace_messages(validation.validator_trace_id)
-
-    element_replacement = task_by_objective["place the explored element on the adopted opening"]
-    element_decision, element_ref = _accepted_ref(ledger, element_replacement)
-    element_version = await business_artifacts.read_by_ref(
-        element_ref,
-        script_build_id=script_build_id,
-        task_id=element_replacement.task_id,
-        attempt_id=cast(str, ledger.decisions[element_decision].attempt_id),
-    )
-    assert element_version.artifact_type is ArtifactKind.ELEMENT_SET
-    assert len(cast(Any, element_version.artifact).paragraph_element_links) == 1
-
-    compose = next(
-        task
-        for task in ledger.tasks.values()
-        if task.task_id != ledger.root_task_id and _task_kind(task) == "compose"
-    )
-    assert compose.status is TaskStatus.COMPLETED
-    contract_ref = next(
-        item
-        for item in compose.current_spec.context_refs
-        if item.startswith("script-build://task-contracts/sha256/")
-    )
-    compose_contract = await FileScriptTaskContractStore(tmp_path / "agent-data").read(
-        ROOT, contract_ref
-    )
-    adopted_kinds = tuple(
-        _task_kind(ledger.tasks[ledger.decisions[decision_id].task_id])
-        for decision_id in compose_contract.contract.compose_order
-    )
-    assert adopted_kinds == ("structure", "paragraph", "element-set")
-    portfolio = next(
-        task
-        for task in ledger.tasks.values()
-        if task.task_id != ledger.root_task_id and _task_kind(task) == "candidate-portfolio"
-    )
-    assert portfolio.status is TaskStatus.COMPLETED
-
-
-@pytest.mark.asyncio
-async def test_mission_service_real_runner_auto_continues_phase_one_to_phase_two_boundary(
-    database: Any, tmp_path: Path
-) -> None:
-    _, sessions = database
-    build_states = SqlAlchemyLegacyBuildStateRepository(sessions)
-    script_build_id = await build_states.create(
-        execution_id=701,
-        topic_build_id=702,
-        topic_id=703,
-        agent_type="script_planner",
-        agent_config={},
-        data_source_url=None,
-        strategies_config={},
-    )
-    prompt_manifest = script_build_prompt_manifest()
-    model_manifest = {
-        "presets": {
-            str(item["preset"]): {
-                "model": "scripted-fake-model",
-                "temperature": 0,
-                "max_iterations": 80,
-            }
-            for item in prompt_manifest
-        }
-    }
-    snapshot_repository = SqlAlchemyInputSnapshotRepository(sessions)
-    snapshot = await snapshot_repository.freeze(
-        ScriptBuildInput(
-            script_build_id=script_build_id,
-            execution_id=701,
-            topic_build_id=702,
-            topic_id=703,
-            topic={"topic": {"id": 703, "result": "A concrete reversal changes a belief"}},
-            account={"account_name": "fixture-account"},
-            prompt_manifest=tuple(dict(item) for item in prompt_manifest),
-            model_manifest=model_manifest,
-        )
-    )
-    bindings = SqlAlchemyMissionBindingRepository(sessions)
-    await bindings.create(
-        script_build_id=script_build_id,
-        root_trace_id=ROOT,
-        input_snapshot_id=int(snapshot.snapshot_id),
-        engine_version="phase-two-real-runner-test",
-        schema_version="v2",
-    )
-    task_store = FileSystemTaskStore(str(tmp_path / "ledger"))
-    trace_store = FileSystemTraceStore(str(tmp_path / "trace"))
-    llm = _PhaseTwoScriptedLLM(task_store)
-    runner = AgentRunner(trace_store=trace_store, llm_call=llm)
-    framework_artifacts = FileSystemArtifactStore(str(tmp_path / "framework-artifacts"))
-    business_artifacts = SqlAlchemyScriptBusinessArtifactRepository(sessions)
-    workspaces = SqlAlchemyCandidateWorkspaceRepository(sessions, business_artifacts)
-    composition = compose_host(
-        HostDependencies(
-            runner=runner,
-            task_store=task_store,
-            framework_artifact_store=framework_artifacts,
-            input_snapshot_service=cast(Any, snapshot_repository),
-            input_snapshots=snapshot_repository,
-            bindings=bindings,
-            business_artifacts=business_artifacts,
-            publications=SqlAlchemyPublicationRepository(sessions),
-            legacy_state=build_states,
-            principal_provider=_PrincipalProvider(),
-            build_authorizer=_Authorizer(),
-            retrieval_adapters={"decode": _DecodeAdapter()},
-            enabled_phase=2,
-            agent_data_root=tmp_path / "agent-data",
-            candidate_workspaces=workspaces,
-            default_model_manifest=model_manifest,
-        )
-    )
-
-    await build_states.set_status(script_build_id, BuildStatus.RUNNING)
-    await composition.mission_service.run(script_build_id)
-
-    ledger = await task_store.load(ROOT)
-    root = ledger.tasks[ledger.root_task_id]
-    assert root.status is TaskStatus.BLOCKED
-    assert root.blocked_reason == PHASE_TWO_CANDIDATE_PORTFOLIO_READY
-    phase_two_blocks = [
-        item
-        for item in ledger.decisions.values()
-        if item.task_id == root.task_id
-        and item.action is DecisionAction.BLOCK
-        and item.reason == PHASE_TWO_CANDIDATE_PORTFOLIO_READY
-    ]
-    phase_one_blocks = [
-        item
-        for item in ledger.decisions.values()
-        if item.task_id == root.task_id
-        and item.action is DecisionAction.BLOCK
-        and item.reason == PHASE_ONE_CAPABILITY_BOUNDARY
-    ]
-    assert len(phase_one_blocks) == len(phase_two_blocks) == 1
-    assert await build_states.get_status(script_build_id) is BuildStatus.PARTIAL
-    assert llm.worker_kinds == [
-        "decode-retrieval",
-        "direction",
-        "structure",
-        "paragraph",
-        "element-set",
-    ]
-    assert llm.validator_kinds == [
-        "decode-retrieval",
-        "direction",
-        "structure",
-        "paragraph",
-        "element-set",
-        "compose",
-        "candidate-portfolio",
-    ]
-    assert llm.submit_attempt_arguments
-    assert all(not item for item in llm.submit_attempt_arguments)
-    assert llm.compose_candidates_saved == 0
-    assert llm.tool_calls_by_name.get("create_script_paragraph", 0) == 0
-
-    compose_task = next(
-        task
-        for task in ledger.tasks.values()
-        if task.task_id != ledger.root_task_id and _task_kind(task) == "compose"
-    )
-    assert compose_task.current_spec_version == 2
-    assert len(compose_task.attempt_ids) == 1
-    assert [ledger.validations[item].verdict for item in compose_task.validation_ids] == [
-        ValidationVerdict.PASSED,
-    ]
-    assert [ledger.decisions[item].action for item in compose_task.decision_ids] == [
-        DecisionAction.REVISE,
-        DecisionAction.ACCEPT,
-    ]
-
-    paragraph_tasks = [
-        task
-        for task in ledger.tasks.values()
-        if task.task_id != ledger.root_task_id and _task_kind(task) == "paragraph"
-    ]
-    assert len(paragraph_tasks) == 1
-    assert (
-        len(
-            [
-                task
-                for task in ledger.tasks.values()
-                if task.task_id != ledger.root_task_id and _task_kind(task) == "compare"
-            ]
-        )
-        == 0
-    )
-
-    expected_identities = {
-        str(item["preset"]): str(item["content_sha256"]) for item in prompt_manifest
-    }
-    for attempt in ledger.attempts.values():
-        worker_trace = await trace_store.get_trace(attempt.worker_trace_id)
-        assert worker_trace is not None
-        if worker_trace.context.get("deterministic_worker"):
-            assert worker_trace.total_tokens == 0
-            assert attempt.worker_preset in {
-                "script_compose_worker",
-                "script_candidate_portfolio_worker",
-            }
-            continue
-        assert (
-            worker_trace.context["role_prompt_identity"]
-            == expected_identities[attempt.worker_preset]
-        )
-        validations = [
-            ledger.validations[item]
-            for item in ledger.tasks[attempt.task_id].validation_ids
-            if ledger.validations[item].attempt_id == attempt.attempt_id
-        ]
-        assert len(validations) == 1
-        validator_trace = await trace_store.get_trace(validations[0].validator_trace_id)
-        if validations[0].summary.startswith("Deterministic preflight rejected"):
-            assert validator_trace is None
-            continue
-        assert validator_trace is not None
-        assert (
-            validator_trace.context["role_prompt_identity"]
-            == expected_identities[validations[0].validator_preset]
-        )
-
-    reopened_traces = FileSystemTraceStore(str(tmp_path / "trace"))
-    planner_trace = await reopened_traces.get_trace(ROOT)
-    assert planner_trace is not None
-    assert planner_trace.status == "incomplete"
-    assert planner_trace.error_message == "mission_blocked"
-    planner_messages = await reopened_traces.get_trace_messages(ROOT)
-    phase_two_policies = [
-        item
-        for item in planner_messages
-        if item.role == "system" and "script-build-phase-two/v1" in str(item.content)
-    ]
-    phase_two_continuations = [
-        item
-        for item in planner_messages
-        if item.role == "user"
-        and '"mission": "continue_toward_root"' in str(item.content)
-        and '"phase": 2' in str(item.content)
-    ]
-    assert len(phase_two_policies) == len(phase_two_continuations) == 1
-
-    portfolio_task = next(
-        task
-        for task in ledger.tasks.values()
-        if task.task_id != ledger.root_task_id and _task_kind(task) == "candidate-portfolio"
-    )
-
-    def belongs_to_phase_two(task: Any) -> bool:
-        current = task
-        while current.parent_task_id is not None:
-            if current.task_id == portfolio_task.task_id:
-                return True
-            current = ledger.tasks[current.parent_task_id]
-        return False
-
-    phase_two_attempts = [
-        attempt
-        for attempt in ledger.attempts.values()
-        if belongs_to_phase_two(ledger.tasks[attempt.task_id])
-    ]
-    assert phase_two_attempts
-    for attempt in phase_two_attempts:
-        worker_trace = await reopened_traces.get_trace(attempt.worker_trace_id)
-        assert worker_trace is not None and worker_trace.status == "completed"
-        messages = await reopened_traces.get_trace_messages(attempt.worker_trace_id)
-        if worker_trace.context.get("deterministic_worker"):
-            assert messages == []
-        else:
-            assert messages
-        validation = next(
-            ledger.validations[item]
-            for item in ledger.tasks[attempt.task_id].validation_ids
-            if ledger.validations[item].attempt_id == attempt.attempt_id
-        )
-        validator_trace = await reopened_traces.get_trace(validation.validator_trace_id)
-        if validation.summary.startswith("Deterministic preflight rejected"):
-            assert validator_trace is None
-            continue
-        assert validator_trace is not None and validator_trace.status == "completed"
-        assert await reopened_traces.get_trace_messages(validation.validator_trace_id)
-
-    async with sessions() as session:
-        rows = (
-            (
-                await session.execute(
-                    select(artifact_version_table).where(
-                        artifact_version_table.c.script_build_id == script_build_id
-                    )
-                )
-            )
-            .mappings()
-            .all()
-        )
-        branch_ids = list(
-            await session.scalars(
-                select(script_build_paragraph.c.branch_id).where(
-                    script_build_paragraph.c.script_build_id == script_build_id
-                )
-            )
-        )
-        build = (
-            (
-                await session.execute(
-                    select(script_build_record).where(script_build_record.c.id == script_build_id)
-                )
-            )
-            .mappings()
-            .one()
-        )
-    kinds = {str(row["artifact_type"]) for row in rows}
-    assert {
-        ArtifactKind.DIRECTION.value,
-        ArtifactKind.PARAGRAPH.value,
-        ArtifactKind.STRUCTURED_SCRIPT.value,
-        ArtifactKind.CANDIDATE_PORTFOLIO.value,
-    } <= kinds
-    assert branch_ids and min(branch_ids) > 0 and 0 not in branch_ids
-    assert BuildStatus(str(build["status"])) is BuildStatus.PARTIAL
-    assert build["error_message"] == PHASE_TWO_CANDIDATE_PORTFOLIO_READY
-    assert build["reson_trace_id"] == ROOT
-
-
-@pytest.mark.asyncio
-async def test_historical_partial_advances_via_http_and_reenters_after_filestore_reload(
-    database: Any,
-    tmp_path: Path,
-) -> None:
-    """Exercise SQL v1->v2 CAS and the only pre-recovery crash-safe HTTP window."""
-
-    _, sessions = database
-    build_states = SqlAlchemyLegacyBuildStateRepository(sessions)
-    script_build_id = await build_states.create(
-        execution_id=801,
-        topic_build_id=802,
-        topic_id=803,
-        agent_type="script_planner",
-        agent_config={},
-        data_source_url=None,
-        strategies_config={},
-    )
-    phase_one_prompts = phase_one_prompt_manifest()
-    full_prompts = script_build_prompt_manifest()
-    phase_one_models = {
-        "presets": {
-            str(item["preset"]): {
-                "model": "scripted-fake-model",
-                "temperature": 0,
-                "max_iterations": 80,
-            }
-            for item in phase_one_prompts
-        }
-    }
-    full_models = {
-        "presets": {
-            str(item["preset"]): {
-                "model": "scripted-fake-model",
-                "temperature": 0,
-                "max_iterations": 80,
-            }
-            for item in full_prompts
-        }
-    }
-    snapshots = SqlAlchemyInputSnapshotRepository(sessions)
-    phase_one_snapshot = await snapshots.freeze(
-        ScriptBuildInput(
-            script_build_id=script_build_id,
-            execution_id=801,
-            topic_build_id=802,
-            topic_id=803,
-            topic={"topic": {"id": 803, "result": "A historical Phase1 checkpoint"}},
-            account={"account_name": "historical-fixture"},
-            prompt_manifest=tuple(dict(item) for item in phase_one_prompts),
-            model_manifest=phase_one_models,
-        )
-    )
-    bindings = SqlAlchemyMissionBindingRepository(sessions)
-    await bindings.create(
-        script_build_id=script_build_id,
-        root_trace_id=ROOT,
-        input_snapshot_id=int(phase_one_snapshot.snapshot_id),
-        engine_version="phase-two-http-reentry-test",
-        schema_version="v1",
-    )
-
-    phase_one_store = FileSystemTaskStore(str(tmp_path / "ledger"))
-    phase_one_host = _persisted_host(
-        sessions=sessions,
-        tmp_path=tmp_path,
-        llm=_PhaseTwoScriptedLLM(phase_one_store),
-        enabled_phase=1,
-        model_manifest=full_models,
-    )
-    await build_states.set_status(script_build_id, BuildStatus.RUNNING)
-    await phase_one_host.mission_service.run(script_build_id)
-
-    phase_one_ledger = await phase_one_store.load(ROOT)
-    phase_one_root = phase_one_ledger.tasks[phase_one_ledger.root_task_id]
-    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"))
-    blocked_llm = _BlockedAfterPolicyLLM(transition_store)
-    transition_host = _persisted_host(
-        sessions=sessions,
-        tmp_path=tmp_path,
-        llm=blocked_llm,
-        enabled_phase=2,
-        model_manifest=full_models,
-    )
-    async with httpx.AsyncClient(
-        transport=httpx.ASGITransport(app=transition_host.app),
-        base_url="http://test",
-    ) as client:
-        response = await client.post(
-            f"/api/pattern/script_builds/{script_build_id}/phase-two/advance"
-        )
-    assert response.status_code == 200, response.text
-    first_advance = response.json()
-    assert first_advance["root_trace_id"] == ROOT
-    assert first_advance["input_snapshot_id"] != phase_one_snapshot.snapshot_id
-    phase_two_snapshot_id = str(first_advance["input_snapshot_id"])
-
-    await asyncio.wait_for(blocked_llm.phase_two_model_call.wait(), timeout=10)
-    transition_ledger = await transition_store.load(ROOT)
-    assert transition_ledger.tasks[transition_ledger.root_task_id].status is TaskStatus.NEEDS_REPLAN
-    assert await build_states.get_status(script_build_id) is BuildStatus.RUNNING
-    crashed_task = transition_host.mission_service._runs[script_build_id]
-    crashed_task.cancel()
-    with pytest.raises(asyncio.CancelledError):
-        await crashed_task
-
-    reopened_after_crash = FileSystemTraceStore(str(tmp_path / "trace"))
-    persisted_messages = await reopened_after_crash.get_trace_messages(ROOT)
-    persisted_events = await reopened_after_crash.get_events(ROOT, 0)
-    assert _phase_two_policy_count(persisted_messages) == 1
-    assert _phase_two_continuation_count(persisted_messages) == 1
-    assert _policy_migration_count(persisted_events) == 1
-
-    # A fresh Host must take the NEEDS_REPLAN reentry path without rewriting policy.
-    retry_store = FileSystemTaskStore(str(tmp_path / "ledger"))
-    retry_host = _persisted_host(
-        sessions=sessions,
-        tmp_path=tmp_path,
-        llm=_PhaseTwoScriptedLLM(retry_store),
-        enabled_phase=2,
-        model_manifest=full_models,
-    )
-    async with httpx.AsyncClient(
-        transport=httpx.ASGITransport(app=retry_host.app),
-        base_url="http://test",
-    ) as client:
-        retry_response = await client.post(
-            f"/api/pattern/script_builds/{script_build_id}/phase-two/advance"
-        )
-    assert retry_response.status_code == 200, retry_response.text
-    assert retry_response.json()["root_trace_id"] == ROOT
-    assert retry_response.json()["input_snapshot_id"] == phase_two_snapshot_id
-    retry_task = retry_host.mission_service._runs[script_build_id]
-    await asyncio.wait_for(asyncio.shield(retry_task), timeout=60)
-
-    final_binding = await bindings.get_by_build(script_build_id)
-    assert final_binding.root_trace_id == ROOT
-    assert str(final_binding.input_snapshot_id) == phase_two_snapshot_id
-    phase_two_snapshot = await snapshots.get(
-        phase_two_snapshot_id,
-        script_build_id=script_build_id,
-    )
-    assert phase_two_snapshot.parent_snapshot_id == phase_one_snapshot.snapshot_id
-    assert phase_two_snapshot.parent_snapshot_sha256 == phase_one_snapshot.canonical_sha256
-    assert (
-        phase_two_snapshot.business_input_sha256
-        == canonical_sha256(phase_one_snapshot.to_input().business_payload()).wire
-    )
-    assert {str(item["preset"]) for item in phase_two_snapshot.prompt_manifest} == {
-        str(item["preset"]) for item in full_prompts
-    }
-
-    async with sessions() as session:
-        snapshot_rows = (
-            (
-                await session.execute(
-                    select(input_snapshot_table)
-                    .where(input_snapshot_table.c.script_build_id == script_build_id)
-                    .order_by(input_snapshot_table.c.version)
-                )
-            )
-            .mappings()
-            .all()
-        )
-        binding_row = (
-            (
-                await session.execute(
-                    select(mission_binding_table).where(
-                        mission_binding_table.c.script_build_id == script_build_id
-                    )
-                )
-            )
-            .mappings()
-            .one()
-        )
-    assert [int(item["version"]) for item in snapshot_rows] == [1, 2]
-    assert int(binding_row["input_snapshot_id"]) == int(phase_two_snapshot_id)
-    assert str(binding_row["root_trace_id"]) == ROOT
-
-    final_ledger = await retry_store.load(ROOT)
-    final_root = final_ledger.tasks[final_ledger.root_task_id]
-    assert final_root.status is TaskStatus.BLOCKED
-    assert final_root.blocked_reason == PHASE_TWO_CANDIDATE_PORTFOLIO_READY
-    assert await build_states.get_status(script_build_id) is BuildStatus.PARTIAL
-    direction = next(
-        item
-        for item in final_ledger.tasks.values()
-        if item.task_id != final_ledger.root_task_id and _task_kind(item) == "direction"
-    )
-    direction_attempt = final_ledger.attempts[direction.attempt_ids[-1]]
-    direction_spec = next(
-        item for item in direction.specs if item.version == direction_attempt.spec_version
-    )
-    assert f"script-build://inputs/{phase_one_snapshot.snapshot_id}" in direction_spec.context_refs
-
-    final_traces = FileSystemTraceStore(str(tmp_path / "trace"))
-    final_messages = await final_traces.get_trace_messages(ROOT)
-    final_events = await final_traces.get_events(ROOT, 0)
-    assert _phase_two_policy_count(final_messages) == 1
-    assert _phase_two_continuation_count(final_messages) == 1
-    assert _policy_migration_count(final_events) == 1
-
-
-@pytest.mark.asyncio
-async def test_real_runner_phase_one_to_three_final_publication_and_legacy_readback(
-    database: Any, tmp_path: Path
-) -> None:
-    _, sessions = database
-    states = SqlAlchemyLegacyBuildStateRepository(sessions)
-    script_build_id = await states.create(
-        execution_id=801,
-        topic_build_id=802,
-        topic_id=803,
-        agent_type="script_planner",
-        agent_config={},
-        data_source_url=None,
-        strategies_config={},
-    )
-    phase_two_prompts = script_build_prompt_manifest()
-    all_prompts = (*phase_two_prompts, *phase_three_prompt_manifest())
-    model_manifest = {
-        "presets": {
-            str(item["preset"]): {
-                "model": "scripted-fake-model",
-                "temperature": 0,
-                "max_iterations": 100,
-            }
-            for item in all_prompts
-        }
-    }
-    snapshots = SqlAlchemyInputSnapshotRepository(sessions)
-    snapshot = await snapshots.freeze(
-        ScriptBuildInput(
-            script_build_id=script_build_id,
-            execution_id=801,
-            topic_build_id=802,
-            topic_id=803,
-            topic={"topic": {"id": 803, "result": "A grounded reversal"}},
-            account={"account_name": "fixture-account"},
-            prompt_manifest=tuple(dict(item) for item in phase_two_prompts),
-            model_manifest=model_manifest,
-        )
-    )
-    bindings = SqlAlchemyMissionBindingRepository(sessions)
-    await bindings.create(
-        script_build_id=script_build_id,
-        root_trace_id=ROOT,
-        input_snapshot_id=int(snapshot.snapshot_id),
-        engine_version="phase-three-real-runner-test",
-        schema_version="v3",
-    )
-    task_store = FileSystemTaskStore(str(tmp_path / "ledger"))
-    trace_store = FileSystemTraceStore(str(tmp_path / "trace"))
-    llm = _PhaseThreeScriptedLLM(task_store)
-    runner = AgentRunner(trace_store=trace_store, llm_call=llm)
-    artifacts = SqlAlchemyScriptBusinessArtifactRepository(sessions)
-    input_service = ScriptInputSnapshotService(
-        legacy_input=cast(Any, None),
-        persona_source=cast(Any, None),
-        strategy_source=cast(Any, None),
-        prompt_source=_PromptCatalog(),
-        snapshots=snapshots,
-    )
-    projection = LegacyDetailProjectionService(sessions, _Authorizer())
-    fencing = FencedCommandGate(sessions)
-    final_uow = SqlAlchemyFinalPublicationUnitOfWork(sessions, projection, fencing)
-    lease_root = tmp_path / "leases"
-
-    def lease_factory(_script_build_id: int) -> OwnerLease:
-        return OwnerLease(sessions, lease_root)
-
-    composition = compose_host(
-        HostDependencies(
-            runner=runner,
-            task_store=task_store,
-            framework_artifact_store=FileSystemArtifactStore(str(tmp_path / "framework-artifacts")),
-            input_snapshot_service=input_service,
-            input_snapshots=snapshots,
-            bindings=bindings,
-            business_artifacts=artifacts,
-            publications=SqlAlchemyPublicationRepository(sessions),
-            legacy_state=states,
-            principal_provider=_PrincipalProvider(),
-            build_authorizer=_Authorizer(),
-            retrieval_adapters={"decode": _DecodeAdapter()},
-            enabled_phase=2,
-            agent_data_root=tmp_path / "agent-data",
-            candidate_workspaces=SqlAlchemyCandidateWorkspaceRepository(sessions, artifacts),
-            default_model_manifest=model_manifest,
-            owner_lease_factory=lease_factory,
-            fenced_command_gate=fencing,
-            final_publication_uow=final_uow,
-            legacy_projection=projection,
-        )
-    )
-    await states.set_status(script_build_id, BuildStatus.RUNNING)
-    await composition.mission_service.run(script_build_id)
-    async with sessions() as session:
-        candidate_ids_before = tuple(
-            (
-                await session.execute(
-                    select(script_build_paragraph.c.id).where(
-                        script_build_paragraph.c.script_build_id == script_build_id,
-                        script_build_paragraph.c.branch_id > 0,
-                    )
-                )
-            ).scalars()
-        )
-
-    assert composition.phase_three is not None
-    await composition.phase_three.advance(script_build_id, Principal("phase-two-owner"))
-    phase_three_run = composition.phase_three._runs[script_build_id]
-    await phase_three_run
-    ledger = await task_store.load(ROOT)
-    root = ledger.tasks[ledger.root_task_id]
-    assert root.status is TaskStatus.COMPLETED
-    assert _task_kind(root) == "root-delivery"
-    assert llm.worker_kinds[-1] == "root-delivery"
-    assert llm.validator_kinds[-1] == "root-delivery"
-
-    assert composition.finalization is not None
-    result = await composition.finalization.finalize(script_build_id, Principal("phase-two-owner"))
-    assert result.committed is True
-    detail = await projection.project_authorized(
-        script_build_id, principal=Principal("phase-two-owner")
-    )
-    assert detail["build"]["status"] == BuildStatus.SUCCESS.value
-    assert projection.to_canonical(detail).canonical_sha256 == result.legacy_projection_digest
-    assert detail["paragraphs"]
-
-    async with sessions() as session:
-        publication = (
-            (
-                await session.execute(
-                    select(publication_table).where(
-                        publication_table.c.script_build_id == script_build_id,
-                        publication_table.c.publication_type == "final",
-                    )
-                )
-            )
-            .mappings()
-            .one()
-        )
-        binding = (
-            (
-                await session.execute(
-                    select(mission_binding_table).where(
-                        mission_binding_table.c.script_build_id == script_build_id
-                    )
-                )
-            )
-            .mappings()
-            .one()
-        )
-        candidate_ids_after = tuple(
-            (
-                await session.execute(
-                    select(script_build_paragraph.c.id).where(
-                        script_build_paragraph.c.script_build_id == script_build_id,
-                        script_build_paragraph.c.branch_id > 0,
-                    )
-                )
-            ).scalars()
-        )
-    assert publication["state"] == "published"
-    assert binding["accepted_root_artifact_version_id"] == publication["artifact_version_id"]
-    assert candidate_ids_after == candidate_ids_before
-
-    await trace_store.record_model_usage(
-        ROOT,
-        sequence=10_000,
-        role="assistant",
-        model="scripted-fake-model",
-        prompt_tokens=123,
-        completion_tokens=7,
-    )
-    planner_trace = await trace_store.get_trace(ROOT)
-    assert planner_trace is not None
-    await trace_store.add_message(
-        Message.create(
-            trace_id=ROOT,
-            role="tool",
-            sequence=planner_trace.last_sequence + 1,
-            parent_sequence=planner_trace.head_sequence,
-            tool_call_id="diagnostic-planner-failure",
-            content={
-                "tool_name": "plan_script_tasks",
-                "result": json.dumps(
-                    {
-                        "failure": {
-                            "code": "INPUT_SCOPE_MISMATCH",
-                            "message": "scope is outside the active direction",
-                            "disposition": "replan_task",
-                            "source_tool": "plan_script_tasks",
-                            "details": {"task_id": root.task_id},
-                        }
-                    }
-                ),
-            },
-        )
-    )
-    planner_started_at = datetime(2026, 7, 22, tzinfo=UTC)
-    await trace_store.update_trace(
-        ROOT,
-        created_at=planner_started_at,
-        completed_at=planner_started_at + timedelta(seconds=12.5),
-        last_activity_at=planner_started_at + timedelta(seconds=12.5),
-    )
-    diagnostics = await _collect_diagnostics(
-        SimpleNamespace(composition=composition), script_build_id
-    )
-    assert diagnostics["current_domain_error"] is None
-    assert "last_domain_error" not in diagnostics
-    assert diagnostics["preset_usage"]["script_planner"]["calls"] >= 1
-    assert diagnostics["preset_usage"]["script_planner"]["tokens"] >= 130
-    assert diagnostics["preset_usage"]["script_planner"]["seconds"] == pytest.approx(12.5)
-    assert diagnostics["error_history_by_code"]["INPUT_SCOPE_MISMATCH"] == 1
-
-
-def _phase_two_policy_count(messages: Sequence[Any]) -> int:
-    return len(
-        [
-            item
-            for item in messages
-            if item.role == "system" and "script-build-phase-two/v1" in str(item.content)
-        ]
-    )
-
-
-def _phase_two_continuation_count(messages: Sequence[Any]) -> int:
-    return len(
-        [
-            item
-            for item in messages
-            if item.role == "user" and '"mission": "continue_toward_root"' in str(item.content)
-        ]
-    )
-
-
-def _policy_migration_count(events: Sequence[Mapping[str, Any]]) -> int:
-    return len(
-        [
-            item
-            for item in events
-            if item.get("event") == "planner_policy_migrated"
-            or item.get("event_type") == "planner_policy_migrated"
-        ]
-    )

+ 0 - 1010
script_build_host/tests/test_phase_two_sql_flow.py

@@ -1,1010 +0,0 @@
-from __future__ import annotations
-
-import asyncio
-import re
-from collections.abc import Mapping, Sequence
-from datetime import UTC, datetime
-from pathlib import Path
-from types import SimpleNamespace
-from typing import Any, cast
-
-import pytest
-from agent import FileSystemArtifactStore, FileSystemTaskStore, FileSystemTraceStore
-from agent.orchestration import (
-    AgentRole,
-    ArtifactRef,
-    AttemptSubmission,
-    CriterionResult,
-    DecisionAction,
-    TaskCoordinator,
-    TaskStatus,
-    ValidationVerdict,
-)
-from agent.orchestration.protocols import ValidatorRunResult, WorkerRunResult
-from sqlalchemy import event, func, select
-
-from script_build_host.application.phase_two_boundary import ScriptPhaseTwoBoundaryVerifier
-from script_build_host.application.phase_two_candidates import PhaseTwoCandidateService
-from script_build_host.application.phase_two_inputs import (
-    AcceptedInputResolver,
-    ActiveFrontierResolver,
-    StoredTaskContractReader,
-)
-from script_build_host.application.phase_two_planning import (
-    PHASE_TWO_CANDIDATE_PORTFOLIO_READY,
-    PhaseTwoPlanningService,
-)
-from script_build_host.domain.artifacts import (
-    ArtifactKind,
-    DirectionArtifact,
-    DirectionGoal,
-    EvidenceRecordV1,
-)
-from script_build_host.domain.errors import PhaseTwoBoundaryNotReady
-from script_build_host.domain.phase_two_artifacts import (
-    CandidatePortfolioArtifactV1,
-    StructuredScriptArtifactV1,
-)
-from script_build_host.domain.records import BuildStatus, MissionBinding
-from script_build_host.domain.task_contracts import ScriptTaskKind
-from script_build_host.infrastructure.legacy_tables import (
-    script_build_element,
-    script_build_paragraph,
-    script_build_paragraph_element,
-    script_build_record,
-)
-from script_build_host.infrastructure.tables import (
-    artifact_version_table,
-    publication_table,
-)
-from script_build_host.infrastructure.task_contract_store import FileScriptTaskContractStore
-from script_build_host.repositories.legacy_state import SqlAlchemyLegacyBuildStateRepository
-from script_build_host.repositories.sqlalchemy import (
-    SqlAlchemyMissionBindingRepository,
-    SqlAlchemyScriptBusinessArtifactRepository,
-)
-from script_build_host.repositories.workspace import SqlAlchemyCandidateWorkspaceRepository
-
-ROOT = "phase-two-sql-root"
-SNAPSHOT_ID = 11
-FULL_SCOPE = "script-build://scopes/full"
-FULL_WRITE = "script-build://writes/full"
-
-
-def _contract(
-    kind: ScriptTaskKind,
-    *,
-    scope: str = FULL_SCOPE,
-    write_scope: Sequence[str] = (FULL_WRITE,),
-    input_refs: Sequence[dict[str, Any]] = (),
-    base_ref: ArtifactRef | None = None,
-    supersedes: Sequence[str] = (),
-    closure: Sequence[dict[str, Any]] = (),
-    adopted: Sequence[str] = (),
-    held: Sequence[str] = (),
-    order: Sequence[str] = (),
-    comparison_refs: Sequence[dict[str, Any]] = (),
-    execution_ready: bool = True,
-) -> dict[str, Any]:
-    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:
-        intent = "replace"
-    else:
-        intent = "explore"
-    criterion = (
-        {
-            "client_key": "closed",
-            "criterion_type": "evidence_nonempty",
-            "params": {"minimum_count": 1},
-            "hard": True,
-        }
-        if kind is ScriptTaskKind.DECODE_RETRIEVAL
-        else {
-            "client_key": "closed",
-            "criterion_type": "custom",
-            "description": "the frozen increment is concrete and independently verifiable",
-            "hard": True,
-        }
-    )
-    payload: dict[str, Any] = {
-        "task_kind": kind.value,
-        "scope_selector": {
-            "anchor": "mission",
-            "path": scope.removeprefix("script-build://scopes/").split("/")[:4],
-        },
-        "intent_class": intent,
-        "objective": f"produce independently verifiable {kind.value} content",
-        "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_key": None,
-        "criteria": [criterion],
-        "goal_ids": (
-            []
-            if kind in {ScriptTaskKind.DIRECTION, ScriptTaskKind.DECODE_RETRIEVAL}
-            else ["goal-1"]
-        ),
-        "supersedes_decision_ids": list(supersedes),
-        "comparison_decision_ids": [str(item["decision_id"]) for item in comparison_refs],
-    }
-    return payload
-
-
-def _decision_ref(
-    decision_id: str,
-    ref: ArtifactRef,
-    *,
-    scope: str,
-    kind: ScriptTaskKind,
-) -> dict[str, Any]:
-    return {
-        "decision_id": decision_id,
-        "artifact_ref": _ref_payload(ref),
-        "scope_ref": scope,
-        "expected_task_kind": kind.value,
-    }
-
-
-def _ref_payload(ref: ArtifactRef) -> dict[str, Any]:
-    return {
-        "uri": ref.uri,
-        "kind": ref.kind,
-        "version": ref.version,
-        "digest": ref.digest,
-        "summary": ref.summary,
-        "metadata": ref.metadata,
-    }
-
-
-def _task_kind(context: Mapping[str, Any]) -> ScriptTaskKind:
-    refs = cast(dict[str, Any], context["task_spec"])["context_refs"]
-    values = [value.rsplit("/", 1)[-1] for value in refs if "/task-kinds/" in value]
-    assert len(values) == 1
-    return ScriptTaskKind(values[0])
-
-
-class _ScriptedExecutor:
-    """Drive real Coordinator stages through the same protected Host services."""
-
-    def __init__(
-        self,
-        *,
-        coordinator: TaskCoordinator,
-        candidates: PhaseTwoCandidateService,
-        artifacts: SqlAlchemyScriptBusinessArtifactRepository,
-        script_build_id: int,
-    ) -> None:
-        self.coordinator = coordinator
-        self.candidates = candidates
-        self.artifacts = artifacts
-        self.script_build_id = script_build_id
-
-    async def run_worker(self, context: dict[str, Any]) -> WorkerRunResult:
-        kind = _task_kind(context)
-        candidate_context = {
-            "root_trace_id": context["root_trace_id"],
-            "task_id": context["task_id"],
-            "attempt_id": context["attempt_id"],
-            "spec_version": context["spec_version"],
-        }
-        if kind is ScriptTaskKind.DECODE_RETRIEVAL:
-            await self.artifacts.freeze(
-                script_build_id=self.script_build_id,
-                task_id=context["task_id"],
-                attempt_id=context["attempt_id"],
-                spec_version=context["spec_version"],
-                artifact=EvidenceRecordV1(
-                    evidence_id=f"evidence-{context['attempt_id']}",
-                    source_type="decode",
-                    tool_name="retrieve_decode",
-                    query={"query": "opening tension", "top_k": 3},
-                    source_refs=("decode-index://fixture/1",),
-                    raw_artifact_ref=None,
-                    summary="A concrete opening needs a visible tension and an observable detail.",
-                    supports=("direction",),
-                    confidence="high",
-                    limitations=(),
-                    content_sha256="",
-                    created_at=datetime.now(UTC),
-                ),
-            )
-        elif kind is ScriptTaskKind.DIRECTION:
-            child_results = cast(list[dict[str, Any]], context["accepted_child_results"])
-            assert len(child_results) == 1
-            evidence_refs = child_results[0]["submission"]["evidence_refs"]
-            await self.artifacts.freeze(
-                script_build_id=self.script_build_id,
-                task_id=context["task_id"],
-                attempt_id=context["attempt_id"],
-                spec_version=context["spec_version"],
-                artifact=DirectionArtifact(
-                    goals=(
-                        DirectionGoal(
-                            "goal-1",
-                            "Use a concrete reversal to reveal the topic",
-                            "A visible reversal makes the direction observable",
-                            success_criteria=("the opening reveals one concrete reversal",),
-                        ),
-                    ),
-                    evidence_refs=(str(evidence_refs[0]["uri"]),),
-                ),
-            )
-        elif kind in {ScriptTaskKind.STRUCTURE, ScriptTaskKind.PARAGRAPH}:
-            workspace = await self.candidates.read_attempt_workspace(context=candidate_context)
-            if workspace["paragraphs"]:
-                paragraph_id = workspace["paragraphs"][0]["paragraph_id"]
-            else:
-                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,
-                )
-        elif kind is ScriptTaskKind.ELEMENT_SET:
-            created = await self.candidates.create_script_element(
-                payload={
-                    "name": "observable reversal detail",
-                    "dimension_primary": "实质",
-                    "dimension_secondary": "narrative evidence",
-                    "topic_support": {"scope": "opening"},
-                    "weight_score": {"score": 0.9},
-                },
-                context=candidate_context,
-            )
-            workspace = await self.candidates.read_attempt_workspace(context=candidate_context)
-            if workspace["paragraphs"]:
-                await self.candidates.batch_link_paragraph_elements(
-                    links=(
-                        {
-                            "paragraph_id": workspace["paragraphs"][0]["paragraph_id"],
-                            "element_ids": [created["element_id"]],
-                        },
-                    ),
-                    context=candidate_context,
-                )
-        elif kind is ScriptTaskKind.COMPARE:
-            pinned = await self.candidates.read_pinned_candidates(context=candidate_context)
-            candidate_decision_ids = [
-                str(item["decision_id"])
-                for item in pinned
-                if item["artifact_ref"]["kind"] == ArtifactKind.PARAGRAPH.value
-            ]
-            criteria = cast(
-                Sequence[Mapping[str, Any]],
-                cast(Mapping[str, Any], context["task_spec"])["acceptance_criteria"],
-            )
-            await self.candidates.save_comparison_candidate(
-                payload={
-                    "criterion_results": [
-                        {
-                            "criterion_id": str(criterion["criterion_id"]),
-                            "candidate_results": [
-                                {
-                                    "decision_id": decision_id,
-                                    "reason": "immutable candidate reviewed",
-                                }
-                                for decision_id in candidate_decision_ids
-                            ],
-                        }
-                        for criterion in criteria
-                    ],
-                    "conflicts": ["the replacement has the more concrete opening"],
-                    "recommended_decision_id": candidate_decision_ids[-1],
-                },
-                context=candidate_context,
-            )
-        elif kind is ScriptTaskKind.COMPOSE:
-            await self.candidates.save_structured_script_candidate(
-                acceptance_notes=("all adopted increments are pinned by ACCEPT decisions",),
-                context=candidate_context,
-            )
-        elif kind is ScriptTaskKind.CANDIDATE_PORTFOLIO:
-            await self.candidates.save_candidate_portfolio(
-                payload={"unresolved_defects": []}, context=candidate_context
-            )
-        else:  # pragma: no cover - this executor is intentionally phase-two bounded
-            raise AssertionError(f"unexpected worker kind: {kind.value}")
-
-        manifest = await self.candidates.resolve_attempt_manifest(context=candidate_context)
-        safe_ref = ArtifactRef(
-            manifest.artifact_ref.uri,
-            manifest.artifact_ref.kind,
-            manifest.artifact_ref.version,
-            manifest.artifact_ref.digest,
-        )
-        await self.coordinator.submit_attempt(
-            {
-                "role": AgentRole.WORKER.value,
-                "root_trace_id": context["root_trace_id"],
-                "task_id": context["task_id"],
-                "attempt_id": context["attempt_id"],
-                "spec_version": context["spec_version"],
-                "trace_id": context["worker_trace_id"],
-                "tool_call_id": f"submit:{context['attempt_id']}",
-                "operation_id": context["operation_id"],
-                "execution_epoch": context["execution_epoch"],
-            },
-            AttemptSubmission(
-                summary=f"kind={safe_ref.kind};status=frozen",
-                artifact_refs=[] if safe_ref.kind == ArtifactKind.EVIDENCE.value else [safe_ref],
-                evidence_refs=[safe_ref] if safe_ref.kind == ArtifactKind.EVIDENCE.value else [],
-            ),
-        )
-        return WorkerRunResult(context["worker_trace_id"], "completed")
-
-    async def run_validator(self, context: dict[str, Any]) -> ValidatorRunResult:
-        criteria = cast(dict[str, Any], context["task_spec"])["acceptance_criteria"]
-        results = [
-            CriterionResult(
-                criterion_id=item["criterion_id"],
-                verdict=ValidationVerdict.PASSED,
-                reason="the immutable SQL artifact is concrete and owner/digest closed",
-            )
-            for item in criteria
-        ]
-        await self.coordinator.submit_validation(
-            {
-                "role": AgentRole.VALIDATOR.value,
-                "root_trace_id": context["root_trace_id"],
-                "task_id": context["task_id"],
-                "attempt_id": context["attempt_id"],
-                "validation_id": context["validation_id"],
-                "snapshot_id": context["snapshot_id"],
-                "trace_id": context["validator_trace_id"],
-                "tool_call_id": f"validate:{context['validation_id']}",
-                "operation_id": context["operation_id"],
-                "execution_epoch": context["execution_epoch"],
-            },
-            ValidationVerdict.PASSED,
-            results,
-            "all hard criteria passed against the immutable snapshot",
-            (),
-            (),
-            (),
-            "accept",
-        )
-        return ValidatorRunResult(context["validator_trace_id"], "completed")
-
-    async def stop(self, trace_id: str) -> bool:
-        del trace_id
-        return True
-
-
-async def _plan_one(
-    planning: PhaseTwoPlanningService,
-    payload: Mapping[str, Any],
-    *,
-    parent_task_id: str | None,
-    call_id: str,
-) -> str:
-    result = await planning.plan_script_tasks(
-        contract_payloads=(payload,),
-        parent_task_id=parent_task_id,
-        context={"root_trace_id": ROOT, "tool_call_id": call_id},
-    )
-    return cast(str, result["task_ids"][0])
-
-
-async def _dispatch_accept(
-    planning: PhaseTwoPlanningService, coordinator: TaskCoordinator, task_id: str
-) -> tuple[str, ArtifactRef]:
-    cycles = await planning.dispatch_script_tasks(
-        task_ids=(task_id,),
-        context={"root_trace_id": ROOT, "tool_call_id": f"dispatch:{task_id}"},
-    )
-    assert cycles[0]["error"] is None
-    validation_id = cast(str, cycles[0]["validation_id"])
-    accepted = await planning.decide_script_task(
-        task_id=task_id,
-        action=DecisionAction.ACCEPT.value,
-        reason="independent validation passed",
-        validation_id=validation_id,
-        replacement_contract=None,
-        child_contracts=(),
-        context={"root_trace_id": ROOT, "tool_call_id": f"accept:{task_id}"},
-    )
-    decision_id = cast(str, accepted["decision_id"])
-    ledger = await coordinator.task_store.load(ROOT)
-    decision = ledger.decisions[decision_id]
-    attempt = ledger.attempts[cast(str, decision.attempt_id)]
-    assert attempt.submission is not None
-    refs = [*attempt.submission.artifact_refs, *attempt.submission.evidence_refs]
-    assert len(refs) == 1
-    return decision_id, refs[0]
-
-
-@pytest.mark.asyncio
-async def test_sql_phase_two_dynamic_replacement_compose_portfolio_and_boundary(
-    database: Any, tmp_path: Path
-) -> None:
-    engine, sessions = database
-    mutation_statements: list[str] = []
-
-    def observe_sql(
-        _connection: Any,
-        _cursor: Any,
-        statement: str,
-        _parameters: Any,
-        _context: Any,
-        _executemany: bool,
-    ) -> None:
-        if statement.lstrip().upper().startswith(("INSERT ", "UPDATE ", "DELETE ")):
-            mutation_statements.append(statement)
-
-    event.listen(engine.sync_engine, "before_cursor_execute", observe_sql)
-    build_states = SqlAlchemyLegacyBuildStateRepository(sessions)
-    script_build_id = await build_states.create(
-        execution_id=101,
-        topic_build_id=202,
-        topic_id=303,
-        agent_type="script-planner",
-        agent_config={},
-        data_source_url=None,
-        strategies_config={},
-    )
-    bindings = SqlAlchemyMissionBindingRepository(sessions)
-    await bindings.create(
-        script_build_id=script_build_id,
-        root_trace_id=ROOT,
-        input_snapshot_id=SNAPSHOT_ID,
-        engine_version="phase-two-test",
-        schema_version="v1",
-    )
-    task_path = tmp_path / "task-ledger"
-    artifact_path = tmp_path / "framework-artifacts"
-    contract_path = tmp_path / "contract-data"
-    task_store = FileSystemTaskStore(str(task_path))
-    framework_artifacts = FileSystemArtifactStore(str(artifact_path))
-    coordinator = TaskCoordinator(
-        task_store,
-        framework_artifacts,
-        FileSystemTraceStore(str(tmp_path / "traces")),
-    )
-    await coordinator.ensure_ledger(
-        ROOT,
-        {
-            "objective": "produce a governed phase-two candidate portfolio",
-            "acceptance_criteria": [
-                {"criterion_id": "portfolio", "description": "portfolio closed", "hard": True}
-            ],
-            "context_refs": [f"script-build://inputs/{SNAPSHOT_ID}"],
-        },
-    )
-    contract_store = FileScriptTaskContractStore(contract_path)
-    contract_reader = StoredTaskContractReader(contract_store)
-    artifacts = SqlAlchemyScriptBusinessArtifactRepository(sessions)
-    accepted_inputs = AcceptedInputResolver(
-        task_store=task_store,
-        artifact_store=framework_artifacts,
-        artifacts=artifacts,
-        contracts=contract_reader,
-    )
-    workspaces = SqlAlchemyCandidateWorkspaceRepository(sessions, artifacts)
-    candidates = PhaseTwoCandidateService(
-        bindings=bindings,
-        task_store=task_store,
-        framework_artifact_store=framework_artifacts,
-        artifacts=artifacts,
-        accepted_inputs=accepted_inputs,
-        active_frontier=ActiveFrontierResolver(),
-        workspaces=workspaces,
-    )
-    boundary = ScriptPhaseTwoBoundaryVerifier(
-        task_store=task_store,
-        artifact_store=framework_artifacts,
-        artifacts=artifacts,
-        bindings=bindings,
-        accepted_inputs=accepted_inputs,
-    )
-    planning = PhaseTwoPlanningService(
-        coordinator=coordinator,
-        bindings=bindings,
-        contracts=contract_store,
-        artifacts=artifacts,
-        closure_gate=boundary,
-    )
-    coordinator.set_executor(
-        _ScriptedExecutor(
-            coordinator=coordinator,
-            candidates=candidates,
-            artifacts=artifacts,
-            script_build_id=script_build_id,
-        )
-    )
-
-    direction = await _plan_one(
-        planning, _contract(ScriptTaskKind.DIRECTION), parent_task_id=None, call_id="direction"
-    )
-    retrieval = await _plan_one(
-        planning,
-        _contract(ScriptTaskKind.DECODE_RETRIEVAL, write_scope=()),
-        parent_task_id=direction,
-        call_id="retrieval",
-    )
-    await _dispatch_accept(planning, coordinator, retrieval)
-    _, 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,
-        _contract(
-            ScriptTaskKind.CANDIDATE_PORTFOLIO,
-            write_scope=("script-build://writes",),
-            execution_ready=False,
-        ),
-        parent_task_id=None,
-        call_id="portfolio",
-    )
-    compose = await _plan_one(
-        planning,
-        _contract(
-            ScriptTaskKind.COMPOSE,
-            write_scope=("script-build://writes",),
-            execution_ready=False,
-        ),
-        parent_task_id=portfolio,
-        call_id="compose",
-    )
-
-    # Exploration order is intentionally Element -> Paragraph -> Structure.
-    old_element = await _plan_one(
-        planning,
-        _contract(
-            ScriptTaskKind.ELEMENT_SET,
-            scope="script-build://scopes/full/elements",
-            write_scope=("script-build://writes/elements",),
-        ),
-        parent_task_id=compose,
-        call_id="element-first",
-    )
-    old_element_decision, old_element_ref = await _dispatch_accept(
-        planning, coordinator, old_element
-    )
-    old_paragraph = await _plan_one(
-        planning,
-        _contract(
-            ScriptTaskKind.PARAGRAPH,
-            scope="script-build://scopes/full/opening",
-            write_scope=("script-build://writes/paragraphs/opening",),
-        ),
-        parent_task_id=compose,
-        call_id="paragraph-second",
-    )
-    old_paragraph_decision, old_paragraph_ref = await _dispatch_accept(
-        planning, coordinator, old_paragraph
-    )
-    structure = await _plan_one(
-        planning,
-        _contract(
-            ScriptTaskKind.STRUCTURE,
-            scope="script-build://scopes/full/opening",
-            write_scope=("script-build://writes/paragraphs/opening",),
-        ),
-        parent_task_id=compose,
-        call_id="structure-third",
-    )
-    structure_decision, structure_ref = await _dispatch_accept(planning, coordinator, structure)
-
-    paragraph_replacement = await _plan_one(
-        planning,
-        _contract(
-            ScriptTaskKind.PARAGRAPH,
-            scope="script-build://scopes/full/opening",
-            write_scope=("script-build://writes/paragraphs/opening",),
-            input_refs=(
-                _decision_ref(
-                    structure_decision,
-                    structure_ref,
-                    scope="script-build://scopes/full/opening",
-                    kind=ScriptTaskKind.STRUCTURE,
-                ),
-                _decision_ref(
-                    old_paragraph_decision,
-                    old_paragraph_ref,
-                    scope="script-build://scopes/full/opening",
-                    kind=ScriptTaskKind.PARAGRAPH,
-                ),
-            ),
-            base_ref=structure_ref,
-            supersedes=(old_paragraph_decision,),
-        ),
-        parent_task_id=compose,
-        call_id="replace-paragraph",
-    )
-    paragraph_decision, paragraph_ref = await _dispatch_accept(
-        planning, coordinator, paragraph_replacement
-    )
-    element_replacement = await _plan_one(
-        planning,
-        _contract(
-            ScriptTaskKind.ELEMENT_SET,
-            scope="script-build://scopes/full/elements",
-            write_scope=("script-build://writes/elements",),
-            input_refs=(
-                _decision_ref(
-                    old_element_decision,
-                    old_element_ref,
-                    scope="script-build://scopes/full/elements",
-                    kind=ScriptTaskKind.ELEMENT_SET,
-                ),
-                _decision_ref(
-                    paragraph_decision,
-                    paragraph_ref,
-                    scope="script-build://scopes/full/opening",
-                    kind=ScriptTaskKind.PARAGRAPH,
-                ),
-            ),
-            base_ref=paragraph_ref,
-            supersedes=(old_element_decision,),
-        ),
-        parent_task_id=compose,
-        call_id="replace-element",
-    )
-    element_decision, _element_ref = await _dispatch_accept(
-        planning, coordinator, element_replacement
-    )
-
-    comparison = await _plan_one(
-        planning,
-        _contract(
-            ScriptTaskKind.COMPARE,
-            scope="script-build://scopes/full/opening",
-            write_scope=(),
-            comparison_refs=(
-                _decision_ref(
-                    old_paragraph_decision,
-                    old_paragraph_ref,
-                    scope="script-build://scopes/full/opening",
-                    kind=ScriptTaskKind.PARAGRAPH,
-                ),
-                _decision_ref(
-                    paragraph_decision,
-                    paragraph_ref,
-                    scope="script-build://scopes/full/opening",
-                    kind=ScriptTaskKind.PARAGRAPH,
-                ),
-            ),
-        ),
-        parent_task_id=compose,
-        call_id="compare-opening-candidates",
-    )
-    await _dispatch_accept(planning, coordinator, comparison)
-
-    with pytest.raises(PhaseTwoBoundaryNotReady, match="CandidatePortfolio"):
-        await planning.decide_script_task(
-            task_id=(await task_store.load(ROOT)).root_task_id,
-            action=DecisionAction.BLOCK.value,
-            reason=PHASE_TWO_CANDIDATE_PORTFOLIO_READY,
-            validation_id=None,
-            replacement_contract=None,
-            child_contracts=(),
-            context={"root_trace_id": ROOT, "tool_call_id": "early-boundary"},
-        )
-
-    adopted = (structure_decision, paragraph_decision, element_decision)
-    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=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)
-
-    await planning.decide_script_task(
-        task_id=portfolio,
-        action=DecisionAction.REVISE.value,
-        reason="freeze the uniquely adopted StructuredScript",
-        validation_id=None,
-        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)
-
-    ledger = await task_store.load(ROOT)
-    compose_attempt = ledger.attempts[cast(str, ledger.decisions[compose_decision].attempt_id)]
-    frontier = await candidates.read_active_frontier(
-        context={
-            "root_trace_id": ROOT,
-            "task_id": compose,
-            "attempt_id": compose_attempt.attempt_id,
-            "spec_version": compose_attempt.spec_version,
-        }
-    )
-    assert [item["decision_id"] for item in frontier["inputs"]] == list(adopted)
-    assert old_element_decision not in {item["decision_id"] for item in frontier["inputs"]}
-    assert old_paragraph_decision not in {item["decision_id"] for item in frontier["inputs"]}
-
-    await planning.decide_script_task(
-        task_id=ledger.root_task_id,
-        action=DecisionAction.BLOCK.value,
-        reason=PHASE_TWO_CANDIDATE_PORTFOLIO_READY,
-        validation_id=None,
-        replacement_contract=None,
-        child_contracts=(),
-        context={"root_trace_id": ROOT, "tool_call_id": "phase-two-boundary"},
-    )
-    await build_states.set_checkpoint(
-        script_build_id,
-        checkpoint_code=PHASE_TWO_CANDIDATE_PORTFOLIO_READY,
-        summary="one validated CandidatePortfolio is ready for phase three",
-        root_trace_id=ROOT,
-    )
-
-    reloaded = await FileSystemTaskStore(str(task_path)).load(ROOT)
-    assert reloaded.tasks[reloaded.root_task_id].status is TaskStatus.BLOCKED
-    assert reloaded.tasks[reloaded.root_task_id].blocked_reason == (
-        PHASE_TWO_CANDIDATE_PORTFOLIO_READY
-    )
-    compose_children = [reloaded.tasks[item] for item in reloaded.tasks[compose].child_task_ids]
-    stored_reader = StoredTaskContractReader(FileScriptTaskContractStore(contract_path))
-    assert [
-        (
-            await stored_reader.read_for_task(
-                root_trace_id=ROOT,
-                task=item,
-                spec_version=item.current_spec_version,
-            )
-        ).task_kind
-        for item in compose_children[:3]
-    ] == [
-        ScriptTaskKind.ELEMENT_SET,
-        ScriptTaskKind.PARAGRAPH,
-        ScriptTaskKind.STRUCTURE,
-    ]
-
-    compose_version = await artifacts.get_by_attempt(
-        script_build_id=script_build_id,
-        task_id=compose,
-        attempt_id=compose_attempt.attempt_id,
-    )
-    assert isinstance(compose_version.artifact, StructuredScriptArtifactV1)
-    assert len(compose_version.artifact.paragraphs) == 1
-    assert compose_version.artifact.elements
-    assert compose_version.artifact.paragraph_element_links
-    portfolio_attempt = reloaded.attempts[
-        cast(str, reloaded.decisions[portfolio_decision].attempt_id)
-    ]
-    portfolio_version = await artifacts.get_by_attempt(
-        script_build_id=script_build_id,
-        task_id=portfolio,
-        attempt_id=portfolio_attempt.attempt_id,
-    )
-    assert isinstance(portfolio_version.artifact, CandidatePortfolioArtifactV1)
-    assert portfolio_version.artifact.accepted_decision_ids == (compose_decision,)
-    assert portfolio_version.artifact.adopted_structured_script_ref == compose_ref.uri
-
-    async with sessions() as session:
-        build = (
-            (
-                await session.execute(
-                    select(script_build_record).where(script_build_record.c.id == script_build_id)
-                )
-            )
-            .mappings()
-            .one()
-        )
-        branch_values = [
-            *(
-                await session.scalars(
-                    select(script_build_paragraph.c.branch_id).where(
-                        script_build_paragraph.c.script_build_id == script_build_id
-                    )
-                )
-            ),
-            *(
-                await session.scalars(
-                    select(script_build_element.c.branch_id).where(
-                        script_build_element.c.script_build_id == script_build_id
-                    )
-                )
-            ),
-            *(
-                await session.scalars(
-                    select(script_build_paragraph_element.c.branch_id).where(
-                        script_build_paragraph_element.c.script_build_id == script_build_id
-                    )
-                )
-            ),
-        ]
-        final_publications = await session.scalar(
-            select(func.count())
-            .select_from(publication_table)
-            .where(
-                publication_table.c.script_build_id == script_build_id,
-                publication_table.c.publication_type == "final",
-            )
-        )
-        phase_two_rows = (
-            (
-                await session.execute(
-                    select(artifact_version_table).where(
-                        artifact_version_table.c.script_build_id == script_build_id,
-                        artifact_version_table.c.artifact_type.in_(
-                            [
-                                ArtifactKind.STRUCTURE.value,
-                                ArtifactKind.PARAGRAPH.value,
-                                ArtifactKind.ELEMENT_SET.value,
-                                ArtifactKind.COMPARISON.value,
-                                ArtifactKind.STRUCTURED_SCRIPT.value,
-                                ArtifactKind.CANDIDATE_PORTFOLIO.value,
-                            ]
-                        ),
-                    )
-                )
-            )
-            .mappings()
-            .all()
-        )
-    mutation_targets = {
-        match.group(1).strip('`"').lower()
-        for statement in mutation_statements
-        if (
-            match := re.match(
-                r"\s*(?:INSERT\s+INTO|UPDATE|DELETE\s+FROM)\s+([`\"\w]+)",
-                statement,
-                re.IGNORECASE,
-            )
-        )
-    }
-    assert mutation_targets <= {
-        "script_build_record",
-        "script_build_mission_binding",
-        "script_build_artifact_version",
-        "script_build_publication",
-        "script_build_paragraph",
-        "script_build_element",
-        "script_build_paragraph_element",
-        "script_build_task_plan_step",
-    }
-    assert not mutation_targets & {
-        "script_build_branch",
-        "script_build_round",
-        "script_build_multipath",
-    }
-    assert BuildStatus(str(build["status"])) is BuildStatus.PARTIAL
-    assert build["error_message"] == PHASE_TWO_CANDIDATE_PORTFOLIO_READY
-    assert build["reson_trace_id"] == ROOT and build["end_time"] is not None
-    assert branch_values and min(branch_values) > 0 and 0 not in branch_values
-    assert final_publications == 0
-    assert (
-        sum(
-            row["artifact_type"] == ArtifactKind.CANDIDATE_PORTFOLIO.value for row in phase_two_rows
-        )
-        == 1
-    )
-    for row in phase_two_rows:
-        if row["artifact_type"] in {
-            ArtifactKind.STRUCTURE.value,
-            ArtifactKind.PARAGRAPH.value,
-            ArtifactKind.ELEMENT_SET.value,
-            ArtifactKind.STRUCTURED_SCRIPT.value,
-        }:
-            assert row["legacy_branch_id"] == row["id"] > 0
-        else:
-            assert row["legacy_branch_id"] is None
-
-
-class _SingleBinding:
-    def __init__(self, root: str) -> None:
-        now = datetime.now(UTC)
-        self.value = MissionBinding(1, 1, root, 11, None, None, "test", "v1", now, now)
-
-    async def get_by_root(self, root_trace_id: str) -> MissionBinding:
-        assert root_trace_id == self.value.root_trace_id
-        return self.value
-
-
-class _BlockingExecutor:
-    def __init__(self) -> None:
-        self.started = asyncio.Event()
-        self.release = asyncio.Event()
-
-    async def run_worker(self, context: dict[str, Any]) -> WorkerRunResult:
-        self.started.set()
-        await self.release.wait()
-        return WorkerRunResult(context["worker_trace_id"], "failed", error="test release")
-
-    async def run_validator(self, context: dict[str, Any]) -> ValidatorRunResult:
-        raise AssertionError(f"unexpected validation: {context}")
-
-    async def stop(self, trace_id: str) -> bool:
-        del trace_id
-        self.release.set()
-        return True
-
-
-@pytest.mark.asyncio
-async def test_same_task_cannot_reserve_parallel_attempts(tmp_path: Path) -> None:
-    root = "parallel-attempt-root"
-    task_store = FileSystemTaskStore(str(tmp_path / "ledger"))
-    executor = _BlockingExecutor()
-    coordinator = TaskCoordinator(
-        task_store,
-        FileSystemArtifactStore(str(tmp_path / "artifacts")),
-        FileSystemTraceStore(str(tmp_path / "traces")),
-        executor=executor,
-    )
-    await coordinator.ensure_ledger(
-        root,
-        {
-            "objective": "parallel attempt guard",
-            "acceptance_criteria": [
-                {"criterion_id": "guard", "description": "guard", "hard": True}
-            ],
-            "context_refs": ["script-build://inputs/11"],
-        },
-    )
-    planning = PhaseTwoPlanningService(
-        coordinator=coordinator,
-        bindings=cast(Any, _SingleBinding(root)),
-        contracts=FileScriptTaskContractStore(tmp_path / "contracts"),
-        artifacts=cast(Any, SimpleNamespace()),
-    )
-    task = await planning.plan_script_tasks(
-        contract_payloads=(_contract(ScriptTaskKind.DIRECTION),),
-        parent_task_id=None,
-        context={"root_trace_id": root},
-    )
-    task_id = task["task_ids"][0]
-    first = asyncio.create_task(
-        planning.dispatch_script_tasks(task_ids=(task_id,), context={"root_trace_id": root})
-    )
-    await asyncio.wait_for(executor.started.wait(), timeout=2)
-    second = await planning.dispatch_script_tasks(
-        task_ids=(task_id,), context={"root_trace_id": root}
-    )
-    assert "Dispatch conflict" in cast(str, second[0]["error"])
-    executor.release.set()
-    await first
-    ledger = await task_store.load(root)
-    assert len(ledger.tasks[task_id].attempt_ids) == 1