fingerprint
A digest of one attempt's whole payload, computed from the same map bindMap builds.
Once an attempt is terminal, a later write for the same id is a replay exactly when this digest matches the string an earlier write stored — compared verbatim, the same rule ExtractionRunTransition.fingerprint follows for a run's own terminal write, applied one level down. Comparing the digest sidesteps the type drift a database round-trip can introduce — Neo4j hands an Int back as a Long, say — since both sides of the comparison come straight from a Kotlin record, always, independent of whatever a query happened to return.
Digested through ExtractionRunFingerprint.ofFields, for the reason headerFingerprint gives one level up: a raw JSON serialization of bindMap would move with the serializer, and this digest is compared verbatim against a string a past write stored, so it has to be a function of the record's fields alone.