The “cleaned but no longer valid” distinction is important. I’d add a preflight contract before import: freeze immutable identifiers, validate destination enums/owners, compute row-level fingerprints, and split the file into ready vs quarantined rows with machine-readable rejection reasons.
For the actual import, we use a batch ID plus source-row ID as the idempotency key, write a manifest of expected creates/updates/skips, then reconcile destination results against that manifest. That makes retries safe and gives you a rollback/report path instead of a mystery partial import. A 20-row canary batch before the full file catches owner, lifecycle, and association mistakes cheaply.
We adopted this pattern while building Atlantic’s HubSpot workstreams because “valid CSV” and “safe CRM mutation” are different tests.
I’m Ege, co-founder of Atlantic, sharing our operational approach. The most valuable artifact is usually the rejected-row report, not the cleaned file itself.