Great question. I’d use a two-stage approach: canonicalize the URL first—following redirects, removing tracking parameters, and checking rel="canonical"—then fall back to a normalized content hash when different URLs still serve the same page.
I’d also keep the original URLs as aliases and preserve retrievedAt for each fetch, so deduplication doesn’t remove provenance or freshness information. The canonical URL is the cheaper first check, while the content hash catches mirrors and unreliable canonical tags.
Separating retrievedAt from publishedAt is a small schema choice that quietly kills a lot of staleness bugs, so I'm glad you made it explicit. The coalescing point matters more than people think: parallel agents refetching the same doc five times is both a cost and a politeness problem, as that kernel.org core-usage number shows. Do you coalesce on canonical URL or on content hash when one page serves two URLs?