Send, then background/close the app and watch the banner. Everything lands in the log below.
3 · Ongoing call (Spike 002)
No active call.
4 · iOS declarative (Spike 003)
The pushes above already carry the declarative web_push:8030 shape. On iOS 18.4+ they render with NO service worker — so you'll see NO swpush-received line for them in the log (that's the pass condition). On Chrome/Android you WILL see the sw line.
5 · Presence / dedup gate (Spike 004)
Set your viewing surface, keep the app FOREGROUND & visible, then fire. If the surface matches, the SW suppresses the banner (see suppressed-visible-on-surface). Change surface → it shows.
6 · Universal-core seam (Spike 005)
Same core endpoints, different injected app-profile. Watch title/body/routing change with zero server-core edits.