An empty diff does not establish valid input. Reading dictionary construction in the snapshot tool led me to inspect what can disappear when an identity occurs more than once. This story concerns the input contract before comparison.

A dictionary retains one value per key

Converting a list into a provider-and-model dictionary can replace an earlier row when the key repeats. A later diff may no longer have evidence that duplication ever existed. Preserving the input population therefore comes before judging comparison accuracy.

This is a source-based risk, not an invented data-loss incident. No customer record or current feed is imported. The independent index checks whether a composite key already exists before inserting each row, then fails instead of guessing which duplicate should win.

Validate before and after symmetrically

Checking only the new snapshot leaves a duplicated old baseline capable of distorting the comparison. Diff indexes both sides, rejecting a duplicate on either and refusing to treat matching duplication as valid input.

Separate tests duplicate before and after. Even identical repeated values fail: accidental equality does not establish that collection and identity assignment were correct. A workflow needing deduplication must define it explicitly before comparison rather than hide it as a dictionary side effect.

Identity policy belongs to the business contract

The synthetic key contains provider and model text. Another domain may need company, item or version conditions. First decide whether matching names really denote the same thing. The function does not understand every service alias or version relationship.

It also avoids automatic lowercasing or whitespace removal. A difference can be cosmetic or meaningful. Rather than merge strings first and erase collisions later, an adopter should define an approved identity policy. This small example assumes that policy has already been established for its two fields.

A failed comparison is not no change

A duplicate exception means comparison could not be completed, not that nothing changed. An integrated screen should not keep presenting an old result as the latest success. The core raises an error without defining operator notifications or final interface wording; integration must distinguish success from review-needed input.

Inputs also remain unchanged. A checker that quietly cleans the evidence can make its own failure harder to investigate. This code performs no automatic overwrite or source-file modification and runs no other business program, preserving material for diagnosis.

Which rows exist matters more than count

Two inventories can have equal length while one repeats an identity and omits another. Count checks alone cannot reveal that state. Uniqueness and value validation must precede added, removed and changed classification, preserving what those labels mean.

The final diff separates numbers, verification metadata and representation. Duplicate rejection is the prerequisite for trusting those distinctions. This article therefore examines a different layer from the previous event-classification story even though both share one package.

Share a small prerequisite instead of another platform

The package’s twelve tests cover duplicates on both sides, added and removed identities, value and metadata changes, units and preserved inputs. Fresh extraction passed. No actual feed’s duplication rate or operational quality was measured, and saved examples contain no company identity or personal record.

Rather than create another comparison platform, I share the promise needed before dictionary conversion. Other collection and merge workflows can ask where rows might disappear, then check the necessary evidence before that transformation. That ordering makes a later result easier to explain.