Three ways the fail-closed gate failed open, all reproduced by new tests first:
- `git diff --name-only` quotes any path it cannot print as UTF-8
(core.quotePath, on by default). The quoted string was handed back to
`git diff -- <path>`, which matches nothing, and an empty diff is `continue`d —
so a credential under `café/` committed cleanly. The list is now NUL-delimited
and read from a temp file, because command substitution deletes NUL bytes and
the names must survive to the loop that reads them.
- The placeholder filter matched the whole added line, so `API_TOKEN=<live> #
example, placeholder, changeme` was exempt on the strength of its own comment.
The filter now reads the assigned value only — the token up to whitespace or
`#`. Truncation errs toward blocking; ALLOW_SECRETS remains the way out.
- `report+=` ended on a command substitution, so the next file's name was glued
onto the previous file's last hit line and the report blamed the wrong file.
Three tests, all red before the change: a quoted path, a placeholder comment
beside a live value, and two leaking files each named on their own line.
Refs #60