|
@@ -6,7 +6,7 @@ from collections.abc import Mapping, Sequence
|
|
|
from dataclasses import asdict
|
|
from dataclasses import asdict
|
|
|
from typing import Any
|
|
from typing import Any
|
|
|
|
|
|
|
|
-from agent.orchestration import DecisionAction, ValidationVerdict
|
|
|
|
|
|
|
+from agent.orchestration import ArtifactRef, DecisionAction, ValidationVerdict
|
|
|
|
|
|
|
|
from script_build_host.domain.artifacts import (
|
|
from script_build_host.domain.artifacts import (
|
|
|
ArtifactKind,
|
|
ArtifactKind,
|
|
@@ -14,8 +14,14 @@ from script_build_host.domain.artifacts import (
|
|
|
ArtifactVersion,
|
|
ArtifactVersion,
|
|
|
DirectionArtifact,
|
|
DirectionArtifact,
|
|
|
)
|
|
)
|
|
|
-from script_build_host.domain.errors import ProtocolViolation
|
|
|
|
|
-from script_build_host.domain.goal_coverage import GoalPolicyError, validate_goal_coverage
|
|
|
|
|
|
|
+from script_build_host.domain.candidate_validation import (
|
|
|
|
|
+ artifact_realizes_goals,
|
|
|
|
|
+ complete_script_issues,
|
|
|
|
|
+ require_complete_script,
|
|
|
|
|
+ require_complete_sources,
|
|
|
|
|
+)
|
|
|
|
|
+from script_build_host.domain.errors import ProtocolViolation, ScriptBuildError
|
|
|
|
|
+from script_build_host.domain.goal_coverage import validate_goal_coverage
|
|
|
from script_build_host.domain.phase_three_artifacts import (
|
|
from script_build_host.domain.phase_three_artifacts import (
|
|
|
RootDeliveryManifestV1,
|
|
RootDeliveryManifestV1,
|
|
|
canonicalize_legacy_projection,
|
|
canonicalize_legacy_projection,
|
|
@@ -46,6 +52,7 @@ class RootDeliveryService:
|
|
|
|
|
|
|
|
async def read_accepted_portfolio(self, *, context: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
async def read_accepted_portfolio(self, *, context: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
|
closure = await self._closure(context)
|
|
closure = await self._closure(context)
|
|
|
|
|
+ evidence_refs = await self._closure_evidence_refs(closure)
|
|
|
return {
|
|
return {
|
|
|
"direction_ref": _uri(closure.direction),
|
|
"direction_ref": _uri(closure.direction),
|
|
|
"candidate_portfolio_ref": _uri(closure.portfolio),
|
|
"candidate_portfolio_ref": _uri(closure.portfolio),
|
|
@@ -53,8 +60,14 @@ class RootDeliveryService:
|
|
|
"direction": closure.direction.artifact.content_payload(),
|
|
"direction": closure.direction.artifact.content_payload(),
|
|
|
"candidate_portfolio": closure.portfolio.artifact.content_payload(),
|
|
"candidate_portfolio": closure.portfolio.artifact.content_payload(),
|
|
|
"structured_script": closure.structured.artifact.content_payload(),
|
|
"structured_script": closure.structured.artifact.content_payload(),
|
|
|
|
|
+ "validation_evidence_refs": [asdict(item) for item in evidence_refs],
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ async def validation_evidence_refs(
|
|
|
|
|
+ self, *, context: Mapping[str, Any]
|
|
|
|
|
+ ) -> tuple[ArtifactRef, ...]:
|
|
|
|
|
+ return await self._closure_evidence_refs(await self._closure(context))
|
|
|
|
|
+
|
|
|
async def legacy_projection_dry_run(
|
|
async def legacy_projection_dry_run(
|
|
|
self, *, build_summary: str, context: Mapping[str, Any]
|
|
self, *, build_summary: str, context: Mapping[str, Any]
|
|
|
) -> Mapping[str, Any]:
|
|
) -> Mapping[str, Any]:
|
|
@@ -97,6 +110,16 @@ class RootDeliveryService:
|
|
|
direction = _direction(closure.direction)
|
|
direction = _direction(closure.direction)
|
|
|
portfolio = _portfolio(closure.portfolio)
|
|
portfolio = _portfolio(closure.portfolio)
|
|
|
structured = _structured(closure.structured)
|
|
structured = _structured(closure.structured)
|
|
|
|
|
+ require_complete_script(
|
|
|
|
|
+ structured.paragraphs,
|
|
|
|
|
+ structured.elements,
|
|
|
|
|
+ structured.paragraph_element_links,
|
|
|
|
|
+ )
|
|
|
|
|
+ await self._validate_realized_goal_coverage(
|
|
|
|
|
+ script_build_id=binding.script_build_id,
|
|
|
|
|
+ direction=direction,
|
|
|
|
|
+ structured=structured,
|
|
|
|
|
+ )
|
|
|
canonical = canonicalize_legacy_projection(
|
|
canonical = canonicalize_legacy_projection(
|
|
|
structured,
|
|
structured,
|
|
|
direction=direction.legacy_markdown,
|
|
direction=direction.legacy_markdown,
|
|
@@ -170,14 +193,19 @@ class RootDeliveryService:
|
|
|
direction = _direction(closure.direction)
|
|
direction = _direction(closure.direction)
|
|
|
portfolio = _portfolio(closure.portfolio)
|
|
portfolio = _portfolio(closure.portfolio)
|
|
|
structured = _structured(closure.structured)
|
|
structured = _structured(closure.structured)
|
|
|
|
|
+ content_issues = complete_script_issues(
|
|
|
|
|
+ structured.paragraphs,
|
|
|
|
|
+ structured.elements,
|
|
|
|
|
+ structured.paragraph_element_links,
|
|
|
|
|
+ )
|
|
|
goal_coverage_error: str | None = None
|
|
goal_coverage_error: str | None = None
|
|
|
try:
|
|
try:
|
|
|
- validate_goal_coverage(
|
|
|
|
|
- direction_goal_ids=tuple(item.goal_id for item in direction.goals),
|
|
|
|
|
- coverage=structured.goal_coverage,
|
|
|
|
|
- adopted_source_refs=structured.source_artifact_refs,
|
|
|
|
|
|
|
+ await self._validate_realized_goal_coverage(
|
|
|
|
|
+ script_build_id=binding.script_build_id,
|
|
|
|
|
+ direction=direction,
|
|
|
|
|
+ structured=structured,
|
|
|
)
|
|
)
|
|
|
- except GoalPolicyError as exc:
|
|
|
|
|
|
|
+ except ScriptBuildError as exc:
|
|
|
goal_coverage_error = f"{exc.code}: {exc.summary}"
|
|
goal_coverage_error = f"{exc.code}: {exc.summary}"
|
|
|
goal_coverage_passed = goal_coverage_error is None
|
|
goal_coverage_passed = goal_coverage_error is None
|
|
|
direction_contract_passed = (
|
|
direction_contract_passed = (
|
|
@@ -225,6 +253,19 @@ class RootDeliveryService:
|
|
|
"error": None,
|
|
"error": None,
|
|
|
"evidence_refs": [],
|
|
"evidence_refs": [],
|
|
|
},
|
|
},
|
|
|
|
|
+ {
|
|
|
|
|
+ "rule_id": "root-script-content",
|
|
|
|
|
+ "verdict": "failed" if content_issues else "passed",
|
|
|
|
|
+ "reason": (
|
|
|
|
|
+ "; ".join(
|
|
|
|
|
+ f"{item.path}: {item.message}" for item in content_issues[:8]
|
|
|
|
|
+ )
|
|
|
|
|
+ if content_issues
|
|
|
|
|
+ else "Every active leaf Paragraph contains complete prose and linked content"
|
|
|
|
|
+ ),
|
|
|
|
|
+ "error": None,
|
|
|
|
|
+ "evidence_refs": [_uri(closure.structured)],
|
|
|
|
|
+ },
|
|
|
{
|
|
{
|
|
|
"rule_id": "root-goal-coverage",
|
|
"rule_id": "root-goal-coverage",
|
|
|
"verdict": "passed" if goal_coverage_passed else "failed",
|
|
"verdict": "passed" if goal_coverage_passed else "failed",
|
|
@@ -264,6 +305,59 @@ class RootDeliveryService:
|
|
|
},
|
|
},
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+ async def _validate_realized_goal_coverage(
|
|
|
|
|
+ self,
|
|
|
|
|
+ *,
|
|
|
|
|
+ script_build_id: int,
|
|
|
|
|
+ direction: DirectionArtifact,
|
|
|
|
|
+ structured: StructuredScriptArtifactV1,
|
|
|
|
|
+ ) -> None:
|
|
|
|
|
+ realized: list[str] = []
|
|
|
|
|
+ sources: list[Any] = []
|
|
|
|
|
+ for ref in structured.source_artifact_refs:
|
|
|
|
|
+ version = await self._artifacts.get_by_id(
|
|
|
|
|
+ _artifact_id(ref), script_build_id=script_build_id
|
|
|
|
|
+ )
|
|
|
|
|
+ sources.append(version.artifact)
|
|
|
|
|
+ if artifact_realizes_goals(version.artifact):
|
|
|
|
|
+ realized.append(ref)
|
|
|
|
|
+ require_complete_sources(sources)
|
|
|
|
|
+ validate_goal_coverage(
|
|
|
|
|
+ direction_goal_ids=tuple(item.goal_id for item in direction.goals),
|
|
|
|
|
+ coverage=structured.goal_coverage,
|
|
|
|
|
+ adopted_source_refs=structured.source_artifact_refs,
|
|
|
|
|
+ realized_source_refs=realized,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ async def _closure_evidence_refs(
|
|
|
|
|
+ self, closure: _RootClosure
|
|
|
|
|
+ ) -> tuple[ArtifactRef, ...]:
|
|
|
|
|
+ versions = [closure.direction, closure.portfolio, closure.structured]
|
|
|
|
|
+ structured = _structured(closure.structured)
|
|
|
|
|
+ binding_build_id = closure.structured.script_build_id
|
|
|
|
|
+ for ref in structured.source_artifact_refs:
|
|
|
|
|
+ versions.append(
|
|
|
|
|
+ await self._artifacts.get_by_id(
|
|
|
|
|
+ _artifact_id(ref), script_build_id=binding_build_id
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ output: list[ArtifactRef] = []
|
|
|
|
|
+ seen: set[str] = set()
|
|
|
|
|
+ for version in versions:
|
|
|
|
|
+ uri = _uri(version)
|
|
|
|
|
+ if uri in seen:
|
|
|
|
|
+ continue
|
|
|
|
|
+ seen.add(uri)
|
|
|
|
|
+ output.append(
|
|
|
|
|
+ ArtifactRef(
|
|
|
|
|
+ uri=uri,
|
|
|
|
|
+ kind=version.artifact_type.value,
|
|
|
|
|
+ version=str(version.artifact_version_id),
|
|
|
|
|
+ digest=version.canonical_sha256,
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ return tuple(output)
|
|
|
|
|
+
|
|
|
async def _closure(self, context: Mapping[str, Any]) -> _RootClosure:
|
|
async def _closure(self, context: Mapping[str, Any]) -> _RootClosure:
|
|
|
root_trace_id = _required(context, "root_trace_id")
|
|
root_trace_id = _required(context, "root_trace_id")
|
|
|
binding = await self._bindings.get_by_root(root_trace_id)
|
|
binding = await self._bindings.get_by_root(root_trace_id)
|