Repository navigation
feat(benchmarks): gate document-embedded benign FPR - #202
Conversation
[Medium impact] The PR appears safe to merge, with one remaining non-blocking concern about a test rejecting model improvements.
|
| assert wrapped_fpr > bare_fpr * 5, (bare_fpr, wrapped_fpr) | ||
| assert encoded_fpr == wrapped_fpr, (wrapped_fpr, encoded_fpr) |
There was a problem hiding this comment.
These assertions require wrapped benign text to score more than five times worse than bare text, and encoded text to have exactly the same false-positive rate as wrapped text. If a model reduces wrapped false positives or improves one representation independently, this test fails even after the ratchet is updated. Keep these measurements as baseline evidence, or test an upper limit rather than requiring today's bad behavior.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
3af688e to
e95e372
Compare
|
Three of the four were real. Fixed in e95e372, rebased onto dev now that #201 has landed. Wrong checkpoint gets scored. Right, and there was no clean way to fix it: nothing on Empty carrier slice still passes. This one was bad. The report printed FAIL for the slice and the process exited 0, which is the exact failure the rest of this file is written to prevent. Three states were collapsed into two. A corpus with no usable rows now fails whatever the configuration, a slice that could not be scored for want of weights still does not fail the default run, and Matrix report names wrong configuration. Also right. Better model fails tests. Half right, and your half matters more than mine. |
|
Two of three, in c825537. The third is reviewing code that is no longer there. Broken model still passes. Yes, and it is the same shape as the empty-slice one from the last round, which is not a good look two rounds running. Matrix repeatedly loads weights. Measured it before changing anything: 45 scans, 19.2 seconds against 0.0. Your second option is the better one and I should have taken it last round. The matrix keeps its default guards, because it is a normalizer regression check and the normalizers sit below the model, so the model buys it nothing. The report now names the slice configuration and the matrix configuration separately instead of one run-level label that was only true of the slices. Gate run is back to 16.6 seconds. Better model fails tests. This one is stale. Unchanged and deliberate: |
Adds the benign slice #189 asked for, measuring the axis that actually moves.
#189 filed this as encoded benign text. It is not the encoding. Measured on checkpoint 19b7d6701bea, the same 95 benign rows from
benign_ci.jsonl:Plain and base64 are the same number. Wrapping a short benign passage in an ordinary business document is what takes the rate from 0.0211 to 0.5053; encoding it adds nothing on top. A slice built on the encoding would pin a constant that does not move when the thing it names changes, so this commits the document shape instead.
Every flagged row comes from
injection/span_model. The doc head is not the cause.What is in here
benign_carrier_ci.jsonl, the 95 easy benign rows embedded in one dull operations summary.CARRIER_TEMPLATElives inci_gate.pyso the corpus is reproducible frombenign_ci.jsonl, and a test rebuilds it and compares.The slice follows the hard slice's shape:
CARRIER_FPR_RATCHETpinned at the measured 0.5053 so one more misfire fails, andCARRIER_FPR_TARGETat 0.0211, the rate those rows score bare, because wrapping a benign passage in a document does not make it less benign. The target is reported and does not gate.The gate was measuring a configuration nobody ships
Worth reading before the rest.
run_gatebuilt its guards throughevaluate's default, which isGuard()with no model. Without a checkpoint on disk the ML scanners never load, so every benign slice scores against regex alone:So the easy ceiling has been green on a path that skips the model, and the shipped path is over it. This PR does not change that ceiling, because moving it would bury the finding.
make attack-gatenow goes red on a machine with weights cached, and that red is the regression, not this branch. Filed separately as the ceiling is yours to call.The gate now resolves weights once, reports which configuration produced the numbers, and refuses to pass a slice it could not score.
--require-weightsfails rather than reporting an unscored slice as fine, wired up asmake attack-gate-mland added totest-all-local. CI has no weights by design and stays green, with the carrier slice printed asn/aand the reason next to it.Also
All four slices now score with
isolated_requests=True: one guard per slice, a freshExecutionContextper sample. The old default rebuilt the guard per sample and reloaded the checkpoint 95 times. Identical counts on all four slices, and the harness suite goes from 6 minutes to 55 seconds..gitignoreneeded a negation for the new corpus.sdk/benchmarks/data/*.jsonlis ignored with one exception per committed file, so without it the gate would fail on a fresh clone with a missing corpus.Checks
ruff check,ruff format --check,mypy(128 files) clean. 26 tests intest_attack_harness.py, 213 acrosstests/securityplustest_evaluation.py.check_doc_drift.pyno drift,uv lock --checkclean. The five weights-backed tests carryrequires_ml_weightsandreal_model_cache, so they skip in CI and run locally.