| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836 |
- from __future__ import annotations
- from dataclasses import replace
- from datetime import UTC, datetime
- from hashlib import sha256
- from types import SimpleNamespace
- from typing import Any, cast
- import pytest
- from agent.orchestration import ArtifactRef
- from script_build_host.application.phase_two_candidates import (
- PhaseTwoCandidateError,
- PhaseTwoCandidateService,
- )
- from script_build_host.application.phase_two_inputs import (
- ActiveFrontierResolver,
- PhaseTwoInputError,
- )
- from script_build_host.domain.artifacts import (
- ArtifactKind,
- ArtifactState,
- ArtifactVersion,
- EvidenceRecordV1,
- )
- from script_build_host.domain.errors import ProtocolViolation
- from script_build_host.domain.phase_two_artifacts import (
- CandidatePortfolioArtifactV1,
- ComparisonArtifactV1,
- ScriptParagraphV1,
- StructuredScriptArtifactV1,
- )
- from script_build_host.domain.task_contracts import (
- AcceptedDecisionRef,
- AcceptedInput,
- AcceptedInputBundleV1,
- ScriptCriterion,
- ScriptIntentClass,
- ScriptTaskBudget,
- ScriptTaskContractV1,
- ScriptTaskKind,
- )
- from script_build_host.domain.workspaces import CandidateWriteContext, WorkspaceError
- from script_build_host.infrastructure.canonical_json import canonical_sha256
- from script_build_host.repositories.sqlalchemy import (
- SqlAlchemyScriptBusinessArtifactRepository,
- )
- from script_build_host.repositories.workspace import (
- SqlAlchemyCandidateWorkspaceRepository,
- )
- DIGEST = "sha256:" + "a" * 64
- SCOPE = "script-build://scopes/opening"
- WRITE = "script-build://writes/paragraphs/opening"
- def _contract(
- kind: ScriptTaskKind,
- *,
- refs: tuple[AcceptedDecisionRef, ...] = (),
- comparison_refs: tuple[AcceptedDecisionRef, ...] = (),
- adopted: tuple[str, ...] = (),
- held: tuple[str, ...] = (),
- order: tuple[str, ...] = (),
- ) -> ScriptTaskContractV1:
- schemas = {
- ScriptTaskKind.PARAGRAPH: "paragraph-artifact/v1",
- ScriptTaskKind.DECODE_RETRIEVAL: "evidence-record/v1",
- ScriptTaskKind.COMPARE: "comparison-artifact/v1",
- ScriptTaskKind.COMPOSE: "structured-script/v1",
- ScriptTaskKind.CANDIDATE_PORTFOLIO: "candidate-portfolio/v1",
- }
- return ScriptTaskContractV1(
- task_kind=kind,
- scope_ref=SCOPE,
- intent_class=(
- ScriptIntentClass.COMPOSE
- if kind is ScriptTaskKind.COMPOSE
- else ScriptIntentClass.COMPARE
- if kind is ScriptTaskKind.COMPARE
- else ScriptIntentClass.EXPLORE
- ),
- objective="produce one bounded candidate increment",
- input_decision_refs=refs if kind is ScriptTaskKind.PARAGRAPH else (),
- base_artifact_ref=None,
- write_scope=(WRITE,),
- gap_ref=None,
- output_schema=schemas[kind],
- criteria=(ScriptCriterion("closed", "output is concretely complete"),),
- budget=ScriptTaskBudget(),
- candidate_closure_decision_refs=(
- refs if kind in {ScriptTaskKind.COMPOSE, ScriptTaskKind.CANDIDATE_PORTFOLIO} else ()
- ),
- adopted_decision_ids=adopted,
- held_or_rejected_decision_ids=held,
- compose_order=order,
- comparison_decision_refs=comparison_refs,
- )
- class _Bindings:
- async def get_by_root(self, root_trace_id: str) -> Any:
- return SimpleNamespace(root_trace_id=root_trace_id, script_build_id=7, input_snapshot_id=11)
- class _TaskStore:
- def __init__(self, ledger: Any) -> None:
- self.ledger = ledger
- async def load(self, root_trace_id: str) -> Any:
- assert root_trace_id == "root"
- return self.ledger
- class _AcceptedInputs:
- def __init__(
- self, contracts: dict[str, ScriptTaskContractV1], bundle: AcceptedInputBundleV1
- ) -> None:
- self.contracts = contracts
- self.bundle = bundle
- async def contract_for_task(self, *, task: Any, **_: Any) -> ScriptTaskContractV1:
- return self.contracts[task.task_id]
- async def resolve(self, **_: Any) -> AcceptedInputBundleV1:
- return self.bundle
- class _FrameworkArtifacts:
- async def get(self, *_: Any) -> Any:
- raise AssertionError("framework snapshot should not be read")
- class _SnapshotArtifacts:
- def __init__(self, ref: ArtifactRef) -> None:
- self.ref = ref
- async def get(self, root_trace_id: str, snapshot_id: str) -> Any:
- assert (root_trace_id, snapshot_id) == ("root", "validation-snapshot")
- return SimpleNamespace(artifact_refs=[self.ref], evidence_refs=[])
- class _RawStore:
- def __init__(self, content: bytes) -> None:
- self.content = content
- async def read_bytes(self, ref: str) -> bytes:
- assert ref.endswith(sha256(self.content).hexdigest())
- return self.content
- class _Artifacts:
- def __init__(self, versions: dict[str, ArtifactVersion]) -> None:
- self.versions = versions
- self.frozen: ArtifactVersion | None = None
- async def read_by_ref(self, ref: ArtifactRef, **_: Any) -> ArtifactVersion:
- return self.versions[ref.uri]
- async def get_by_attempt(self, **owners: Any) -> ArtifactVersion:
- for value in self.versions.values():
- if value.attempt_id == owners["attempt_id"]:
- return value
- raise AssertionError("Attempt artifact not found")
- async def freeze(self, *, artifact: Any, **owners: Any) -> tuple[ArtifactVersion, ArtifactRef]:
- digest = canonical_sha256(artifact.content_payload()).wire
- artifact = replace(artifact, canonical_sha256=digest)
- artifact_kind = (
- ArtifactKind.COMPARISON
- if isinstance(artifact, ComparisonArtifactV1)
- else ArtifactKind.CANDIDATE_PORTFOLIO
- )
- version = ArtifactVersion(
- artifact_version_id=99,
- script_build_id=owners["script_build_id"],
- task_id=owners["task_id"],
- attempt_id=owners["attempt_id"],
- spec_version=owners["spec_version"],
- artifact_type=artifact_kind,
- canonical_sha256=digest,
- state=ArtifactState.FROZEN,
- artifact=artifact,
- created_at=datetime.now(UTC),
- frozen_at=datetime.now(UTC),
- )
- self.frozen = version
- ref = ArtifactRef(
- "script-build://artifact-versions/99",
- artifact_kind.value,
- "99",
- digest,
- )
- self.versions[ref.uri] = version
- return version, ref
- def _scope_fixture(
- contract: ScriptTaskContractV1,
- *,
- bundle: AcceptedInputBundleV1 | None = None,
- task_id: str = "task",
- attempt_id: str = "attempt",
- ) -> tuple[Any, dict[str, Any]]:
- task = SimpleNamespace(task_id=task_id, attempt_ids=[attempt_id])
- attempt = SimpleNamespace(
- attempt_id=attempt_id,
- task_id=task_id,
- spec_version=1,
- accepted_child_decision_ids=(),
- )
- ledger = SimpleNamespace(
- tasks={task_id: task},
- attempts={attempt_id: attempt},
- decisions={},
- )
- accepted = _AcceptedInputs(
- {task_id: contract},
- bundle or AcceptedInputBundleV1((), DIGEST),
- )
- return (ledger, accepted), {
- "root_trace_id": "root",
- "task_id": task_id,
- "attempt_id": attempt_id,
- "spec_version": 1,
- }
- @pytest.mark.asyncio
- async def test_phase_one_manifest_does_not_require_candidate_workspace() -> None:
- contract = _contract(ScriptTaskKind.DECODE_RETRIEVAL)
- (ledger, accepted), context = _scope_fixture(contract)
- evidence = EvidenceRecordV1(
- evidence_id="evidence",
- source_type="decode",
- tool_name="retrieve_decode",
- query={},
- source_refs=("source",),
- raw_artifact_ref=None,
- summary="bounded evidence",
- supports=("goal",),
- confidence="high",
- limitations=(),
- content_sha256=DIGEST,
- created_at=datetime.now(UTC),
- )
- version = ArtifactVersion(
- 1,
- 7,
- "task",
- "attempt",
- 1,
- ArtifactKind.EVIDENCE,
- DIGEST,
- ArtifactState.FROZEN,
- evidence,
- datetime.now(UTC),
- datetime.now(UTC),
- )
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=cast(Any, _Artifacts({"script-build://artifact-versions/1": version})),
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=None,
- )
- manifest = await service.resolve_attempt_manifest(context=context)
- assert manifest.artifact_ref.kind == "evidence"
- assert manifest.scope_ref == SCOPE
- @pytest.mark.asyncio
- async def test_paragraph_workspace_uses_positive_branch_and_freezes_on_manifest(
- database: Any,
- ) -> None:
- _, sessions = database
- repository = SqlAlchemyScriptBusinessArtifactRepository(sessions)
- workspaces = SqlAlchemyCandidateWorkspaceRepository(sessions, repository)
- contract = _contract(ScriptTaskKind.PARAGRAPH)
- (ledger, accepted), context = _scope_fixture(contract)
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=repository,
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=workspaces,
- )
- created = await service.create_script_paragraph(
- payload={"paragraph_index": 1, "name": "opening", "content_range": {}},
- context=context,
- )
- manifest = await service.resolve_attempt_manifest(context=context)
- version = await repository.get_by_attempt(
- script_build_id=7, task_id="task", attempt_id="attempt"
- )
- assert created["paragraph_id"] > 0
- assert version.artifact_version_id > 0
- assert manifest.artifact_ref.digest == version.canonical_sha256
- assert manifest.artifact_ref.kind == "paragraph"
- with pytest.raises(PhaseTwoCandidateError, match="WRITE_SCOPE_VIOLATION"):
- await service.create_script_paragraph(
- payload={
- "paragraph_index": 2,
- "name": "forged",
- "content_range": {},
- "branch_id": 0,
- },
- context=context,
- )
- @pytest.mark.asyncio
- async def test_paragraph_batch_maps_parent_keys_and_rolls_back_atomically(database: Any) -> None:
- _, sessions = database
- repository = SqlAlchemyScriptBusinessArtifactRepository(sessions)
- workspaces = SqlAlchemyCandidateWorkspaceRepository(sessions, repository)
- contract = _contract(ScriptTaskKind.PARAGRAPH)
- (ledger, accepted), context = _scope_fixture(contract)
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=repository,
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=workspaces,
- )
- created = await service.create_script_paragraphs(
- paragraphs=(
- {
- "client_key": "root",
- "paragraph_index": 1,
- "name": "opening",
- "content_range": {"beats": ["setup", "turn"]},
- },
- {
- "client_key": "child",
- "parent_client_key": "root",
- "paragraph_index": 2,
- "level": 2,
- "name": "turn",
- "content_range": {"beats": ["turn"]},
- },
- ),
- context=context,
- )
- assert created["created"] == 2
- assert set(created["paragraph_ids_by_client_key"]) == {"root", "child"}
- contract2 = replace(contract, objective="second isolated workspace")
- (ledger2, accepted2), context2 = _scope_fixture(
- contract2, task_id="task-2", attempt_id="attempt-2"
- )
- service2 = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger2),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=repository,
- accepted_inputs=accepted2,
- active_frontier=ActiveFrontierResolver(),
- workspaces=workspaces,
- )
- with pytest.raises(WorkspaceError, match="child content range"):
- await service2.create_script_paragraphs(
- paragraphs=(
- {
- "client_key": "root",
- "paragraph_index": 1,
- "name": "root",
- "content_range": {"beats": ["setup"]},
- },
- {
- "client_key": "child",
- "parent_client_key": "root",
- "paragraph_index": 2,
- "level": 2,
- "name": "child",
- "content_range": {"beats": ["missing"]},
- },
- ),
- context=context2,
- )
- write_context = CandidateWriteContext(
- script_build_id=7,
- task_id="task-2",
- attempt_id="attempt-2",
- spec_version=1,
- objective=contract2.objective,
- input_refs=(),
- write_scope=contract2.write_scope,
- )
- assert not (await workspaces.snapshot(write_context)).paragraphs
- @pytest.mark.asyncio
- async def test_candidate_service_discards_existing_draft_workspace_on_abandoned_attempt(
- database: Any,
- ) -> None:
- _, sessions = database
- repository = SqlAlchemyScriptBusinessArtifactRepository(sessions)
- workspaces = SqlAlchemyCandidateWorkspaceRepository(sessions, repository)
- contract = _contract(ScriptTaskKind.PARAGRAPH)
- (ledger, accepted), context = _scope_fixture(contract)
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=repository,
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=workspaces,
- )
- await service.create_script_paragraph(
- payload={"paragraph_index": 1, "name": "draft", "content_range": {}},
- context=context,
- )
- await service.discard_attempt(
- root_trace_id="root",
- task_id="task",
- attempt_id="attempt",
- reason="validator blocked the bounded increment",
- )
- write_context = CandidateWriteContext(
- script_build_id=7,
- task_id="task",
- attempt_id="attempt",
- spec_version=1,
- objective=contract.objective,
- input_refs=(),
- write_scope=contract.write_scope,
- )
- assert (await workspaces.require(write_context)).state is ArtifactState.DISCARDED
- with pytest.raises(WorkspaceError, match="ATTEMPT_WORKSPACE_FROZEN"):
- await service.create_script_paragraph(
- payload={"paragraph_index": 2, "name": "late", "content_range": {}},
- context=context,
- )
- @pytest.mark.asyncio
- async def test_frozen_image_must_be_accepted_and_digest_and_mime_are_rechecked() -> None:
- content = b"\x89PNG\r\n\x1a\n" + b"safe"
- raw_ref = f"script-build://raw-artifacts/sha256/{sha256(content).hexdigest()}"
- ref = ArtifactRef("script-build://artifact-versions/2", "evidence", "2", DIGEST)
- item = AcceptedInput("d1", ref, ScriptTaskKind.DECODE_RETRIEVAL, SCOPE, "explicit")
- bundle = AcceptedInputBundleV1((item,), DIGEST)
- contract = _contract(ScriptTaskKind.PARAGRAPH)
- (ledger, accepted), context = _scope_fixture(contract, bundle=bundle)
- evidence = EvidenceRecordV1(
- "e",
- "decode",
- "retrieve_decode",
- {},
- ("source",),
- raw_ref,
- "summary",
- ("goal",),
- "high",
- (),
- DIGEST,
- datetime.now(UTC),
- )
- version = ArtifactVersion(
- 2,
- 7,
- "child",
- "child-attempt",
- 1,
- ArtifactKind.EVIDENCE,
- DIGEST,
- ArtifactState.FROZEN,
- evidence,
- datetime.now(UTC),
- datetime.now(UTC),
- )
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=cast(Any, _Artifacts({ref.uri: version})),
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=None,
- raw_artifacts=_RawStore(content),
- )
- images = await service.view_frozen_images(raw_artifact_refs=[raw_ref], context=context)
- assert images[0]["media_type"] == "image/png"
- assert "url" not in images[0]
- with pytest.raises(PhaseTwoCandidateError, match="outside the accepted"):
- await service.view_frozen_images(raw_artifact_refs=[_RAW_OTHER], context=context)
- @pytest.mark.asyncio
- async def test_active_frontier_write_conflict_is_not_resolved_by_completion_order() -> None:
- ref1 = ArtifactRef("script-build://artifact-versions/1", "paragraph", "1", DIGEST)
- ref2 = ArtifactRef("script-build://artifact-versions/2", "paragraph", "2", DIGEST)
- refs = (
- AcceptedDecisionRef("d1", ref1, SCOPE, ScriptTaskKind.PARAGRAPH),
- AcceptedDecisionRef("d2", ref2, SCOPE, ScriptTaskKind.PARAGRAPH),
- )
- contract = _contract(
- ScriptTaskKind.COMPOSE, refs=refs, adopted=("d1", "d2"), order=("d1", "d2")
- )
- bundle = AcceptedInputBundleV1(
- (
- AcceptedInput("d1", ref1, ScriptTaskKind.PARAGRAPH, SCOPE, "explicit"),
- AcceptedInput("d2", ref2, ScriptTaskKind.PARAGRAPH, SCOPE, "explicit"),
- ),
- DIGEST,
- )
- (ledger, accepted), context = _scope_fixture(contract, bundle=bundle)
- child_contract = _contract(ScriptTaskKind.PARAGRAPH)
- for decision_id in ("d1", "d2"):
- task_id = f"task-{decision_id}"
- ledger.tasks[task_id] = SimpleNamespace(task_id=task_id, attempt_ids=[f"a-{decision_id}"])
- ledger.attempts[f"a-{decision_id}"] = SimpleNamespace(
- attempt_id=f"a-{decision_id}", task_id=task_id, spec_version=1
- )
- ledger.decisions[decision_id] = SimpleNamespace(
- task_id=task_id, attempt_id=f"a-{decision_id}"
- )
- accepted.contracts[task_id] = child_contract
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=cast(Any, _Artifacts({})),
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=None,
- )
- with pytest.raises(PhaseTwoInputError, match="WRITE_SCOPE_CONFLICT"):
- await service.read_active_frontier(context=context)
- @pytest.mark.asyncio
- async def test_comparison_candidates_are_contract_derived_and_pass_fairness_precheck() -> None:
- candidate_refs = (
- ArtifactRef("script-build://artifact-versions/1", "paragraph", "1", DIGEST),
- ArtifactRef("script-build://artifact-versions/2", "paragraph", "2", DIGEST),
- )
- decision_refs = tuple(
- AcceptedDecisionRef(
- f"candidate-{index}",
- ref,
- SCOPE,
- ScriptTaskKind.PARAGRAPH,
- )
- for index, ref in enumerate(candidate_refs, start=1)
- )
- bundle = AcceptedInputBundleV1(
- tuple(
- AcceptedInput(
- decision_ref.decision_id,
- decision_ref.artifact_ref,
- ScriptTaskKind.PARAGRAPH,
- SCOPE,
- "explicit",
- )
- for decision_ref in decision_refs
- ),
- DIGEST,
- )
- contract = _contract(ScriptTaskKind.COMPARE, comparison_refs=decision_refs)
- (ledger, accepted), context = _scope_fixture(contract, bundle=bundle)
- artifacts = _Artifacts({})
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=cast(Any, artifacts),
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=None,
- )
- criterion_results = [
- {
- "criterion_id": "closed",
- "candidate_results": [
- {"artifact_ref": candidate_refs[0].uri, "reason": "uses one visible detail"},
- {"artifact_ref": candidate_refs[1].uri, "reason": "remains more abstract"},
- ],
- }
- ]
- frozen = await service.save_comparison_candidate(
- payload={
- "criterion_results": criterion_results,
- "conflicts": ["both candidates write the opening scope"],
- "recommendation": candidate_refs[0].uri,
- },
- context=context,
- )
- assert artifacts.frozen is not None
- comparison = artifacts.frozen.artifact
- assert isinstance(comparison, ComparisonArtifactV1)
- assert comparison.candidate_artifact_refs == tuple(ref.uri for ref in candidate_refs)
- assert comparison.criterion_results == tuple(criterion_results)
- frozen_ref = ArtifactRef.from_dict(frozen["artifact_ref"])
- service._framework_artifact_store = _SnapshotArtifacts(frozen_ref)
- rules = await service.deterministic_precheck(
- context={**context, "snapshot_id": "validation-snapshot"}
- )
- assert {str(item["rule_id"]): item["verdict"] for item in rules} == {
- "business-artifact-owner": "passed",
- "realized-content": "passed",
- "candidate-lineage": "passed",
- "comparison-fairness": "passed",
- }
- with pytest.raises(PhaseTwoCandidateError, match="derived from the frozen contract"):
- await service.save_comparison_candidate(
- payload={
- "candidate_artifact_refs": [candidate_refs[1].uri],
- "criterion_results": criterion_results,
- "recommendation": candidate_refs[1].uri,
- },
- context=context,
- )
- @pytest.mark.asyncio
- async def test_portfolio_adoption_and_canonical_digest_come_from_frozen_contract() -> None:
- ref1 = ArtifactRef("script-build://artifact-versions/1", "structured_script", "1", DIGEST)
- ref2 = ArtifactRef("script-build://artifact-versions/2", "structured_script", "2", DIGEST)
- refs = (
- AcceptedDecisionRef("d1", ref1, SCOPE, ScriptTaskKind.COMPOSE),
- AcceptedDecisionRef("d2", ref2, SCOPE, ScriptTaskKind.COMPOSE),
- )
- contract = _contract(
- ScriptTaskKind.CANDIDATE_PORTFOLIO,
- refs=refs,
- adopted=("d1",),
- held=("d2",),
- order=("d1",),
- )
- bundle = AcceptedInputBundleV1(
- (
- AcceptedInput("d1", ref1, ScriptTaskKind.COMPOSE, SCOPE, "explicit"),
- AcceptedInput("d2", ref2, ScriptTaskKind.COMPOSE, SCOPE, "explicit"),
- ),
- DIGEST,
- )
- (ledger, accepted), context = _scope_fixture(contract, bundle=bundle)
- paragraph = ScriptParagraphV1(1, 1, 1, None, "opening", {})
- def structured(identifier: int) -> ArtifactVersion:
- artifact = StructuredScriptArtifactV1(
- direction_ref="script-build://artifact-versions/10",
- input_closure_digest=DIGEST,
- paragraphs=(paragraph,),
- elements=(),
- paragraph_element_links=(),
- source_artifact_refs=(f"script-build://artifact-versions/{20 + identifier}",),
- evidence_refs=(),
- acceptance_notes=("accepted",),
- canonical_sha256=DIGEST,
- )
- return ArtifactVersion(
- identifier,
- 7,
- f"compose-{identifier}",
- f"compose-attempt-{identifier}",
- 1,
- ArtifactKind.STRUCTURED_SCRIPT,
- DIGEST,
- ArtifactState.FROZEN,
- artifact,
- datetime.now(UTC),
- datetime.now(UTC),
- )
- artifacts = _Artifacts({ref1.uri: structured(1), ref2.uri: structured(2)})
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=cast(Any, artifacts),
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=None,
- )
- result = await service.save_candidate_portfolio(
- payload={
- "unresolved_defects": [
- {
- "severity": "warning",
- "defect_code": "MINOR_STYLE",
- "criterion_id": "closed",
- "scope_ref": SCOPE,
- "observed_excerpt": "opening cadence is slightly repetitive",
- "evidence_refs": [],
- "invalidated_inputs": [],
- "recommended_action_class": "revise",
- }
- ],
- },
- context=context,
- )
- assert artifacts.frozen is not None
- portfolio = artifacts.frozen.artifact
- assert isinstance(portfolio, CandidatePortfolioArtifactV1)
- assert portfolio.accepted_decision_ids == ("d1",)
- assert portfolio.rejected_or_held_decision_ids == ("d2",)
- assert result["artifact_ref"]["digest"] == canonical_sha256(portfolio.content_payload()).wire
- with pytest.raises(PhaseTwoCandidateError, match="outside the accepted closure"):
- await service.save_candidate_portfolio(
- payload={
- "unresolved_defects": [
- {
- "severity": "warning",
- "defect_code": "UNTRUSTED_EVIDENCE",
- "criterion_id": "closed",
- "scope_ref": SCOPE,
- "observed_excerpt": "unsupported warning",
- "evidence_refs": ["script-build://artifact-versions/999"],
- "invalidated_inputs": [],
- "recommended_action_class": "revise",
- }
- ],
- },
- context=context,
- )
- with pytest.raises(ProtocolViolation, match=r"unsupported enum|must not be blank"):
- await service.save_candidate_portfolio(
- payload={
- "unresolved_defects": [
- {"severity": "warning", "defect_code": "INCOMPLETE_WARNING"}
- ],
- },
- context=context,
- )
- with pytest.raises(PhaseTwoCandidateError, match="derived from the frozen contract"):
- await service.save_candidate_portfolio(
- payload={"adopted_structured_script_ref": ref2.uri}, context=context
- )
- @pytest.mark.asyncio
- async def test_candidate_portfolio_command_replay_reuses_one_frozen_version(database: Any) -> None:
- _, sessions = database
- repository = SqlAlchemyScriptBusinessArtifactRepository(sessions)
- paragraph = ScriptParagraphV1(1, 1, 1, None, "opening", {})
- async def freeze_structured(identifier: int) -> tuple[ArtifactVersion, ArtifactRef]:
- return await repository.freeze(
- script_build_id=7,
- task_id=f"compose-{identifier}",
- attempt_id=f"compose-attempt-{identifier}",
- spec_version=1,
- artifact=StructuredScriptArtifactV1(
- direction_ref="script-build://artifact-versions/10",
- input_closure_digest=DIGEST,
- paragraphs=(paragraph,),
- elements=(),
- paragraph_element_links=(),
- source_artifact_refs=(f"script-build://artifact-versions/{20 + identifier}",),
- evidence_refs=(),
- acceptance_notes=("accepted",),
- ),
- )
- (_, stored_ref1), (_, stored_ref2) = await freeze_structured(1), await freeze_structured(2)
- ref1 = ArtifactRef(
- stored_ref1.uri,
- stored_ref1.kind,
- stored_ref1.version,
- stored_ref1.digest,
- )
- ref2 = ArtifactRef(
- stored_ref2.uri,
- stored_ref2.kind,
- stored_ref2.version,
- stored_ref2.digest,
- )
- refs = (
- AcceptedDecisionRef("d1", ref1, SCOPE, ScriptTaskKind.COMPOSE),
- AcceptedDecisionRef("d2", ref2, SCOPE, ScriptTaskKind.COMPOSE),
- )
- contract = _contract(
- ScriptTaskKind.CANDIDATE_PORTFOLIO,
- refs=refs,
- adopted=("d1",),
- held=("d2",),
- order=("d1",),
- )
- bundle = AcceptedInputBundleV1(
- (
- AcceptedInput("d1", ref1, ScriptTaskKind.COMPOSE, SCOPE, "explicit"),
- AcceptedInput("d2", ref2, ScriptTaskKind.COMPOSE, SCOPE, "explicit"),
- ),
- DIGEST,
- )
- (ledger, accepted), context = _scope_fixture(
- contract,
- bundle=bundle,
- task_id="portfolio",
- attempt_id="portfolio-attempt",
- )
- service = PhaseTwoCandidateService(
- bindings=cast(Any, _Bindings()),
- task_store=_TaskStore(ledger),
- framework_artifact_store=_FrameworkArtifacts(),
- artifacts=repository,
- accepted_inputs=accepted,
- active_frontier=ActiveFrontierResolver(),
- workspaces=None,
- )
- first = await service.save_candidate_portfolio(
- payload={"unresolved_defects": []}, context=context
- )
- replay = await service.save_candidate_portfolio(
- payload={"unresolved_defects": []}, context=context
- )
- frozen = await repository.get_by_attempt(
- script_build_id=7,
- task_id="portfolio",
- attempt_id="portfolio-attempt",
- )
- assert first == replay
- assert first["artifact_ref"]["version"] == str(frozen.artifact_version_id)
- assert frozen.artifact_type is ArtifactKind.CANDIDATE_PORTFOLIO
- _RAW_OTHER = "script-build://raw-artifacts/sha256/" + "b" * 64
|