fix(governance): green the Validate Hypatia Baseline gate on main - #856
Merged
Merged
Conversation
`governance / Validate Hypatia Baseline` has been the only real red on main (run 35783374301, job 106934294699, 38s -- a real verdict, not a startup death): "Gate failed: 5 unfiltered finding(s) at or above 'info'". A standing red on main is itself a detector-blinding condition -- while it sits there nobody can distinguish a new gate failure from the old one. Five findings, two clusters, two different causes. 1. Three security_errors/secret_detected (critical) at lib/hypatia/scanner_suppression.ex:252. The comment documenting the three @secret_patterns that match on form alone spells out the three shapes -- and is therefore matched by all three of the patterns it documents. Same family as the RE005 defect in #834: documenting a false-positive class creates instances of it. Cured with a line-scoped inline directive. Not a baseline entry: the baseline match key has no `line` field, so an entry would suppress every present and future secret finding in this file. Not @training_corpus_paths either -- that is compiled into the scanner, which the gate builds from a pin, so it cannot affect this gate at all, and it would blind the scanner core to a real leak. 2. Two code_safety findings (high) on ffi/zig/src/main.zig -- zig_ptr_cast and zig_align_cast on the mandatory opaque-handle idiom. Already filed as #834. Cured with two .hypatia-baseline.json acknowledgements carrying tracking_issue: #834. The baseline is the only mechanism that can reach these: the rule reports at main.zig:1 rather than the cast site, so no per-line directive applies, and the only other source-level option is file-wide. These entries are to be DELETED when #834 lands -- #834's own acceptance criteria require main.zig clean without changing the cast. Root cause of cluster 1, for the record: the gate builds the scanner from 0e91342 (2026-09-06) and scans HEAD. bc8812e -- label-aware secret suppression, #782, 2026-09-14 -- is an ancestor of main and is NOT an ancestor of the pin. Our own cure for this class is in the repo and absent from the scanner enforcing the gate. Bumping the pin is a required check on ~120 caller repos, so it is filed separately rather than folded in here. Verified by local reproduction of CI's three steps (scanner built from the pin, apply-baseline.sh at its own sparse-checkout pin 874ffe58, BLOCKING_THRESHOLD info): control 6 raw / 5 kept / 1 suppressed, matching CI field-for-field; cured 3 raw / 0 kept / 3 suppressed. Five mutants, all killed: - ghp_ + 36 chars in lib/hypatia/cli.ex -> red (kept 1) - ghp_ + 36 chars elsewhere in this file -> red (kept 1); the allow is LINE-scoped, not file-scoped - delete the two zig baseline entries -> red (kept 2) - delete the inline directive line -> red (kept 3), regenerating the original failure exactly - revert all -> green (kept 0) Accepted trade-off, stated rather than left to be found: a real secret placed on the one directive-covered line would be suppressed. That line is a fixed documentation literal listing three regex shapes; any edit to it is visible in review, and every other line and file still fails the gate. The 45 pre-existing baseline entries are byte-identical (16 insertions, 0 deletions) -- the entries were spliced textually. Rewriting the file through jq silently re-encodes \u2014 escape sequences as literal em dashes in entries it was never asked to touch, which would have shown up as a spurious deletion. Ratchet-exception: .hypatia-baseline.json — two acknowledgements for the opaque-handle false positive tracked in #834. The ledger grows 45 -> 47 and the exemption ratchet is right to ask. The baseline is the only mechanism that can reach a finding the rule reports at main.zig:1, and #834's acceptance criteria require deleting both entries when it lands. Both carry a note and a tracking_issue, so neither is anonymous debt. Refs #855, #834, #782 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0113HQM9LVGkNCzU1WwkJZSV
Contributor
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This was referenced Sep 22, 2026
Contributor
|
❌ Failed to create Coding Agent finishing-touch task. Please try again. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
governance / Validate Hypatia Baselinehas been the only real red onmain— run 35783374301, job 106934294699, 38s. A 38-second job is a realverdict, not a startup death:
A standing red on
mainis itself a detector-blinding condition: while it sitsthere, nobody can tell a new gate failure from the old one. That is the
reason to clear it, and the reason not to clear it by widening a suppression.
BLOCKING_THRESHOLDisinfoand the scanner only emits>= medium, so"kept" must reach zero. There is no headroom to trade.
The five findings, two clusters, two different causes
1 — three
security_errors/secret_detected(critical) atlib/hypatia/scanner_suppression.ex:252The comment documenting the three
@secret_patternsthat match on formalone spells those three shapes out — and is therefore matched by all three
of the patterns it documents. Same family as the RE005 defect in #834:
documenting a false-positive class creates instances of it.
Cured with a line-scoped inline directive. Deliberately not the
alternatives:
severityANDrule_moduleANDtypeAND (fileexact ORfile_patternglob) — there isno
linefield. An entry would suppress every present and futuresecret finding in this file.
@training_corpus_paths/@default_exemptions. Those are compiledinto the scanner, and the gate builds the scanner from a pin (below), so they
cannot affect this gate at all — and they would blind the scanner core to a
real leak.
2 — two
code_safety(high) onffi/zig/src/main.zigzig_ptr_castandzig_align_caston the mandatory opaque-handle idiom.main.zigdeclarespub const Handle = opaque {}, so the concreteHandleStateis never named in the C header and recovering it requires exactly@ptrCast(@alignCast(handle)). Already filed as #834.Cured with two
.hypatia-baseline.jsonacknowledgements carryingtracking_issue. The baseline is the only mechanism that can reach these:the rule reports the finding at
main.zig:1rather than the cast site, so noper-line directive applies, and the only other source-level option is
file-wide.
Root cause of cluster 1 — stated, but deliberately not fixed here
The gate builds the scanner from a pinned commit and scans
HEAD. They aredifferent commits.
0e913426(2026-09-06)main@8e8943f(2026-09-22)bc8812e— label-aware secret suppression, #782, 2026-09-14 — is our own fixfor this exact false-positive class. Measured both directions:
Corroborated by a grep count of
context_safe_line?|@form_ambiguous|labelinlib/hypatia/scanner_suppression.ex: pinned = 4, HEAD = 12.So the cure is in the repo and absent from the scanner enforcing the gate.
Bumping
HYPATIA_PINin standards'governance-reusable.ymlis a requiredstatus check on ~120 caller repos, so it is filed separately rather than
folded in here. Tracked in #855.
Verification — local reproduction of CI's three steps
Scanner built from the pin
0e913426;apply-baseline.shat its ownsparse-checkout pin
874ffe58(not the reusable workflow'sda2c748a);BLOCKING_THRESHOLD=info.main)The control reproduces CI field-for-field: 5 kept at or above
info.Mutants — six, all killed
ghp_+ 36 chars inlib/hypatia/cli.exghp_+ 36 chars elsewhere in this fileRatchet-exceptiontrailer (same tree)Mutant 4 is the strongest: removing one comment line regenerates the original
raw=6/ three-secret-finding state, so the cure and the disease are provablythe same object.
The fixture token was used only inside an isolated tree under
$CLAUDE_JOB_DIR/tmp. No fixture token ever touched this worktree.Accepted trade-off — stated, not left to be found
A real secret placed on the one directive-covered line would be
suppressed. This is measured (mutant 2b), not assumed.
That line is a fixed documentation literal listing three regex shapes. Any edit
to it is visible in review, and every other line of this file, and every
other file, still fails the gate — proven by mutants 1 and 2. The inline
directive is strictly narrower than either alternative: a baseline entry would
cover the whole file forever, and a
file_allowed?header directive wouldcover the whole file too.
The exemption ratchet
Both ratchet jobs carry
if: github.event_name == 'pull_request', so they wereskippedon every push tomainand fire here for the first time..hypatia-baseline.jsonisLEDGERS[0], and this PR grows it 45 → 47, so thecommit carries a declared
Ratchet-exception:naming that ledger.Ratchet rules 2 and 3 pass on their own merits rather than by exception: both
new entries carry a
noteand atracking_issue(neither is anonymousdebt), and both use an exact
filerather than afile_patternwildcard.Verified by running standards' actual
check-exemption-ratchet.sh:The debt ratchet is a no-op — no
.machine_readable/Debtfile.a2mlat base orHEAD.
Diff integrity
The baseline change is purely additive and the 45 pre-existing entries are
byte-identical (
jq -Sboth sides +cmp -s→ IDENTICAL). The entries werespliced textually on purpose: rewriting the file through
jqsilentlyre-encodes
—escape sequences as literal em dashes in entries it wasnever asked to touch, which would have shown up as a spurious deletion.
ffi/zig/src/main.zigis not modified — #834 requires the cast stay as itis.
Caveat
Local toolchain is Elixir 1.18.3 / OTP 27; CI uses 1.19.4 / OTP 28.3. The
scanner source is the pinned commit in both cases — only the BEAM differs.
The PR run is the authoritative measurement.
Refs #855, #834, #782. Not
Closes— #855's acceptance criterion is green onmain, which only the post-merge run can prove.🤖 Generated with Claude Code
https://claude.ai/code/session_0113HQM9LVGkNCzU1WwkJZSV