| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- from __future__ import annotations
- import pytest
- from script_build_host.domain.artifacts import (
- ArtifactKind,
- DirectionArtifact,
- DirectionConstraint,
- DirectionGoal,
- DirectionPreference,
- )
- from script_build_host.domain.errors import ProtocolViolation
- from script_build_host.repositories.sqlalchemy import _hydrate_artifact
- from script_build_host.tools.gateway import _direction_goals
- from script_build_host.tools.registry import _save_direction_candidate_schema
- EVIDENCE_REF = "script-build://artifact-versions/1"
- def _goal(index: int, *, parent: str | None = None) -> DirectionGoal:
- return DirectionGoal(
- goal_id=f"goal-{index}",
- parent_goal_id=parent,
- statement=f"goal statement {index}",
- rationale=f"goal rationale {index}",
- success_criteria=(f"goal criterion {index}",),
- )
- def test_direction_supports_hierarchy_rules_and_deterministic_projection() -> None:
- direction = DirectionArtifact(
- goals=(_goal(1), _goal(2, parent="goal-1"), _goal(3)),
- constraints=(
- DirectionConstraint("constraint-1", "facts remain traceable", "hard boundary"),
- ),
- preferences=(DirectionPreference("preference-1", "prefer a concise opening", priority=1),),
- evidence_refs=(EVIDENCE_REF,),
- )
- payload = direction.content_payload()
- assert payload["schema_version"] == "script-direction/v1"
- assert payload["goals"][1]["parent_goal_id"] == "goal-1"
- assert payload["constraints"][0]["constraint_id"] == "constraint-1"
- assert payload["preferences"][0]["priority"] == 1
- assert "### goal-1: goal statement 1" in direction.legacy_markdown
- assert "#### goal-2: goal statement 2" in direction.legacy_markdown
- assert "## Constraints" in direction.legacy_markdown
- assert "## Preferences" in direction.legacy_markdown
- assert EVIDENCE_REF in direction.legacy_markdown
- def test_direction_accepts_thirty_goal_nodes_and_rejects_thirty_one() -> None:
- DirectionArtifact(
- goals=tuple(_goal(index) for index in range(1, 31)), evidence_refs=(EVIDENCE_REF,)
- )
- with pytest.raises(ValueError, match="between one and thirty"):
- DirectionArtifact(
- goals=tuple(_goal(index) for index in range(1, 32)),
- evidence_refs=(EVIDENCE_REF,),
- )
- @pytest.mark.parametrize(
- ("goals", "message"),
- [
- ((_goal(1, parent="missing"),), "parent must exist"),
- (
- (
- _goal(1),
- _goal(2, parent="goal-1"),
- _goal(3, parent="goal-2"),
- ),
- "only two levels",
- ),
- (
- (DirectionGoal("goal-1", "statement", "rationale", success_criteria=()),),
- "requires success criteria",
- ),
- (
- (DirectionGoal("goal-1", "statement", "", success_criteria=("check",)),),
- "requires a rationale",
- ),
- ],
- )
- def test_direction_rejects_invalid_goal_hierarchy(
- goals: tuple[DirectionGoal, ...], message: str
- ) -> None:
- with pytest.raises(ValueError, match=message):
- DirectionArtifact(goals=goals, evidence_refs=(EVIDENCE_REF,))
- def test_direction_tool_contract_keeps_schema_name_and_removes_model_markdown() -> None:
- parameters = _save_direction_candidate_schema()["function"]["parameters"]
- assert parameters["properties"]["goals"]["maxItems"] == 30
- assert "constraints" in parameters["required"]
- assert "preferences" in parameters["required"]
- assert "legacy_markdown" not in parameters["properties"]
- assert "criteria" not in parameters["properties"]
- def _client_goal(key: str, parent: str | None = None) -> dict[str, object]:
- return {
- "client_key": key,
- "parent_client_key": parent,
- "statement": f"statement for {key}",
- "rationale": f"rationale for {key}",
- "success_criteria": [f"criterion for {key}"],
- }
- def test_direction_client_keys_generate_stable_order_independent_goal_ids() -> None:
- first, first_map = _direction_goals(
- "task-a", [_client_goal("Parent"), _client_goal("Child", "Parent")]
- )
- second, second_map = _direction_goals(
- "task-a", [_client_goal(" child ", " parent "), _client_goal("parent")]
- )
- assert first_map == second_map
- assert {item.goal_id for item in first} == {item.goal_id for item in second}
- child = next(item for item in second if item.parent_goal_id)
- assert child.parent_goal_id == first_map["parent"]
- @pytest.mark.parametrize(
- ("goals", "message"),
- [
- ([_client_goal("same"), _client_goal("SAME")], "must be unique"),
- ([_client_goal("child", "missing")], "does not exist"),
- ([_client_goal("self", "self")], "cannot parent itself"),
- (
- [
- _client_goal("root"),
- _client_goal("child", "root"),
- _client_goal("grandchild", "child"),
- ],
- "only two levels",
- ),
- ([_client_goal(str(index)) for index in range(31)], "between one and thirty"),
- ],
- )
- def test_direction_client_keys_reject_invalid_batches(
- goals: list[dict[str, object]], message: str
- ) -> None:
- with pytest.raises(ValueError, match=message):
- _direction_goals("task-a", goals)
- def test_direction_hydration_rejects_historical_structure_without_compatibility() -> None:
- historical_payload = {
- "schema_version": "script-direction/v1",
- "goals": [{"goal_id": "goal-1", "statement": "old goal", "rationale": "old rationale"}],
- "criteria": [{"criterion_id": "criterion-1", "description": "old criterion"}],
- "domain_criteria": [],
- "evidence_refs": [EVIDENCE_REF],
- "legacy_markdown": "# old projection",
- }
- with pytest.raises(ProtocolViolation, match="current structured contract"):
- _hydrate_artifact(ArtifactKind.DIRECTION, historical_payload, "sha256:" + "a" * 64)
|