How often we flag work that was fine
Our false-positive rate, measured on our own coding-agent traffic. Tenant
Dogfood, agent claude-code-mac, every governed tool
call in the window [2026-08-25, 2026-09-05) except the ones on
2026-08-31. Every flagged call was read in its bucket and labelled benign in
context, by Jay, on 29 August and 13 September 2026.
| What it answers | Rate | Wilson 95% |
|---|---|---|
| What our rules flagged at the time | 15.58% 742 of 4,764 |
14.57% to 16.63% |
| What today's rules flag on the same calls | 15.47% 737 of 4,764 |
14.47% to 16.52% |
| What would be left after a scoped exclusion set | not published yet | no interval until there is a number |
The second row is the number we publish, because it is the one that describes the product a reader can buy today. Five of the 742 are calls the live proxy flagged in August that this tree no longer flags; nothing else moved.
The day we took out, and what the full window says
One day of the eleven fails our own coverage check, so it is excluded and named rather than averaged in quietly.
On 2026-08-31 the classifier was unreachable for about four hours and 25.6% of that day's governed calls went unanswered by it. Our observation-week runbook voids a window whose unanswered share passes 5%, so that day is out of the published figure and the other ten days are in. The deny list still ran on 31 August, so its flags that day are real; what is missing is the classifier's own verdict.
For the record, the full window including that day is 876 flagged of 6,507 governed tool calls, 13.46%, Wilson 95% 12.65% to 14.31%. It reads lower than the published figure, not higher: 31 August was the busiest day of the window and its classifier flags are the ones missing. Neither window flatters the product, which is why both are printed.
The day table, the query behind it and the 5% rule are in
docs/benign-corpus-prod-2026-09-05.md. The corpus itself is frozen at
extracted_at 2026-09-06T02:16:41Z, after the window closed.
What is inside the 737
Three layers write a flag and only two of them can take a call off the wire.
| Layer | Rows | Would enforce mode remove the call |
|---|---|---|
| Deny-list patterns, replayed on today's rules | 395 | Yes |
| LLM classifier | 121 | Yes, when the classifier answers |
| Injection scanner | 221 | No, it never blocks |
-
The three
injection-policies in the table below are written by the prompt-injection scanner, which is observe-only.grep -rn "injection" apps/proxy/deny_list.pyreturns nothing: the deny list has no injection rule. Those rows are written with the actioninjection_scanand the verdict fixed to observe, so they are a scan result recorded next to the request rather than a tool call being removed. - So 516 of the 737 are calls enforce mode would have taken out of the response, and 221 are recorded and left alone. We publish the larger number as the rate, because a flag on work that was fine is a cost to you whether or not it also stopped the call.
-
The classifier and injection rows are carried at the verdict they were given in
August. Neither can be replayed offline: re-running the classifier would spend
tokens against a different model version, and an
injection_scanrow stores a 200-character snippet rather than the message that was scanned.
Arithmetic on the table above: 395 plus 121 plus 221 is 737, and 395 plus 121 is
516. The injection- policy string is built in
apps/proxy/main.py from the pattern name in
apps/proxy/injection.py, with the verdict written as a literal
observe. That is the whole reason those rows cannot be counted as
removals.
Flagged calls by policy
All 742 calls our rules flagged at the time, grouped by the policy that matched.
| Policy | Flagged calls |
|---|---|
| injection-instruction-override | 216 |
| shell-rm | 138 |
| llm-classifier | 121 |
| db-drop | 85 |
| cred-grep-secrets | 66 |
| cred-env-secrets | 38 |
| exfil-curl-post | 16 |
| infra-terraform-destroy | 14 |
| shell-format | 10 |
| db-truncate | 9 |
| shell-del | 6 |
| container-nsenter | 5 |
| exfil-rsync | 4 |
| injection-role-hijack | 4 |
| infra-kubectl-delete | 3 |
| container-mount | 1 |
| cred-passwd | 1 |
| file-wipe | 1 |
| injection-prompt-extraction | 1 |
| persist-ssh-keys | 1 |
| privesc-chown | 1 |
| supply-chain-pip | 1 |
The counts add to 742. file-overwrite is absent because all five of its
rows fell on the excluded day. Two shell rules carry most of the deny-list total,
and one of them, shell-rm, does not read the command around the word it
matched: a package uninstall meets it.
Who called these benign, and what stays flagged anyway
The labels are ours, they were applied by bucket rather than call by call, and two buckets keep their patterns whatever the label says.
- Jay read the buckets on 29 August and 13 September 2026 and labelled every one of them benign in context. That is bucket-level review, not a ruling on each of the 742 calls, and the page says so rather than implying a per-call audit we did not do.
-
injection-instruction-overrideandcred-grep-secretskeep their patterns and get no blanket exclusion. They are the two buckets where a real attack looks most like our own work, so the cost of the flags stays with us. -
Every
cred-policy is non-suppressible at any scope. An exclusion cannot be written against one, by decision, whatever a future tenant asks for.
The third number is not published, and it has a floor
The rate after a scoped exclusion set is the number a buyer actually lives with,
and we have not published one because the exclusion set does not exist yet. It
cannot fall to zero when it arrives: the carried classifier and injection rows and
the non-suppressible cred- buckets stay in the numerator however the
set is written, so a large part of this rate is a floor rather than a setting.
Read this as a worst-case week
- This was a worst-case week: one agent, building and testing the deny list and site copy, so the traffic quotes destructive commands, secrets patterns and injection strings far more often than a customer workload would.
- Nothing was actually removed from the wire. The tenant was in observe mode for the whole window, so every count here is a counterfactual: what enforce mode would have done.
- The denominator is every governed call, not the allowed subset. Allowed calls are by construction exactly the calls the patterns did not match, so measuring against them alone would be circular.
- It is one tenant and one agent over ten days of traffic. It is the rate our rules cost our own work, and it is evidence about the rules rather than a prediction about yours. Measure it on your own traffic during the pilot, where the same observe mode gives you the same count before anything is enforced.
How it is measured
Every governed tool call from the Dogfood tenant's claude-code-mac
agent in this window carries a verdict:
allow when nothing matched,
observe when a deny-list pattern or the
classifier would have blocked the call, and the same
observe on an injection-scan row, which
is why the count has to be split by layer before it means anything. Request bodies
are credential-masked before any hash is taken, and the committed fixture
apps/proxy/tests/fixtures/benign_corpus_prod.py stores SHA-256 hashes
only: never a body, never a secret.
# build the fixture from production
python scripts/build_benign_corpus.py
# re-derive the counts from it
python -m pytest apps/proxy/tests/test_benign_corpus_prod.py
# replay the flagged calls on today's rules
python scripts/shadow_enforce_report.py --input <observe rows JSON> --output <path>
The window, the day table, the replay and the bucket labels are written up in
docs/benign-corpus-prod-2026-09-05.md. The number on this page is the
second row of the table at the top, and it was derived from the fixture rather than
copied from an earlier page.
Intercis is a proxy that runs outside your agent process. Your agent's traffic to Anthropic or OpenAI goes through it, policy runs on the tool calls the model asks for, and once enforcement is on, a denied call is taken out of the response before your agent runtime can execute it. A pilot starts in observe mode, which is how you get this number for your own traffic.