Class: Browserctl::Replay::FingerprintMatcher
- Inherits:
-
Object
- Object
- Browserctl::Replay::FingerprintMatcher
- Defined in:
- lib/browserctl/replay/fingerprint_matcher.rb
Overview
Scores candidate snapshot entries against a recorded fingerprint and returns the best match above a configurable threshold.
Inputs are the wire-shape fingerprints emitted by Snapshot::Fingerprint:
{ text:, role:, neighbors: [...], position: { index:, depth: } }
Score is a weighted sum in [0.0, 1.0]:
text 0.40 (exact match; case-insensitive)
role 0.20 (exact match)
neighbors 0.25 (Jaccard over the neighbor sets)
position 0.15 (proximity in (index, depth) space)
Defaults reflect the v0.11 acceptance bar: text + role together (0.60) are enough to clear the default threshold, so a renamed neighbor or a shifted index doesn’t break replay.
Defined Under Namespace
Classes: Match
Constant Summary collapse
- DEFAULT_THRESHOLD =
0.6- WEIGHTS =
{ text: 0.40, role: 0.20, neighbors: 0.25, position: 0.15 }.freeze
Instance Method Summary collapse
-
#best(target_fp, candidates) ⇒ Object
Returns the highest-scoring candidate entry above the threshold, or nil if no candidate qualifies.
-
#initialize(threshold: DEFAULT_THRESHOLD, weights: WEIGHTS) ⇒ FingerprintMatcher
constructor
A new instance of FingerprintMatcher.
- #score(target, candidate) ⇒ Object
Constructor Details
#initialize(threshold: DEFAULT_THRESHOLD, weights: WEIGHTS) ⇒ FingerprintMatcher
Returns a new instance of FingerprintMatcher.
26 27 28 29 |
# File 'lib/browserctl/replay/fingerprint_matcher.rb', line 26 def initialize(threshold: DEFAULT_THRESHOLD, weights: WEIGHTS) @threshold = threshold @weights = weights end |
Instance Method Details
#best(target_fp, candidates) ⇒ Object
Returns the highest-scoring candidate entry above the threshold, or nil if no candidate qualifies. ‘candidates` must be an array of snapshot entries (hashes with a :fingerprint key). The returned Match wraps the candidate hash and the numeric score.
35 36 37 38 39 40 41 42 43 44 |
# File 'lib/browserctl/replay/fingerprint_matcher.rb', line 35 def best(target_fp, candidates) scored = candidates .map { |c| Match.new(candidate: c, score: score(target_fp, c[:fingerprint])) } .sort_by { |m| -m.score } winner = scored.first return nil unless winner && winner.score >= @threshold winner end |
#score(target, candidate) ⇒ Object
46 47 48 49 50 51 52 53 |
# File 'lib/browserctl/replay/fingerprint_matcher.rb', line 46 def score(target, candidate) return 0.0 unless target && candidate (@weights[:text] * text_score(target[:text], candidate[:text])) + (@weights[:role] * bool_score(target[:role] == candidate[:role])) + (@weights[:neighbors] * jaccard(target[:neighbors], candidate[:neighbors])) + (@weights[:position] * position_score(target[:position], candidate[:position])) end |