You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠️needs-verification — do not implement this as-specified. The straightforward fixes trade an always-run robustness property for a rare, self-healing blip. Re-analyze the trade-off (below) and prove the chosen fix preserves the authoritative close path before writing any code.
Summary
After a purchase/restore opens the AI gate locally from StoreKit truth, the backend entitlement write happens on a detached task (verifyPurchaseWithBackend). If a foreground authoritative revalidation (whoami → AISubscriptionGate.apply()) runs before the backend has processed the JWS, whoami returns has_subscription:false and apply()closes the gate a just-paid user opened. Self-heals on the next revalidation once the backend catches up.
Severity: low — transient and self-healing; a genuinely paid user may briefly see AI locked during the verify window.
Why this is needs-verification, not "just fix it"
Every straightforward fix either:
weakens the always-run authoritative apply() / close path — the exact mechanism that promptly locks out revoked / expired / downgraded subscribers — or
adds a per-purchase network dependency on an always-run path.
Trading that always-run robustness to patch a rare, self-healing blip is the wrong trade (owner constraint, 2026-08-19: "not trading edge case harmless minor prob for always runtime robustness"). That is why this is deferred and flagged for verification rather than implemented.
Required verification before any implementation
Quantify the residual window. The Support server-granted free trial in account and paywall UI #49 open-only seam (AISubscriptionGate.refreshAfterLocalPurchase) already reopens the gate when a racing whoami/backend confirms the subscription — the only remaining exposure is the window where the backend has not yet processed the JWS and a foreground whoami fires. Measure how reachable that actually is before designing anything.
Prove the candidate fix does not weaken the authoritative close: revoked / expired / downgraded users must still be locked out promptly by the always-run path.
Prove the candidate fix adds no always-run network call.
Acceptance (if a fix clears verification)
Red-first test reproducing the transient close during the verify window.
Two-sided green: a genuinely-unentitled user is still closed promptly by apply(); a just-paid user is not transiently closed.
Summary
After a purchase/restore opens the AI gate locally from StoreKit truth, the backend entitlement write happens on a detached task (
verifyPurchaseWithBackend). If a foreground authoritative revalidation (whoami→AISubscriptionGate.apply()) runs before the backend has processed the JWS,whoamireturnshas_subscription:falseandapply()closes the gate a just-paid user opened. Self-heals on the next revalidation once the backend catches up.main— not introduced by the signup-trial work (Support server-granted free trial in account and paywall UI #49 / Do not open the AI gate when restoring a different account's subscription #50).Why this is
needs-verification, not "just fix it"Every straightforward fix either:
apply()/ close path — the exact mechanism that promptly locks out revoked / expired / downgraded subscribers — orTrading that always-run robustness to patch a rare, self-healing blip is the wrong trade (owner constraint, 2026-08-19: "not trading edge case harmless minor prob for always runtime robustness"). That is why this is deferred and flagged for verification rather than implemented.
Required verification before any implementation
AISubscriptionGate.refreshAfterLocalPurchase) already reopens the gate when a racing whoami/backend confirms the subscription — the only remaining exposure is the window where the backend has not yet processed the JWS and a foreground whoami fires. Measure how reachable that actually is before designing anything.Acceptance (if a fix clears verification)
apply(); a just-paid user is not transiently closed.Related: #49 (open-only
refreshAfterLocalPurchaseseam), #50 (restore-path work).