1
0
Fork 0

measure(fp): the upload door costs a human on 16-53% of benign documents

Every field measurement this repo had published was per URL. None answered
what a consumer actually feels: how often does an ordinary document fail to
persist unattended? Three benign populations, each against its own
denominator, run through screen_output under PRESET_USER_UPLOAD and counted
at document granularity:

  vendor-harvest    98 of 185 (53.0%) non-WARN -- 64 fail-secure, 34 held
  generated-notes   88 of 547 (16.1%) non-WARN -- 61 fail-secure, 27 held
  reference-corpus 133 of 389 (34.2%) non-WARN -- 80 fail-secure, 53 held

The number is bad and ships as measured; PLAN-v1 committed to that in advance
("et roedt FP-resultat er like verdifullt"). The response is a documented
limitation, not a recalibration: moving the grading fires the locked
linkedin-studio notification promise, and the drivers are residuals
LIMITATIONS already concedes. Counted at each document's worst severity,
active:raw-html -- the MDX-component over-reach -- is a top driver in 52 of
vendor-harvest's 98 and 53 of generated-notes' 88; about ten per population
are genuinely injection-shaped text, which security-adjacent documentation
honestly contains.

Method traps closed rather than stepped in:

- The unit is in the number. Document-level rates are NOT comparable to the
  URL-level 16/16, 28/28, 149/1694 above them, and the three rows are not
  summable -- the 2400 != 2401 defect class one level up.
- The gate is the strict one. The trusted door WARNs every non-CRITICAL
  finding, so it would have handed back a beautiful, meaningless near-zero;
  it is printed as a footnote and labelled structurally blind.
- "not WARN" is only a risk statement while the default action map sends
  exactly NONE and LOW to WARN. action_map became a supported override last
  commit, so the equivalence is pinned in the suite and the sweep aborts if
  it breaks.
- Ground truth for benign is provenance, not inspection, and says so.
- The populations are disjoint as documents but not independent as content:
  184 of generated-notes' 547 are same-named derivatives of vendor-harvest.
  Measured, not assumed, and the two rows read as one observation.

Also fixed: tests/test_wiring.py credited a consumer's capture store with
35 of 35 query-carrying URLs. That consumer retracted the number the next day
and re-measured 28 of 28 on the same 81-URL corpus. LIMITATIONS was corrected
then; the comment was not, so a retracted figure has been sitting beside a
live one since 07-27.

717 tests (was 716, none changed), coverage matrix 128/128 with 6/6 gaps
holding, every population swept twice with identical counts.
This commit is contained in:
Kjell Tore Guttormsen 2026-08-10 21:32:56 +02:00
commit d1bff6047d
6 changed files with 358 additions and 3 deletions

View file

@ -27,9 +27,12 @@ from llm_ingestion_guard import (
Report,
Source,
Disposition,
Risk,
DEFAULT_ACTION_MAP,
PRESET_TRUSTED_SOURCE,
PRESET_USER_UPLOAD,
)
from llm_ingestion_guard.calibration import RISK_RANK
def _scan_input(text: str) -> Report:
@ -147,6 +150,24 @@ def test_false_positive_is_not_blocked_on_the_upload_gate(cid, text):
f"{cid} wrongly disposed {decision.disposition.value}: {decision.reasons}")
# --- the metric behind the published false-positive rate --------------------
def test_the_published_fp_metric_is_a_risk_statement():
"""`docs/fp-sweep.py` measures benign corpora as *documents disposed
non-WARN*, and `docs/LIMITATIONS.md` publishes those counts as a statement
about assessed risk. The two are the same statement only while the default
action map sends exactly ``NONE`` and ``LOW`` to WARN. Re-map that an
`action_map` is a supported override as of the axis separation and the
published number silently becomes a different claim with nothing failing.
Pinned here, beside the corpus the method was designed on."""
elevated = RISK_RANK[Risk.ELEVATED.value]
for risk in Risk:
assert (DEFAULT_ACTION_MAP[risk] is Disposition.WARN) == (
RISK_RANK[risk.value] < elevated
), f"{risk.value} breaks the equivalence the published rate rests on"
# --- counter-corpus: exfil-SHAPED URLs must keep hard-failing ---------------
# The dangerous half of the 0.3.1 recalibration. Loosening ordinary carriers is
# only honest if the EchoLeak class still blocks, so every URL form that can

View file

@ -141,8 +141,12 @@ _FIELD_QUERY_URLS = [
# claude-code-llm-wiki: 16/16 query-carrying external URLs in a 527-document
# vendor-docs corpus were publisher-authored campaign tracking.
("vendor-tracking", "https://claude.com/pricing?utm_source=docs&utm_medium=referral"),
# linkedin-studio: 35/35 in an 81-URL capture store were content identity —
# linkedin-studio: 28/28 in an 81-URL capture store were content identity —
# the parameter *is* the resource, so stripping it does not dereference.
# (This comment said 35/35 until 2026-08-10. That number was retracted by the
# consumer itself a day after it was given — their re-run enumerated every URL
# and landed on 28, and `docs/LIMITATIONS.md` was corrected then while this
# comment was not. The denominator, 81, was confirmed by the same re-run.)
("content-identity-video", "https://www.youtube.com/watch?v=dQw4w9WgXcQ"),
("content-identity-feed", "https://www.youtube.com/feeds/videos.xml?channel_id=UC7cs8q"),
("pagination", "https://www.stortinget.no/no/Saker-og-publikasjoner/?all=true"),