Commit Graph

2 Commits

Author SHA1 Message Date
Martin Vogel 64bd272cb2 fix(ci): mark the new lane scripts executable, and let the contract see them
scripts/ci/lint-mem.sh and scripts/ci/msan-lane.sh were committed at mode
100644, so the workflow step that runs them directly died with
"Permission denied" (exit 126). scripts/lint-mem-gate.py gets the same
treatment: it is invoked through python3 today, but it carries a shebang
and should not depend on that.

The exec-bit contract already exists to catch precisely this, and it did
not, because it derives its candidate set from `git ls-files -s '*.sh'`
-- tracked files only. A brand-new script is invisible there until it is
committed, so the check passes on the run where the defect is introduced
and only starts failing on the run that ships it. The window where the
contract is most useful was the one window it could not see.

It now also considers not-yet-tracked scripts by their filesystem mode.
Verified against the real defect rather than in the abstract: with
lint-mem.sh untracked and non-executable the contract reports
"_lint.yml:76 executes scripts/ci/lint-mem.sh directly, but its committed
mode is 100644", and passes once the bit is set.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 15:42:32 +02:00
Martin Vogel 0c223d9027 build(lint): pin analyzer suppressions to the code they argue about
The memory gate stays gating, and it now has a way to record a genuine
false positive that cannot quietly outlive its own reasoning.

A whitelist entry names one (file, function, check) and carries two
things: why the analyzer is wrong, argued from the code, and what was
tried before concluding that. The entry is pinned to the sha256 of that
function's text. Edit the function and the entry stops counting -- the
finding comes back and has to be argued again against the code as it now
is. This is the part that matters. A suppression that survives the code
it was written about reads as "reviewed" while being nothing of the
kind, which is worse than no suppression at all.

The gate fails on: a finding with no entry, a finding whose entry has
gone stale, and an entry that asserts rather than argues (there is a
floor on how much reasoning an entry must actually contain -- the
mechanical half of "argued, not asserted"; whether the argument is
correct stays a review question). An entry that matches no finding is
reported but does not fail, because analyzer versions differ across
platforms.

NOLINT is still not honoured here and the gate does not read it.

The whitelist ships empty: the analyzer is currently clean across
LINT_SRCS, so nothing is being suppressed today. The mechanism exists
for the first finding that genuinely warrants it.

Verified by exercising each path rather than only the clean one: an
unaccounted finding fails and names its enclosing function; an argued
entry with a matching hash passes; the same entry fails as STALE once
the hash no longer matches the function; and an entry that says only
"false positive" fails for not arguing its case.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 15:37:33 +02:00