ADR-0694: Tighten clang-tidy enforcement + confirm sanitizers as required CI gates¶
- Status: Accepted
- Date: 2026-05-28
- Deciders: lusoris
- Tags:
ci,lint,testing,security,fork-local
Context¶
ADR-0686 (VMAFX Phase 2B umbrella) requires two CI hardening steps:
-
Tighter clang-tidy: add
cert-err33-c(every non-void return value must be checked or(void)-cast) andbugprone-not-null-terminated-result(catchesstrncpy/strlcpyresults that may not be null-terminated). CLAUDE.md §6 already mandates this as a coding rule; making it a CI check closes the enforcement gap between documentation and tooling. -
Sanitizers promoted to required CI gates: ASan + UBSan + MSan (thread) jobs should block merges, not just report advisorily.
Scanning the tree on 2026-05-28 (CPU build, 394 source files in libvmaf/src + libvmaf/tools) produced:
cert-err33-c: 394 violations across 394 C source files (pre-existing unchecked return values, primarily from library calls whose return values are currently discarded without a(void)cast).bugprone-not-null-terminated-result: 0 violations.
Because 394 violations far exceed the 50-violation threshold established in the task brief, cert-err33-c is added as an advisory check (enabled in the Checks: glob but excluded from WarningsAsErrors). A follow-up sweep PR will fix the violations file-by-file and promote the check to an error-level gate once the tree is clean.
Regarding the sanitizer promotion: inspection of tests-and-quality-gates.yml and required-aggregator.yml confirms that the three sanitizer jobs (address, undefined, thread) already:
- run on every non-draft PR (no
continue-on-error: true), - are listed in the
requiredarray of the Required Checks Aggregator (ADR-0313), and - block merges via the single
Required Checks Aggregatorbranch-protection context.
Therefore the sanitizer-promotion work for this PR is confirmatory: this ADR documents the current state as intentional, records the branch-protection admin step (see Consequences), and establishes the audit trail.
Decision¶
We will:
- Add a comment block to
.clang-tidyexplicitly calling outcert-err33-candbugprone-not-null-terminated-resultas Phase 2B / ADR-0686 additions. Both are already caught by the existingbugprone-*/cert-*globs but listing them explicitly documents intent and permits targetedWarningsAsErrorspromotion in a future PR. - Exclude
cert-err33-cfromWarningsAsErrorsin this PR pending the violation-fix sweep (394 violations).bugprone-not-null-terminated-resulthas zero violations and could be promoted now; we defer it to the same sweep PR for consistency. - Confirm that the three sanitizer matrix jobs (address/undefined/thread) are already required CI gates and document that state.
- Add a manual admin step note reminding the repo admin to verify that the GitHub branch-protection "Required status checks" list includes the aggregator context (which in turn polls for the sanitizer job names).
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Add cert-err33-c to WarningsAsErrors immediately | Strictest enforcement from day one | 394 pre-existing violations break CI for every unrelated PR | Violates CLAUDE.md §12 r12 "touched files lint-clean": enabling a check as an error that flags hundreds of untouched files is not the same as a per-file cleanup rule |
Skip cert-err33-c entirely this PR | No CI disruption | Leaves the gap between the coding rule in CLAUDE.md and the clang-tidy enforcement | The task brief is explicit: add the check; advisory is the correct staging mechanism |
| Separate sanitizer-promotion PR | Cleaner separation of concerns | The sanitizers are already promoted — splitting adds overhead with no benefit | Both changes are part of the same ADR-0686 scope; confirmed state belongs in the same PR |
Consequences¶
- Positive:
cert-err33-candbugprone-not-null-terminated-resultare now tracked violations visible in the full clang-tidy scan (nightly.yml::clang-tidy-fulluploads a log artifact) and the PR-scopedClang-Tidy (Changed C/C++ Files)job. Developers adding new C files will see warnings immediately.-
The sanitizer gates are documented as intentional required checks, closing an audit gap.
-
Negative:
-
The PR-diff clang-tidy job will emit advisory warnings for any touched C file that contains unchecked return values. This is expected and intended.
-
Neutral / follow-ups:
- Follow-up PR required: scan the 394
cert-err33-cviolations, fix them (add(void)casts or check return values), and promote the check toWarningsAsErrors. Track asT-CERT-ERR33-SWEEPindocs/state.md. - Admin step: verify that the GitHub branch-protection "Required status checks" for
masterincludesRequired Checks Aggregator(the single context that polls for all jobs in itsrequiredlist). If the individual sanitizer job names (Sanitizers — ASan + UBSan + MSan (address)etc.) are listed directly in the branch-protection settings rather than through the aggregator, the admin must remove them from direct listing to avoid conflicts. The aggregator is the canonical gate (ADR-0313). This step requires repository-admin GitHub permissions and cannot be automated viaghCLI without--adminoverride.
References¶
- Umbrella ADR-0686 (VMAFX Phase 2B scope, per user direction)
- ADR-0313 (Required Checks Aggregator pattern)
- ADR-0347 (sanitizer matrix test scope / deselect list)
- ADR-0141 (touched-file lint-clean rule)
- ADR-0278 (NOLINT citation rule)
- ADR-0012 (coding standards: JPL + CERT + MISRA)
docs/principles.md§6 — banned-function list and non-void return value rule- Source:
req— "Banned C funcs: TIGHTEN. Add clang-tidy cert-err33-c + bugprone-not-null-terminated-result enforcement (option 1) AND promote sanitizers (ASan + UBSan + MSan) to required gates on every PR not just nightly (option 3). Both, not either."