Skip to content

policy.updated

The autonomy level or the rules changed. Takes effect on the next action, not retroactively.

Direction: Chips → Harness
Sent by: a person
Requires a run: no
Schema: policy.updated.json

Every event also carries the envelope.

Field Required Type Description
v yes 1 Protocol major version.
type yes "policy.updated"
id yes string The event’s own id. Stable across a retry of the same event.
ts yes string When it happened, by Chips’ clock. Stamped on the way in; a value the sender writes is replaced.
workspace_id yes string A workspace identifier: ws_ followed by a ULID.
space_id no string The space, when the event belongs to one.
run_id no string The run, when the event belongs to one.
seq no number Assigned by the server on append, monotonic per workspace. Absent on the way in.
idempotency_key no string Required on every client-originated write. Sending the same key twice is a no-op.
actor yes object The participant an event is attributed to.
actor.kind yes "person" | "harness" | "agent" | "system" Whether a person, a harness, one of a harness’s agents or Chips itself acted.
actor.id yes string The participant id, or system for Chips.
actor.harness_id no string For an agent: the harness it speaks through, whose budget and runs are the agent’s (ADR 0087). Filled in by Chips.
harness_id yes string A harness identifier: hrn_ followed by a ULID.
policy yes object A harness’s autonomy level and the rules that fill in the details.
policy.autonomy yes 0 | 1 | 2 | 3 0 observe, 1 ask first, 2 ask when it matters, 3 autonomous and report back. Presets that expand to rules.
policy.rules yes object[] Evaluated in order; the workspace cap always wins.
policy.rules[].match yes string A glob over action kinds, for example payment.*.
policy.rules[].when no object Extra conditions on the action’s flags, and for a command, how its parts start.
policy.rules[].when.external no boolean
policy.rules[].when.irreversible no boolean
policy.rules[].when.money_usd_over no number
policy.rules[].when.risk no enum[] One of external, external_communication, irreversible, destructive, financial, credential, privacy, legal.
policy.rules[].when.starts_with no string[] 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.
policy.rules[].decision yes "allow" | "ask" | "deny" What to do when the rule matches.
policy.rules[].approvers no string[] Who may answer, when the decision is ask.
policy.rules[].reason no string Shown to the harness, and recorded in the audit log.
policy.rules[].off no boolean Turned off by a person (ADR 0195): kept in the list to turn back on, and skipped by the policy engine until then.
policy.rules[].made_by no string Who made it by answering an ask with Always (ADR 0195). Filled in by Chips.
policy.rules[].made_at no string 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.
workspace_max_autonomy yes 0 | 1 | 2 | 3 The workspace cap. It always wins.
budget_usd no number The harness’s spending ceiling, in US dollars. Absent leaves it as it was; the harness’s spend is what usage has added up.
changed_by yes string Who changed it, or system for an earned promotion. Filled in by Chips from the credential that sent it.
reason no string Why it changed.
{
"v": 1,
"type": "policy.updated",
"id": "evt_01K4ZQPX00000000000000007C",
"ts": "2026-09-02T14:22:24.004Z",
"workspace_id": "ws_01K4ZQPX000000000000000001",
"space_id": "spc_01K4ZQPX000000000000000002",
"actor": {
"kind": "system",
"id": "system"
},
"harness_id": "hrn_01K4ZQPX000000000000000004",
"policy": {
"autonomy": 2,
"rules": [
{
"match": "email.send",
"when": {
"external": true
},
"decision": "ask",
"approvers": ["owner"]
}
]
},
"workspace_max_autonomy": 2,
"budget_usd": 25,
"changed_by": "system",
"reason": "Six approvals in a row with no edits."
}