Overview › Engagement

Engagement

Area: 10 Engagement · 7 items · Source: PADI Mobile Scope Alignment v-ceb35f4d Subject: the unified PADI app — Expo React Native. Every verdict below is about what that app can build, not about what today's apps do.

What this card covers, and what it does not

The area holds 12 items — 2 carried forward from web, 7 in the Q1 2027 Delta lane, 3 post Q1 2027.

This card is the seven Q1 Delta itemsCOMM-01, ENG-01, ENG-02, ENG-03, ENG-04, ENG-05, ENG-07.

MYP-07 (Email communication preferences) and PREF-02 (Manage contact preferences) are carried forward from web and analysed in full in Logged In Experience, where they are recorded as one capability under two identifiers. Count them once across the two sub-tasks.

Post Q1 2027, named and not analysed: ENG-08 (dive-log reminder / interactive notifications), ENG-09 (content / articles / blog surface in-app), ENG-10 (behavioural nudges). Two of the three are contradicted by the backlog — see the lane table below.

ENG-06 does not exist in the artefact — and unlike the study's three earlier ID gaps, this one is not benign. See The finding to lead with.

Not one of the seven items carries a requirement. No detail drawer, no Impact, no Effort. Everything below is written against a title and a flag.


The finding to lead with

The artefact promises an in-app inbox, has no item for it, and the backlog funds it for Q1.

The area blurb reads, verbatim: "Email and lifecycle messaging are carried forward. The delta is push as a channel and the in-app inbox."

Searching the artefact for inbox returns that sentence and nothing else. No ENG- item is the inbox. The IDs run 01–05, then 07–10; ENG-06 is missing, and the inbox is the capability missing with it.

Jira has it. PAM-49 In-App Message Centre / Inbox, epic PAM-12 Engagement, fix version Q1 - Release 2027, with a written requirement:

"I want every push notification also delivered into a persistent in-app inbox… The same notification is also saved into the in-app inbox… Diver opens the inbox at any time to see a list of past notifications."

So a funded Q1 story sits in no feasibility sub-task, because the scope artefact has no line for it. Anyone scoping this area from the artefact alone will miss it.

The good news, established below: it is not greenfield. Two working implementations already exist in the estate.

And the artefact and the backlog disagree on three items, in both directions

Item Artefact lane Jira story Jira lane
ENG-07 Dive buddy notifications Q1 2027 PAM-50 Later - Release 2027
ENG-08 Dive log reminders post Q1 PAM-77 Q1 - Release 2027
ENG-10 Behavioural nudges post Q1 PAM-75 Q1 - Release 2027

Learning's mismatches all ran one way, which reads as a stale flag. These run both ways, which does not. One of the two records is wrong on three of this area's ten items.

A fourth notification item sits outside the Engagement epic entirely — PAM-66 Cert-renewal / expiry reminders, under PAM-6 Certifications, at Q1.


How to read the evidence in this card

The unified app is a new client. It inherits no code from the apps below; they are evidence that an endpoint answers a real client, never a head start.

Citations name repositories, never folders. Paths are relative to the repository root. Nothing under .claude/worktrees/ is cited.

This area has three mobile surfaces, not two — and that is the whole story

Every other card in this study reads two mobile apps that behave alike. Engagement does not:

Surface Push stack Device registry Inbox Preferences
padiww/ios-padi-app · padiww/android-padi-app Salesforce Marketing Cloud SDK none at PADI — token goes to the vendor disabled none — hands off to web
diviac/travel-rn (PADI Adventures) Firebase Cloud Messaging POST account/devices/ GET account/messages/ 5 categories × 3 channels
PadiWW/padi.mobile.app (the unified app) nothing nothing nothing nothing

Reading only the PADI app would put five of these seven items in the gap column. Adventures is a PADI-owned React Native app that has already built most of this area — on a different backend, a different push provider and a different auth realm. That is the single most useful fact in this card, and it is also the trap: existing in Adventures is not available to the unified app without a realm decision.

The realm is the catch. the API inventory §7 records Adventures as travel.padi.com/api/mobile/v1 (Django), Authorization: JWT not Bearer, no refresh token, session in unencrypted AsyncStorage. Everything below that cites diviac/travel-rn inherits that caveat.


COMM-01 — Communication channels other than email

Flags: PRK · Status: Existing — mobile (Adventures) · Feasibility: push yes; SMS unverified as a sending channel

What the unified app has to deliver

Reaching a diver by something other than email.

What it can rely on

Push is real and shipping on both mobile surfaces, by two different routes — see ENG-01.

SMS is offered as a choice but never observed as a channel. diviac/travel-rn app/components/form/NotificationsPreferencesForm.js renders an SMS column across all five preference categories, so the Adventures data model has an SMS flag per category. But searching all five PADI web repositories and both PADI mobile apps for a sending integration — Twilio, an SMS gateway, a message-send endpoint — returns nothing. The only sms matches are Apple Developer 2FA references in padiww/ios-padi-app fastlane/Fastfile, which are CI plumbing.

A checkbox is not a channel. What is established: Adventures stores an SMS preference. What is not: that anything sends one.

What it has to build

Nothing, for push. For SMS, the question is not client work at all.

What blocks it

SCOPE-43 — which channels does PADI commit to platform-wide? The answer decides whether ENG-02 has three channels to arbitrate between or two.


ENG-01 — Push notifications (app-native, interactive)

Flags: Now · Status: Existing — mobile, by two different routes · Feasibility: delivery is solved; "interactive" is not, and the registry is realm-bound

What the unified app has to deliver

Rich push with action buttons, images and a deep link straight to the relevant screen. PAM-44 states it plainly:

"I want to receive rich push notifications with action buttons, images, and direct links to the relevant screen… Diver taps an action button directly within the notification."

What it can rely on

Route 1 — the PADI apps, on Salesforce Marketing Cloud. padiww/ios-padi-app Podfile:24 vendors MarketingCloudSDK 9.0.3; padiww/android-padi-app gradle/libs.versions.toml:64 pins 8.1.5, with a dedicated Maven repository at build.gradle.kts:20. Firebase is present in both apps but only Analytics, Crashlytics, Performance and Dynamic Links — there is no Firebase Messaging artifact in either.

The device token never reaches PADI. padiww/ios-padi-app PADI/data/repository/impl/PushNotificationHandlerImpl.swift:175-178 hands it to SFMCSdk.mp.setDeviceToken; padiww/android-padi-app app/src/main/java/com/duns/padiapp/util/AppUtils.kt:90-99 reads pushMessageManager.pushToken and calls setProfileId. Diver identity binds inside the vendor SDK via setContactKey (PADI/data/repository/impl/SessionRepositoryImpl.swift:243), never through a PADI endpoint.

So on the PADI realm there is no device-registration API to reuse.

Route 2 — Adventures, on Firebase, with a registry. diviac/travel-rn package.json carries @react-native-firebase/messaging ^21.6.1, react-native-push-notification ^8.1.1 and @react-native-community/push-notification-ios ^1.11.0. app/containers/App.js:59-84 requests permission, calls messaging().registerDeviceForRemoteMessages() and fetches the token — and app/containers/AuthWrapper.js:34-47 registers it against the Adventures backend:

accountDevices.create({ registrationId: fcmToken.data, name, deviceId: getUniqueId() })

against endpoint: 'account/devices/' (app/containers/AuthWrapper.js:93). The same flow patches installationDatetime onto the account with the comment "required for some push notifications".

Foreground messages are rendered locally — app/containers/App.js:99-107 calls PushNotification.localNotification from the messaging().onMessage handler, onto a channel created at app/containers/Configure.js:13-19 and named "PADI Bookings notifications".

A PADI-owned device registry therefore exists. It is simply not on the PADI realm.

What it has to build

The interactive half, everywhere. Neither PADI app defines a single UNNotificationCategory or UNNotificationAction (iOS) or NotificationCompat.Action (Android). Android has exactly two notification channels, both generic — "UrlNotification" / "PADI Notification" (padiww/android-padi-app app/src/main/java/com/duns/padiapp/PadiApplication.kt:212-224) and the SDK default. Tap-to-open a URL is the whole of today's behaviour.

Rich, actionable notifications are routine work in Expo. The platform question is the real one, and it is not the app's to answer.

What blocks it

PADI-32 — which push platform does the unified app use? The three answers have very different shapes: stay on Marketing Cloud and inherit its registry; adopt the Adventures FCM stack and its account/devices/ registry along with the Travel realm; or have PADI build a first-party device registry. Nothing in the app can be estimated until this is settled.

A note on the Marketing Cloud configuration. The SFMC tenant settings are compiled into the shipped binaries. That is a security finding, it is already recorded as INT-3, and it is handled on that track rather than here. This card describes the architecture only.


ENG-02 — Channel-strategy policy (push-first, email/SMS by exception)

Flags: PRK · Status: Requirement only · Feasibility: not a build

What the unified app has to deliver

Nothing. This item produces a written policy — when a message goes by push, when it falls back to email, when SMS is warranted — and no code.

It is the same shape as FAM-08 on PAM-156: a title that reads like a feature and resolves to a decision. Giving it a feasibility verdict would be manufacturing one.

What blocks it

Its own premise. "Push-first, email/SMS by exception" presumes three working channels. Email works. Push works, on two incompatible stacks. SMS has never been observed sending anything (COMM-01). A policy arbitrating between three channels cannot be written until it is known how many there are.

It also cannot be written before ENG-03: a channel strategy that users can override needs a per-channel preference model, and PADI's does not have a channel dimension at all.

Sequence it after SCOPE-43 and ENG-03, not before.


ENG-03 — Push notification preferences / granular opt-in

Flags: PRK · Status: Gap on the PADI realm · Existing on Adventures · Feasibility: the model exists, on the wrong side of an auth boundary

What the unified app has to deliver

Per-notification-type opt-in, so a diver can take booking alerts and decline promotions.

What it can rely on

PADI's preference centre is topic-only. There is no channel dimension. The same API is called identically from three web front ends — PadiWW/account.padi.com src/api/auth/index.js:3,35-57, PadiWW/learning.padi.com src/store/modules/auth/actions.js:259,289,317 and PadiWW/pro.padi.com src/store/modules/auth/actions.js:274,310,335-337 — and the payload is a flat set of booleans: padiCommunications, environmentConservation, diveAbroad, events, localDiving, diveCentricTrips, thirdParty, communicationLanguage.

Every one is an interest topic, and every label reads "email me…". There is no channel field anywhere in any payload. A second endpoint handles Salesforce recipient topics with numeric SFMC preference IDs (PadiWW/pro.padi.com src/components/preferences/PreferencesForms.vue:158-166), and it is topic-keyed too.

Adventures has exactly the model this item describes. diviac/travel-rn app/components/form/NotificationsPreferencesForm.js:200 posts to /account/preferences/notification/, and the form is a 5 × 3 matrix of booleans named preferences.{category}{Channel}:

Category Description in the UI Channels
accountNotifications bookings, vouchers and cancellations Email · Sms · Push
reminders cart activity, payment, reviews, referrals Email · Sms · Push
divingOpportunities diving near you when you travel Email · Sms · Push
adventuresPromotionsUpdates Email · Sms · Push
travelInspirationalNews Email · Sms · Push

What it has to build

This is not re-plumbing an existing per-channel field — PADI has none. The work is a new preference model on the PADI realm, with Adventures as a proven reference design rather than a reusable API.

Three constraints carry over from what exists:

  • Neither PADI app reads or writes a preference natively. Android's only route is a jump page that opens the web preference centre in an external browserpadiww/android-padi-app app/src/main/java/com/duns/padiapp/domain/BaseAppSettingsIpm.kt:252-256, reached from the delete-account flow at compose/deleteaccount/DeleteAccountScreen.kt:45-51.
  • Some communications are non-optional and that is encoded in the UI, not the data. PadiWW/pro.padi.com src/components/preferences/ProPreferences.vue:8-24 renders training bulletins, quality-management notices and renewal notifications as statically checked items with opt-out unavailable. The transactional-versus-marketing split exists only as hard-coded markup.
  • Consent and preference are the same payload on web. thirdParty — consent to share data with partners — sits beside marketing topics in one flat object (src/components/preferences/DiverPreferences.vue:69-92). Mobile keeps legal consent architecturally separate (p/personalization/api/Personalization/attributes/{key}, China PIPL). Whichever model the unified app adopts inherits one of these two positions, which bears directly on MYP-09 and PREF-03.

What blocks it

SCOPE-44 — does the unified app adopt the Adventures model and its realm, or does PADI extend the preference centre with a channel dimension? And SCOPE-45 — is the transactional/marketing split moved into the data model, where it belongs, rather than being re-hard-coded in a second client?


ENG-04 — Trip / booking / next-dive reminders

Flags: Now · Status: Delivery exists — Gap is the trigger · Feasibility: narrower than it first appears

What the unified app has to deliver

A reminder before a trip, a booking, or a next dive.

What it can rely on — more than the PADI apps suggest

Read only the PADI apps and this looks like a total gap. Adventures shows otherwise: the entire delivery chain already exists and runs in production.

  • Push transport — FCM (diviac/travel-rn app/containers/App.js:59-84)
  • A device registry — POST account/devices/ (app/containers/AuthWrapper.js:34-47,93)
  • A notification channel named for the purpose — "PADI Bookings notifications" (app/containers/Configure.js:13-19)
  • A Reminders preference category the diver can toggle per channel (app/components/form/NotificationsPreferencesForm.js:38-43,145-147)
  • A persistent message store the app reads back — GET account/messages/ (app/screens/NotificationsScreen.js:83-84)

So a PADI-owned app already sends reminders to registered devices, honours a per-channel preference, and keeps a record. This item is not starting from zero.

What it has to build — and this is the real gap

Nothing schedules anything, on any surface. Searched across all three mobile codebases:

  • No UNCalendarNotificationTrigger or UNTimeIntervalNotificationTrigger in padiww/ios-padi-app
  • No AlarmManager, and no WorkManager job that posts a notification, in padiww/android-padi-app — the existing workers do data sync only
  • No localNotificationSchedule in diviac/travel-rn either. Its one localNotification call renders a foreground FCM message (app/containers/App.js:99-107); it does not schedule a future one.

And the triggers that do exist are event-driven, not date-driven. Adventures' two booking-related categories describe "bookings, vouchers and cancellations" and "cart activity, payment, reviews, referrals" — these fire when something happens. A reminder fires because a date is approaching, which nothing observed does.

So the valid gap is precise: not delivery, not preferences, not the registry — the scheduler and the date-proximity trigger. Two ways to close it, and they are very different items:

Approach Where it runs What it needs
Local, from cached data The device Nothing from PADI. The app already holds trips and dives; schedule a local notification against the stored date. Works offline.
Server-scheduled campaign PADI A service that knows every diver's trip and dive dates and emits on a schedule. Not observed anywhere.

13-post-q1-certifications-ecard.md reached exactly this fork for certification-expiry reminders and recorded the same conclusion: local-notification-from-a-cached-date is the cheap form, buildable with nothing new from PADI. The same answer applies here and should be offered explicitly.

What blocks it

SCOPE-46 — local or server-scheduled? The local form is cheap, offline-capable and unblocked; the server form is a platform PADI does not have.

One dependency travels with the trip half: booking data is TRV-04, on the Travel realm, inside the deferred PAM-158.


ENG-05 — Push notifications for Club membership users

Flags: PRK · Status: Gap — mobile · Feasibility: blocked on the same platform question as ENG-01, and unverifiable on PADI's side

What the unified app has to deliver

Club-member-targeted push — renewal notices being the obvious case.

What it can rely on

The audience data is served. Club membership status comes from GET /c/club/member/subscription, analysed in full as MYP-05 in Logged In Experience, which also records renewalStatus and autoRenewEnabled as available, and the target-state reference adds GET /pros/renewals/info. Knowing who to notify, and when a renewal is due, is not the problem.

What it has to build

Everything between knowing and telling.

Club renewal push is buildable only if interactive notifications and device-token registration exist. On the PADI realm neither doesENG-01 establishes there is no PADI device registry and no interactive notification anywhere in the PADI apps. So this item inherits ENG-01's blocker entirely and adds nothing it can solve on its own.

There is also no Club-specific notification channel, topic or category anywhere. All PADI-app push arrives on one generic channel. Segmenting by membership is not a client capability; it is a campaign-tool capability.

And the campaign tool cannot be inspected. Targeting would be configured in Salesforce Marketing Cloud, and Axelerant has no SFMC access. So the segment structure, the available audience attributes and whether membership status is even synced into SFMC are all unverifiable from here. This card can state what the app would need; it cannot state what SFMC already holds.

What blocks it

PADI-32 (the push platform decision) first — nothing here moves before it. Then PADI-33: is Club membership status available as a segmentation attribute in Marketing Cloud, and can Axelerant see the configuration? Without an answer, any estimate covers the app's share only and silently omits the part that actually sends the message.


ENG-07 — Dive buddy notifications

Flags: PRK · Status: Gap — the data model comes first · Feasibility: far larger than the title implies

What the unified app has to deliver

Notifying a diver about something a buddy did.

What it can rely on

Almost nothing, because a buddy is not an entity. On both platforms a buddy is a single free-text string on a dive-log entry:

  • padiww/ios-padi-app PADI/model/view/LogbookExperience.swift:44-57buddies decoded as a string, exposed by getBuddies() -> String?, entered through one text field (PADI/feature/logbook/experience/view/ExperienceViewController.swift:20,28)
  • padiww/android-padi-app app/src/main/java/com/duns/padiapp/compose/logbook/createlogbook/CreateLogbookViewModel.kt:216buddy: String? = "", with a matching single field in ExperienceScreen.kt:41-134

There is no buddy list, no connection or friend model, no identity linkage between a buddy name and a PADI account — searched across both PADI apps, diviac/travel-rn, padiww/padi-aware-flutter and every POC. PadiWW/padi.mobile.app src/features/dives/types.ts:4 mentions a buddy only in a comment deferring the real model to PAM-51.

A typed name is not a person. There is nobody to notify.

What it has to build

A social graph: buddy identity resolution, an invitation or confirmation flow, a relationship store, privacy rules about what a buddy may see, and only then a notification. The notification is the last and smallest part of this item, and estimating it as a notification feature would understate it by an order of magnitude.

What blocks it

SCOPE-47 — is ENG-07 in scope as a social feature, or only as notifications over a buddy model someone else delivers? And the lane disagreement is sharpest here: the artefact places it in Q1 2027 while PAM-50 sits at Later - Release 2027. The backlog looks right.


The in-app inbox — ENG-06, which does not exist

The artefact's missing item has two working implementations in the estate, plus a third that is switched off. This is the cheapest surprise in the card.

Implementation Where Evidence
AWS AppSync GraphQL — query, mark-read, delete, live subscriptions PadiWW/learning.padi.com, PadiWW/pro.padi.com src/components/notification/GlobalNotification.vue, src/components/notification/Drawer.vue; schema duplicated at PadiWW/pro.padi.com src/graphql/header/index.js:3-49
REST message list, paginated diviac/travel-rn app/screens/NotificationsScreen.js:83-84GET account/messages/
Salesforce MobilePush inboxdeliberately disabled padiww/ios-padi-app PADI/data/repository/impl/PushNotificationHandlerImpl.swift:32let inbox = false, applied at :47

So PAM-49 is a new client over an existing service, not a new backend — and there is a real design choice between PADI's AppSync store, the Adventures REST store, and re-enabling the vendor's.

That choice should be made deliberately, because it is the same choice ENG-01 faces about push transport, and answering them separately would produce an app whose notifications and notification history live in different systems.


Summary

Item Status Feasibility
COMM-01 Channels other than email Existing — mobile (Adventures) Push yes; SMS stored but never observed sending
ENG-01 Push notifications (interactive) Existing — mobile, two routes Delivery solved; interactive unbuilt; registry is realm-bound
ENG-02 Channel-strategy policy Requirement only Not a build. Blocked by its own premise
ENG-03 Granular opt-in Gap (PADI) · Existing (Adventures) PADI's model has no channel dimension at all
ENG-04 Trip / booking / next-dive reminders Delivery exists · Gap is the trigger Nothing schedules; local form is cheap and unblocked
ENG-05 Club-member push Gap — mobile Inherits ENG-01; SFMC configuration unverifiable
ENG-07 Dive buddy notifications Gap — data model first Buddy is a text field; the social graph is the item

Counted as distinct capabilities: 6. ENG-05 has no independent build — it is ENG-01 plus a targeting rule that lives in the campaign tool. Estimating it separately double-counts ENG-01.

The headline for estimation. Two of the seven are flagged Now. One of those two (ENG-01) cannot be estimated until PADI chooses a push platform, and the other (ENG-04) is cheap in its local form and a platform build in its server form. Neither confirmed item has a single number.

And reading only the PADI apps would have been wrong five times. Adventures has push, a device registry, an inbox and a full per-channel preference model. It is the most engagement-complete app PADI owns, and it is the one this study had least reason to open.

What has to be answered

PADI owns these:

  1. PADI-32 — Which push platform does the unified app use: Salesforce Marketing Cloud (the PADI apps' stack, no PADI-side registry), Firebase with the Adventures account/devices/ registry, or a first-party device registry PADI builds? Blocks ENG-01 and ENG-05, and nothing in either moves before it.
  2. PADI-33 — Is Club membership status available as a segmentation attribute in Marketing Cloud, and can Axelerant see the configuration? Without SFMC access the sending half of ENG-05 cannot be verified at all.

Scope owns these:

  1. SCOPE-43 — Which channels does PADI commit to platform-wide? SMS is offered in the Adventures UI and sends nothing anywhere. ENG-02 cannot be written until this is answered.
  2. SCOPE-44 — Does the app adopt the Adventures preference model and its Travel realm, or does PADI add a channel dimension to its own preference centre?
  3. SCOPE-45 — Is the transactional-versus-marketing split moved into the preference data model, instead of being hard-coded in the UI a second time?
  4. SCOPE-46 — Are reminders local (cheap, offline, nothing from PADI) or server-scheduled (a platform that does not exist)?
  5. SCOPE-47 — Is ENG-07 in scope as a social feature, or only as notifications over a buddy model delivered elsewhere?
  6. SCOPE-48 — Which inbox does PAM-49 build on: PADI's AppSync store, the Adventures REST store, or Salesforce's disabled one? Answer it together with PADI-32.

And three that are bookkeeping, not engineering

  1. The artefact promises an in-app inbox and has no item for it. PAM-49 is funded for Q1 and appears in no feasibility sub-task. Either add ENG-06 or accept that the artefact is not the scope of record.
  2. Three items disagree with the backlog, in both directionsENG-07 (artefact Q1 / Jira Later), ENG-08 and ENG-10 (artefact post-Q1 / Jira Q1).
  3. PAM-66 Cert-renewal reminders sits under the Certifications epic, not Engagement, while being the same capability as ENG-04's server-scheduled form.