diff --git a/src/remove_ai_watermarks/cli.py b/src/remove_ai_watermarks/cli.py index 4e85c31..4bcbf41 100644 --- a/src/remove_ai_watermarks/cli.py +++ b/src/remove_ai_watermarks/cli.py @@ -626,6 +626,14 @@ def cmd_identify(ctx: click.Context, source: Path, no_visible: bool, as_json: bo console.print(f"\n Verdict: {verdict} [dim](confidence: {report.confidence})[/]") console.print(f" Platform: {report.platform or '[dim]undetermined[/]'}") + if report.is_ai_generated is None: + console.print( + " [dim]No locally-readable AI signal found. This is not the same as 'clean': " + "metadata is often stripped by re-encoding, screenshots, or upload, and SynthID-class " + "pixel watermarks (Gemini / Nano Banana / gpt-image) have no local detector. " + "See caveats below.[/]" + ) + if report.integrity_clashes: console.print("\n [bold red]⚠ Integrity clash[/] [dim](provenance signals contradict each other)[/]") for clash in report.integrity_clashes: diff --git a/tests/test_cli.py b/tests/test_cli.py index ecea89e..b47428c 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -375,6 +375,14 @@ class TestIdentifyCommand: assert result.exit_code == 0 assert "unknown" in result.output + def test_identify_unknown_explains_why(self, runner, tmp_clean_png): + # An unknown verdict must explain itself inline (issue #22: users read a bare + # "unknown" as the tool being broken) rather than only in the caveats section. + result = runner.invoke(main, ["identify", str(tmp_clean_png), "--no-visible"]) + assert result.exit_code == 0 + assert "No locally-readable AI signal found" in result.output + assert "not the same as 'clean'" in result.output + def test_identify_ai_png_reports_platform(self, runner, tmp_png_with_ai_metadata): result = runner.invoke(main, ["identify", str(tmp_png_with_ai_metadata), "--no-visible"]) assert result.exit_code == 0