rein-aharness/rein_aharness/glas_execution.py
tegwick d00ffcb402 feat(runtime): consume governed Activity Core closes
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a06ba0-10aa-7ea0-b20a-4f3fac39efe9
2026-09-04 19:54:07 +02:00

181 lines
6 KiB
Python

"""Profile-authoritative execution of activity-core ops runs through Glas.
Glas and sand-boxer are sibling runtime packages rather than hard dependencies
of rein-aharness's legacy paths. Imports therefore stay at the execution edge:
profile-absent coexistence continues to work, while a profiled row fails closed
with an actionable error if the governed runtime is not installed.
"""
from __future__ import annotations
import math
from collections.abc import Callable
from typing import Any
from rein_aharness.execution_cancel import ExecutionCancel, ExecutionCancelled, resolve_cancel
from rein_aharness.ops_run_client import OpsRun, OpsRunConfig, resolve_ops_target
GLAS_APPROACH = "glas-profile"
GLAS_ACTOR = "agt"
_SCALAR_REFS = (
"correlation_id",
"assignment_ref",
"role_ref",
"duty_ref",
)
_LIST_REFS = ("goal_refs", "resource_envelope_refs")
_EVIDENCE_STRING_FIELDS = (
"request_id",
"correlation_id",
"actor",
"project",
"target_repo",
"contract_version",
"profile_ref",
"rein_id",
"rein_version",
"model_route",
"resolved_model",
"sandbox_profile",
"sandbox_id",
"tool_profile",
"outcome",
"failure_stage",
"error",
"started_at",
"finished_at",
"commit_sha",
"tool_events_completeness",
)
_EVIDENCE_NUMBER_FIELDS = (
"duration_s",
"tokens_spent",
"token_budget",
"tool_events_count",
)
class GlasExecutionError(RuntimeError):
"""The authoritative Glas invocation could not produce a GatewayResult."""
def normalise_execution_evidence_for_close(raw: Any) -> dict[str, Any]:
"""Retain only bounded Glas evidence fields safe for durable close state."""
if not isinstance(raw, dict):
return {}
result: dict[str, Any] = {}
for key in _EVIDENCE_STRING_FIELDS:
value = raw.get(key)
if isinstance(value, str):
result[key] = value[:2000] if key == "error" else value[:500]
for key in _EVIDENCE_NUMBER_FIELDS:
value = raw.get(key)
if (
isinstance(value, (int, float))
and not isinstance(value, bool)
and math.isfinite(value)
and value >= 0
):
result[key] = value
artifacts = raw.get("artifacts")
if isinstance(artifacts, list):
result["artifacts"] = [
item[:1000]
for item in artifacts[:20]
if isinstance(item, str) and item
]
refs: dict[str, Any] = {}
raw_refs = raw.get("refs")
if isinstance(raw_refs, dict):
for key in _SCALAR_REFS:
value = raw_refs.get(key)
if isinstance(value, str) and value:
refs[key] = value[:500]
for key in _LIST_REFS:
value = raw_refs.get(key)
if isinstance(value, list):
refs[key] = [
item[:500]
for item in value[:50]
if isinstance(item, str) and item
]
if refs:
result["refs"] = refs
return result
def _request_kwargs(run: OpsRun, config: OpsRunConfig, report_to_hub: bool) -> dict[str, Any]:
if not run.harness_profile_ref:
raise GlasExecutionError(f"ops_run {run.id} has no harness_profile_ref")
refs = run.execution_refs
kwargs: dict[str, Any] = {
"harness_profile_ref": run.harness_profile_ref,
"repo": str(resolve_ops_target(run, config)),
"title": run.title or "(untitled)",
"description": run.description or "",
# Activity Core's worker_id owns the queue lease; it is not a Glas
# actor type. Sand-boxer validates governed execution actors as
# adm|agt|atm, so this runtime enters the gateway as an agent.
"actor": GLAS_ACTOR,
"project": "rein-aharness",
"request_id": run.id,
"report_to_hub": report_to_hub,
}
for key in _SCALAR_REFS:
value = refs.get(key)
if isinstance(value, str) and value:
kwargs[key] = value
for key in _LIST_REFS:
value = refs.get(key)
if isinstance(value, list):
kwargs[key] = [item for item in value if isinstance(item, str)]
return kwargs
def execute_profiled_run(
run: OpsRun,
config: OpsRunConfig,
*,
report_to_hub: bool = True,
request_factory: Callable[..., Any] | None = None,
gateway: Callable[[Any], Any] | None = None,
cancel: ExecutionCancel | None = None,
) -> dict[str, Any]:
"""Invoke Glas and return its complete JSON-compatible GatewayResult."""
guard = resolve_cancel(cancel)
if guard is not None:
guard.check()
if request_factory is None or gateway is None:
try:
from glas_harness.contract import ExecutionRequest
from glas_harness.gateway import run_execution
except ImportError as exc:
raise GlasExecutionError(
"profiled ops_run requires glas-harness with sand-boxer support; "
"install the sibling checkouts into the claim-worker environment"
) from exc
request_factory = request_factory or ExecutionRequest
gateway = gateway or run_execution
try:
request = request_factory(**_request_kwargs(run, config, report_to_hub))
result = gateway(request)
raw = result.model_dump(mode="json") if hasattr(result, "model_dump") else result
except ExecutionCancelled:
raise
except GlasExecutionError:
raise
except Exception as exc:
if guard is not None and guard.cancelled:
raise ExecutionCancelled(guard.reason or "cancelled") from None
raise GlasExecutionError(f"Glas gateway invocation failed: {exc}") from exc
if guard is not None:
guard.check()
if not isinstance(raw, dict) or not isinstance(raw.get("ok"), bool):
raise GlasExecutionError("Glas gateway returned an invalid GatewayResult")
if not isinstance(raw.get("evidence"), dict):
raise GlasExecutionError("Glas GatewayResult is missing execution evidence")
return raw