Skip to main content
Your team already looks at traces. Put every protected action there: one span per action, named for it, one span event per step, an error status for failed and ambiguous, and no argument values unless you opt in, because a trace backend is not the receipt store.

The policy

runnable

The code

runnable file=main.py

What the agent sees

The agent sees nothing different; the sink never blocks and never changes an outcome. The trace backend sees:
A refusal is UNSET, not an error: CTRLRun doing its job is not a fault in the trace.

The receipt

runnable
The receipts are still in the store and the JSONL file, chained; the spans carry the receipt id so a trace can be joined back to the evidence. Deleting a trace deletes nothing CTRLRun relies on.

When an AMBIGUOUS appears

The span’s status is ERROR and its result attribute is ambiguous, which is the alert to build: a span whose result is ambiguous is an effect waiting for ctrlrun resolve. The resolution is a later event, not a change to the span.

Next