Playbooks
A playbook is the source of a decision pack: a YAML file that names the pack, lists its actions, declares how facts are extracted and states the rules. This page explains each section, using the built-in example pack (data protection, compliance.regulatory_pii_guard.v1) as the worked example.
About the example
This page uses the built-in data-protection pack so you can read a complete, working playbook without writing one first. The same sections describe any decision pack: eligibility, limits, routing and so on. See Core concepts.
The whole file at a glance#
pack_id: compliance.regulatory_pii_guard.v1
version: 1.0.0
name: Regulatory PII and Sensitive Data Guard
kind: compliance
description: "Fail-closed PII inspection for GLBA, PCI-DSS, HIPAA and GDPR/CCPA."
budget:
max_latency_ms: 1.0
max_tokens: 0
actions:
- permit_clean_payload
- block_glba_ssn_leakage
- block_pci_dss_pan_leakage
- block_hipaa_phi_leakage
- redact_and_permit_contact_pii
codons:
- code: PII_SSN_EXTRACT
kind: codon
codon_type: extract_pattern
trigger: on_input
params:
pattern: '\b(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b'
binds_to: has_ssn
matches_field: ssn_matches
remedy_template: "[REDACTED_SSN]"
# ... card (with checksum: luhn), health ID, email, phone, IP
hard_rules:
- id: RULE-GLBA-SSN-BLOCK
condition: state.has_ssn == True
action: block_glba_ssn_leakage
description: "Statutory violation: Social Security Number (GLBA / FCRA) detected"
severity: fatal
# ... PCI-DSS, HIPAA (fatal), contact details (warning)
default_action: permit_clean_payload
Quote descriptions that contain a colon
A value such as Statutory violation: ... must be quoted, or YAML reads the second colon as a new key and the file fails to parse.
Identity#
| Key | Purpose |
|---|---|
pack_id | Required. Stable, dotted, ends in a major version: <domain>.<name>.v<N>. Licenses grant packs by this ID, so changing it is a breaking change. |
version | Semantic version of this revision. Bump it on every change you ship. |
name, kind, description | Human-facing metadata. |
Budget#
budget.max_latency_ms and budget.max_tokens record the pack's intended envelope: under a millisecond, zero model tokens. They document intent for reviewers; the runtime does not enforce them in 0.2.1. In the next release a compiled pack enforces them: max_latency_ms is checked on every decision, a slower decision raises DecisionBudgetExceededError with its result withheld, and any other budget key is rejected. Set max_latency_ms with room for your slowest host.
Actions and the default#
actions is the closed set of outcomes the pack may return. default_action names the outcome when no rule fires; a compiled pack always returns permit_clean_payload then. In the next release a compiled pack enforces both: actions, when present, must list every action the pack can return, including the five of the built-in checks, and default_action may only be permit_clean_payload. See Playbook schema. Name actions so callers can route on the prefix, as the example pack does:
permit_…the payload may proceed unchanged,redact_…the payload may proceed asremedy.clean_text,block_…the payload must stop.
Codons#
Each codon extracts one typed fact from the input into state. The keys that matter:
| Key | Meaning |
|---|---|
code | Unique codon ID, upper snake case. |
codon_type | extract_pattern for regular-expression extraction. Also defined: checksum_validator, range_filter, custom. |
trigger | on_input: run on every evaluation. |
params.pattern | The regular expression. params.flags takes MULTILINE, IGNORECASE, DOTALL, ASCII. |
checksum | luhn discards matches that fail the Luhn check. |
binds_to | The boolean state field set when anything matches. |
matches_field | The state field that holds the matches. |
remedy_template | Replacement text for each match in remedy.clean_text. |
Rules#
A rule pairs a condition over state with an action and a severity (fatal, warning or info). Rules are evaluated in file order; put the most severe first.
What runs in 0.2.1#
The playbook format is ahead of the pack compiler. Know which parts execute. The compiler rejects declarations a compiled pack would not execute, with UnsupportedPackDeclarationError, instead of sealing them silently:
| Section | Built-in example pack | Pack you compile |
|---|---|---|
| The example pack's built-in codons and rules | Run | Run |
rules entries with type: regex or type: luhn_checksum | n/a | Run on every payload, next to the built-in checks |
Your own codons | n/a | Rejected at compile time; executing them is Planned |
Your own hard_rules | n/a | Rejected at compile time; executing them is Planned |
budget enforcement | Not in 0.2.1. For a pack you compile: fixed in the next release (max_latency_ms checked on every decision) | |
To add your own detection today, declare regex rules:
pack_id: acme.project_codes.v1
version: 1.0.0
actions: [permit_clean_payload, block_project_code, block_glba_ssn_leakage,
block_pci_dss_pan_leakage, block_hipaa_phi_leakage, redact_and_permit_contact_pii]
rules:
- id: project_code
type: regex
pattern: '\bPROJ-[A-Z]+-\d[A-Z]\b'
action: block
default_action: permit_clean_payload
How regex rules behave in 0.2.1:
- Every rule and every built-in check runs on every payload, and every match is reported in
triggers. The first fatal match decides the action, with your rules checked before the built-in ones; with no fatal match, the first warning decides. - The returned action is
block_<id>(hereblock_project_code) with severityFATALforaction: block, andredact_<id>with severityWARNINGforaction: redact. Name the ruleidso that the result reads well, and list the same name underactionsfor reviewers. - A
redactrule replaces each match with[REDACTED_<ID>], the rule ID in upper case. When ablockrule fires and nothing else was redacted,remedy.clean_textis[BLOCKED: Prohibited Content]; otherwise the blocked value stays inclean_text, so never forward the text of a blocked result. Fixed in the next release: any fired block rule withholds the whole text. - Use non-capturing groups,
(?:...). With a capturing group, only the first group's text is reported and redacted. Fixed in the next release: the whole match is reported and redacted.
Compile it with your license and run it; see Packs and the custom rules tutorial. The full key list is in the playbook schema reference.
Writing good playbooks#
- Anchor patterns with
\band test them against both true and near-miss inputs. - Prefer a checksum where the identifier has one; it removes most false positives.
- Avoid nested quantifiers such as
(a+)+. Patterns run on every payload with no timeout. - Keep a golden test set next to the playbook and run it in CI before compiling. See Testing policies.