feat: notify orgs of incompatible bundles + combined compatibility analytics#2372
Conversation
…alytics Track bundle-compatibility failures end to end so orgs can react to OTA updates that need a native rebuild. - CLI emits a dedicated `Bundle Incompatible` event from `bundle upload` (after the version is created, so version_new_id resolves) and from the `capgo bundle compatibility` command (silent internal callers excluded). Existing analytics events are left untouched so dashboards stay clean. - Backend `/private/events` turns that event into a Bento org-member notification via the new `bundle_incompatible` email preference. Pure helper `bundle_compatibility_recovery.ts` mirrors the builder-onboarding recovery pattern; payload carries org_id, app_id and new/old version id+name (backend resolves version_new_id + org/app names). - Add `bundle_incompatible` to the email-preference key/interface and the opt-out tag map, plus a migration that backfills/defaults users.email_preferences (mirrors add_builder_onboarding_pref). - Frontend: opt-out toggle in account Notifications + en.json strings. - Tests: unit coverage for the Bento payload helper. PostHog: a merged command+upload compatibility column insight was added to the "Capgo CLI Tracking" dashboard (created via API, not in this repo).
|
Warning Review limit reached
More reviews will be available in 9 minutes and 20 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR adds a complete "Bundle Incompatible" notification feature that detects when uploaded app bundles are incompatible with a channel's native packages, emits tracking events from the CLI, routes them through Bento's backend notification service, and allows users to control whether they receive email notifications for this event. ChangesBundle Incompatible Notification Feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b2b1854ac3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review: the `* * * * *` cron reopened the send window every minute, so retries of the same incompatible app/channel/version could re-email org admins each minute instead of collapsing to a single alert. Add an opt-in `once` flag to BentoTrackingPayload that routes through sendNotifToOrgMembersOnce (permanent per-(event, org, uniqId) claim) instead of the reopening cron window. `cron` is now optional and only used when `once` is unset, so the onboarding/builder signals are unchanged. The bundle_incompatible signal sets `once: true`.
…ling Clears the two SonarCloud findings on the PR (events.ts): a nested ternary (MAJOR) and a negated `!= null` condition (MINOR). A single `toIdString` helper now coerces both version_new_id and version_old_id from tag/DB values to a clean non-empty string | undefined.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cli/src/bundle/compatibility.ts`:
- Around line 136-156: The analytics queries and trackEvent call inside the
hasIncompatible && !silent block (including Promise.all, channelResult,
appResult, and trackEvent) must be wrapped in a try-catch so telemetry failures
cannot bubble and break the command; update the code in compatibility.ts to
perform the supabase queries and call trackEvent inside a try block and swallow
or log the error (debug) in the catch so any network/auth/timeouts do not throw
from this path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 096ab347-8fbc-4d02-8ba3-5dabd2a4555a
📒 Files selected for processing (11)
cli/src/bundle/compatibility.tscli/src/bundle/upload.tsmessages/en.jsonsrc/pages/settings/account/Notifications.vuesupabase/functions/_backend/private/events.tssupabase/functions/_backend/utils/bundle_compatibility_recovery.tssupabase/functions/_backend/utils/org_email_notifications.tssupabase/functions/_backend/utils/tracking.tssupabase/functions/_backend/utils/user_preferences.tssupabase/migrations/20260530114525_add_bundle_incompatible_pref.sqltests/bundle-compatibility-recovery.unit.test.ts
…command CodeRabbit: the awaited org/version lookups for the `Bundle Incompatible` signal in `capgo bundle compatibility` could reject (network/auth/timeout) and throw out of the command. Wrap the lookups + trackEvent in try/catch and swallow, matching the "telemetry must never break a command" pattern in track.ts.
…ersion_old The `capgo bundle compatibility` command re-queries the channel separately for the live (old) version, which can come back unresolved on a race or transient failure. version_old is required for this signal, so gate the emission on it — don't send an incompatibility event missing the version it's about. The upload flow is unaffected (it captures version_old from the same data it checks).
|



Summary (AI generated)
Bundle Incompatibleevent (source=upload|command) when a bundle's native dependencies don't match the version currently live on the target channel. Frombundle uploadit fires after the new version is created (soversion_new_idresolves); fromcapgo bundle compatibilityit fires for the explicit command only (the silentsdk.ts/releaseType.tscallers are excluded). Existing analytics events are unchanged./private/eventsturns that event into a Bento org-member notification via a newbundle_incompatibleemail preference. Pure helperbundle_compatibility_recovery.tsmirrorsbuilder_onboarding_recovery.ts. Payload carriesorg_id,app_id,version_new_id,version_new_name,version_old_id,version_old_name(+ org/app names, channel, source); the backend resolvesversion_new_idand org/app names.bundle_incompatibleto theEmailPreferenceKeyunion/interface and the opt-out tag map, plus a migration that backfills + defaultsusers.email_preferences(mirrorsadd_builder_onboarding_pref).messages/en.jsonstrings.A+BoverBundle Compatibility Checked+Bundle Upload Compatibility Checked, broken down byresult) to the Capgo CLI Tracking dashboard (712327).Motivation (AI generated)
We recently added compatibility-check tracking to PostHog for both
bundle uploadand the standalonecapgo bundle compatibilitycommand. This change (1) combines those two signals into a single dashboard column, and (2) proactively alerts org admins when an OTA bundle is incompatible with the channel's live native packages — which usually means a native app-store update / rebuild is required — so they can act instead of shipping an update that won't apply.Business Impact (AI generated)
Incompatible OTA updates are a common source of "my update didn't apply" confusion and support load. Surfacing them to org admins (and feeding a Bento lifecycle automation that can nudge toward a native rebuild / Capgo Builder) reduces support burden and creates an upsell touchpoint, while the combined dashboard improves visibility into how often compatibility checks fail across CLI flows.
Test Plan (AI generated)
Bundle Incompatiblefires on an incompatiblebundle upload(after the version exists / channel is set) with correctversion_new_*andversion_old_*Bundle Incompatiblefires for the explicitcapgo bundle compatibilitycommand only (not the silentsdk.ts/releaseType.tscallers);version_new_*empty therebundle_incompatiblepreference and rate-limited/deduped perapp+channel+versionusers.email_preferencesdefault + backfill includebundle_incompatible: truebundle_incompatible_disabledBento tag)resultbun run typecheck, lint, and the new unit test passAlready verified locally: backend + CLI
tsgotypecheck, frontendvue-tsc,oxlint+eslint, 591 unit tests (incl. the 6 new ones), and the migration SQL executed against local Postgres inside a rolled-back transaction.Generated with AI
Summary by CodeRabbit