autonomy-unit-tests Mission source convergence checks passed at exact head
autonomy-smoke-tests Mission source convergence checks passed at exact head
git-diff-check Mission source convergence checks passed at exact head
py-compile Mission source convergence checks passed at exact head
reconciliation-contract Mission source convergence checks passed at exact head
release-state-machine Mission source convergence checks passed at exact head
secret-scan Mission source convergence checks passed at exact head
signer-validator-contract Mission source convergence checks passed at exact head
owner-approval-gate Mission source convergence checks passed at exact head
campaign-approval-gate Mission source convergence checks passed at exact head
82 lines
3.7 KiB
Python
82 lines
3.7 KiB
Python
"""Canonical semantic branch-protection model used by AIOS governance gates."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from typing import Any, Mapping
|
|
|
|
|
|
SCHEMA = "aios.semantic-protection.v1"
|
|
USER_IDS = {"owner": 2, "aios-autonomy-agent": 153}
|
|
|
|
|
|
def canonical_json(value: Any) -> bytes:
|
|
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
|
|
|
|
|
def _typed_json(value: Any) -> list[Any]:
|
|
if value in (None, "null"):
|
|
return []
|
|
if isinstance(value, str):
|
|
return json.loads(value)
|
|
return list(value)
|
|
|
|
|
|
def _user_ids(names: Any) -> list[int]:
|
|
result = []
|
|
for name in names or []:
|
|
if name not in USER_IDS:
|
|
raise ValueError("UNKNOWN_GOVERNANCE_USER:" + str(name))
|
|
result.append(USER_IDS[name])
|
|
return sorted(result)
|
|
|
|
|
|
def semantic_policy(protection: Mapping[str, Any]) -> dict[str, Any]:
|
|
"""Drop transport metadata and canonicalize semantic security fields."""
|
|
return {
|
|
"schema_version": SCHEMA,
|
|
"repo_id": 1,
|
|
"protection_id": 2,
|
|
"branch_name": protection.get("branch_name"),
|
|
"can_push": protection.get("enable_push"),
|
|
"can_force_push": protection.get("enable_force_push"),
|
|
"enable_merge_whitelist": protection.get("enable_merge_whitelist"),
|
|
"merge_whitelist_user_ids": _user_ids(protection.get("merge_whitelist_usernames")),
|
|
"merge_whitelist_team_ids": [],
|
|
"required_approvals": protection.get("required_approvals"),
|
|
"enable_status_check": protection.get("enable_status_check"),
|
|
"status_check_contexts": sorted(protection.get("status_check_contexts") or []),
|
|
"block_admin_merge_override": protection.get("block_admin_merge_override"),
|
|
"block_on_official_review_requests": protection.get("block_on_official_review_requests"),
|
|
"block_on_outdated_branch": protection.get("block_on_outdated_branch"),
|
|
"block_on_rejected_reviews": protection.get("block_on_rejected_reviews"),
|
|
"dismiss_stale_approvals": protection.get("dismiss_stale_approvals"),
|
|
"ignore_stale_approvals": protection.get("ignore_stale_approvals"),
|
|
"enable_approvals_whitelist": protection.get("enable_approvals_whitelist"),
|
|
"approvals_whitelist_user_ids": _user_ids(protection.get("approvals_whitelist_username")),
|
|
"approvals_whitelist_team_ids": [],
|
|
"enable_bypass_allowlist": protection.get("enable_bypass_allowlist"),
|
|
"bypass_allowlist_user_ids": _user_ids(protection.get("bypass_allowlist_usernames")),
|
|
"bypass_allowlist_team_ids": [],
|
|
"enable_force_push_allowlist": protection.get("enable_force_push_allowlist"),
|
|
"force_push_allowlist_user_ids": _user_ids(protection.get("force_push_allowlist_usernames")),
|
|
"enable_whitelist": protection.get("enable_push_whitelist"),
|
|
"whitelist_user_ids": _user_ids(protection.get("push_whitelist_usernames")),
|
|
"whitelist_team_ids": [],
|
|
"force_push_allowlist_deploy_keys": protection.get("force_push_allowlist_deploy_keys"),
|
|
"whitelist_deploy_keys": protection.get("push_whitelist_deploy_keys"),
|
|
"require_signed_commits": protection.get("require_signed_commits"),
|
|
"priority": protection.get("priority"),
|
|
"protected_file_patterns": _typed_json(protection.get("protected_file_patterns")),
|
|
"unprotected_file_patterns": _typed_json(protection.get("unprotected_file_patterns")),
|
|
}
|
|
|
|
|
|
def semantic_digest(protection: Mapping[str, Any]) -> str:
|
|
return hashlib.sha256(canonical_json(semantic_policy(protection))).hexdigest()
|
|
|
|
|
|
def transport_digest(protection: Mapping[str, Any]) -> str:
|
|
return hashlib.sha256(canonical_json(protection)).hexdigest()
|