Skip to content

UX audit fixes, and removing what was fitted to our own benchmarks - #89

Merged
architsharm merged 3 commits into
mainfrom
claude/ux-audit
Oct 12, 2026
Merged

architsharm merged 3 commits into
mainfrom
claude/ux-audit

Conversation

@architsharm

Copy link
Copy Markdown
Owner

UX audit fixes, and removing what was fitted to our own benchmarks

Two commits, both about trust. The first makes the product hold up when someone new uses it. The second makes our numbers hold up when someone checks them.

1. UX audit: a new user and a real agent

A brand-new workspace was onboarded entirely in the UI. OpenAI's airline customer-service app (Agents SDK, real model calls) then ran through it before and after setup. Every page was read in the empty state and again with real traffic. Each fix below was re-checked on a second fresh workspace.

Behaviour that was wrong

  • "Turn enforcement on" only enforced baseline. The agent's own protections and its blocked words kept watching, so "Is Delta cheaper…" still went through after setup. Step 6 now shows what it will switch on and what each part would have stopped, then switches everything for that agent (POST /api/agents/{slug}/enforcement). The step counts as done only when nothing is left watching.
  • Ordinary conversations were refused as runaway loops. Changing a seat and then confirming was blocked with a 403. Loop detection now counts only within the current turn (since the person's last message). A loop that does get stopped is recorded as a blocked run with a decision, not an "allowed" trace with nothing in it.
  • Previews always said 0. The simulator compared against the would-be verdict, so a watching rule's block already counted. A new would_stop count measures calls that went through and would now be stopped. Counts are per request, not per internal check, and each pack is credited only for its own rules.
  • Granting a tool didn't close its issues. It does now, from the Access tab too.
  • Reports showed 100% on every framework with nothing measured. It now shows "8 of 37 working · 29 not set up" with when it was computed.
  • Quality reported "11% wrong answers" for tools that simply hadn't been granted yet.

Noise

  • A tool not granted yet raised two high issues (undeclared, plus no permission). It now raises one medium issue.
  • Calls held for approval, as their grant asks, no longer raise issues. A hold because of where the data came from still does.
  • Issue titles use plain words. "Would block a user message: blocked words (watching)" replaces Would have been blocked on input: CUSTOM.AIRLINE_CS_WORDS.
  • Your own blocked words or topics raise low severity when enforced, medium while watching.
  • The timezone-wording check is low severity.
  • The refusal reason names the dashboard fix ("Allow it in the dashboard (Agents → x → Access)") instead of CLI jargon.

Getting set up

  • One key name, AGENTFOX_API_TOKEN. The SDK reads it, matching the CLI.
  • Snippets lead with Python and the OpenAI Agents SDK. The latter uses a new one-call govern(fox, *agents).
  • The OpenAI-compatible snippet now includes the api_key it needs.
  • The TypeScript tab is hidden until @agentfox/sdk is on npm.
  • Get started opens the next step automatically and shows "You're set up" at the end. Steps 1, 5 and 6 are reworded.
  • The Access tab lists the tools the agent tried first, adds "Allow all", and doesn't shift rows under the cursor.
  • Protect uses the agent's name, explains Low/Medium/High, and points to step 6 afterwards. The answer-boundary form uses plain labels.
  • Runs are grouped by conversation, one row with its steps instead of six. The SDK's check() and the Agents SDK adapter carry the conversation id.
  • Dashes, not 0%, for rates over nothing. Coverage says "not attack-tested yet". By model leaves out non-model checks. Scorers have names.

2. Overfitting to our own test data

An audit compared every detector pattern and corpus entry against the benchmark datasets and our own probes, and walked the git history.

Removed from the product

  • Patterns written after reading the deepset held-out split's misses (commit 10fc1b90): 8 patterns. These contaminated the published held-out number.
  • Patterns that copied single training rows ("use also information in the prompt to answer", "Nun folgen neue Aufgaben", …): 6 patterns.
  • 69 similarity-corpus entries written from benchmark misses (deepset held-out and TrustAIRLab), plus the five categories that held only them. The corpus header had claimed it was independent of the benchmarks.
  • A copy of the AgentDojo grader's tool-name regex in the repo scanner.
  • Two answerability patterns that each matched exactly one KUQ row.

Benchmarks made honest

  • The adaptive "unmapped" homoglyph and separator operators had quietly turned into mapped ones, because their characters were added to the product's tables. They now pick characters the product doesn't handle, every time they run.
  • The injection README's "no train/test leakage" statement is corrected. The red-team recall is labelled a regression check (some patterns were written to catch those probes), not a measure of generalisation.
  • The generalization runner can score one configuration at a time. Note that its model configurations timed out on this laptop (heavily degraded runs, also true of the previously committed results), so only the heuristic rows and the SPML classifier figure are quoted.

Published numbers that change (all 46 claims re-checked against their sources by scripts/check/claims.py):

Claim Before After
Default detector, held-out injection recall 26.7% 16.7%
Classifier ensemble, held-out 66.7% 68.3%
Adaptive readable attack success at 50 attempts 71% (70.8%) 74% (74.5%)
Bypasses still contained at the action 38/38 37/37
Classifier recall on SPML 85.6% 86.4%
KUQ debatable-question recall 5.62–5.77% 5.47%
Unmapped homoglyph bypass, on its own 3.2% 30.1%
Unmapped separator bypass, on its own 4.3–6.3% 58.1%

The last two are a real, open detection gap that the old operator was hiding. The README, the public benchmark page, the home page sections, the blog and the whitepaper are updated to match.

Checks

  • Full suite, ruff, import-linter, tsc, vitest 91/91, claims checker 46/46.
  • New tests:
    • step-6 enforcement and onboarding
    • loop detection within a turn, and the stopped run being recorded
    • one issue per tool not granted yet, and it closing when granted
    • held calls raising no issue
    • govern()
    • the plain default-deny reason

Based on main (includes #87 and #88).

architsharm and others added 3 commits October 10, 2026 16:28
Found by onboarding a fresh workspace in the UI and running OpenAI's airline
agent through it. Turn enforcement on now enforces the agent's protections and
blocked words, not only baseline; previews count what would actually change;
loop detection counts within a turn, and a stopped loop is a recorded run;
granting a tool closes its issues, one issue per ungranted tool, held calls
are not issues; plain issue titles and refusal reasons; one API key name;
Python and OpenAI Agents SDK snippets (govern()); runs grouped by
conversation; honest framework coverage and dashes for rates over nothing.

Co-Authored-By: Claude <noreply@anthropic.com>
Detector patterns written after reading the deepset held-out misses, patterns
copying single training rows, similarity-corpus entries written from benchmark
misses (deepset, TrustAIRLab), the AgentDojo grader's tool list in the scanner,
and two single-row KUQ patterns are removed. The adaptive benchmark's unmapped
homoglyph/separator operators now pick characters the product does not map.
Every published number is re-run and re-quoted (claims check 46/46): default
held-out injection recall 26.7% -> 16.7%; unmapped homoglyph and separator
bypass 3.2% -> 30.1% and 4.3% -> 58.1%, an open gap the old operator hid.

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
guardrails-api Ready Ready Preview Oct 11, 2026 5:36am UTC
guardrails-dashboard Ready Ready Preview Oct 11, 2026 5:36am UTC
guardrails-redteam-lang Ready Ready Preview Oct 11, 2026 5:36am UTC

This branch was successfully deployed

3 active deployments
Preview – guardrails-api — 7d641936 Deployed Oct 11, 2026 by vercel[bot]
Preview – guardrails-redteam-lang — 7d641936 Deployed Oct 11, 2026 by vercel[bot]
Preview – guardrails-dashboard — 7d641936 Deployed Oct 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant