///|
pub(all) struct AutonomyBudget {
model_tokens : Int
tool_calls : Int
storage_bytes : Int
attempts : Int
concurrency : Int
} derive(Debug, Eq, ToJson)
///|
pub fn AutonomyBudget::quality_issues(self : AutonomyBudget) -> Array[String] {
let issues : Array[String] = []
for
pair in [
("model_tokens", self.model_tokens),
("tool_calls", self.tool_calls),
("storage_bytes", self.storage_bytes),
("attempts", self.attempts),
("concurrency", self.concurrency),
] {
let (field, value) = pair
if value < 1 {
issues.push("budget \{field} must be positive")
}
}
issues
}
///|
pub(all) struct AutonomyBudgetUsage {
model_tokens : Int
tool_calls : Int
storage_bytes : Int
attempts : Int
concurrency : Int
} derive(Debug, Eq, ToJson)
///|
pub fn AutonomyBudgetUsage::quality_issues(
self : AutonomyBudgetUsage,
) -> Array[String] {
let issues : Array[String] = []
for
pair in [
("model_tokens", self.model_tokens),
("tool_calls", self.tool_calls),
("storage_bytes", self.storage_bytes),
("attempts", self.attempts),
("concurrency", self.concurrency),
] {
let (field, value) = pair
if value < 0 {
issues.push("usage \{field} cannot be negative")
}
}
issues
}
///|
pub(all) struct AutonomyGrant {
product_id : String
operations : Array[String]
authority_ceiling : AuthorityClass
claim_ceiling : ClaimClass
artifact_prefixes : Array[String]
} derive(Debug, Eq, ToJson)
///|
pub fn AutonomyGrant::quality_issues(self : AutonomyGrant) -> Array[String] {
let issues : Array[String] = []
if self.product_id.trim().is_empty() {
issues.push("grant product_id is required")
}
if self.operations.is_empty() {
issues.push("grant operations are required")
}
if self.artifact_prefixes.is_empty() {
issues.push("grant artifact prefixes are required")
}
if self.authority_ceiling.requires_explicit_human() {
issues.push("unattended grants cannot include external or physical effects")
}
if self.claim_ceiling.allows(PhysicalReadiness) {
issues.push("unattended grants cannot reach physical readiness")
}
for prefix in self.artifact_prefixes {
if !autonomy_artifact_ref_is_safe(prefix) {
issues.push("grant artifact prefix is invalid: \{prefix}")
}
}
issues
}
///|
pub(all) struct AutonomyEnvelope {
contract_id : String
envelope_id : String
mode : String
goal_ref : String
workspace_root : String
grants : Array[AutonomyGrant]
budget : AutonomyBudget
external_effects_allowed : Bool
physical_effects_allowed : Bool
granted_by : String
source_digest : String
recorded_at : String
expires_at : String
revoked : Bool
} derive(Debug, Eq, ToJson)
///|
pub fn AutonomyEnvelope::quality_issues(
self : AutonomyEnvelope,
) -> Array[String] {
let issues : Array[String] = []
if self.contract_id != "moongate.autonomy-envelope.v1" {
issues.push("unsupported autonomy envelope contract")
}
for
pair in [
("envelope_id", self.envelope_id),
("goal_ref", self.goal_ref),
("workspace_root", self.workspace_root),
("granted_by", self.granted_by),
] {
let (field, value) = pair
if value.trim().is_empty() {
issues.push("\{field} is required")
}
}
if self.mode != "unattended-digital" {
issues.push("only unattended-digital mode is supported")
}
if !autonomy_artifact_ref_is_safe(self.goal_ref) {
issues.push("goal_ref must be workspace-relative")
}
if self.workspace_root.trim().is_empty() ||
!(self.workspace_root.has_prefix("/") ||
(self.workspace_root.length() > 2 && self.workspace_root[1] == ':')) {
issues.push("workspace_root must be explicit and absolute")
}
if self.grants.is_empty() {
issues.push("at least one autonomy grant is required")
}
let products : Array[String] = []
for grant in self.grants {
if products.contains(grant.product_id) {
issues.push("duplicate autonomy grant: \{grant.product_id}")
} else {
products.push(grant.product_id)
}
for issue in grant.quality_issues() {
issues.push("grant \{grant.product_id}: \{issue}")
}
}
for issue in self.budget.quality_issues() {
issues.push(issue)
}
if self.external_effects_allowed {
issues.push("unattended-digital mode cannot allow external effects")
}
if self.physical_effects_allowed {
issues.push("unattended-digital mode cannot allow physical effects")
}
if !autonomy_digest_is_valid(self.source_digest) {
issues.push("source_digest must be a full sha256 digest")
}
if !autonomy_timestamp_is_canonical(self.recorded_at) {
issues.push("recorded_at must be canonical UTC")
}
if !autonomy_timestamp_is_canonical(self.expires_at) {
issues.push("expires_at must be canonical UTC")
}
if self.expires_at <= self.recorded_at {
issues.push("expires_at must be after recorded_at")
}
issues
}
///|
pub(all) struct AutonomyEventRequest {
contract_id : String
event_id : String
run_id : String
product_id : String
operation : String
requested_authority : AuthorityClass
claim : ClaimClass
artifact_paths : Array[String]
external_destination : String
budget_request : AutonomyBudgetUsage
recorded_at : String
} derive(Debug, Eq, ToJson)
///|
pub(all) struct AutonomyBudgetBalance {
model_tokens : Int
tool_calls : Int
storage_bytes : Int
attempts : Int
concurrency : Int
} derive(Debug, Eq, ToJson)
///|
pub(all) struct AutonomyDecision {
contract_id : String
decision_id : String
envelope_id : String
event_id : String
accepted : Bool
findings : Array[String]
remaining_budget : AutonomyBudgetBalance
checked_at : String
} derive(Debug, Eq, ToJson)