If two judges with different settings share one identity, the cache cannot tell them apart. Results computed for one get served for the other, and nothing fails.

The defect

In Microsoft PyRIT, two DecodingScorer instances with different matcher settings could return opposite verdicts on the same input while sharing both their hash and their eval_hash. Cached evaluation metrics could then be reused across scorers that do not behave the same way.

The same defect had already been described and fixed for SubStringScorer. Both classes hold a text matcher at the same point of the same method. Only the sibling passed the matcher's parameters into its identifier.

The fix

get_matcher_params = getattr(self._text_matcher, "get_identifier_params", None)
"text_matcher_params": get_matcher_params() if callable(get_matcher_params) else None,

Two lines, copied from the sibling on purpose. Case sensitivity, whitespace handling, threshold and n-gram size now reach the identity. Custom matchers that only implement is_match keep working through the callable guard.

The evidence

Scorer categories are absent from both classes' identifiers. Widening one class and not the other would have broken the symmetry the fix relied on, so I kept the change symmetric and offered to cover categories for both in a follow-up.

What I take from it

When a bug is fixed in one class, read its siblings. The second instance of a defect is usually sitting one file away, waiting for nobody to look.

Ayoub Hammoudi, Software engineer, full-stack and AI platforms. github.com/Ayoubhm07