Defaults, and the four actions
Lesson 12 was about which rule matched. This lesson is about what happens when none of them do, and about the fact that four actions produce only two outcomes.
Every document carries a defaults block. The important field is the action taken when no rule matched.
from agent_os.policies.schema import PolicyDocument
from agent_os.policies.evaluator import PolicyEvaluator
for name in ["open.yaml", "shut.yaml"]:
evaluator = PolicyEvaluator(policies=[PolicyDocument.from_yaml(name)])
decision = evaluator.evaluate({"tool": "something_nobody_wrote_a_rule_for"})
print(f"{name:11}", decision.allowed, "|", decision.action)The default default is allow. That is the opposite of the Python policy engine from lesson 5, which denies anything it was not told about, and it is the single setting most worth changing in a policy you intend to rely on.
Four actions, two outcomes
A rule's action can be allow, deny, block or audit. Only two of those let the call through.
policy = PolicyDocument.from_yaml("actions.yaml")
evaluator = PolicyEvaluator(policies=[policy])
for tool in ["lookup_order", "issue_refund", "send_email", "delete_order"]:
decision = evaluator.evaluate({"tool": tool})
print(f"{tool:14} action={decision.action:7} allowed={decision.allowed}")audit allows the call and marks it as worth recording. It is how you watch a tool you are not ready to forbid, and it is the declarative twin of the shadow mode in lesson 23.
deny and block both refuse and differ only in the word that reaches your logs. Pick one and use it consistently; the decision object reports whichever you wrote.
When evaluation itself goes wrong
If the evaluator raises while working through the rules, it does not pass the error upwards and it does not let the call through. It returns a refusal with a reason saying so.
That is the fail-closed behaviour you want and the opposite of the missing-field case in lesson 11, where a rule that could not be evaluated was simply skipped. The difference is that a missing field is a condition that did not match, while an exception is the engine admitting it does not know.
- Remove the
defaultsblock entirely and find out which action you get. - Change
audittodenyand watchallowedflip. - Set the default to
denyand rerun lesson 10's script, where the forty pound refund matched nothing.
Slow is fine. Stopping is the only problem.