feat(ack): add structured worker model routing
This commit is contained in:
@@ -0,0 +1,992 @@
|
||||
#!/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"})
|
||||
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"})
|
||||
DEFAULT_KEYS = frozenset({"developer", "test", "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 _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")
|
||||
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 role == "test" and tier != "standard":
|
||||
errors.append(f"{where}.tier: Test 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 == "test" and "strong" in tiers:
|
||||
errors.append(f"{role_where}: Test 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(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")
|
||||
|
||||
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")
|
||||
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") != "per-cli-allowlist-v1":
|
||||
errors.append(
|
||||
f"{where}.environmentPolicy: must be 'per-cli-allowlist-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(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",
|
||||
"canonical_sha256",
|
||||
"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",
|
||||
]
|
||||
Reference in New Issue
Block a user