Fail closed on invalid Glas profiles
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 22s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
This commit is contained in:
tegwick 2026-08-22 23:02:41 +02:00
parent 561538c95e
commit b933cf52c8
12 changed files with 455 additions and 9 deletions

View file

@ -17,6 +17,12 @@ from typing import Any
import yaml
from activity_core.glas_profile import (
ProfileRefError,
require_harness_profile,
validate_profile_ref,
)
class ParseError(Exception):
"""Raised when a definition file cannot be parsed."""
@ -50,6 +56,72 @@ _FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
_FENCED_BLOCK_RE = re.compile(r"^```(\w+)\s*\n(.*?)\n```", re.DOTALL | re.MULTILINE)
def _instruction_can_emit_tasks(instruction: dict[str, Any]) -> bool:
"""Return false only for the explicit deterministic report-only shape."""
model = str(instruction.get("model") or "").strip().lower()
return not (
model in {"none", "deterministic", "unused"}
and bool(instruction.get("report_sinks"))
)
def _validate_execution_declarations(
rules: list[dict[str, Any]],
instructions: list[dict[str, Any]],
file: Path,
) -> None:
"""Validate declared profile structure without mirroring the Glas catalog."""
profile_required = require_harness_profile()
declarations: list[tuple[str, dict[str, Any], bool]] = []
for rule in rules:
action = rule.get("action")
if not isinstance(action, dict):
raise ParseError(
file,
None,
f"rule {rule.get('id')!r} action must be a YAML mapping",
)
declarations.append((f"rule {rule.get('id')!r}", action, True))
for instruction in instructions:
declarations.append(
(
f"instruction {instruction.get('id')!r}",
instruction,
_instruction_can_emit_tasks(instruction),
)
)
for location, declaration, can_emit_tasks in declarations:
raw_profile = declaration.get("harness_profile_ref")
if raw_profile is None:
if profile_required and can_emit_tasks:
raise ParseError(
file,
None,
f"{location} must declare harness_profile_ref while "
"ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE is enabled",
)
else:
try:
declaration["harness_profile_ref"] = validate_profile_ref(raw_profile)
except ProfileRefError as exc:
raise ParseError(
file,
None,
f"{location} has invalid harness_profile_ref: {exc}",
) from exc
execution_refs = declaration.get("execution_refs")
if execution_refs is not None and not isinstance(execution_refs, dict):
raise ParseError(
file,
None,
f"{location} execution_refs must be a YAML mapping",
)
def _scan_dirs() -> list[Path]:
dirs: list[Path] = []
default_dir = Path("activity-definitions")
@ -164,6 +236,8 @@ def parse_file(path: Path) -> ActivityDefinitionDef:
raise ParseError(path, None, "instruction block missing required field 'id'")
instructions.append(block_data)
_validate_execution_declarations(rules, instructions, path)
return ActivityDefinitionDef(
id=str(fm["id"]),
name=str(fm["name"]),