Round-3 review found two clause mutants of the main path surviving the selftest:
the sg_rc check forced true (M9) and the main-path opt-out widened to any
non-empty value (M10). The present case was pinned only through require_semgrep,
and no full shimmed run used a semgrep that succeeds.
--selftest now runs the script end to end with shimmed docker/curl/timeout (ZAP
clean) and a semgrep shim that records each call: succeeding (unset and =0) ->
exit 0 with semgrep run; failing -> exit 2; explicit opt-out with semgrep
present -> exit 0 with semgrep not run. Witnessed red: M9 fails two cases, M10
fails the skip=0 case, and forcing the main-path skip off (`if false`) fails
the opt-out case; M1-M8 stay red.
The header's exit contract claimed exit 2 always meant the SAST half did not
run, but a missing docker also exits 2. It now lists 0/1/2/other, with 2 as
"a scanner half did not run", and the host line states the requirement instead
of naming bumblebee.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: f9ce6bc6-5b36-491a-8d72-89c1f70dd3e1