Remove assume_ai, add tophat front-end and rival margin, fix two CLI defects

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Victor Kuznetsov
2026-07-20 08:14:50 -07:00
co-authored by Claude Opus 4.8
parent a8f3536d3e
commit cfefd9d819
26 changed files with 1715 additions and 257 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ class TestRemoveVisibleArray:
def test_array_accepts_knobs(self):
arr = np.zeros((256, 256, 3), np.uint8)
result, removed = raiw.remove_visible(arr, sensitivity="assume_ai", backend="cv2")
result, removed = raiw.remove_visible(arr, sensitivity="strict", backend="cv2")
assert removed == []
assert result.shape == arr.shape
+25
View File
@@ -573,6 +573,31 @@ class TestMetadataCommand:
assert result.exit_code == 0
assert "stripped" in result.output
def test_metadata_remove_reports_failure_when_the_strip_was_a_no_op(self, runner, tmp_path):
"""A file PIL cannot decode is copied through UNCHANGED by the fail-safe.
That is correct (never crash a worker on a partial upload) but the command used
to print "AI metadata stripped ->" and exit 0 for it, so a caller could not tell
a real strip from a no-op and the output still read as AI. Found on real Samsung
Galaxy S22 C2PA PNGs during the corpus parity audit, 2026-07-19.
"""
# PNG signature + a C2PA (caBX) chunk, then garbage: the byte scanner sees the
# marker, PIL cannot decode it.
src = tmp_path / "undecodable.png"
payload = b"c2pa" + b"\x00" * 32
chunk = len(payload).to_bytes(4, "big") + b"caBX" + payload + b"\x00\x00\x00\x00"
src.write_bytes(b"\x89PNG\r\n\x1a\n" + chunk + b"NOTAPNG" * 8)
out = tmp_path / "cleaned.png"
result = runner.invoke(main, ["metadata", str(src), "--remove", "-o", str(out)])
from remove_ai_watermarks.metadata import get_ai_metadata
if not get_ai_metadata(src):
pytest.skip("fixture does not register as an AI-metadata carrier")
assert result.exit_code != 0, "a no-op strip must not report success"
assert "stripped ->" not in result.output
def test_metadata_remove_in_place(self, runner, tmp_png_with_ai_metadata):
"""With ``-o`` omitted, the strip overwrites the source in place (default
output_path=None). Previously every test passed an explicit ``-o``."""
+7 -13
View File
@@ -107,10 +107,10 @@ class TestPillRegistry:
class TestPillGate:
"""Pill removal is gated (``_keep_pill``): the reliable bottom-right wordmark
removes it unrestricted, the metadata (``"jimeng"`` provenance) / assume_ai arm
removes it ONLY on a flat footprint (safe fill), Doubao/no-confirmation never
remove it. Fakes each mark's detect so no image content is needed; cv2 backend so
nothing downloads. Frame flatness matters, so tests pass a flat or textured frame."""
removes it unrestricted, the metadata arm (``"jimeng"`` provenance) removes it ONLY
on a flat footprint (safe fill), Doubao/no-confirmation never remove it. Fakes each
mark's detect so no image content is needed; cv2 backend so nothing downloads. Frame
flatness matters, so tests pass a flat or textured frame."""
@staticmethod
def _fakes(monkeypatch: pytest.MonkeyPatch, keys: set[str]) -> None:
@@ -149,16 +149,10 @@ class TestPillGate:
_, removed = registry.remove_auto_marks(_textured_frame())
assert "Jimeng AI生成 pill" in removed
def test_pill_kept_via_assume_ai_on_flat_footprint(self, monkeypatch: pytest.MonkeyPatch) -> None:
# assume_ai (no metadata) removes the pill on a flat footprint (safe fill)...
def test_pill_dropped_on_textured_footprint(self, monkeypatch: pytest.MonkeyPatch) -> None:
# The metadata arm keeps the flatness guard: textured false fires visibly smear.
self._fakes(monkeypatch, {"jimeng_pill"})
_, removed = registry.remove_auto_marks(np.full((400, 300, 3), 150, np.uint8), sensitivity="assume_ai")
assert "Jimeng AI生成 pill" in removed
def test_pill_dropped_via_assume_ai_on_textured_footprint(self, monkeypatch: pytest.MonkeyPatch) -> None:
# ...but even assume_ai keeps the flatness guard (textured false fires smear).
self._fakes(monkeypatch, {"jimeng_pill"})
_, removed = registry.remove_auto_marks(_textured_frame(), sensitivity="assume_ai")
_, removed = registry.remove_auto_marks(_textured_frame(), provenance=frozenset({"jimeng"}))
assert "Jimeng AI生成 pill" not in removed
def test_pill_dropped_without_metadata_or_wordmark(self, monkeypatch: pytest.MonkeyPatch) -> None:
+191
View File
@@ -0,0 +1,191 @@
"""Policy-level tests for the shared text-mark engine config.
These assert TUNING that was set by corpus measurement, not algorithm behaviour --
they exist so a future edit cannot silently revert a calibrated constant back to a
value that was measured to be wrong. The measurements themselves live in
`docs/module-internals.md` and in the comment at
`_text_mark_engine._DEFAULT_PROVENANCE_NCC_FACTOR`.
"""
class TestRivalMargin:
"""Detection among same-corner marks is COMPETITIVE, not just absolute.
Doubao "豆包AI生成" and Jimeng "★ 即梦AI" both sit bottom-right in near-white CJK
and survive binarization as similar blobs, so an absolute NCC gate cannot tell
them apart -- 33 of jimeng's 68 false additions were Doubao marks. Measured
separability scoring both templates on the SAME blob (n=40 jimeng / 75 doubao):
absolute ncc_jimeng 0.96, ncc_jimeng MINUS ncc_doubao 0.99. Corpus effect of the
margin gate: jimeng precision 38% -> 63% with genuine detections unchanged at 40
(false fires 65 -> 23).
"""
def test_jimeng_competes_against_doubao(self):
from remove_ai_watermarks import jimeng_engine
assert "doubao_alpha.png" in jimeng_engine._CONFIG.rivals
def test_doubao_has_no_rival_margin(self):
"""Asymmetric by measurement, not oversight: the symmetric gate cost Doubao 7
genuine detections to prevent 5 false ones (1.4:1 against), while Jimeng gained
25pp for free. Doubao's absolute detector is already 86% precise."""
from remove_ai_watermarks import doubao_engine
assert doubao_engine._CONFIG.rivals == ()
def test_a_doubao_shaped_blob_loses_the_jimeng_margin(self):
"""The decisive case: a blob matching Doubao BETTER than Jimeng must not be
booked as a Jimeng wordmark, however high its absolute Jimeng score."""
import numpy as np
from remove_ai_watermarks import jimeng_engine
from remove_ai_watermarks._text_mark_engine import glyph_silhouette
eng = jimeng_engine.JimengEngine()
doubao_blob = glyph_silhouette("doubao_alpha.png")
assert doubao_blob is not None
canvas = np.zeros((doubao_blob.shape[0] + 20, doubao_blob.shape[1] + 20), np.uint8)
canvas[10 : 10 + doubao_blob.shape[0], 10 : 10 + doubao_blob.shape[1]] = doubao_blob
width = int(doubao_blob.shape[1] / jimeng_engine._CONFIG.alpha_width_frac)
jimeng_score = eng._template_match_score(canvas, width)
assert not eng._rival_margin_ok(jimeng_score, canvas, width)
class TestPerMarkProvenanceRelaxation:
"""The provenance NCC relaxation is PER MARK, not one shared multiplier.
Corpus-measured 2026-07-18 on the default `auto` path (4417 unique TC260
carriers, blind hand-label, two-sided control): the single shared 0.7 ran at
76% precision on doubao but 17% on jimeng, because jimeng's relaxed silhouette
keys on "text in the bottom-right corner" rather than the wordmark -- 33 of its
68 false additions were DOUBAO marks. Full table at
`_text_mark_engine._DEFAULT_PROVENANCE_NCC_FACTOR`.
"""
class TestScaleBasis:
"""Mark geometry scales with a PER-MARK image dimension, measured not assumed.
Every tuned fraction was calibrated on PORTRAIT captures, where width and short
side coincide, so the basis was never exercised until landscape inputs were
measured. Corpus-measured 2026-07-18 (2572 unique TC260 carriers): doubao
detection was portrait 60% / square 41% / **landscape 0% of 435** -- a width-scaled
box is inflated by the aspect ratio on a wide image and the glyph never lands in
it. A short-side basis recovered 56% of the previously-undetected landscape set.
The same switch broke JIMENG (labelled landscape positives 13/13 -> 0/13), whose
wordmark tracks the width -- hence per-mark, not a house rule.
"""
def test_doubao_scales_with_the_short_side(self):
from remove_ai_watermarks import doubao_engine
assert doubao_engine._CONFIG.scale_basis == "short"
def test_jimeng_scales_with_width(self):
"""Measured, not an oversight: the short-side basis took jimeng's labelled
landscape positives from 13/13 to 0/13."""
from remove_ai_watermarks import jimeng_engine
assert jimeng_engine._CONFIG.scale_basis == "width"
def test_samsung_keeps_width_because_it_is_unmeasured(self):
"""1 addition corpus-wide, so there is no evidence either way; an unmeasured
change is not an improvement."""
from remove_ai_watermarks import samsung_engine
assert samsung_engine._CONFIG.scale_basis == "width"
def test_basis_only_differs_on_non_square_images(self):
"""The basis is a no-op wherever width IS the short side, which is why the bug
survived: every calibration capture was portrait."""
import numpy as np
from remove_ai_watermarks import doubao_engine, jimeng_engine
portrait = np.zeros((1600, 900, 3), np.uint8)
landscape = np.zeros((900, 1600, 3), np.uint8)
d, j = doubao_engine.DoubaoEngine(), jimeng_engine.JimengEngine()
assert d.scale_base(portrait) == j.scale_base(portrait) == 900
assert d.scale_base(landscape) == 900
assert j.scale_base(landscape) == 1600
def test_landscape_box_stays_inside_the_frame(self):
"""The concrete failure: a width-scaled box on a wide image overshoots the
mark's real footprint. The short-side box must be proportionally smaller."""
import numpy as np
from remove_ai_watermarks import doubao_engine
landscape = np.zeros((900, 2400, 3), np.uint8)
loc = doubao_engine.DoubaoEngine().locate(landscape)
assert loc.w < int(2400 * doubao_engine._CONFIG.width_frac)
assert loc.x + loc.w <= 2400
assert loc.y + loc.h <= 900
class TestTophatFrontend:
"""Detection can correlate the CONTINUOUS top-hat instead of a binarized blob.
`extract_mask` thresholds the top-hat into a 0/255 glyph blob, which is fine for a
mark stamped bold and opaque and destructive for a faint one -- a thin translucent
overlay shatters into specks and no template can match a blob that is not there
(measured: 千问 scored 0.170 mean vs doubao's 0.723 through the binary path, 0% over
the gate). The `tophat` front-end never binarizes: the saturation/luma gates become
weights, and the response is max-normalized so the score is contrast-invariant.
Corpus effect on the 240-image unbiased recall sample: doubao recall 89% -> 92% at
an unchanged 99% precision.
"""
def test_doubao_uses_the_continuous_frontend(self):
from remove_ai_watermarks import doubao_engine
assert doubao_engine._CONFIG.detect_frontend == "tophat"
def test_other_marks_stay_binary_until_measured(self):
"""A front-end switch must be measured per mark before it ships; jimeng and
samsung have no such measurement yet."""
from remove_ai_watermarks import jimeng_engine, samsung_engine
assert jimeng_engine._CONFIG.detect_frontend == "binary"
assert samsung_engine._CONFIG.detect_frontend == "binary"
def test_response_is_contrast_invariant(self):
"""The whole point: a faint mark and a bold one produce the same response, so a
single threshold works for both. Binarizing is what loses the faint one."""
import numpy as np
from remove_ai_watermarks import doubao_engine
eng = doubao_engine.DoubaoEngine()
h, w = 400, 900
out = []
for amplitude in (12, 90): # a barely-there overlay and a bold one
img = np.full((h, w, 3), 100, np.uint8)
loc = eng.locate(img)
x, y, bw, bh = loc.bbox
img[y + bh // 3 : y + 2 * bh // 3, x + bw // 4 : x + 3 * bw // 4] = 100 + amplitude
resp = eng.tophat_response(img, loc)
assert resp is not None
out.append(resp)
# max-normalized, so the two responses agree despite a 7.5x contrast difference
assert abs(int(out[0].max()) - int(out[1].max())) <= 1
def test_flat_input_yields_no_response(self):
"""A blank corner has no top-hat at all; the engine must return None rather
than divide by a zero peak."""
import numpy as np
from remove_ai_watermarks import doubao_engine
eng = doubao_engine.DoubaoEngine()
img = np.full((400, 900, 3), 128, np.uint8)
assert eng.tophat_response(img, eng.locate(img)) is None
def test_threshold_is_frontend_specific(self):
"""The continuous front-end scores higher overall (0.809 vs 0.723 mean on the
same positives), so it needs its own gate; the binary-era 0.40 left the
provenance-relaxed gate low enough to admit 8 false fires where 0.50 admits 1."""
from remove_ai_watermarks import doubao_engine
assert doubao_engine._CONFIG.detect_ncc_threshold == 0.50
+48
View File
@@ -0,0 +1,48 @@
"""Pure tests for the strength/steps profile helpers (no model, no torch needed)."""
from __future__ import annotations
import pytest
from remove_ai_watermarks.noai.watermark_profiles import resolve_strength, viable_steps
class TestViableSteps:
"""Guards the crash found by the release smoke matrix on 2026-07-19.
diffusers derives its img2img timesteps as ``int(steps * strength)``. When that
rounds to zero the pipeline builds an empty tensor and dies deep inside attention
with "cannot reshape tensor of 0 elements into shape [0, -1, 1, 512]". At the
default strength 0.15 that was every ``--steps`` below 7, reachable with entirely
valid CLI arguments and no special flags.
"""
@pytest.mark.parametrize(
("steps", "strength"),
[(1, 0.15), (2, 0.15), (5, 0.15), (6, 0.15), (5, 0.10), (9, 0.10), (1, 0.5)],
)
def test_never_returns_a_count_that_denoises_zero_steps(self, steps: int, strength: float):
assert int(viable_steps(steps, strength) * strength) >= 1
@pytest.mark.parametrize(
("steps", "strength"),
[(50, 0.15), (20, 0.15), (7, 0.15), (10, 0.10), (2, 0.5), (50, 1.0)],
)
def test_leaves_a_workable_count_untouched(self, steps: int, strength: float):
assert viable_steps(steps, strength) == steps
def test_raises_only_to_the_minimum_needed(self):
# strength 0.15 needs 7 (int(7*0.15)==1); it must not jump to some larger default.
assert viable_steps(5, 0.15) == 7
assert viable_steps(1, 0.10) == 10
def test_the_vendor_defaults_all_have_a_reachable_floor(self):
for vendor in (None, "openai", "google"):
strength = resolve_strength(None, vendor)
assert int(viable_steps(1, strength) * strength) >= 1
@pytest.mark.parametrize("strength", [0.0, -0.1])
def test_a_non_positive_strength_cannot_loop_or_divide_by_zero(self, strength: float):
# No denoising is possible at all here; return the caller's value rather than
# dividing by zero or spinning.
assert viable_steps(20, strength) == 20
+115 -61
View File
@@ -113,24 +113,51 @@ class TestFill:
class TestProvenanceGate:
"""The Gemini trust gate relaxes from 0.5 to 0.35 when provenance confirms Google;
tested deterministically by stubbing the engine's raw detection confidence."""
"""The Gemini trust gate relaxes from GEMINI_SPARKLE_TRUST_CONF to
_GEMINI_PROVENANCE_MIN_CONF when provenance confirms Google; tested
deterministically by stubbing the engine's raw detection confidence."""
def _stub(self, monkeypatch: pytest.MonkeyPatch, conf: float) -> None:
from remove_ai_watermarks.gemini_engine import DetectionResult
# `detected` mirrors the engine's own internal floor (0.35), which is
# independent of the registry gate under test here.
def fake_detect(image, force_size=None, *, trust_provenance=False):
return DetectionResult(detected=conf >= 0.35, confidence=conf, region=(10, 10, 48, 48))
monkeypatch.setattr(reg._engine("gemini"), "detect_watermark", fake_detect)
def test_midband_conf_needs_provenance(self, monkeypatch: pytest.MonkeyPatch):
# conf 0.42 sits in [0.35, 0.5): demoted without provenance, trusted with it.
self._stub(monkeypatch, 0.42)
# Comfortably inside the relaxed band: demoted without provenance, trusted with it.
conf = (reg._GEMINI_PROVENANCE_MIN_CONF + reg.GEMINI_SPARKLE_TRUST_CONF) / 2
self._stub(monkeypatch, conf)
img = np.zeros((256, 256, 3), np.uint8)
assert reg.get_mark("gemini").detect(img).detected is False
assert reg.get_mark("gemini").detect(img, provenance=True).detected is True
def test_below_provenance_gate_rejected_even_with_provenance(self, monkeypatch: pytest.MonkeyPatch):
"""Provenance relaxes the gate, it does not remove it.
Guards the 2026-07-18 raise of _GEMINI_PROVENANCE_MIN_CONF (0.35 -> 0.42).
The engine still reports `detected` down at its own 0.35 floor, so without
the registry gate this confidence would be accepted and inpainted. Measured
precision just below the gate was 13% (n=30, 95% CI 5-30%) on real
Google-metadata uploads -- i.e. ~7 of 8 accepts there destroy pixels on an
image that never carried a sparkle, and report a removal that did not happen.
"""
# 0.38 is inside the measured 13%-precision band and above the engine's own
# 0.35 floor, so the engine reports `detected` and only the registry gate can
# reject it. Hardcoded on purpose: if the gate is ever lowered back under this
# value, this test must fail on the BEHAVIOUR below, not on its own arithmetic.
self._stub(monkeypatch, 0.38)
img = np.zeros((256, 256, 3), np.uint8)
assert reg.get_mark("gemini").detect(img).detected is False
assert reg.get_mark("gemini").detect(img, provenance=True).detected is False
def test_provenance_gate_stays_below_the_strict_gate(self):
"""The relaxed gate must actually relax, and must not collapse onto the floor."""
assert 0.35 < reg._GEMINI_PROVENANCE_MIN_CONF < reg.GEMINI_SPARKLE_TRUST_CONF
def test_high_conf_detected_either_way(self, monkeypatch: pytest.MonkeyPatch):
self._stub(monkeypatch, 0.72)
img = np.zeros((256, 256, 3), np.uint8)
@@ -175,19 +202,6 @@ class TestSensitivity:
== "strict"
)
def test_assume_ai_without_evidence_is_assumed_not_confirmed(self):
# asserting the image is AI says nothing about WHICH vendor made it, so the mark
# is relaxed on assumption only -- it must not inherit the confirmed-vendor bypass
assert (
reg.resolve_trust("gemini", sensitivity="assume_ai", provenance=frozenset(), strict_keys=set()) == "assumed"
)
def test_assume_ai_with_metadata_is_confirmed(self):
assert (
reg.resolve_trust("gemini", sensitivity="assume_ai", provenance=frozenset({"gemini"}), strict_keys=set())
== "confirmed"
)
def test_auto_relaxes_on_own_metadata(self):
assert (
reg.resolve_trust("gemini", sensitivity="auto", provenance=frozenset({"gemini"}), strict_keys=set())
@@ -210,34 +224,74 @@ class TestSensitivity:
reg.resolve_trust("doubao", sensitivity="auto", provenance=frozenset(), strict_keys={"jimeng"}) == "strict"
)
def test_assumed_floor_rejects_weak_sparkle_but_passes_strong(self):
# the gate-bypassed sparkle detector fires on ~60% of ordinary photos at its bare
# 0.35 threshold; only a match well clear of that floor is trustworthy on assumption
assert reg.assumed_floor_ok("gemini", 0.35) is False
assert reg.assumed_floor_ok("gemini", 0.50) is True
def test_assumed_floor_default_passes_for_unfloored_marks(self):
# text marks relax cleanly (<1% bypassed false-fire), so they carry no floor
assert reg.assumed_floor_ok("doubao", 0.36) is True
def test_remove_auto_marks_accepts_all_sensitivities(self):
blank = np.zeros((256, 256, 3), np.uint8)
for s in ("auto", "strict", "assume_ai"):
for s in ("auto", "strict"):
_, removed = reg.remove_auto_marks(blank, sensitivity=s, backend="cv2")
assert removed == []
class TestNoBlanketRelaxation:
"""There is NO path that relaxes a mark's gate without same-product evidence.
``assume_ai`` was that path and was removed 2026-07-19: it bypassed every mark's
false-positive gate on the caller's bare assertion that the image is AI, which says
nothing about WHICH vendor or WHERE -- exactly what the bypass is contracted to
require. Before it carried a confidence floor it filled a phantom sparkle on 59.8%
of genuine camera photos. A user who can SEE a mark is served by `erase --region`
(they supply the coordinates) or `--mark <name> --no-detect` for a text mark.
"""
def test_sensitivity_has_exactly_two_levels(self):
import typing
assert set(typing.get_args(reg.Sensitivity)) == {"auto", "strict"}
def test_trust_ladder_has_no_assumed_level(self):
import typing
assert set(typing.get_args(reg.Trust)) == {"strict", "confirmed"}
def test_no_sensitivity_relaxes_without_same_product_evidence(self):
for sens in ("auto", "strict"):
assert reg.resolve_trust("gemini", sensitivity=sens, provenance=frozenset(), strict_keys=set()) == "strict"
def test_the_assumed_floor_helper_is_gone(self):
"""It existed only to make the blanket relaxation tolerable."""
assert not hasattr(reg, "assumed_floor_ok")
assert not hasattr(reg, "_ASSUMED_CONF_FLOOR")
def test_the_removed_value_raises_instead_of_silently_meaning_auto(self):
"""`Sensitivity` is a Literal and unenforced at runtime, so a 0.15 caller passing
the removed value would quietly get `auto` -- a silent semantic change on exactly
the release where they need to be told. The error names the replacement."""
import pytest
with pytest.raises(ValueError, match="erase"):
reg.validate_sensitivity("assume_ai")
with pytest.raises(ValueError, match="unknown sensitivity"):
reg.validate_sensitivity("aggressive")
assert reg.validate_sensitivity("auto") == "auto"
assert reg.validate_sensitivity("strict") == "strict"
def test_context_rejects_it_too(self):
"""The arbiter's own entry point validates, so a direct `decide()` caller cannot
smuggle the removed mode past the public API."""
import pytest
with pytest.raises(ValueError, match=r"removed in 0\.16"):
reg.Context(sensitivity="assume_ai")
class TestArbiter:
"""``decide`` is the PURE removal arbiter: (candidates, context) -> ordered
winners, no image / no I/O. Tested in isolation by handing it fabricated
Candidates -- this is the payoff of separating decision from perception."""
@staticmethod
def _c(key, *, strict=False, relaxed=False, flat=False, relaxed_conf=1.0):
# relaxed_conf defaults high so a test that does not care about the assumed-trust
# confidence floor exercises the trust logic, not the floor.
def _c(key, *, strict=False, relaxed=False, flat=False):
feats = {"footprint_flat": 1.0} if flat else {}
return reg.Candidate(key, f"L:{key}", strict, relaxed, relaxed_conf, feats)
return reg.Candidate(key, f"L:{key}", strict, relaxed, feats)
def _keys(self, cands, ctx):
return {d.candidate.key for d in reg.decide(cands, ctx)}
@@ -249,35 +303,6 @@ class TestArbiter:
# relaxed-only detection must NOT fire under strict
assert self._keys([self._c("gemini", relaxed=True)], reg.Context(sensitivity="strict")) == set()
def test_assume_ai_uses_relaxed(self):
fired = reg.decide([self._c("gemini", relaxed=True)], reg.Context(sensitivity="assume_ai"))
assert [d.candidate.key for d in fired] == ["gemini"]
assert fired[0].relax is True
def test_assume_ai_drops_sparkle_below_the_assumed_floor(self):
# REGRESSION (2026-07-16): assume_ai passed trust_provenance=True to the engine,
# bypassing the sparkle false-positive gate on the mere ASSERTION that the image is
# AI -- but that flag is contracted to mean "metadata proved this vendor". The bare
# bypassed gate (conf 0.35) fired on 59.8% of 256 genuine camera captures, so
# `--sensitivity assume-ai` filled a phantom sparkle on ~6 of every 10 clean photos.
weak = self._c("gemini", relaxed=True, relaxed_conf=0.40)
assert reg.decide([weak], reg.Context(sensitivity="assume_ai")) == []
def test_assume_ai_keeps_sparkle_confirmed_by_metadata_below_the_floor(self):
# the floor exists because the vendor is UNKNOWN; once metadata names Google the
# bypass is contract-legal again, so a weak match is still trusted
weak = self._c("gemini", relaxed=True, relaxed_conf=0.40)
ctx = reg.Context(sensitivity="assume_ai", provenance=frozenset({"gemini"}))
assert [d.candidate.key for d in reg.decide([weak], ctx)] == ["gemini"]
def test_assume_ai_is_monotonic_over_strict(self):
# a mark the STRICT gate accepted must never be dropped by the assumed floor:
# assume_ai only ever adds recall
weak_but_strict = self._c("gemini", strict=True, relaxed=True, relaxed_conf=0.40)
fired = reg.decide([weak_but_strict], reg.Context(sensitivity="assume_ai"))
assert [d.candidate.key for d in fired] == ["gemini"]
assert fired[0].relax is False # accepted on the strict verdict, so mask at strict
def test_auto_relaxes_on_provenance(self):
c = [self._c("gemini", relaxed=True)]
assert self._keys(c, reg.Context(provenance=frozenset({"gemini"}))) == {"gemini"}
@@ -309,6 +334,35 @@ class TestArbiter:
]
assert "jimeng_pill" in self._keys(cands, reg.Context())
def test_weak_pill_detection_does_not_confirm_the_jimeng_wordmark(self):
"""The pill is too false-fire-prone (~7%) to grant a sibling `confirmed` trust.
Corpus-measured defect (2026-07-18): a pill false fire on clean non-ByteDance
content confirmed jimeng, relaxing its NCC gate 0.45 -> 0.3825; jimeng then
false-fired, and _keep_pill's wordmark arm removed the pill UNRESTRICTED,
skipping the flatness guard. Closed loop on the default `auto` path.
"""
# Only the pill is strictly detected. jimeng scores in the band that is
# reachable ONLY via the relaxed gate.
cands = [
self._c("jimeng_pill", strict=True, relaxed=True, flat=False),
self._c("jimeng", strict=False, relaxed=True),
]
fired = {d.candidate.key for d in reg.decide(cands, reg.Context("auto", frozenset()))}
assert "jimeng" not in fired, "a weak pill hit must not relax the jimeng wordmark"
# and with jimeng gone, the pill loses the wordmark arm too -- no unrestricted
# removal of a textured footprint on content nothing confirmed.
assert "jimeng_pill" not in fired
def test_real_jimeng_wordmark_still_corroborates_the_pill(self):
"""The fix removes only the pill's TESTIMONY, not the wordmark's."""
cands = [
self._c("jimeng", strict=True, relaxed=True),
self._c("jimeng_pill", strict=True, relaxed=True, flat=False),
]
fired = {d.candidate.key for d in reg.decide(cands, reg.Context("auto", frozenset()))}
assert fired == {"jimeng", "jimeng_pill"}
class TestProvenanceMaskThreading:
"""Regression for the provenance-relaxed Gemini no-op (#1) and the false 'removed'