"""Harness profile selection for queued ops runs (ACT-ADR-006). activity-core names an **approved, versioned harness profile** on a governed ops_run and passes the attribution references through. It does not resolve the profile, select a rein, or mirror the glas-harness catalogue — that catalogue is authoritative and Glas exposes no network validation service, so a local copy would drift and produce a second, disagreeing opinion about what is executable. What we can check locally is *structure*, and one structural property matters a great deal: ``GlasProfiles.resolve`` (glas_harness/profiles.py) treats a ref without ``@version`` as matching every version of that id and raises ``AmbiguousProfileError`` when more than one exists. Requiring the pin here turns a late ambiguity failure into an emission-time error, without knowing a single profile id. Everything else — whether a well-formed ref names a profile that exists, is enabled, and matches the contract version — is the execution-side Glas resolver's mandatory fail-closed check, by design. """ from __future__ import annotations import os import re from typing import Any # `harness.agent-dev-local@1.0.0` — id, then a pinned version. _PROFILE_ID_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$") _PROFILE_VERSION_PATTERN = re.compile(r"^[0-9]+(\.[0-9]+)*([A-Za-z0-9.\-+]*)$") # Passed through from workforce/leadership vocabulary (info-tech-canon). We # carry these; we do not author or interpret them. EXECUTION_REF_KEYS: tuple[str, ...] = ( "correlation_id", "assignment_ref", "role_ref", "duty_ref", "goal_refs", "resource_envelope_refs", ) _LIST_REF_KEYS = frozenset({"goal_refs", "resource_envelope_refs"}) class ProfileRefError(ValueError): """A harness profile reference is absent or structurally unusable.""" def require_harness_profile() -> bool: """Whether a governed ops_run must carry a profile ref. Off during migration (ACT-ADR-006 coexistence): definitions adopt the ref one at a time. Turn on once inventory shows no caller depends on ``approach_hint`` for runtime selection. """ return os.getenv("ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE", "").strip().lower() in { "1", "true", "yes", "on", } def validate_profile_ref(raw: Any) -> str: """Return a normalised, pinned profile ref or raise ProfileRefError.""" if raw is None or not isinstance(raw, str) or not raw.strip(): raise ProfileRefError("harness_profile_ref is empty") ref = raw.strip() if any(character.isspace() for character in ref): raise ProfileRefError(f"harness_profile_ref contains whitespace: {ref!r}") profile_id, separator, version = ref.partition("@") if not separator: # Glas would match every version of this id and refuse as ambiguous. raise ProfileRefError( f"harness_profile_ref must pin a version as '@': {ref!r}" ) if not _PROFILE_ID_PATTERN.match(profile_id): raise ProfileRefError(f"harness_profile_ref has an invalid id: {ref!r}") if not _PROFILE_VERSION_PATTERN.match(version): raise ProfileRefError(f"harness_profile_ref has an invalid version: {ref!r}") return ref def resolve_execution_selector( harness_profile_ref: Any, approach_hint: Any = None, ) -> tuple[str | None, str | None]: """Return ``(harness_profile_ref, approach_hint)`` for a queued ops_run. ACT-ADR-006 §2: the two coexist with **distinct semantics**. The profile ref is the authoritative execution-constellation selector; ``approach_hint`` is a legacy definition-matching hint only. It must never override, synthesize, or fall back from an absent or invalid profile ref — a silent fallback would reintroduce exactly the claim-time routing failure this replaces. So the hint is carried through untouched and is never consulted here. """ hint = approach_hint if isinstance(approach_hint, str) and approach_hint.strip() else None if harness_profile_ref is None or ( isinstance(harness_profile_ref, str) and not harness_profile_ref.strip() ): if require_harness_profile(): raise ProfileRefError( "governed ops_run has no harness_profile_ref " "(approach_hint cannot substitute for one)" ) return None, hint # Malformed refs always raise, migration flag or not: a ref that was meant # to route this run and cannot must not silently degrade to the legacy hint. return validate_profile_ref(harness_profile_ref), hint def normalise_execution_refs(raw: Any) -> dict[str, Any]: """Keep only the contract's attribution refs, as strings / lists of strings.""" if not isinstance(raw, dict): return {} refs: dict[str, Any] = {} for key in EXECUTION_REF_KEYS: value = raw.get(key) if key in _LIST_REF_KEYS: if isinstance(value, (list, tuple)): items = [str(item).strip() for item in value if str(item).strip()] if items: refs[key] = items elif isinstance(value, str) and value.strip(): refs[key] = value.strip() return refs