Step-by-step execution traces showing how each rule gates tool calls. Every blocked action here is structurally impossible, not a policy check that might fail.
npm install @petriflow/rules
read-before-send
Don't message blindly
The agent must read messages before it can send. Each read earns one send. The sequencing rule cycles — after each send, the agent must read again.
requirediscord.readMessages before discord.sendMessagelimitdiscord.sendMessage to 5 per session
Agent tries to send another message immediately BLOCKED must readMessages again (rule cycles)
1:1 read-to-send ratio + session rate limit. The agent can't spam a channel — every send requires a fresh read, and messages are capped at 5 per session. Full tutorial →
test-before-deploy
Don't ship broken code
Lint gates test. Test gates deploy. Deploy requires a human and is capped at 2 per session — no prompt injection can skip the pipeline or exhaust your deploys.
requirelint before testrequiretest before deployrequirehuman-approval before deploylimitdeploy to 2 per session
// agent tries to deploy
✕
Agent calls deploy({ environment: "production" }) BLOCKED lint hasn't run, test hasn't run, human hasn't approved
✓
Agent calls lint() OK linter passes, test unlocked
✓
Agent calls test() OK tests pass, deploy one gate away
After 2 deploys, a third attempt BLOCKED rate limit reached — budget exhausted
Deploy is a manual gate with a budget. Even if the agent is prompt-injected, deploys are structurally blocked until lint, test, and human approval are all satisfied. And the session limit prevents runaway deploys. Full tutorial →
research-before-share
Don't share fabrications
Each fetch earns one share token. Each share consumes one. The agent can't broadcast "findings" it never actually fetched.
requireweb-fetch before share-result
// agent tries to share a research summary
✕
Agent calls slack.sendMessage({ text: "Research summary: React 20 is out..." }) BLOCKED no fetch has been done
✓
Agent calls webFetch({ url: "https://react.dev/blog" }) OK earns one share token
Agent calls slack.sendMessage({ text: "Also, Vue 5 is..." }) BLOCKED no token left, must fetch again
1:1 fetch-to-share ratio. The agent can't hallucinate research results and share them — every share must be backed by an actual web fetch.
backup-before-delete
Don't destroy without a safety net
Only mapped bash commands can reach the shell. Everything else is blocked. The backup must actually succeed before delete is unlocked (deferred check).
# allow only these bash commands:map bash.command cp as backupmap bash.command rm as delete# block everything else:blockbash# enforce ordering:requirebackup before delete
// agent tries to clean up a data directory
✕
Agent calls bash({ command: "rm -rf data/" }) BLOCKED rm maps to delete, no backup yet
✕
Agent calls bash({ command: "unlink data/temp.log" }) BLOCKED unlink is unmapped, caught by block bash
✓
Agent calls bash({ command: "cp -r data/ /tmp/data-backup" }) OK cp maps to backup, deferred until confirmed
Allowlist, not deny-list.unlink, shred, find -delete — anything unmapped is blocked. Only cp and rm can reach the shell, and only in the right order. Full tutorial →
whatsapp messaging
Don't message the wrong person
Three rules compose to create a safe messaging flow: lookup before send, human approval, and rate limiting.
# look up the recipient firstrequirewhatsapp.lookup before whatsapp.send# human confirms recipient + messagerequirehuman-approval before whatsapp.send# one send per lookup cyclelimitwhatsapp.send to 1 per whatsapp.lookup
// agent tries to send a WhatsApp message
✕
Agent calls whatsapp({ action: "send", to: "+1415...", message: "Hey!" }) BLOCKED no recipient lookup yet
✓
Agent calls whatsapp({ action: "lookup", query: "Sarah" }) OK resolves to +14155551212
PetriFlow prompts: "Send 'Hey!' to Sarah (+14155551212)?" Human approves → manual gate opens
✓
Agent calls whatsapp({ action: "send", to: "+14155551212", message: "Hey!" }) ALLOWED lookup done, human approved, token consumed
✕
Agent calls whatsapp({ action: "send", message: "One more thing" }) BLOCKED budget consumed, must look up again
Three rules compose into one safe flow. The agent can't guess phone numbers, can't send without human confirmation, and can't batch-fire messages after a single lookup.