policy.yaml
File location
Section titled “File location”.wayglass/policy.yaml at the root of the repository. wayglass init writes the example below if the file does not exist. The Worker reads the copy on the trunk, at the commit each intent forked from; see Policy.
The file is parsed strictly. Unknown keys are errors, so a typo such as whne: cannot turn a rule into a catch-all. wayglass propose and wayglass policy test both fail with policy_invalid on a file that does not validate.
Top level
Section titled “Top level”| Key | Required | Meaning |
|---|---|---|
version |
yes | Must be 1 |
classify |
no | How changes are tagged: area, data_class, risk |
rules |
no | Ordered list of rules; the first enforcing match decides |
default |
no | Used when no enforcing rule matches. Defaults to human_approve with the reason “No rule matched; a person decides.” |
classify
Section titled “classify”classify: area: <name>: [<glob>, ...] data_class: <name>: { content: [<regex>, ...], paths: [<glob>, ...] } risk: <level>: { lines_changed_over: <n>, files_changed_over: <n> }| Key | Rules |
|---|---|
area.<name> |
At least one glob. ** matches any number of directories, including none. A change gets the area when any changed path matches |
data_class.<name> |
At least one of content (Go RE2 regular expressions, matched against each added line) or paths (globs, matched against changed paths) |
risk.<level> |
<level> is medium, high or critical; low is the default and takes no threshold. At least one threshold, neither negative. The highest level whose threshold is exceeded applies |
See Tags for what each tag means. Jurisdiction is not configured here; it comes from the project’s namespace.
Each rule:
| Key | Required | Meaning |
|---|---|---|
when |
yes | Condition; must set at least one field |
action |
yes | auto_merge, agent_review, human_approve, multi_approve, quarantine or block |
reason |
yes | Why; recorded with every decision this rule makes |
name |
no | Label for the rule in decisions, logs and policy test. Must be unique. Without one, the label is rules[<index>] |
approvers |
only for multi_approve |
Number of distinct approvers, at least 2. Default 2. Not allowed on other actions |
mode |
no | enforce (default) or shadow. A shadow rule is recorded but never decides |
shadow |
no | Older spelling of mode: shadow. shadow: true with mode: enforce is an error |
| Field | Matches when |
|---|---|
area |
Any changed file is in any of the listed areas |
only_area |
Every changed file is in at least one of the listed areas. Use it for permissive rules |
data_class |
The change has any of the listed data classes |
risk |
The change’s risk is one of the listed levels |
jurisdiction |
The project’s jurisdiction is one of the listed values (case-insensitive) |
Each field takes a string or a list of strings. Every field that is set must match (AND); within a field, any value matches (OR). Areas (in area and only_area) and data classes must be defined under classify.
default
Section titled “default”default: { action: human_approve, reason: "No rule matched; a person decides." }action and reason are required; approvers follows the same rule as in a rule.
Actions
Section titled “Actions”| Action | Decision status | propose exits |
|---|---|---|
auto_merge |
approved |
0 |
agent_review |
pending_agent |
0 |
human_approve |
pending_human |
4 |
multi_approve |
pending_human |
4 |
quarantine |
quarantined |
4 |
block |
blocked |
3 |
Example
Section titled “Example”The policy wayglass init writes:
version: 1classify: area: docs: ["docs/**", "**/*.md"] web: ["web/**"] auth: ["**/auth/**", "**/login*"] infra: ["worker/wrangler.jsonc", ".github/**"] data_class: secrets: { content: ["(?i)api[_-]?key", "art_v[0-9]+_([a-z]+_)?[0-9a-f]{40}"] } pii: { content: ["(?i)\\bssn\\b", "(?i)date_of_birth"] } risk: high: { lines_changed_over: 400 }rules: - { when: { data_class: secrets }, action: block, reason: "Secrets never land." } - { when: { area: infra }, action: multi_approve, approvers: 2, reason: "Infra changes affect everyone." } - { when: { area: auth }, action: human_approve, reason: "Auth is a security boundary." } - { when: { jurisdiction: eu, data_class: pii }, action: quarantine, reason: "EU personal data needs DPO review." } - { when: { only_area: docs, risk: low }, action: auto_merge, reason: "Routine docs change; every file is docs." } - { when: { risk: low }, action: agent_review, reason: "Low risk; second agent checks intent vs diff." }default: { action: human_approve, reason: "No rule matched; a person decides." }Shadow mode
Section titled “Shadow mode”rules: - name: trial-pii-review when: { data_class: pii } action: quarantine reason: "Trial: all personal data gets a DPO look." mode: shadowHow shadow matches are recorded, and how to replay history through a draft policy with wayglass policy test, is covered in Policy.