Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
174 lines
6.6 KiB
Python
174 lines
6.6 KiB
Python
"""Profile-driven execution selection (ACT-ADR-006 / ACTIVITY-WP-0032-T02)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from activity_core.glas_profile import (
|
|
EXECUTION_REF_KEYS,
|
|
ProfileRefError,
|
|
normalise_execution_refs,
|
|
resolve_execution_selector,
|
|
validate_profile_ref,
|
|
)
|
|
|
|
|
|
class TestProfileRefStructure:
|
|
def test_accepts_a_pinned_ref(self) -> None:
|
|
assert (
|
|
validate_profile_ref("harness.agent-dev-local@1.0.0")
|
|
== "harness.agent-dev-local@1.0.0"
|
|
)
|
|
|
|
def test_strips_surrounding_whitespace(self) -> None:
|
|
assert validate_profile_ref(" harness.agent-dev@1.0.0\n") == "harness.agent-dev@1.0.0"
|
|
|
|
def test_requires_a_version_pin(self) -> None:
|
|
"""Glas resolves an unpinned ref against every version and refuses it
|
|
as ambiguous — catch that here instead, without knowing any profile."""
|
|
with pytest.raises(ProfileRefError, match="must pin a version"):
|
|
validate_profile_ref("harness.agent-dev-local")
|
|
|
|
@pytest.mark.parametrize("bad", ["", " ", None, 42, ["harness@1.0.0"]])
|
|
def test_rejects_empty_or_non_string(self, bad) -> None:
|
|
with pytest.raises(ProfileRefError):
|
|
validate_profile_ref(bad)
|
|
|
|
@pytest.mark.parametrize(
|
|
"bad",
|
|
["harness agent@1.0.0", "harness.agent@1.0 .0", "harness\tagent@1.0.0"],
|
|
)
|
|
def test_rejects_internal_whitespace(self, bad: str) -> None:
|
|
with pytest.raises(ProfileRefError, match="whitespace"):
|
|
validate_profile_ref(bad)
|
|
|
|
@pytest.mark.parametrize("bad", ["@1.0.0", "-harness@1.0.0", "har/ness@1.0.0"])
|
|
def test_rejects_invalid_ids(self, bad: str) -> None:
|
|
with pytest.raises(ProfileRefError, match="invalid id"):
|
|
validate_profile_ref(bad)
|
|
|
|
@pytest.mark.parametrize("bad", ["harness@", "harness@v1.0.0", "harness@latest"])
|
|
def test_rejects_invalid_versions(self, bad: str) -> None:
|
|
with pytest.raises(ProfileRefError, match="invalid version"):
|
|
validate_profile_ref(bad)
|
|
|
|
|
|
class TestNoFallbackToApproachHint:
|
|
"""ACT-ADR-006 §2 — the single most important property of this change.
|
|
|
|
approach_hint must never override, synthesize, or fall back from an absent
|
|
or invalid harness_profile_ref. A silent fallback would reintroduce the
|
|
claim-time routing failure of 2026-08-17.
|
|
"""
|
|
|
|
def test_hint_does_not_synthesize_a_missing_profile(self) -> None:
|
|
profile, hint = resolve_execution_selector(None, "sbom-rescan")
|
|
|
|
assert profile is None, "approach_hint must not become a profile ref"
|
|
assert hint == "sbom-rescan"
|
|
|
|
def test_hint_does_not_rescue_a_malformed_profile(self) -> None:
|
|
with pytest.raises(ProfileRefError, match="must pin a version"):
|
|
resolve_execution_selector("harness.agent-dev-local", "sbom-rescan")
|
|
|
|
def test_malformed_profile_raises_even_with_no_hint(self) -> None:
|
|
with pytest.raises(ProfileRefError):
|
|
resolve_execution_selector("not a ref", None)
|
|
|
|
def test_profile_does_not_overwrite_the_legacy_hint(self) -> None:
|
|
"""Coexistence, not replacement: both are carried."""
|
|
profile, hint = resolve_execution_selector(
|
|
"harness.agent-dev-local@1.0.0", "sbom-rescan"
|
|
)
|
|
|
|
assert profile == "harness.agent-dev-local@1.0.0"
|
|
assert hint == "sbom-rescan"
|
|
|
|
def test_absent_profile_is_allowed_during_coexistence(self) -> None:
|
|
assert resolve_execution_selector(None, None) == (None, None)
|
|
|
|
def test_absent_profile_is_an_error_once_required(self, monkeypatch) -> None:
|
|
monkeypatch.setenv("ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE", "true")
|
|
|
|
with pytest.raises(ProfileRefError, match="cannot substitute"):
|
|
resolve_execution_selector(None, "sbom-rescan")
|
|
|
|
def test_required_mode_still_accepts_a_valid_profile(self, monkeypatch) -> None:
|
|
monkeypatch.setenv("ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE", "1")
|
|
|
|
profile, _ = resolve_execution_selector("harness.agent-dev@2.1.0", None)
|
|
|
|
assert profile == "harness.agent-dev@2.1.0"
|
|
|
|
def test_unknown_but_well_formed_profile_is_left_for_glas(self) -> None:
|
|
profile, _ = resolve_execution_selector(
|
|
"harness.not-in-any-local-catalog@9.9.9",
|
|
None,
|
|
)
|
|
|
|
assert profile == "harness.not-in-any-local-catalog@9.9.9"
|
|
|
|
def test_blank_hint_is_normalised_away(self) -> None:
|
|
assert resolve_execution_selector(None, " ") == (None, None)
|
|
|
|
|
|
class TestExecutionRefs:
|
|
def test_keeps_the_contract_refs(self) -> None:
|
|
refs = normalise_execution_refs(
|
|
{
|
|
"correlation_id": "activity-42",
|
|
"assignment_ref": "role-assignment:agent-7:42",
|
|
"role_ref": "role:maintainer@3",
|
|
"duty_ref": "duty:bounded-change@2",
|
|
"goal_refs": ["goal:reliable-delivery@5"],
|
|
"resource_envelope_refs": ["resource-envelope:standard-coding@1"],
|
|
}
|
|
)
|
|
|
|
assert set(refs) == set(EXECUTION_REF_KEYS)
|
|
assert refs["goal_refs"] == ["goal:reliable-delivery@5"]
|
|
|
|
def test_drops_unknown_keys(self) -> None:
|
|
"""We carry the contract's refs; we do not become a bag for anything."""
|
|
refs = normalise_execution_refs(
|
|
{"role_ref": "role:maintainer@3", "api_key": "sk-nope", "extra": {"a": 1}}
|
|
)
|
|
|
|
assert refs == {"role_ref": "role:maintainer@3"}
|
|
|
|
def test_drops_empty_and_wrongly_typed_values(self) -> None:
|
|
refs = normalise_execution_refs(
|
|
{
|
|
"correlation_id": " ",
|
|
"role_ref": 17,
|
|
"goal_refs": "not-a-list",
|
|
"resource_envelope_refs": ["ok", "", " "],
|
|
}
|
|
)
|
|
|
|
assert refs == {"resource_envelope_refs": ["ok"]}
|
|
|
|
@pytest.mark.parametrize("bad", [None, "refs", ["a"], 5])
|
|
def test_non_dict_becomes_empty(self, bad) -> None:
|
|
assert normalise_execution_refs(bad) == {}
|
|
|
|
|
|
def test_ops_run_model_carries_the_new_columns() -> None:
|
|
from activity_core.orm import OpsRun
|
|
|
|
columns = OpsRun.__table__.columns
|
|
assert "harness_profile_ref" in columns
|
|
assert columns["harness_profile_ref"].nullable is True
|
|
assert "execution_refs" in columns
|
|
assert columns["execution_refs"].nullable is False
|
|
|
|
|
|
def test_queue_projection_exposes_selection_fields() -> None:
|
|
"""Executors claim from this projection; the profile must reach them."""
|
|
import inspect
|
|
|
|
from activity_core import ops_run_queue
|
|
|
|
source = inspect.getsource(ops_run_queue)
|
|
assert '"harness_profile_ref": row.harness_profile_ref' in source
|
|
assert '"execution_refs"' in source
|