Skip to content

feat: notify orgs of incompatible bundles + combined compatibility analytics#2372

Merged
WcaleNieWolny merged 5 commits into
mainfrom
wolny/happy-euclid-264226
May 30, 2026
Merged

feat: notify orgs of incompatible bundles + combined compatibility analytics#2372
WcaleNieWolny merged 5 commits into
mainfrom
wolny/happy-euclid-264226

Conversation

@WcaleNieWolny
Copy link
Copy Markdown
Contributor

@WcaleNieWolny WcaleNieWolny commented May 30, 2026

Summary (AI generated)

  • CLI emits a dedicated Bundle Incompatible event (source = upload | command) when a bundle's native dependencies don't match the version currently live on the target channel. From bundle upload it fires after the new version is created (so version_new_id resolves); from capgo bundle compatibility it fires for the explicit command only (the silent sdk.ts / releaseType.ts callers are excluded). Existing analytics events are unchanged.
  • Backend /private/events turns that event into a Bento org-member notification via a new bundle_incompatible email preference. Pure helper bundle_compatibility_recovery.ts mirrors builder_onboarding_recovery.ts. Payload carries org_id, app_id, version_new_id, version_new_name, version_old_id, version_old_name (+ org/app names, channel, source); the backend resolves version_new_id and org/app names.
  • Adds bundle_incompatible to the EmailPreferenceKey union/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 (Issues & Errors section) + messages/en.json strings.
  • Tests: unit coverage for the Bento payload helper.
  • PostHog (out-of-band, not in this repo): added a merged command+upload compatibility column insight (A+B over Bundle Compatibility Checked + Bundle Upload Compatibility Checked, broken down by result) to the Capgo CLI Tracking dashboard (712327).

Motivation (AI generated)

We recently added compatibility-check tracking to PostHog for both bundle upload and the standalone capgo bundle compatibility command. 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 Incompatible fires on an incompatible bundle upload (after the version exists / channel is set) with correct version_new_* and version_old_*
  • Bundle Incompatible fires for the explicit capgo bundle compatibility command only (not the silent sdk.ts / releaseType.ts callers); version_new_* empty there
  • Backend emits the Bento event only when org/app resolve; gated by the bundle_incompatible preference and rate-limited/deduped per app+channel+version
  • Migration applies cleanly; users.email_preferences default + backfill include bundle_incompatible: true
  • Notifications page shows the new toggle and persists opt-out (adds the bundle_incompatible_disabled Bento tag)
  • Combined PostHog insight renders command + upload, broken down by result
  • bun run typecheck, lint, and the new unit test pass

Already verified locally: backend + CLI tsgo typecheck, frontend vue-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

  • New Features
    • Added bundle incompatibility notification setting. Users can now enable or disable email notifications when an uploaded bundle is incompatible with the channel's native packages.

Review Change Stack

…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).
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

Warning

Review limit reached

@WcaleNieWolny, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dc72bfde-483a-49e1-8e52-20ec3349d74c

📥 Commits

Reviewing files that changed from the base of the PR and between 76b895a and f66bc29.

📒 Files selected for processing (2)
  • cli/src/bundle/compatibility.ts
  • supabase/functions/_backend/private/events.ts
📝 Walkthrough

Walkthrough

This 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.

Changes

Bundle Incompatible Notification Feature

Layer / File(s) Summary
Bundle Incompatible Event Builder & Tracking Infrastructure
supabase/functions/_backend/utils/bundle_compatibility_recovery.ts, supabase/functions/_backend/utils/tracking.ts, tests/bundle-compatibility-recovery.unit.test.ts
New BUNDLE_INCOMPATIBLE_EVENT constant and buildBundleCompatibilityBentoEvent builder function emit a one-time Bento signal payload with idempotent uniqId and preference key. BentoTrackingPayload interface makes cron optional and adds once flag; dispatch logic conditionally calls sendNotifToOrgMembersOnce when once is true, otherwise applies default cron.
Email Preference System Extension
supabase/functions/_backend/utils/org_email_notifications.ts, supabase/functions/_backend/utils/user_preferences.ts, supabase/migrations/20260530114525_add_bundle_incompatible_pref.sql
Adds bundle_incompatible to EmailPreferenceKey union and EmailPreferences interface; maps disabled preference to bundle_incompatible_disabled tag; migration backfills and defaults the preference to enabled for all users.
User-Facing Notification Settings
src/pages/settings/account/Notifications.vue, messages/en.json
New notification toggle in "Issues & Errors" section binds to bundle_incompatible preference; adds message text describing the incompatible-bundle and potential app-store-update scenario.
Backend Event Routing & Dispatch
supabase/functions/_backend/private/events.ts
Imports BUNDLE_INCOMPATIBLE_EVENT and wires bundleIncompatibleBentoEvent into event handler; performs org/app lookups, resolves new version id from name, and includes the event in the Bento payload selection chain.
CLI Compatibility Data Capture
cli/src/bundle/upload.ts
verifyCompatibility query now fetches disable_auto_update and nested version fields; captures currently-linked "old" version and returns compatibility object with result plus old version id/name.
CLI Incompatibility Event Emission
cli/src/bundle/compatibility.ts, cli/src/bundle/upload.ts
Compatibility check emits Bundle Incompatible tracking event (when incompatible and not silent mode) with old-version tags; upload flow conditionally fires Bento event when compatibility.result === 'incompatible'.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Cap-go/capgo#2364: Modifies bundle compatibility verification flow in the same CLI files to emit telemetry events based on compatibility results; both PRs enhance the analytics pipeline around bundle uploads.
  • Cap-go/capgo#2367: Previously expanded the Bento event-routing logic in /private/events.ts for builder onboarding; this PR extends the same pattern to bundle incompatibility events.

Suggested labels

codex

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main changes: it combines two key objectives (org notification for incompatible bundles and analytics consolidation) in a clear, concise manner that reflects the feature's primary impact.
Description check ✅ Passed The PR description follows the required template structure with Summary (AI-generated detail), Motivation, Business Impact, and Test Plan sections. All major changes are documented and checklist items are provided.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented May 30, 2026

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing wolny/happy-euclid-264226 (f66bc29) with main (db9ff96)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread supabase/functions/_backend/utils/bundle_compatibility_recovery.ts Outdated
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.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1f38e46 and 76b895a.

📒 Files selected for processing (11)
  • cli/src/bundle/compatibility.ts
  • cli/src/bundle/upload.ts
  • messages/en.json
  • src/pages/settings/account/Notifications.vue
  • supabase/functions/_backend/private/events.ts
  • supabase/functions/_backend/utils/bundle_compatibility_recovery.ts
  • supabase/functions/_backend/utils/org_email_notifications.ts
  • supabase/functions/_backend/utils/tracking.ts
  • supabase/functions/_backend/utils/user_preferences.ts
  • supabase/migrations/20260530114525_add_bundle_incompatible_pref.sql
  • tests/bundle-compatibility-recovery.unit.test.ts

Comment thread cli/src/bundle/compatibility.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).
@sonarqubecloud
Copy link
Copy Markdown

@WcaleNieWolny WcaleNieWolny merged commit 63d1253 into main May 30, 2026
44 checks passed
@WcaleNieWolny WcaleNieWolny deleted the wolny/happy-euclid-264226 branch May 30, 2026 12:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant