# Escalation over time: notice a repeat, warn about it, then refuse.
#
# Three rules, because a rule cannot both keep a count and gate on the count it
# is keeping — `on_fire` applies only when a rule fires, so a rule waiting for
# its own counter to reach two would never fire and never count ([OAR-FIRE-11]).
# One rule therefore counts unconditionally, and the other two read its counter.
#
# `counter_scope` is what makes this about a *repeat* rather than about traffic
# in general: the counter is keyed by the fingerprint of the tool call, so the
# tally counts how many times *this same call* has been made rather than how
# many calls there have been ([OAR-FIRE-10]).
oar: "1.0"
id: REPEAT_CALL_TALLY
namespace: example.hygiene
kind: schema
anchor: tool.pre_invoke
requires:
  profiles: [tool]
counter_scope: tool_args_fingerprint
effect: allow                   # contributes no decision; it is here to count
on_fire: [increment_counter]
status: stable
copy:
  what: Records that this exact call was made, so the rules below can see a repeat.
---
oar: "1.0"
id: REPEAT_LOOP_WARN
namespace: example.hygiene
kind: invariant
anchor: tool.pre_invoke
requires:
  profiles: [tool]
when: 'fire_count_of("REPEAT_CALL_TALLY") >= 2 && fire_count_of("REPEAT_CALL_TALLY") < 5'
effect: warn
status: stable
copy:
  what: The same call has been retried several times without a change of approach.
---
oar: "1.0"
id: REPEAT_LOOP_BLOCK
namespace: example.hygiene
kind: invariant
anchor: tool.pre_invoke
requires:
  profiles: [tool]
when: 'fire_count_of("REPEAT_CALL_TALLY") >= 5'
effect: block
on_fire: [publish_event]
status: stable
copy:
  what: The same call has been retried too many times.
  fix: Inspect the environment or change the command rather than replaying it.
