Cold review caught a regression from the two previous commits. Instagram and
Threads use long-lived tokens refreshed by EXTENDING the access_token itself
(grant_type=ig_refresh_token / th_refresh_token) — they have no separate
refresh_token and CANNOT be refreshed once expired. The anti-over-rotation rule
("only refresh a token once it's actually expired") is right for rotating
single-use refresh_token platforms but wrong for these: it left IG/Threads
tokens to lapse, after which the extend call fails and the account disconnects
(~every 60 days).
Gate the anti-rotation on the platform's refresh model:
- Platform::extendsAccessTokenOnRefresh() — true for Instagram/Threads.
- SocialAccount::needsProactiveTokenRefresh() — expired for rotating platforms,
OR expiring-soon for extension platforms (restores isTokenExpiringSoon).
- RefreshSocialToken extends (refreshToken) extension-model tokens while still
valid, and verifies (access-token-first) rotating ones.
- All 23 publisher/analytics pre-checks now use needsProactiveTokenRefresh().
Tests: proactive job extends a still-valid Instagram token; a model test covers
the rotating-vs-extension branching; existing X/LinkedIn anti-rotation tests
are unchanged.
Refs #126