{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "v": {
      "type": "number",
      "const": 1,
      "description": "Protocol major version."
    },
    "type": {
      "type": "string",
      "const": "policy.updated"
    },
    "id": {
      "type": "string",
      "pattern": "^evt_[0-7][0-9A-HJKMNP-TV-Z]{25}$",
      "description": "The event's own id. Stable across a retry of the same event."
    },
    "ts": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
      "description": "When it happened, by Chips' clock. Stamped on the way in; a value the sender writes is replaced."
    },
    "workspace_id": {
      "type": "string",
      "pattern": "^ws_[0-7][0-9A-HJKMNP-TV-Z]{25}$",
      "description": "A workspace identifier: `ws_` followed by a ULID."
    },
    "space_id": {
      "description": "The space, when the event belongs to one.",
      "type": "string",
      "pattern": "^spc_[0-7][0-9A-HJKMNP-TV-Z]{25}$"
    },
    "run_id": {
      "description": "The run, when the event belongs to one.",
      "type": "string",
      "pattern": "^run_[0-7][0-9A-HJKMNP-TV-Z]{25}$"
    },
    "seq": {
      "description": "Assigned by the server on append, monotonic per workspace. Absent on the way in.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "idempotency_key": {
      "description": "Required on every client-originated write. Sending the same key twice is a no-op.",
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "actor": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "person",
            "harness",
            "agent",
            "system"
          ],
          "description": "Whether a person, a harness, one of a harness's agents or Chips itself acted."
        },
        "id": {
          "type": "string",
          "minLength": 1,
          "description": "The participant id, or `system` for Chips."
        },
        "harness_id": {
          "description": "For an agent: the harness it speaks through, whose budget and runs are the agent's (ADR 0087). Filled in by Chips.",
          "type": "string",
          "pattern": "^hrn_[0-7][0-9A-HJKMNP-TV-Z]{25}$"
        }
      },
      "required": [
        "kind",
        "id"
      ],
      "description": "The participant an event is attributed to."
    },
    "harness_id": {
      "type": "string",
      "pattern": "^hrn_[0-7][0-9A-HJKMNP-TV-Z]{25}$",
      "description": "A harness identifier: `hrn_` followed by a ULID."
    },
    "policy": {
      "type": "object",
      "properties": {
        "autonomy": {
          "anyOf": [
            {
              "type": "number",
              "const": 0
            },
            {
              "type": "number",
              "const": 1
            },
            {
              "type": "number",
              "const": 2
            },
            {
              "type": "number",
              "const": 3
            }
          ],
          "description": "0 observe, 1 ask first, 2 ask when it matters, 3 autonomous and report back. Presets that expand to rules."
        },
        "rules": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "match": {
                "type": "string",
                "minLength": 1,
                "maxLength": 120,
                "description": "A glob over action kinds, for example `payment.*`."
              },
              "when": {
                "description": "Extra conditions on the action's flags, and for a command, how its parts start.",
                "type": "object",
                "properties": {
                  "external": {
                    "type": "boolean"
                  },
                  "irreversible": {
                    "type": "boolean"
                  },
                  "money_usd_over": {
                    "type": "number",
                    "minimum": 0
                  },
                  "risk": {
                    "maxItems": 8,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "external",
                        "external_communication",
                        "irreversible",
                        "destructive",
                        "financial",
                        "credential",
                        "privacy",
                        "legal"
                      ],
                      "description": "A risk class the policy engine can match a rule against."
                    }
                  },
                  "starts_with": {
                    "description": "For an action that carries a command (ADR 0239): the words a part of it starts with. A command goes ahead on such rules only when every part starts with one that allows it.",
                    "minItems": 1,
                    "maxItems": 8,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 400
                    }
                  }
                }
              },
              "decision": {
                "type": "string",
                "enum": [
                  "allow",
                  "ask",
                  "deny"
                ],
                "description": "What to do when the rule matches."
              },
              "approvers": {
                "description": "Who may answer, when the decision is `ask`.",
                "maxItems": 16,
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^(owner|any|lead|role:[a-z][a-z0-9_]*|usr_[0-9A-HJKMNP-TV-Z]{26}|hrn_[0-9A-HJKMNP-TV-Z]{26})$",
                  "description": "`owner`, `any`, `lead`, `role:<name>`, or a specific person id; a lead harness's id once resolved."
                }
              },
              "reason": {
                "description": "Shown to the harness, and recorded in the audit log.",
                "type": "string",
                "maxLength": 400
              },
              "off": {
                "description": "Turned off by a person (ADR 0195): kept in the list to turn back on, and skipped by the policy engine until then.",
                "type": "boolean"
              },
              "made_by": {
                "description": "Who made it by answering an ask with Always (ADR 0195). Filled in by Chips.",
                "type": "string",
                "minLength": 1,
                "maxLength": 120
              },
              "made_at": {
                "description": "When a person's answer made it (ADR 0195): Always, or This session. Filled in by Chips. A rule a person's answer made never allows an action that matters.",
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              }
            },
            "required": [
              "match",
              "decision"
            ],
            "description": "A rule the policy engine evaluates."
          },
          "description": "Evaluated in order; the workspace cap always wins."
        }
      },
      "required": [
        "autonomy",
        "rules"
      ],
      "description": "A harness's autonomy level and the rules that fill in the details."
    },
    "workspace_max_autonomy": {
      "anyOf": [
        {
          "type": "number",
          "const": 0
        },
        {
          "type": "number",
          "const": 1
        },
        {
          "type": "number",
          "const": 2
        },
        {
          "type": "number",
          "const": 3
        }
      ],
      "description": "The workspace cap. It always wins."
    },
    "budget_usd": {
      "description": "The harness's spending ceiling, in US dollars. Absent leaves it as it was; the harness's spend is what `usage` has added up.",
      "type": "number",
      "minimum": 0
    },
    "changed_by": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120,
      "description": "Who changed it, or `system` for an earned promotion. Filled in by Chips from the credential that sent it."
    },
    "reason": {
      "description": "Why it changed.",
      "type": "string",
      "maxLength": 1000
    }
  },
  "required": [
    "v",
    "type",
    "id",
    "ts",
    "workspace_id",
    "actor",
    "harness_id",
    "policy",
    "workspace_max_autonomy",
    "changed_by"
  ],
  "description": "The autonomy level or the rules changed. Takes effect on the next action, not retroactively.",
  "$id": "https://chipschat.ai/schema/v1/policy.updated.json",
  "title": "PolicyUpdated"
}
