6d89b3b33e
Three review findings from @kartik-mem0 on this PR. Anonymous id reuse, reported twice and one defect. The id is offered to PostHog as $anon_distinct_id on first sign-in and that merge is permanent, so keeping it after a logout or a key change puts every later anonymous event on the account that just left. It is now rotated on both routes, and 'aliased' is cleared with it so the fresh id can be merged into whatever account comes next. Rotation is deliberately not triggered by a plain lookup failure with no cached email: there is no previous account to leak to, and churning ids there would fragment the person for anyone offline on first run. Legacy rows are verified instead of adopted. A row written before fingerprints existed carries an email and no fingerprint; adopting the current key bound that key to the previous account's email permanently, and every run after agreed with itself. It now resolves once and takes the answer. If the lookup fails it keeps the cached email and retries next flush rather than dropping a real attribution, which is safe because the network that failed /v1/ping/ is about to fail the PostHog POST too. My original comment justifying the shortcut claimed the check would cost a request on every flush forever; that was wrong, the fingerprint is stored after one success. A failed upgrade claim is released. The sentinel was created before the marker rewrite and left behind if the rewrite failed, so claim_version_change returned early on every later run and that version's upgrade was never recorded again. Five tests, covering both rotation routes, legacy verification, the firewalled legacy case, and retrying a failed upgrade claim. 300 passed, 8 skipped. Claude-Session: https://claude.ai/code/session_01C7tEmH86HAr7GoAAKCEHZb