AID-M-009.001: Operating Scope & Functional Autonomy Profiles
Key question: what may this agent do on its own? This gives downstream policy, monitoring, and approvals a clear contract to enforce.
Example: a customer-support agent may Observe and Analyze autonomously, but Execute refunds and Persist long-term customer memory still remain supervised.
Pillars: app, infra
Lifecycle: scoping, operation
Scope boundary
Owns the approved operating scope and per-function autonomy profile across Observe, Analyze, Decide, Execute, Delegate, and Persist. It answers which functions the agent may perform independently or only under supervision.
AID-M-009.002: AID-M-009.001 classifies the degree of autonomy allowed for each functional stage; AID-M-009.002 defines hard limits on concrete tools, data, environments, effects, budgets, and delegation depth. An agent may be autonomous for a function yet still be prohibited from particular actions inside that function.
AID-M-009.006: AID-M-009.001 defines the functional autonomy profile; AID-M-009.006 maps the resulting capability and blast-radius tier to mandatory defensive control evidence. Autonomy classification is an input to assurance requirements, not proof that those controls are deployed and effective.
Implementation guidance
- AID-M-009.001-G001: Classify each agent deployment by operating scope and define a per-function autonomy profile.
- AID-M-009.001-G002: Validate autonomy profiles in CI and emit an approval evidence record before deployment.
AID-M-009.001-G001
Concept:
Start governance by classifying what kind of agent you are deploying and how much autonomy it gets for each function type. This is a design-time control owned by the product, security, and platform teams before runtime policy is written.
Step 1: Assign an operating scope
Classify each deployment as No Agency, Prescribed Agency, Supervised Agency, or Full Agency. Scope selection determines the default oversight posture and whether autonomous execution is allowed at all.
Step 2: Define per-function autonomy
# File: profiles/tier2_support_agent.yaml
agent:
id: support-agent-prod-001
operating_scope: prescribed
autonomy_profile:
observe: autonomous
analyze: autonomous
decide: supervised
execute: supervised
delegate: forbidden
persist: supervisedAction: Store this profile in version control and require design review approval whenever an agent's operating scope or function-level autonomy changes.
AID-M-009.001-G002
Concept
Validate the complete autonomy profile against a signed governance policy and a signed approval receipt. The validator must reject duplicate keys, unknown fields or functions, an unsupported operating scope, and an approval that is expired or bound to different profile bytes.
Validate the closed schemas and approval binding
The approval service is an external dependency: it authenticates reviewers and authorizes approver IDs. Its adapter emits a signed receipt containing the exact profile and policy digests; this validator does not accept approver IDs from the profile itself.
# File: governance/validate_autonomy_profile.py
from __future__ import annotations
import hashlib
import json
import os
from datetime import datetime, timezone
from pathlib import Path
import yaml
class StrictSafeLoader(yaml.SafeLoader):
pass
def unique_mapping(loader, node, deep=False):
loader.flatten_mapping(node)
result = {}
for key_node, value_node in node.value:
key = loader.construct_object(key_node, deep=deep)
if key in result:
raise ValueError(f"duplicate YAML key: {key}")
result[key] = loader.construct_object(value_node, deep=deep)
return result
StrictSafeLoader.add_constructor(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
unique_mapping,
)
def unique_object(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError(f"duplicate JSON key: {key}")
result[key] = value
return result
def regular_bytes(path: Path) -> bytes:
if path.is_symlink() or not path.is_file():
raise ValueError(f"governance input is not a regular file: {path}")
raw = path.read_bytes()
if not raw:
raise ValueError(f"governance input is empty: {path}")
return raw
def sha256(raw: bytes) -> str:
return hashlib.sha256(raw).hexdigest()
profile_path = Path(os.environ["AUTONOMY_PROFILE"])
policy_path = Path(os.environ["AUTONOMY_PROFILE_POLICY"])
approval_path = Path(os.environ["AUTONOMY_PROFILE_APPROVAL"])
profile_raw = regular_bytes(profile_path)
policy_raw = regular_bytes(policy_path)
approval_raw = regular_bytes(approval_path)
profile_doc = yaml.load(profile_raw.decode("utf-8", errors="strict"), Loader=StrictSafeLoader)
policy = json.loads(policy_raw.decode("utf-8", errors="strict"), object_pairs_hook=unique_object)
approval = json.loads(approval_raw.decode("utf-8", errors="strict"), object_pairs_hook=unique_object)
if not isinstance(profile_doc, dict) or set(profile_doc) != {"agent"}:
raise ValueError("profile document schema differs")
agent = profile_doc["agent"]
if not isinstance(agent, dict) or set(agent) != {"id", "operating_scope", "autonomy_profile"}:
raise ValueError("agent profile fields differ")
if not isinstance(agent["id"], str) or not agent["id"]:
raise ValueError("agent ID is invalid")
policy_keys = {
"schema_version", "policy_version", "allowed_operating_scopes",
"required_functions", "allowed_modes",
}
if not isinstance(policy, dict) or set(policy) != policy_keys:
raise ValueError("autonomy policy fields differ")
if policy["schema_version"] != "aidefend.autonomy-profile-policy.v1":
raise ValueError("autonomy policy schema differs")
for field in ("allowed_operating_scopes", "required_functions", "allowed_modes"):
values = policy[field]
if (
not isinstance(values, list)
or not values
or any(not isinstance(value, str) or not value for value in values)
or len(values) != len(set(values))
):
raise ValueError(f"autonomy policy population is invalid: {field}")
if agent["operating_scope"] not in policy["allowed_operating_scopes"]:
raise ValueError("operating scope is not allowed")
profile = agent["autonomy_profile"]
if not isinstance(profile, dict) or set(profile) != set(policy["required_functions"]):
raise ValueError("autonomy function population differs")
invalid_modes = {
name: mode for name, mode in profile.items()
if mode not in policy["allowed_modes"]
}
if invalid_modes:
raise ValueError(f"unsupported autonomy modes: {invalid_modes}")
approval_keys = {
"schema_version", "approval_id", "decision", "profile_sha256",
"policy_sha256", "approver_ids", "issued_at", "expires_at",
}
if not isinstance(approval, dict) or set(approval) != approval_keys:
raise ValueError("approval receipt fields differ")
if (
approval["schema_version"] != "aidefend.autonomy-profile-approval.v1"
or approval["decision"] != "APPROVED"
or approval["profile_sha256"] != sha256(profile_raw)
or approval["policy_sha256"] != sha256(policy_raw)
or not isinstance(approval["approval_id"], str)
or not approval["approval_id"]
):
raise ValueError("approval decision or digest binding differs")
approvers = approval["approver_ids"]
if (
not isinstance(approvers, list)
or not approvers
or any(not isinstance(item, str) or not item for item in approvers)
or len(approvers) != len(set(approvers))
):
raise ValueError("approval identity population is invalid")
issued = datetime.fromisoformat(approval["issued_at"].replace("Z", "+00:00"))
expires = datetime.fromisoformat(approval["expires_at"].replace("Z", "+00:00"))
now = datetime.now(timezone.utc)
if issued.tzinfo is None or expires.tzinfo is None or issued > now or expires <= now or expires <= issued:
raise ValueError("approval validity window is invalid")
result = {
"schema_version": "aidefend.autonomy-profile-validation.v1",
"agent_id": agent["id"],
"operating_scope": agent["operating_scope"],
"profile_sha256": sha256(profile_raw),
"policy_version": policy["policy_version"],
"policy_sha256": sha256(policy_raw),
"approval_id": approval["approval_id"],
"approver_ids": approvers,
"approval_receipt_sha256": sha256(approval_raw),
"function_count": len(profile),
"validation_status": "PASS",
}
output = Path("evidence/autonomy-profile-validation.json")
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8")
if json.loads(output.read_text(encoding="utf-8")) != result:
raise RuntimeError("autonomy validation readback differs")
Verify signed inputs before running the validator
set -euo pipefail
umask 077
root="$(mktemp -d)"
trap 'rm -rf -- "$root"' EXIT
install -m 0400 -- "$AUTONOMY_PROFILE" "$root/profile.yaml"
install -m 0400 -- "$AUTONOMY_PROFILE_POLICY" "$root/policy.json"
install -m 0400 -- "$AUTONOMY_PROFILE_POLICY_BUNDLE" "$root/policy.sigstore.json"
install -m 0400 -- "$AUTONOMY_PROFILE_APPROVAL" "$root/approval.json"
install -m 0400 -- "$AUTONOMY_PROFILE_APPROVAL_BUNDLE" "$root/approval.sigstore.json"
cosign verify-blob --key "$AUTONOMY_POLICY_VERIFY_KEY" --bundle "$root/policy.sigstore.json" "$root/policy.json" >/dev/null
cosign verify-blob --key "$AUTONOMY_APPROVAL_VERIFY_KEY" --bundle "$root/approval.sigstore.json" "$root/approval.json" >/dev/null
export AUTONOMY_PROFILE="$root/profile.yaml"
export AUTONOMY_PROFILE_POLICY="$root/policy.json"
export AUTONOMY_PROFILE_APPROVAL="$root/approval.json"
python governance/validate_autonomy_profile.py
Action: Block deployment unless the validator emits PASS for the exact profile, policy, and approval receipt. Duplicate YAML keys, extra functions, omitted functions, unsupported modes or scope, stale approval, or a digest mismatch must never be downgraded to a warning.
Open-source tools
- Open Policy Agent (OPA)
- Cedar
- Cerbos PDP
Commercial / hosted tools
- OneTrust AI Governance
Threat mappings
MITRE ATLAS
- N/A
MAESTRO
- Inaccurate Agent Capability Description (L7)
OWASP LLM Top 10 2026
- N/A
OWASP ML Top 10 2023
- N/A
OWASP Top 10 for Agentic Applications 2026
- N/A
NIST Adversarial Machine Learning 2025
- N/A
Cisco Integrated AI Security and Safety Framework
- AISubtech-5.2.1 Agent Profile Tampering (Rejects deployment of an autonomy profile lacking approval for its exact bytes)
- AITech-5.2 Configuration Persistence (Rejects deployment of an autonomy profile lacking approval for its exact bytes)
Google Secure AI Framework 2.0 - Risks
- N/A
Databricks AI Security Framework 3.0
- N/A