push: ship Wave 3 push server (root /api/push/*, APNs dispatch)
Root Flask blueprint at /api/push/* with register/unregister/status/test
endpoints, backed by a dedicated push runtime that subscribes to cortex
finish events for Daily Briefing and runs a 60s periodic check for
Pre-Meeting Prep. Three categories ship: SOLSTONE_DAILY_BRIEFING,
SOLSTONE_PRE_MEETING_PREP, SOLSTONE_AGENT_ALERT. Commitment Nudge is
deferred to Wave 3.1 (LedgerItem has no machine-readable due date); the
constant + payload shape are defined server-side for iOS forward-compat.
APNs transport is httpx + PyJWT with ES256 bearer JWTs cached for 55
minutes. BadDeviceToken / Unregistered responses auto-prune the device.
Device tokens are never logged beyond the last 4 chars; JWTs and .p8
contents never logged at all. PII fallback is hardcoded in payload
builders — lock-screen bodies are generic; detail lives in `data`.
Write ownership: think/push/devices.py owns push_devices.json,
think/push/triggers.py owns nudge_log.jsonl. Layer hygiene stays clean
without allowlist entries.
Live APNs validation is deferred pending Apple Developer enrollment; the
post-enrollment checklist lives in docs/design/push.md §10.
Design: docs/design/push.md
Spec sections: cpo/specs/in-flight/mobile-ux-native-ios-android.md §§3, 3.2, 3.3, 3.4, 8.3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>