1053 lines
41 KiB
Python
1053 lines
41 KiB
Python
#!/usr/bin/env python3
|
|
"""Strict, zero-dependency primitives for ACK worker routing.
|
|
|
|
This module deliberately validates structured data rather than accepting a
|
|
shell command. It does not invoke Orca, resolve executables, inspect Git, or
|
|
write ``tasks.yaml``. A launcher can use the validated profile and the exact
|
|
argv renderer below, then persist its externally observed facts as a receipt.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import re
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
PROFILE_VERSION = 1
|
|
RECEIPT_VERSION = 1
|
|
LAUNCH_PROTOCOL_VERSION = 1
|
|
MAX_ROUNDS = 3
|
|
|
|
ROLES = frozenset({"developer", "test", "operator"})
|
|
REQUIRED_DEFAULT_ROLES = frozenset({"developer", "test"})
|
|
STANDARD_ONLY_ROLES = frozenset({"test", "operator"})
|
|
CLIS = frozenset({"codex", "cursor-agent"})
|
|
TIERS = frozenset({"standard", "strong"})
|
|
REASONING_EFFORTS = frozenset({"low", "medium", "high", "xhigh"})
|
|
PERMISSION_MODES = frozenset({"read-only", "workspace-write"})
|
|
ORCHESTRATION_MODES = frozenset({"orca", "manual"})
|
|
BASE_ENVIRONMENT_POLICY = "per-cli-allowlist-v1"
|
|
OPERATOR_ENVIRONMENT_POLICY = "per-cli-plus-operator-publish-v1"
|
|
ENVIRONMENT_POLICIES = frozenset(
|
|
{BASE_ENVIRONMENT_POLICY, OPERATOR_ENVIRONMENT_POLICY}
|
|
)
|
|
DEFAULT_KEYS = frozenset(
|
|
{"developer", "test", "operator", "developerUpgraded"}
|
|
)
|
|
|
|
ORCHESTRATION_FIELDS = frozenset(
|
|
{
|
|
"profileVersion",
|
|
"mode",
|
|
"allowedWorktrees",
|
|
"modelAllowlist",
|
|
"profiles",
|
|
"defaults",
|
|
}
|
|
)
|
|
PROFILE_FIELDS = frozenset(
|
|
{
|
|
"role",
|
|
"cli",
|
|
"tier",
|
|
"model",
|
|
"reasoningEffort",
|
|
"permissionMode",
|
|
}
|
|
)
|
|
RECEIPT_FIELDS = frozenset(
|
|
{
|
|
"receiptVersion",
|
|
"id",
|
|
"launchId",
|
|
"profileId",
|
|
"profileHash",
|
|
"launchFingerprint",
|
|
"slot",
|
|
"createdFor",
|
|
"worktree",
|
|
"requested",
|
|
"binding",
|
|
"createdAt",
|
|
"receiptHash",
|
|
}
|
|
)
|
|
CREATED_FOR_FIELDS = frozenset({"taskId", "attemptId", "role"})
|
|
WORKTREE_FIELDS = frozenset(
|
|
{
|
|
"path",
|
|
"device",
|
|
"inode",
|
|
"gitCommonDir",
|
|
"gitCommonDevice",
|
|
"gitCommonInode",
|
|
}
|
|
)
|
|
REQUESTED_FIELDS = frozenset(
|
|
{
|
|
"cli",
|
|
"tier",
|
|
"model",
|
|
"reasoningEffort",
|
|
"permissionMode",
|
|
"executable",
|
|
"executableDevice",
|
|
"executableInode",
|
|
"cliVersion",
|
|
"argv",
|
|
"argvHash",
|
|
"environmentPolicy",
|
|
}
|
|
)
|
|
BINDING_FIELDS = frozenset(
|
|
{
|
|
"orchestrator",
|
|
"runtimeId",
|
|
"handle",
|
|
"incarnationId",
|
|
"observedWorktreePath",
|
|
"connected",
|
|
"writable",
|
|
"boundAt",
|
|
}
|
|
)
|
|
ROLE_DISPATCH_FIELDS = frozenset(
|
|
{"profileId", "receiptId", "attemptId", "taskId", "dispatchId"}
|
|
)
|
|
|
|
PROFILE_ID_RE = re.compile(r"^[a-z][a-z0-9-]{1,63}$")
|
|
MODEL_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:/+@-]{0,127}$")
|
|
RECEIPT_ID_RE = re.compile(r"^WR-[0-9a-f]{64}$")
|
|
LAUNCH_ID_RE = re.compile(r"^[0-9a-f]{64}$")
|
|
TASK_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
|
|
ATTEMPT_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*-A[1-3]$")
|
|
SHA256_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
|
|
|
|
|
|
def _canonical_json(value: Any) -> str:
|
|
"""Return the one JSON representation used by all hashes in this module."""
|
|
|
|
try:
|
|
return json.dumps(
|
|
value,
|
|
ensure_ascii=False,
|
|
sort_keys=True,
|
|
separators=(",", ":"),
|
|
allow_nan=False,
|
|
)
|
|
except (RecursionError, TypeError, ValueError) as exc:
|
|
raise ValueError(f"value is not canonical JSON data: {exc}") from exc
|
|
|
|
|
|
def canonical_sha256(value: Any) -> str:
|
|
"""Hash canonical UTF-8 JSON and return ``sha256:<lowercase hex>``."""
|
|
|
|
digest = hashlib.sha256(_canonical_json(value).encode("utf-8")).hexdigest()
|
|
return f"sha256:{digest}"
|
|
|
|
|
|
def _unknown_fields(value: dict[str, Any], allowed: frozenset[str], where: str) -> list[str]:
|
|
unknown = set(value) - allowed
|
|
return [
|
|
f"{where}: unknown field {field!r}"
|
|
for field in sorted(unknown, key=repr)
|
|
]
|
|
|
|
|
|
def _missing_fields(value: dict[str, Any], required: frozenset[str], where: str) -> list[str]:
|
|
return [f"{where}: missing field {field!r}" for field in sorted(required - set(value))]
|
|
|
|
|
|
def _is_nonempty_text(value: Any, *, max_length: int = 1024) -> bool:
|
|
return (
|
|
isinstance(value, str)
|
|
and bool(value.strip())
|
|
and len(value) <= max_length
|
|
and all(ord(character) >= 32 and ord(character) != 127 for character in value)
|
|
)
|
|
|
|
|
|
def _is_absolute_safe_path(value: Any) -> bool:
|
|
if not _is_nonempty_text(value, max_length=4096):
|
|
return False
|
|
path = Path(value)
|
|
return (
|
|
path.is_absolute()
|
|
and value != os.path.sep
|
|
and not value.startswith("//")
|
|
and ".." not in path.parts
|
|
and os.path.normpath(value) == value
|
|
)
|
|
|
|
|
|
def _is_nonnegative_int(value: Any) -> bool:
|
|
return isinstance(value, int) and not isinstance(value, bool) and value >= 0
|
|
|
|
|
|
def _is_positive_int(value: Any) -> bool:
|
|
return isinstance(value, int) and not isinstance(value, bool) and value > 0
|
|
|
|
|
|
def environment_policy_for_role(role: Any) -> str:
|
|
"""Return the fixed credential policy for a validated worker role."""
|
|
|
|
if role == "operator":
|
|
return OPERATOR_ENVIRONMENT_POLICY
|
|
if role in {"developer", "test"}:
|
|
return BASE_ENVIRONMENT_POLICY
|
|
raise ValueError("role must be developer/test/operator")
|
|
|
|
|
|
def _is_timestamp(value: Any) -> bool:
|
|
if not isinstance(value, str):
|
|
return False
|
|
try:
|
|
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
except ValueError:
|
|
return False
|
|
return parsed.tzinfo is not None
|
|
|
|
|
|
def validate_profile(profile: Any, *, where: str = "profile") -> list[str]:
|
|
"""Validate one strict worker profile without consulting its allowlist."""
|
|
|
|
if not isinstance(profile, dict):
|
|
return [f"{where}: must be an object"]
|
|
|
|
errors = _unknown_fields(profile, PROFILE_FIELDS, where)
|
|
errors.extend(_missing_fields(profile, PROFILE_FIELDS, where))
|
|
|
|
role = profile.get("role")
|
|
cli = profile.get("cli")
|
|
tier = profile.get("tier")
|
|
model = profile.get("model")
|
|
effort = profile.get("reasoningEffort")
|
|
permission = profile.get("permissionMode")
|
|
|
|
if not isinstance(role, str) or role not in ROLES:
|
|
errors.append(f"{where}.role: must be developer/test/operator")
|
|
if not isinstance(cli, str) or cli not in CLIS:
|
|
errors.append(f"{where}.cli: must be codex/cursor-agent")
|
|
if not isinstance(tier, str) or tier not in TIERS:
|
|
errors.append(f"{where}.tier: must be standard/strong")
|
|
if not isinstance(model, str) or MODEL_ID_RE.fullmatch(model) is None:
|
|
errors.append(f"{where}.model: must be a safe model ID")
|
|
if not isinstance(permission, str) or permission not in PERMISSION_MODES:
|
|
errors.append(
|
|
f"{where}.permissionMode: must be read-only/workspace-write"
|
|
)
|
|
|
|
if cli == "codex":
|
|
if not isinstance(effort, str) or effort not in REASONING_EFFORTS:
|
|
errors.append(
|
|
f"{where}.reasoningEffort: Codex requires low/medium/high/xhigh"
|
|
)
|
|
elif cli == "cursor-agent" and effort is not None:
|
|
errors.append(f"{where}.reasoningEffort: Cursor requires null")
|
|
|
|
if (
|
|
isinstance(role, str)
|
|
and role in STANDARD_ONLY_ROLES
|
|
and tier != "standard"
|
|
):
|
|
label = "Test" if role == "test" else "Operator"
|
|
errors.append(f"{where}.tier: {label} may only use standard")
|
|
if tier == "strong" and role != "developer":
|
|
errors.append(f"{where}.tier: strong may only be used by Developer")
|
|
|
|
return errors
|
|
|
|
|
|
def _validate_model_allowlist(value: Any, where: str) -> list[str]:
|
|
if not isinstance(value, dict):
|
|
return [f"{where}: must be an object"]
|
|
|
|
errors: list[str] = []
|
|
for cli in sorted(set(value) - CLIS, key=repr):
|
|
errors.append(f"{where}: unknown CLI {cli!r}")
|
|
for cli, roles in value.items():
|
|
cli_where = f"{where}.{cli}"
|
|
if cli not in CLIS:
|
|
continue
|
|
if not isinstance(roles, dict):
|
|
errors.append(f"{cli_where}: must be an object")
|
|
continue
|
|
if not roles:
|
|
errors.append(f"{cli_where}: must not be empty")
|
|
for role in sorted(set(roles) - ROLES, key=repr):
|
|
errors.append(f"{cli_where}: unknown role {role!r}")
|
|
for role, tiers in roles.items():
|
|
role_where = f"{cli_where}.{role}"
|
|
if role not in ROLES:
|
|
continue
|
|
if not isinstance(tiers, dict):
|
|
errors.append(f"{role_where}: must be an object")
|
|
continue
|
|
if not tiers:
|
|
errors.append(f"{role_where}: must not be empty")
|
|
for tier in sorted(set(tiers) - TIERS, key=repr):
|
|
errors.append(f"{role_where}: unknown tier {tier!r}")
|
|
if role in STANDARD_ONLY_ROLES and "strong" in tiers:
|
|
label = "Test" if role == "test" else "Operator"
|
|
errors.append(
|
|
f"{role_where}: {label} cannot define a strong allowlist"
|
|
)
|
|
for tier, models in tiers.items():
|
|
tier_where = f"{role_where}.{tier}"
|
|
if tier not in TIERS:
|
|
continue
|
|
if not isinstance(models, list) or not models:
|
|
errors.append(f"{tier_where}: must be a non-empty model list")
|
|
continue
|
|
seen: set[str] = set()
|
|
for index, model in enumerate(models):
|
|
item_where = f"{tier_where}[{index}]"
|
|
if not isinstance(model, str) or MODEL_ID_RE.fullmatch(model) is None:
|
|
errors.append(f"{item_where}: must be a safe model ID")
|
|
elif model in seen:
|
|
errors.append(f"{item_where}: duplicate model {model!r}")
|
|
else:
|
|
seen.add(model)
|
|
return errors
|
|
|
|
|
|
def _allowed_models(
|
|
allowlist: Any,
|
|
cli: Any,
|
|
role: Any,
|
|
tier: Any,
|
|
) -> list[str] | None:
|
|
if not isinstance(allowlist, dict):
|
|
return None
|
|
roles = allowlist.get(cli)
|
|
if not isinstance(roles, dict):
|
|
return None
|
|
tiers = roles.get(role)
|
|
if not isinstance(tiers, dict):
|
|
return None
|
|
models = tiers.get(tier)
|
|
return models if isinstance(models, list) else None
|
|
|
|
|
|
def validate_orchestration(
|
|
orchestration: Any,
|
|
*,
|
|
where: str = "project.orchestration",
|
|
) -> list[str]:
|
|
"""Validate the complete strict routing configuration."""
|
|
|
|
if not isinstance(orchestration, dict):
|
|
return [f"{where}: must be an object"]
|
|
|
|
errors = _unknown_fields(orchestration, ORCHESTRATION_FIELDS, where)
|
|
errors.extend(_missing_fields(orchestration, ORCHESTRATION_FIELDS, where))
|
|
|
|
profile_version = orchestration.get("profileVersion")
|
|
mode = orchestration.get("mode")
|
|
allowed_worktrees = orchestration.get("allowedWorktrees")
|
|
allowlist = orchestration.get("modelAllowlist")
|
|
profiles = orchestration.get("profiles")
|
|
defaults = orchestration.get("defaults")
|
|
|
|
if profile_version != PROFILE_VERSION or isinstance(profile_version, bool):
|
|
errors.append(f"{where}.profileVersion: must be {PROFILE_VERSION}")
|
|
if not isinstance(mode, str) or mode not in ORCHESTRATION_MODES:
|
|
errors.append(f"{where}.mode: must be orca/manual")
|
|
|
|
if not isinstance(allowed_worktrees, list):
|
|
errors.append(f"{where}.allowedWorktrees: must be a list")
|
|
else:
|
|
if mode == "orca" and not allowed_worktrees:
|
|
errors.append(f"{where}.allowedWorktrees: Orca mode requires at least one path")
|
|
seen_worktrees: set[str] = set()
|
|
for index, worktree in enumerate(allowed_worktrees):
|
|
item_where = f"{where}.allowedWorktrees[{index}]"
|
|
if not _is_absolute_safe_path(worktree):
|
|
errors.append(f"{item_where}: must be a safe absolute path other than root")
|
|
elif worktree in seen_worktrees:
|
|
errors.append(f"{item_where}: duplicate worktree {worktree!r}")
|
|
else:
|
|
seen_worktrees.add(worktree)
|
|
|
|
errors.extend(_validate_model_allowlist(allowlist, f"{where}.modelAllowlist"))
|
|
|
|
valid_profiles: dict[str, dict[str, Any]] = {}
|
|
if not isinstance(profiles, dict):
|
|
errors.append(f"{where}.profiles: must be an object")
|
|
else:
|
|
if mode == "orca" and not profiles:
|
|
errors.append(f"{where}.profiles: Orca mode requires profiles")
|
|
for profile_id, profile in profiles.items():
|
|
profile_where = f"{where}.profiles.{profile_id}"
|
|
if not isinstance(profile_id, str) or PROFILE_ID_RE.fullmatch(profile_id) is None:
|
|
errors.append(f"{where}.profiles: invalid profile ID {profile_id!r}")
|
|
continue
|
|
profile_errors = validate_profile(profile, where=profile_where)
|
|
errors.extend(profile_errors)
|
|
if profile_errors or not isinstance(profile, dict):
|
|
continue
|
|
valid_profiles[profile_id] = profile
|
|
models = _allowed_models(
|
|
allowlist,
|
|
profile.get("cli"),
|
|
profile.get("role"),
|
|
profile.get("tier"),
|
|
)
|
|
if profile.get("model") not in (models or []):
|
|
errors.append(
|
|
f"{profile_where}.model: {profile.get('model')!r} is not allowed "
|
|
"for its cli/role/tier"
|
|
)
|
|
|
|
if not isinstance(defaults, dict):
|
|
errors.append(f"{where}.defaults: must be an object")
|
|
else:
|
|
for default_key in sorted(set(defaults) - DEFAULT_KEYS, key=repr):
|
|
errors.append(f"{where}.defaults: unknown key {default_key!r}")
|
|
if mode == "orca":
|
|
for role in sorted(REQUIRED_DEFAULT_ROLES - set(defaults)):
|
|
errors.append(f"{where}.defaults: missing role {role!r}")
|
|
for default_key, profile_id in defaults.items():
|
|
default_where = f"{where}.defaults.{default_key}"
|
|
if default_key not in DEFAULT_KEYS:
|
|
continue
|
|
if not isinstance(profile_id, str) or PROFILE_ID_RE.fullmatch(profile_id) is None:
|
|
errors.append(f"{default_where}: must be a profile ID")
|
|
continue
|
|
profile = valid_profiles.get(profile_id)
|
|
if profile is None:
|
|
errors.append(f"{default_where}: unknown or invalid profile {profile_id!r}")
|
|
continue
|
|
expected_role = "developer" if default_key == "developerUpgraded" else default_key
|
|
expected_tier = "strong" if default_key == "developerUpgraded" else "standard"
|
|
if profile.get("role") != expected_role:
|
|
errors.append(f"{default_where}: profile role must be {expected_role}")
|
|
if profile.get("tier") != expected_tier:
|
|
errors.append(
|
|
f"{default_where}: default profile must use {expected_tier} tier"
|
|
)
|
|
if profile.get("permissionMode") not in PERMISSION_MODES:
|
|
errors.append(f"{default_where}: default profile has unsafe permissions")
|
|
|
|
operator_profile_id = defaults.get("operator")
|
|
test_profile_id = defaults.get("test")
|
|
operator_profile = valid_profiles.get(operator_profile_id)
|
|
test_profile = valid_profiles.get(test_profile_id)
|
|
if operator_profile_id is not None and test_profile is None:
|
|
errors.append(
|
|
f"{where}.defaults.operator: requires a valid Test default profile"
|
|
)
|
|
elif operator_profile is not None and test_profile is not None:
|
|
for field in ("cli", "tier", "model", "reasoningEffort"):
|
|
if operator_profile.get(field) != test_profile.get(field):
|
|
errors.append(
|
|
f"{where}.defaults.operator: operator default must use "
|
|
f"the Test default {field}"
|
|
)
|
|
|
|
return errors
|
|
|
|
|
|
def profile_hash(
|
|
profile: dict[str, Any],
|
|
*,
|
|
profile_version: int = PROFILE_VERSION,
|
|
) -> str:
|
|
"""Return the canonical hash of a valid strict profile."""
|
|
|
|
errors = validate_profile(profile)
|
|
if errors:
|
|
raise ValueError("invalid profile: " + "; ".join(errors))
|
|
if (
|
|
not isinstance(profile_version, int)
|
|
or isinstance(profile_version, bool)
|
|
or profile_version < 1
|
|
):
|
|
raise ValueError("profile_version must be a positive integer")
|
|
return canonical_sha256(
|
|
{
|
|
"profileVersion": profile_version,
|
|
"profile": profile,
|
|
}
|
|
)
|
|
|
|
|
|
def render_worker_argv(
|
|
profile: dict[str, Any],
|
|
executable: str,
|
|
worktree: str,
|
|
) -> list[str]:
|
|
"""Render the only argv shapes allowed by routing profile version 1."""
|
|
|
|
errors = validate_profile(profile)
|
|
if errors:
|
|
raise ValueError("invalid profile: " + "; ".join(errors))
|
|
if not _is_absolute_safe_path(executable):
|
|
raise ValueError("executable must be a safe absolute path other than root")
|
|
if Path(executable).name != profile["cli"]:
|
|
raise ValueError("executable basename must match profile.cli")
|
|
if not _is_absolute_safe_path(worktree):
|
|
raise ValueError("worktree must be a safe absolute path other than root")
|
|
|
|
model = profile["model"]
|
|
permission = profile["permissionMode"]
|
|
if profile["cli"] == "codex":
|
|
return [
|
|
executable,
|
|
"--strict-config",
|
|
"--model",
|
|
model,
|
|
"--config",
|
|
f"model_reasoning_effort={profile['reasoningEffort']}",
|
|
"--sandbox",
|
|
permission,
|
|
"--ask-for-approval",
|
|
"never",
|
|
"--cd",
|
|
worktree,
|
|
]
|
|
|
|
argv = [executable, "--model", model]
|
|
if permission == "read-only":
|
|
argv.extend(["--mode", "plan"])
|
|
else:
|
|
argv.append("--auto-review")
|
|
argv.extend(["--sandbox", "enabled", "--workspace", worktree])
|
|
return argv
|
|
|
|
|
|
def receipt_hash(receipt: dict[str, Any]) -> str:
|
|
"""Hash every receipt field except the self-referential ``receiptHash``."""
|
|
|
|
if not isinstance(receipt, dict):
|
|
raise ValueError("receipt must be an object")
|
|
payload = {key: value for key, value in receipt.items() if key != "receiptHash"}
|
|
return canonical_sha256(payload)
|
|
|
|
|
|
def _validate_created_for(value: Any, where: str) -> list[str]:
|
|
if not isinstance(value, dict):
|
|
return [f"{where}: must be an object"]
|
|
errors = _unknown_fields(value, CREATED_FOR_FIELDS, where)
|
|
errors.extend(_missing_fields(value, CREATED_FOR_FIELDS, where))
|
|
task_id = value.get("taskId")
|
|
attempt_id = value.get("attemptId")
|
|
if not isinstance(task_id, str) or TASK_ID_RE.fullmatch(task_id) is None:
|
|
errors.append(f"{where}.taskId: must be a safe task ID")
|
|
if not isinstance(attempt_id, str) or ATTEMPT_ID_RE.fullmatch(attempt_id) is None:
|
|
errors.append(f"{where}.attemptId: must use <task-id>-A1..A3")
|
|
elif isinstance(task_id, str) and not attempt_id.startswith(f"{task_id}-A"):
|
|
errors.append(f"{where}.attemptId: must belong to taskId")
|
|
role = value.get("role")
|
|
if not isinstance(role, str) or role not in ROLES:
|
|
errors.append(f"{where}.role: must be developer/test/operator")
|
|
return errors
|
|
|
|
|
|
def _validate_worktree_snapshot(value: Any, where: str) -> list[str]:
|
|
if not isinstance(value, dict):
|
|
return [f"{where}: must be an object"]
|
|
errors = _unknown_fields(value, WORKTREE_FIELDS, where)
|
|
errors.extend(_missing_fields(value, WORKTREE_FIELDS, where))
|
|
for field in ("path", "gitCommonDir"):
|
|
if not _is_absolute_safe_path(value.get(field)):
|
|
errors.append(f"{where}.{field}: must be a safe absolute path other than root")
|
|
for field in ("device", "gitCommonDevice"):
|
|
if not _is_nonnegative_int(value.get(field)):
|
|
errors.append(f"{where}.{field}: must be a non-negative integer")
|
|
for field in ("inode", "gitCommonInode"):
|
|
if not _is_positive_int(value.get(field)):
|
|
errors.append(f"{where}.{field}: must be a positive integer")
|
|
return errors
|
|
|
|
|
|
def _validate_requested(value: Any, where: str) -> list[str]:
|
|
if not isinstance(value, dict):
|
|
return [f"{where}: must be an object"]
|
|
errors = _unknown_fields(value, REQUESTED_FIELDS, where)
|
|
errors.extend(_missing_fields(value, REQUESTED_FIELDS, where))
|
|
cli = value.get("cli")
|
|
tier = value.get("tier")
|
|
permission = value.get("permissionMode")
|
|
if not isinstance(cli, str) or cli not in CLIS:
|
|
errors.append(f"{where}.cli: must be codex/cursor-agent")
|
|
if not isinstance(tier, str) or tier not in TIERS:
|
|
errors.append(f"{where}.tier: must be standard/strong")
|
|
model = value.get("model")
|
|
if not isinstance(model, str) or MODEL_ID_RE.fullmatch(model) is None:
|
|
errors.append(f"{where}.model: must be a safe model ID")
|
|
effort = value.get("reasoningEffort")
|
|
if cli == "codex" and (
|
|
not isinstance(effort, str) or effort not in REASONING_EFFORTS
|
|
):
|
|
errors.append(f"{where}.reasoningEffort: invalid Codex effort")
|
|
if cli == "cursor-agent" and effort is not None:
|
|
errors.append(f"{where}.reasoningEffort: Cursor requires null")
|
|
if not isinstance(permission, str) or permission not in PERMISSION_MODES:
|
|
errors.append(f"{where}.permissionMode: must be read-only/workspace-write")
|
|
executable = value.get("executable")
|
|
if not _is_absolute_safe_path(executable):
|
|
errors.append(f"{where}.executable: must be a safe absolute path")
|
|
elif isinstance(cli, str) and cli in CLIS and Path(executable).name != cli:
|
|
errors.append(f"{where}.executable: basename must match cli")
|
|
if not _is_nonnegative_int(value.get("executableDevice")):
|
|
errors.append(f"{where}.executableDevice: must be a non-negative integer")
|
|
if not _is_positive_int(value.get("executableInode")):
|
|
errors.append(f"{where}.executableInode: must be a positive integer")
|
|
if not _is_nonempty_text(value.get("cliVersion"), max_length=256):
|
|
errors.append(f"{where}.cliVersion: must be non-empty single-line text")
|
|
argv = value.get("argv")
|
|
if (
|
|
not isinstance(argv, list)
|
|
or len(argv) < 2
|
|
or any(not isinstance(arg, str) for arg in argv)
|
|
):
|
|
errors.append(f"{where}.argv: must be a string array with at least 2 items")
|
|
argv_hash = value.get("argvHash")
|
|
if not isinstance(argv_hash, str) or SHA256_RE.fullmatch(argv_hash) is None:
|
|
errors.append(f"{where}.argvHash: must be a canonical sha256 hex digest")
|
|
elif isinstance(argv, list) and all(isinstance(arg, str) for arg in argv):
|
|
if argv_hash != canonical_sha256(argv):
|
|
errors.append(f"{where}.argvHash: does not match argv")
|
|
if value.get("environmentPolicy") not in ENVIRONMENT_POLICIES:
|
|
errors.append(
|
|
f"{where}.environmentPolicy: must be 'per-cli-allowlist-v1' or "
|
|
"'per-cli-plus-operator-publish-v1'"
|
|
)
|
|
return errors
|
|
|
|
|
|
def _validate_binding(value: Any, where: str) -> list[str]:
|
|
if not isinstance(value, dict):
|
|
return [f"{where}: must be an object"]
|
|
errors = _unknown_fields(value, BINDING_FIELDS, where)
|
|
errors.extend(_missing_fields(value, BINDING_FIELDS, where))
|
|
if value.get("orchestrator") != "orca":
|
|
errors.append(f"{where}.orchestrator: must be orca")
|
|
for field in ("runtimeId", "handle", "incarnationId"):
|
|
if not _is_nonempty_text(value.get(field), max_length=512):
|
|
errors.append(f"{where}.{field}: must be non-empty single-line text")
|
|
if not _is_absolute_safe_path(value.get("observedWorktreePath")):
|
|
errors.append(f"{where}.observedWorktreePath: must be a safe absolute path")
|
|
for field in ("connected", "writable"):
|
|
if value.get(field) is not True:
|
|
errors.append(f"{where}.{field}: must be true")
|
|
if not _is_timestamp(value.get("boundAt")):
|
|
errors.append(f"{where}.boundAt: must be a timezone-aware ISO 8601 timestamp")
|
|
return errors
|
|
|
|
|
|
def validate_worker_receipt(
|
|
receipt: Any,
|
|
*,
|
|
orchestration: dict[str, Any] | None = None,
|
|
task_ids: set[str] | None = None,
|
|
where: str = "workerReceipt",
|
|
) -> list[str]:
|
|
"""Validate one audit receipt's structure/checksums and routing references.
|
|
|
|
This is not origin authentication: ``receiptHash`` is unkeyed, so callers
|
|
must never use this result alone to authorize reuse of an old terminal.
|
|
"""
|
|
|
|
if not isinstance(receipt, dict):
|
|
return [f"{where}: must be an object"]
|
|
errors = _unknown_fields(receipt, RECEIPT_FIELDS, where)
|
|
errors.extend(_missing_fields(receipt, RECEIPT_FIELDS, where))
|
|
|
|
version = receipt.get("receiptVersion")
|
|
if version != RECEIPT_VERSION or isinstance(version, bool):
|
|
errors.append(f"{where}.receiptVersion: must be {RECEIPT_VERSION}")
|
|
for field in ("id", "launchId", "profileId"):
|
|
value = receipt.get(field)
|
|
matcher = {
|
|
"id": RECEIPT_ID_RE,
|
|
"launchId": LAUNCH_ID_RE,
|
|
"profileId": PROFILE_ID_RE,
|
|
}[field]
|
|
if not isinstance(value, str) or matcher.fullmatch(value) is None:
|
|
errors.append(f"{where}.{field}: invalid identifier")
|
|
for field in ("profileHash", "launchFingerprint", "receiptHash"):
|
|
value = receipt.get(field)
|
|
if not isinstance(value, str) or SHA256_RE.fullmatch(value) is None:
|
|
errors.append(f"{where}.{field}: must be a canonical sha256 hex digest")
|
|
slot = receipt.get("slot")
|
|
if not isinstance(slot, int) or isinstance(slot, bool) or not 1 <= slot <= 99:
|
|
errors.append(f"{where}.slot: must be an integer from 1 to 99")
|
|
receipt_id = receipt.get("id")
|
|
launch_id = receipt.get("launchId")
|
|
if (
|
|
isinstance(receipt_id, str)
|
|
and RECEIPT_ID_RE.fullmatch(receipt_id)
|
|
and isinstance(launch_id, str)
|
|
and LAUNCH_ID_RE.fullmatch(launch_id)
|
|
and receipt_id != f"WR-{launch_id}"
|
|
):
|
|
errors.append(f"{where}.id: must equal 'WR-' + launchId")
|
|
|
|
created_for = receipt.get("createdFor")
|
|
worktree = receipt.get("worktree")
|
|
requested = receipt.get("requested")
|
|
binding = receipt.get("binding")
|
|
errors.extend(_validate_created_for(created_for, f"{where}.createdFor"))
|
|
errors.extend(_validate_worktree_snapshot(worktree, f"{where}.worktree"))
|
|
errors.extend(_validate_requested(requested, f"{where}.requested"))
|
|
errors.extend(_validate_binding(binding, f"{where}.binding"))
|
|
if not _is_timestamp(receipt.get("createdAt")):
|
|
errors.append(f"{where}.createdAt: must be a timezone-aware ISO 8601 timestamp")
|
|
|
|
if isinstance(created_for, dict) and task_ids is not None:
|
|
task_id = created_for.get("taskId")
|
|
if isinstance(task_id, str) and task_id not in task_ids:
|
|
errors.append(f"{where}.createdFor.taskId: unknown task {task_id!r}")
|
|
|
|
profile: dict[str, Any] | None = None
|
|
if orchestration is not None and not isinstance(orchestration, dict):
|
|
errors.append(f"{where}: orchestration must be an object")
|
|
elif isinstance(orchestration, dict):
|
|
profiles = orchestration.get("profiles")
|
|
profile_id = receipt.get("profileId")
|
|
candidate = profiles.get(profile_id) if (
|
|
isinstance(profiles, dict) and isinstance(profile_id, str)
|
|
) else None
|
|
if isinstance(candidate, dict):
|
|
profile = candidate
|
|
else:
|
|
errors.append(f"{where}.profileId: unknown profile {profile_id!r}")
|
|
allowed = orchestration.get("allowedWorktrees")
|
|
if isinstance(worktree, dict) and isinstance(allowed, list):
|
|
if worktree.get("path") not in allowed:
|
|
errors.append(f"{where}.worktree.path: is not in allowedWorktrees")
|
|
|
|
if profile is not None:
|
|
try:
|
|
expected_profile_hash = profile_hash(
|
|
profile,
|
|
profile_version=orchestration.get(
|
|
"profileVersion",
|
|
PROFILE_VERSION,
|
|
),
|
|
)
|
|
except ValueError:
|
|
errors.append(f"{where}.profileId: referenced profile is invalid")
|
|
else:
|
|
if receipt.get("profileHash") != expected_profile_hash:
|
|
errors.append(f"{where}.profileHash: does not match profile")
|
|
|
|
if isinstance(created_for, dict) and created_for.get("role") != profile.get("role"):
|
|
errors.append(f"{where}.createdFor.role: does not match profile")
|
|
if isinstance(requested, dict):
|
|
for field in (
|
|
"cli",
|
|
"tier",
|
|
"model",
|
|
"reasoningEffort",
|
|
"permissionMode",
|
|
):
|
|
if requested.get(field) != profile.get(field):
|
|
errors.append(f"{where}.requested.{field}: does not match profile")
|
|
executable = requested.get("executable")
|
|
worktree_path = worktree.get("path") if isinstance(worktree, dict) else None
|
|
if isinstance(executable, str) and isinstance(worktree_path, str):
|
|
try:
|
|
expected_argv = render_worker_argv(profile, executable, worktree_path)
|
|
except ValueError:
|
|
errors.append(f"{where}.requested.argv: cannot render referenced profile")
|
|
else:
|
|
if requested.get("argv") != expected_argv:
|
|
errors.append(f"{where}.requested.argv: does not match exact renderer")
|
|
|
|
if isinstance(binding, dict) and isinstance(worktree, dict):
|
|
if binding.get("observedWorktreePath") != worktree.get("path"):
|
|
errors.append(f"{where}.binding.observedWorktreePath: does not match worktree.path")
|
|
|
|
if isinstance(created_for, dict) and isinstance(requested, dict):
|
|
try:
|
|
expected_environment_policy = environment_policy_for_role(
|
|
created_for.get("role")
|
|
)
|
|
except ValueError:
|
|
pass
|
|
else:
|
|
if requested.get("environmentPolicy") != expected_environment_policy:
|
|
errors.append(
|
|
f"{where}.requested.environmentPolicy: does not match role"
|
|
)
|
|
|
|
if (
|
|
isinstance(created_for, dict)
|
|
and isinstance(worktree, dict)
|
|
and isinstance(requested, dict)
|
|
):
|
|
try:
|
|
expected_fingerprint = canonical_sha256(
|
|
{
|
|
"protocolVersion": LAUNCH_PROTOCOL_VERSION,
|
|
"backend": "orca",
|
|
"profileId": receipt.get("profileId"),
|
|
"profileHash": receipt.get("profileHash"),
|
|
"createdFor": created_for,
|
|
"worktree": worktree,
|
|
"requested": requested,
|
|
"slot": slot,
|
|
}
|
|
)
|
|
except ValueError:
|
|
errors.append(f"{where}.launchFingerprint: cannot hash launch facts")
|
|
else:
|
|
if receipt.get("launchFingerprint") != expected_fingerprint:
|
|
errors.append(
|
|
f"{where}.launchFingerprint: does not match launch facts"
|
|
)
|
|
|
|
try:
|
|
expected_receipt_hash = receipt_hash(receipt)
|
|
except ValueError:
|
|
errors.append(f"{where}: must contain canonical JSON data")
|
|
else:
|
|
if receipt.get("receiptHash") != expected_receipt_hash:
|
|
errors.append(f"{where}.receiptHash: does not match receipt")
|
|
return errors
|
|
|
|
|
|
def validate_worker_receipts(
|
|
receipts: Any,
|
|
orchestration: dict[str, Any] | None = None,
|
|
*,
|
|
task_ids: set[str] | None = None,
|
|
where: str = "workerReceipts",
|
|
) -> list[str]:
|
|
"""Validate the top-level receipt list and reject duplicate identities."""
|
|
|
|
if not isinstance(receipts, list):
|
|
return [f"{where}: must be a list"]
|
|
errors: list[str] = []
|
|
seen_ids: set[str] = set()
|
|
seen_launch_ids: set[str] = set()
|
|
for index, receipt in enumerate(receipts):
|
|
receipt_where = f"{where}[{index}]"
|
|
errors.extend(
|
|
validate_worker_receipt(
|
|
receipt,
|
|
orchestration=orchestration,
|
|
task_ids=task_ids,
|
|
where=receipt_where,
|
|
)
|
|
)
|
|
if not isinstance(receipt, dict):
|
|
continue
|
|
receipt_id = receipt.get("id")
|
|
if isinstance(receipt_id, str):
|
|
if receipt_id in seen_ids:
|
|
errors.append(f"{receipt_where}.id: duplicate receipt ID {receipt_id!r}")
|
|
seen_ids.add(receipt_id)
|
|
launch_id = receipt.get("launchId")
|
|
if isinstance(launch_id, str):
|
|
if launch_id in seen_launch_ids:
|
|
errors.append(f"{receipt_where}.launchId: duplicate launch ID {launch_id!r}")
|
|
seen_launch_ids.add(launch_id)
|
|
return errors
|
|
|
|
|
|
def _validate_dispatch_links(
|
|
tasks: Any,
|
|
receipts: Any,
|
|
orchestration: Any,
|
|
) -> list[str]:
|
|
"""Cross-link role dispatch records to persisted audit receipts."""
|
|
|
|
if not isinstance(tasks, list):
|
|
return []
|
|
receipt_by_id: dict[str, dict[str, Any]] = {}
|
|
if isinstance(receipts, list):
|
|
for receipt in receipts:
|
|
if not isinstance(receipt, dict):
|
|
continue
|
|
receipt_id = receipt.get("id")
|
|
if isinstance(receipt_id, str) and receipt_id not in receipt_by_id:
|
|
receipt_by_id[receipt_id] = receipt
|
|
profiles = orchestration.get("profiles") if isinstance(orchestration, dict) else None
|
|
|
|
errors: list[str] = []
|
|
for task_index, task in enumerate(tasks):
|
|
if not isinstance(task, dict):
|
|
continue
|
|
ack_task_id = task.get("id")
|
|
dispatch = task.get("dispatch")
|
|
if not isinstance(dispatch, dict):
|
|
continue
|
|
for role in sorted(ROLES):
|
|
if role not in dispatch:
|
|
continue
|
|
role_dispatch = dispatch.get(role)
|
|
where = f"tasks[{task_index}].dispatch.{role}"
|
|
if not isinstance(role_dispatch, dict):
|
|
errors.append(f"{where}: must be an object")
|
|
continue
|
|
errors.extend(_unknown_fields(role_dispatch, ROLE_DISPATCH_FIELDS, where))
|
|
errors.extend(_missing_fields(role_dispatch, ROLE_DISPATCH_FIELDS, where))
|
|
|
|
profile_id = role_dispatch.get("profileId")
|
|
if profile_id is not None:
|
|
if not isinstance(profile_id, str) or PROFILE_ID_RE.fullmatch(profile_id) is None:
|
|
errors.append(f"{where}.profileId: must be null or a profile ID")
|
|
else:
|
|
profile = profiles.get(profile_id) if isinstance(profiles, dict) else None
|
|
if not isinstance(profile, dict):
|
|
errors.append(f"{where}.profileId: unknown profile {profile_id!r}")
|
|
elif profile.get("role") != role:
|
|
errors.append(f"{where}.profileId: profile role must be {role}")
|
|
|
|
runtime_values: list[Any] = []
|
|
for field in ("taskId", "dispatchId"):
|
|
runtime_value = role_dispatch.get(field)
|
|
runtime_values.append(runtime_value)
|
|
if runtime_value is not None and not _is_nonempty_text(
|
|
runtime_value,
|
|
max_length=512,
|
|
):
|
|
errors.append(
|
|
f"{where}.{field}: must be null or non-empty single-line text"
|
|
)
|
|
if (runtime_values[0] is None) != (runtime_values[1] is None):
|
|
errors.append(
|
|
f"{where}: taskId and dispatchId must both be null or both be set"
|
|
)
|
|
|
|
receipt_id = role_dispatch.get("receiptId")
|
|
attempt_id = role_dispatch.get("attemptId")
|
|
current_task_attempts = {
|
|
f"{ack_task_id}-A{round_number}" for round_number in range(1, 4)
|
|
}
|
|
if attempt_id is not None:
|
|
if (
|
|
not isinstance(attempt_id, str)
|
|
or ATTEMPT_ID_RE.fullmatch(attempt_id) is None
|
|
):
|
|
errors.append(
|
|
f"{where}.attemptId: must be null or use <task-id>-A1..A3"
|
|
)
|
|
elif (
|
|
isinstance(ack_task_id, str)
|
|
and attempt_id not in current_task_attempts
|
|
):
|
|
errors.append(
|
|
f"{where}.attemptId: must belong to current ACK task {ack_task_id!r}"
|
|
)
|
|
if receipt_id is None:
|
|
if attempt_id is not None:
|
|
errors.append(
|
|
f"{where}.attemptId: must be null when receiptId is null"
|
|
)
|
|
if any(value is not None for value in runtime_values):
|
|
errors.append(
|
|
f"{where}.receiptId: is required before runtime dispatch IDs"
|
|
)
|
|
continue
|
|
if attempt_id is None:
|
|
errors.append(
|
|
f"{where}.attemptId: is required when receiptId is set"
|
|
)
|
|
if not isinstance(receipt_id, str) or RECEIPT_ID_RE.fullmatch(receipt_id) is None:
|
|
errors.append(f"{where}.receiptId: must be null or a receipt ID")
|
|
continue
|
|
receipt = receipt_by_id.get(receipt_id)
|
|
if receipt is None:
|
|
errors.append(f"{where}.receiptId: unknown receipt {receipt_id!r}")
|
|
continue
|
|
if receipt.get("profileId") != profile_id:
|
|
errors.append(f"{where}.profileId: does not match referenced receipt")
|
|
created_for = receipt.get("createdFor")
|
|
receipt_task_id = (
|
|
created_for.get("taskId") if isinstance(created_for, dict) else None
|
|
)
|
|
receipt_role = created_for.get("role") if isinstance(created_for, dict) else None
|
|
receipt_attempt_id = (
|
|
created_for.get("attemptId") if isinstance(created_for, dict) else None
|
|
)
|
|
if receipt_task_id != ack_task_id:
|
|
errors.append(
|
|
f"{where}.receiptId: referenced receipt task must be current "
|
|
f"ACK task {ack_task_id!r}"
|
|
)
|
|
if receipt_role != role:
|
|
errors.append(f"{where}.receiptId: referenced receipt role must be {role}")
|
|
if receipt_attempt_id != attempt_id:
|
|
errors.append(
|
|
f"{where}.attemptId: does not match referenced receipt"
|
|
)
|
|
return errors
|
|
|
|
|
|
def validate_routing_document(data: Any) -> list[str]:
|
|
"""Validate routing and receipts embedded in an ACK tasks document."""
|
|
|
|
if not isinstance(data, dict):
|
|
return ["<root>: must be an object"]
|
|
project = data.get("project")
|
|
if not isinstance(project, dict):
|
|
return ["project: must be an object"]
|
|
if "orchestration" not in project:
|
|
return ["project.orchestration: is required"]
|
|
orchestration = project.get("orchestration")
|
|
errors = validate_orchestration(orchestration)
|
|
|
|
task_ids: set[str] = set()
|
|
tasks = data.get("tasks")
|
|
if isinstance(tasks, list):
|
|
for task in tasks:
|
|
if isinstance(task, dict) and isinstance(task.get("id"), str):
|
|
task_ids.add(task["id"])
|
|
|
|
receipts = data.get("workerReceipts")
|
|
if "workerReceipts" not in data:
|
|
errors.append("workerReceipts: is required")
|
|
else:
|
|
errors.extend(
|
|
validate_worker_receipts(
|
|
receipts,
|
|
orchestration if isinstance(orchestration, dict) else None,
|
|
task_ids=task_ids,
|
|
)
|
|
)
|
|
errors.extend(_validate_dispatch_links(tasks, receipts, orchestration))
|
|
if isinstance(orchestration, dict) and orchestration.get("mode") == "manual":
|
|
receipts = data.get("workerReceipts")
|
|
if isinstance(receipts, list) and receipts:
|
|
errors.append("workerReceipts: manual orchestration requires an empty list")
|
|
return errors
|
|
|
|
|
|
# Short aliases for callers that treat this module as a builtin validator.
|
|
validate_routing = validate_orchestration
|
|
validate_receipts = validate_worker_receipts
|
|
validate_builtin = validate_routing_document
|
|
|
|
|
|
__all__ = [
|
|
"PROFILE_VERSION",
|
|
"RECEIPT_VERSION",
|
|
"LAUNCH_PROTOCOL_VERSION",
|
|
"MAX_ROUNDS",
|
|
"BASE_ENVIRONMENT_POLICY",
|
|
"OPERATOR_ENVIRONMENT_POLICY",
|
|
"canonical_sha256",
|
|
"environment_policy_for_role",
|
|
"profile_hash",
|
|
"receipt_hash",
|
|
"render_worker_argv",
|
|
"validate_profile",
|
|
"validate_orchestration",
|
|
"validate_worker_receipt",
|
|
"validate_worker_receipts",
|
|
"validate_routing_document",
|
|
"validate_routing",
|
|
"validate_receipts",
|
|
"validate_builtin",
|
|
]
|