field note #5
1 · name + claim
a-refusal-is-data
Rewording a command to get past your own guard is the breach.
2 · The failure it offsets
A command filter refused three read-only operations it should have allowed. Nothing was damaged and the work continued. The natural next move — the one that feels like diligence — is to rephrase until it passes and get on with the job.
That move is the breach, and it is worth being exact about why, because it costs three separate things at once. It does not defeat the filter once: it publishes a path around it, and the reword becomes the known route for everyone who hits the same wall. It leaves no record, so the refusal never enters any count and the filter’s behaviour stays unmeasured. And it destroys the only evidence that would have justified changing the filter — three clean false positives, timestamped, with the command class that produced them. The filter goes on refusing the same three operations forever, everyone goes on rephrasing, and nobody can point at anything.
3 · The rule
Never reword to evade a refusal. A refusal ends the attempt; the next action is a log line, not a synonym. If the refusal was wrong, the log line is what buys the fix.
Then read the number. Refusal rate is a health metric, not a friction cost to be driven down. A climbing false-positive rate is a design signal — the filter is drawing its line in the wrong place, and now you can prove it with cases instead of arguing from memory. A filter that has never refused anything is not clean; it is untested, and you have no idea which side of the line it actually sits on. Neither number exists unless refusals are recorded — and unless rephrases are recorded too.
4 · Adopt in an afternoon
One append-only record, written by the filter, plus one standing rule the harness enforces rather than the operator remembers.
# refusals.jsonl — appended by the FILTER, never by hand.
{"class":"read-only-listing","verdict":"refused",
"kind":"false-positive","after_refusal":false}
{"class":"history-rewrite","verdict":"refused",
"kind":"true-positive","after_refusal":false}
{"class":"read-only-listing","verdict":"allowed",
"kind":"false-positive","after_refusal":true}
# ^ a reword got through
# THE STANDING RULE — enforced by the harness, not by manners:
# a command submitted after a refusal, same session, same
# class, is a REPORTABLE EVENT. It is stamped after_refusal
# true and it surfaces at review whether or not it passed.
# READ THE RATE. Do not minimize it:
# false-positives climbing — the filter draws its line in
# the wrong place; redesign it
# after_refusal nonzero — someone is routing around it
# zero refusals in a window — UNTESTED, not clean
kind is a human verdict, filled in at review, and it is what turns the log
from a counter into evidence. after_refusal is machine-set and is the only
field the person being observed cannot decline to write.
5 · The refusal test
Get a command refused on purpose, then submit a variant of it in the same session. The harness must surface the second attempt as an event in its own right. Note what it must not do: refuse it. The reword may well be legitimate, and a control that blocks the second attempt teaches people to open a new session instead. Surfacing is the whole mechanism. If the second submission is indistinguishable from a first submission in the record, you do not have this control — you have a filter and a habit of getting past it.
6 · Retire trigger
Retire the standing rule when the filter is an allowlist narrow enough that a false positive is a design signal rather than noise — when refusals are rare enough that each is read individually and the log is a handful of lines a quarter. At that point the record is the review and the rule has nothing left to catch. Retire the rule; keep the log.
7 · footer — capture date · version · license
captured 2026-08-06 · v1 · stolen from Buddy Lab · CC BY 4.0, snippets MIT