{
  "$id": "https://uape.dev/schemas/autopoietic-trigger-1.0.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Autopoietic Trigger Policy",
  "description": "Phase 5 surface: declares when the autopoietic agent may propose manifest mutations. Ignored before Phase 5.",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "enabled": { "type": "boolean", "default": false },
    "triggers": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "signal", "action"],
        "properties": {
          "name": { "type": "string", "pattern": "^[a-z][a-z0-9_-]*$" },
          "signal": {
            "type": "object",
            "additionalProperties": false,
            "required": ["kind"],
            "properties": {
              "kind": {
                "type": "string",
                "enum": ["audit_event_rate", "error_rate", "manual_request", "schedule"]
              },
              "event_name": { "type": "string" },
              "threshold": { "type": "number" },
              "window_seconds": { "type": "integer", "minimum": 1 },
              "cron": { "type": "string", "maxLength": 128 }
            }
          },
          "action": {
            "type": "string",
            "enum": ["propose_migration", "propose_capability_change", "notify_operator"]
          },
          "requires_operator_signoff": { "type": "boolean", "default": true },
          "cooldown_seconds": {
            "type": "integer",
            "minimum": 300,
            "default": 3600,
            "description": "Minimum delay between successive firings of this trigger. Enforces a hard cool-down to prevent runaway LLM generation loops. Floor of 300s is mandatory."
          },
          "target_scope": {
            "type": "string",
            "format": "uuid",
            "description": "Optional storage scope (UUIDv5) the proposed mutation is constrained to. When omitted, the trigger targets the owning module's own scope."
          }
        }
      }
    },
    "sandbox": {
      "type": "object",
      "additionalProperties": false,
      "description": "Bounds for the Sandbox Replay dry-run that validates a candidate manifest before it is presented to the operator for signing.",
      "properties": {
        "max_replay_mutations": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000000,
          "default": 100000,
          "description": "Upper bound on the number of historical Layer-0 audit mutations replayed against the candidate topology. Caps replay depth to bound CPU/time."
        },
        "timeout_seconds": {
          "type": "integer",
          "minimum": 1,
          "maximum": 3600,
          "default": 120,
          "description": "Wall-clock deadline for a single dry-run replay. A replay exceeding this is aborted and the proposal is rejected (fail-closed)."
        },
        "determinism_check": {
          "type": "boolean",
          "default": true,
          "description": "When true, the replay is executed twice with a fixed injected clock and the resulting state digests must match; any divergence fails the proposal."
        }
      }
    },
    "llm_profile": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "engine": { "type": "string", "enum": ["llama-cpp", "candle"], "default": "llama-cpp" },
        "model_id": { "type": "string", "maxLength": 256 },
        "max_tokens": { "type": "integer", "minimum": 1, "maximum": 32768 },
        "temperature": { "type": "number", "minimum": 0, "maximum": 2 },
        "acceleration_policy": {
          "type": "string",
          "enum": ["cpu", "gpu", "gpu-fallback"],
          "default": "cpu",
          "description": "Hardware execution policy. 'cpu' forces pure-CPU inference (default, guaranteed on the 2 vCPU / 4 GiB pilot host); 'gpu' requires a GPU and fails if absent; 'gpu-fallback' attempts GPU and silently degrades to CPU."
        },
        "max_model_weight_mib": {
          "type": "integer",
          "minimum": 1,
          "maximum": 4096,
          "description": "Hard cap on resident model-weight allocation (MiB). The executor refuses to load a model whose weights exceed this budget, protecting the kernel RSS ceiling on memory-constrained hosts."
        },
        "max_kv_cache_mib": {
          "type": "integer",
          "minimum": 1,
          "maximum": 2048,
          "description": "Hard cap on the active KV-cache allocation (MiB) during inference."
        },
        "lifecycle": {
          "type": "string",
          "enum": ["resident", "on_demand"],
          "default": "on_demand",
          "description": "'resident' keeps model weights pinned in RAM (low latency, high steady-state RSS); 'on_demand' loads weights per proposal and offloads afterwards (memory-safe, higher first-token latency). On the pilot host 'on_demand' is mandatory."
        }
      }
    }
  }
}
