Keep fingerprints stable across merges, selections and renames - #59
Draft
tauanbinato wants to merge 1 commit into
Draft
tauanbinato wants to merge 1 commit into
tauanbinato wants to merge 1 commit into
Conversation
A repeat is identified by its copies, a file outline by its path with its members matched by similarity, and a custom hunk by the definition around it and its own run of changed lines. A finding of a renamed file is also known under its old path. Baseline entries written before still accept their findings through the fingerprints they had then, and the next baseline write rewrites them, keeping reasons and notes. SARIF carries both fingerprints for a release.
This was referenced Oct 4, 2026
This branch has not been deployed
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.
Closes #56
Fingerprints stay the same across merges, stacked branches, changes to which files a check selects, and renames. A dismissed finding is no longer raised again because main moved underneath it.
What changes
path::function, sorted, orpath#hashfor a copy outside a function. The owner path, the statement-window hash and the representative pair no longer count. A baseline accepts a repeat while every copy is one that an accepted repeat names. A copy that goes keeps the repeat accepted; a new copy that joins raises it again.inevidence. Hand-written examples keep each hunk whole.--baseand in the hook's turns, a finding of a renamed file is also known under its old path (aliases).--basestill does not search unchanged files.Decisions (user, 2026-10-03, recorded on #56)
fingerprint_v1until a baseline write rewrites it to v2 with its reason and note. The writes arejevgate baseline,--merge, andbaseline mark, which rewrites the v1 entries matched by findings of the last report. Gates stay green on upgrade. One whole check plus a write produces the one-time diff.members.version: 1. Baselines without the new fields load unchanged.Other consumers after the switch
partialFingerprintscarries bothjevgateFingerprint/v1(the v1 fingerprint where it changed, else the same value) andjevgateFingerprint/v2, so code-scanning alerts keep their identity across the upgrade. v1 is dropped in the release after this one (user, 2026-10-04, recorded on Keep finding fingerprints stable across merges of main or a parent branch #56); this release's notes mark it deprecated.changes.rslineage: persistent and resolved also match through earlier fingerprints. A snapshot taken before the upgrade or rename does not show resolved plus introduced pairs (tested).Tests and what they protect
fingerprints::a_repeat_has_one_fingerprint_whichever_copy_a_check_selects: a check ofb.rswitha.rsas context gives the same fingerprint as a whole run. The v1 values differ, which is the bug.fingerprints::a_repeat_a_new_copy_joins_is_asked_about_again_and_one_a_copy_leaves_is_not: new duplication is never hidden, and a removed copy keeps the acceptance.fingerprints::an_old_baseline_accepts_through_earlier_fingerprints_until_a_write_rewrites_it: a hand-written 0.35 baseline (v1 entry,later, note#12) still accepts its finding.markrewrites it to v2 and keeps the reason and note. The turn's dismissals are only the mark, and the gate passes.fingerprints::writing_the_baseline_over_an_old_one_moves_each_entry_to_its_current_fingerprint: coversbaselinewith and without--merge. No v1 entry is kept, the reason and note survive, andmembersis written.organization::an_accepted_outline_stays_accepted_while_its_members_stay_similar: 34 of 35 names shared stays accepted; 34 of 45 is raised again.changed::a_hunk_keeps_its_identity_when_a_change_nearby_or_a_function_above_comes_and_goes, plushunks::each_run_of_changed_lines_is_a_hunk_with_the_unchanged_lines_around_it: the parent-edit and new-function-above cases from the spike.custom::a_renamed_files_finding_stays_accepted_through_its_old_path: rename aliases.entries::an_outline_stays_similar_until_a_fifth_of_its_names_change: the 0.8 threshold.changes.rslineage test (earlier fingerprints), and the blank-line hunk test (now two runs).Validation
cargo fmt --checkandcargo clippy --locked --all-targets -- -D warnings: clean.cargo test --locked: 898, 83 and 3 passed.rules_test::a_question_that_separates_its_examples_passes_and_a_rerun_asks_nothingfailed once in a full run (modelsreadtypesafe/jev-1.13), then passed alone and in two later full runs. It looks like a pre-existing parallel-test flake.jevgate check --base origin/main --rule all --include-tests, with the installed 0.35.0 that CI runs: passes with no new findings.Findings fixed
baseline.rs: matching moved tobaseline/entries.rs, listing and stats tobaseline/listing.rs.compose/identity.rs.Finding::fingerprints(lineage and the hook)entries,keptandmark_entriesin the baselinesitesinduplicates.rsnote_renamesin the planenclosingfor hunksrenamed_onlyfor grouped findingsknownandsplitinto smaller functions.Dismissals (one at a time, with the reason)
src/baseline.rs:19file-organization, intended: the baseline file's reads and writes; this PR moved matching and listing out.src/baseline.rs:358shared-logic, wrong: oneread_latestcall, optional for a mark and required for a write.src/hook/events.rs:311told, intended: as on main; one partition condition changed.src/units/compose/comments.rs:44comment_findings, intended: as on main; only the identity is named and aliased.src/units/compose/mod.rs:667finding, intended: as on main; it only takes the fingerprint fromknown().src/units/compose/mod.rs:374shared-logic, wrong: one first-line expression in three struct literals.src/units/compose/mod.rs:350shared-logic, wrong: a two-arm match on the custom question in two different outputs.src/units/custom/hunks.rs:189split, wrong: a short loop over parts that helpers compute.src/units/custom/items.rs:199shared-logic, wrong: each kind zips its own items with their ids, and the items differ.src/units/outcome/mod.rs:258rule_outcome, intended: as on main; only a pattern field was added.src/units/plan/mod.rs:98plan, intended: as on main; only thenote_renamescall was added.src/units/tests/custom.rs:6file-organization, intended: the custom-question tests, one file per rule; this PR adds a rename test.src/units/tests/mod.rs:34file-organization, intended: the unit tests' shared helpers; this PR addsaccept_all.The location marks also re-marked 7 older entries at the same lines. I restored their original reasons and notes, so the baseline diff only adds entries. The dismissals were made with the installed 0.35.0, so they carry the v1 fingerprints CI matches. 0.36 accepts them through
fingerprint_v1until a write rewrites them.Known limits
--basechecks, the hook andmarkkeep the reason.COMPOSITIONis now v15. Rule versions and the answer cache are unchanged. Custom hunk questions ask about smaller units, so their requests differ from 0.35's.