Skip to content

feat(frontend): native-incompatibility guidance + Builder CTA on the Compatibility page#2480

Merged
WcaleNieWolny merged 11 commits into
mainfrom
feat/dependencies-incompatible-fix-guidance
Jun 12, 2026
Merged

feat(frontend): native-incompatibility guidance + Builder CTA on the Compatibility page#2480
WcaleNieWolny merged 11 commits into
mainfrom
feat/dependencies-incompatible-fix-guidance

Conversation

@WcaleNieWolny

@WcaleNieWolny WcaleNieWolny commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

What

The app Compatibility page (/app/:app/compatibility) now explains what an incompatibility means and how to fix it, modeled on the (currently disabled) incompatible-bundle notification email. Companion to the merged backend fix #2482.

When the app has any unresolved compatibility event, a collapsible guidance panel (neutral card, localStorage-persisted collapse state) appears above the events table:

  • What this means — Capgo delivers web/JS instantly but can't change native code; when a bundle's native deps don't match the build a device has, the update can crash until users install a new native build.
  • Ship a new native build — primary CTA opens the in-app Builds tab (/app/:app/builds); fires builder_cta_compatibility_clicked.
  • Roll back the channel — primary "Roll back for me": after a confirm dialog listing the exact changes (channel → previous bundle), points each affected channel back to the previous bundle of its most recent unresolved event. Plus a secondary Manage channels link.
  • Why native changes need an app-store update — collapsible explainer + docs link to the live-updates compatibility guide.

Rollback safety

  • The CTA and dialog only cover channels the user holds channel.promote_bundle on (the permission the DB trigger enforces on every channels.version write); users without permission are never offered the action.
  • Targets are snapshotted at dialog-open, so the writes performed on confirm are exactly the changes displayed.
  • Per-channel outcome reporting: partial failures name the failed channel(s) and acknowledge which rolled back.
  • Channels with disable_auto_update_under_native OFF get an explicit warning in the confirm dialog (devices on a newer native build could receive the older bundle).
  • Post-rollback refreshes (0s/4s/10s, timers cleared on unmount) surface the queued auto-resolution; the merged fix(backend): don't raise a mirror compatibility event on guarded rollback #2482 prevents the rollback from raising a mirror event.
  • Chained incompatibilities unwind one step per click (by design — see fix(backend): don't raise a mirror compatibility event on guarded rollback #2482 discussion); copy avoids overpromising one-click safety.

Test plan

  • App with unresolved incompatibility → /app/:app/compatibility shows the panel; collapse persists across reloads.
  • "Open Capgo Builder" lands on the Builds tab; PostHog event fires.
  • "Roll back for me" (permitted user) → confirm dialog lists channel → bundle; on confirm the channel is rolled back, single success toast, event auto-resolves within ~10s with no new mirror event (needs fix(backend): don't raise a mirror compatibility event on guarded rollback #2482 deployed).
  • User without channel.promote_bundle does not see "Roll back for me" (Manage channels remains).
  • Channel with downgrade guard off → warning appended to the confirm dialog.
  • Resolved-only app → no panel.

oxlint, eslint, vue-tsc all green.

…ies page

When the dependency diff is incompatible, show an actionable guidance panel
(modeled on the incompatible-bundle email): what it means (OTA can't change
native code, devices on the old native build may crash), how to fix it
(rebuild with Capgo Builder — opens the sell deck — or ship a native build;
roll the channel back to the last compatible bundle meanwhile), and a
collapsible 'why native changes need an app-store update' explainer. Tracks
builder_cta_compatibility_clicked.
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a compatibility fix guidance card for unresolved events, including rebuild and rollback actions, and updates backend compatibility-event processing to suppress duplicate mirror events when channels revert to an unresolved baseline.

Changes

Compatibility remediation flow

Layer / File(s) Summary
Revert-aware compatibility event decisions
supabase/functions/_backend/triggers/compatibility_events.ts, supabase/functions/_backend/triggers/on_channel_update.ts, tests/compatibility-events-decide.unit.test.ts
decideCompatibilityEvents now accepts unresolved events with channel_id, on_channel_update loads and passes unresolved channel events into that decision, and tests cover when revert-driven mirror events are suppressed or still emitted.
Guidance card actions and copy
messages/en.json, src/pages/app/[app].compatibility.vue
Adds compat-fix-* copy plus compatibility-page logic and UI for unresolved-event guidance, including persisted expansion state, Builder tracking/navigation, rollback target calculation and confirmation, channel updates, manage-channels navigation, and docs links.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CompatibilityPage
  participant PostHog
  participant Supabase
  participant Router

  User->>CompatibilityPage: Open unresolved compatibility view
  CompatibilityPage->>User: Render fix guidance card
  User->>CompatibilityPage: Click rebuild CTA
  CompatibilityPage->>PostHog: Track click with app_id
  CompatibilityPage->>Router: Navigate to /builds
  User->>CompatibilityPage: Confirm rollback
  CompatibilityPage->>Supabase: Update channel bundle targets
  CompatibilityPage->>CompatibilityPage: Refresh compatibility data
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Cap-go/capgo#2442: Both PRs change src/pages/app/[app].compatibility.vue and related compatibility UI text.
  • Cap-go/capgo#2444: Both PRs modify the backend compatibility-event pipeline in compatibility_events.ts and on_channel_update.ts.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% 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 title accurately describes the main feature: adding native-incompatibility guidance and Builder CTA on the Compatibility page, which matches the primary changes in the PR.
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.
Description check ✅ Passed The PR description comprehensively covers What/How/Why, includes rollback safety details, and provides a detailed test plan with checkboxes.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@coderabbitai coderabbitai Bot added the codex label Jun 10, 2026
@codspeed-hq

codspeed-hq Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing feat/dependencies-incompatible-fix-guidance (693867e) with main (b1b9bf3)

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@src/pages/app/`[app].bundle.[bundle].dependencies.vue:
- Around line 447-453: The button in the template that calls openBuilder uses
custom Tailwind classes; replace those with DaisyUI button classes to ensure
consistent interactive styling—swap the class list on the <button> that
references openBuilder to use the DaisyUI primary/ghost classes (d-btn and
appropriate modifier like d-btn-primary or d-btn-sm/d-btn-lg as needed) and
remove the bespoke bg-blue-500/hover/bg/ focus ring utilities; keep the same
attributes (type="button" and `@click`="openBuilder") and the existing translation
text (t('compat-fix-rebuild-cta')) so behavior and content remain unchanged.
- Line 452: Replace the hardcoded "→" after the translation key with the shared
arrow icon component (e.g., use IconChevronRight or IconArrowRight) to match
project UI patterns; update the component registration/import where this
template lives and swap the literal arrow in the template that contains "{{
t('compat-fix-rebuild-cta') }} →" for the icon component (apply any existing
sizing/spacing classes used for other icons to maintain visual consistency).
🪄 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: e9ef9ba5-7489-4d1e-9ed2-5a9d1e7a2182

📥 Commits

Reviewing files that changed from the base of the PR and between 534b0e2 and 2d7258a.

📒 Files selected for processing (2)
  • messages/en.json
  • src/pages/app/[app].bundle.[bundle].dependencies.vue
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread src/pages/app/[app].bundle.[bundle].dependencies.vue Outdated
Comment thread src/pages/app/[app].bundle.[bundle].dependencies.vue Outdated
…tibility page

The per-bundle dependency-compare view was the wrong home for the
'this needs a native build' explainer. Move it to the app-level
Compatibility page (/app/:app/compatibility), shown whenever the app has
unresolved incompatibility events. Make the copy generic (no per-bundle
name), keep the Capgo Builder CTA (sell deck) + rollback guidance + the
collapsible 'why native changes need an app-store update' explainer.
Revert the Dependencies page to its original state.
@WcaleNieWolny WcaleNieWolny changed the title feat(frontend): explain & fix native-incompatibility on the Dependencies page feat(frontend): native-incompatibility guidance + Builder CTA on the Compatibility page Jun 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@src/pages/app/`[app].compatibility.vue:
- Around line 393-410: Replace the hand-crafted button class strings with the
project's DaisyUI primitives: for the rebuild CTA button referenced by
`@click`="openBuilder" and data-test="compatibility-rebuild-cta" remove the custom
inline classes and use the d- prefixed button variant (e.g., d-btn and the
appropriate modifier such as d-btn-primary) so spacing/behavior matches the rest
of the app; likewise replace the channel-manage inline-styled button that calls
router.push(`/app/${encodeURIComponent(id)}/channels`) with the DaisyUI
button/link primitive (e.g., d-btn or d-btn-ghost) and keep the same
`@click/router.push` logic and text, preserving accessibility attributes and
data-test where present.
🪄 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: 972b608d-9577-49dc-9c1a-01e0736eda47

📥 Commits

Reviewing files that changed from the base of the PR and between 2d7258a and 91c730d.

📒 Files selected for processing (2)
  • messages/en.json
  • src/pages/app/[app].compatibility.vue
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread src/pages/app/[app].compatibility.vue Outdated
- Cleaner, more readable layout: card header + two distinct fix-option
  cards (rebuild / roll back) instead of dense stacked paragraphs; bump
  body copy to text-sm.
- The Builder CTA now opens the in-app Builds tab instead of the 5-slide
  presentation deck.
- Add a docs link to the bundle-compatibility / disable-updates strategy
  section.
…sible

- Drop the amber header band / border / icon circle for the standard neutral
  card palette (slate borders, white/slate-900 bg); keep just a small amber
  alert icon as the cue.
- Make the whole guidance panel collapsible via a header toggle, persisting
  the collapsed/expanded choice in localStorage (default expanded).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@src/pages/app/`[app].compatibility.vue:
- Around line 68-74: The openBuilder function currently routes to the Builds tab
using router.push(`/app/${encodeURIComponent(id.value)}/builds`) but the PR
intends to open the 5-slide BuilderPresentationModal; update openBuilder to
invoke the modal presentation instead of navigating (or, if Builds is correct,
update the PR text). Specifically, replace the router.push call in openBuilder
with the same modal-opening logic used by other CTAs (e.g., the method or event
that shows BuilderPresentationModal in other components) and keep the telemetry
pushEvent('builder_cta_compatibility_clicked', config.supaHost, { app_id:
id.value }) intact; if you choose to keep routing, change the PR description to
state that the CTA navigates to Builds rather than opening the modal.
🪄 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: 2f62beff-bea0-4ed7-9802-29dfd7cdc5b6

📥 Commits

Reviewing files that changed from the base of the PR and between 91c730d and b0aecdc.

📒 Files selected for processing (2)
  • messages/en.json
  • src/pages/app/[app].compatibility.vue
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread src/pages/app/[app].compatibility.vue
…ty docs link

- 'Roll back the channel' card now has a primary 'Roll back for me' button
  that, after a confirm dialog listing the exact changes (channel → last
  compatible bundle), points each affected channel back to the previous
  bundle from its most recent unresolved compatibility event. Skips
  deleted channels/bundles; checks channel.promote_bundle per channel.
  'Manage channels' stays as the secondary button.
- Point the docs link at the live-updates compatibility guide.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/app/[app].compatibility.vue (1)

166-175: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Optional: localStorage key name and stored values are inverted.

The key is capgo-compat-guidance-collapsed but stores '0' when open and '1' when collapsed (line 174). The logic is correct but reading localStorage.getItem(guidanceCollapseKey) !== '1' (line 169) to determine guidanceOpen is less intuitive than storing the open state directly or renaming the key to match the stored semantics.

♻️ Option: store the open state
-const guidanceCollapseKey = 'capgo-compat-guidance-collapsed'
-const guidanceOpen = ref(typeof localStorage === 'undefined' || localStorage.getItem(guidanceCollapseKey) !== '1')
+const guidanceOpenKey = 'capgo-compat-guidance-open'
+const guidanceOpen = ref(typeof localStorage === 'undefined' || localStorage.getItem(guidanceOpenKey) === '1')
 
 function toggleGuidance() {
   guidanceOpen.value = !guidanceOpen.value
   if (typeof localStorage !== 'undefined')
-    localStorage.setItem(guidanceCollapseKey, guidanceOpen.value ? '0' : '1')
+    localStorage.setItem(guidanceOpenKey, guidanceOpen.value ? '1' : '0')
 }
🤖 Prompt for 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.

In `@src/pages/app/`[app].compatibility.vue around lines 166 - 175, The code uses
guidanceCollapseKey and stores '0' for open and '1' for collapsed which is
counterintuitive; change the storage to represent the open state directly (or
rename the key to guidance-open). Update guidanceOpen's initialization to read
localStorage.getItem(guidanceOpenKey) === '1' (or !== '0' if you keep booleans
inverted) and update toggleGuidance to set localStorage.setItem(guidanceOpenKey,
guidanceOpen.value ? '1' : '0') so the stored value matches the meaning of
guidanceOpen; adjust symbol names (guidanceCollapseKey → guidanceOpenKey or
similar) and ensure toggleGuidance and guidanceOpen use the new key and
consistent stored values.
🤖 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 `@src/pages/app/`[app].compatibility.vue:
- Around line 109-139: The rollbackChannels function currently leaves partial
state and reports only a generic error; change it to first check permissions for
all rollbackTargets (use checkPermissions in the initial loop) and collect any
permission-denied targets and abort with a clear toast listing denied channel
names (do not proceed if any denied), then perform the updates but collect
per-channel results (successes and failures) when calling
supabase.from('channels').update for each target, log detailed errors with
channelName/channelId, and finally show a detailed toast summarizing "rolled
back: {succeeded}; failed: {failed}" (add the i18n key like
rollback-partial-failure) instead of a single generic error, and still call
refreshData() after reporting.
- Around line 113-119: The permission check loop using
checkPermissions('channel.promote_bundle', { channelId: target.channelId })
returns a generic toast via toast.error(t('no-permission')) which doesn't
indicate which target.channelId failed; update the failure path in the loop to
call toast.error with a channel-specific message key (e.g.,
t('no-permission-channel', { channel: target.name || target.channelId })) so the
user sees which channel blocked the action, and add the new
"no-permission-channel" entry to messages/en.json with a template like "You do
not have permission to update channel \"{channel}\"".

---

Outside diff comments:
In `@src/pages/app/`[app].compatibility.vue:
- Around line 166-175: The code uses guidanceCollapseKey and stores '0' for open
and '1' for collapsed which is counterintuitive; change the storage to represent
the open state directly (or rename the key to guidance-open). Update
guidanceOpen's initialization to read localStorage.getItem(guidanceOpenKey) ===
'1' (or !== '0' if you keep booleans inverted) and update toggleGuidance to set
localStorage.setItem(guidanceOpenKey, guidanceOpen.value ? '1' : '0') so the
stored value matches the meaning of guidanceOpen; adjust symbol names
(guidanceCollapseKey → guidanceOpenKey or similar) and ensure toggleGuidance and
guidanceOpen use the new key and consistent stored values.
🪄 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: 595def3a-9ccf-42df-9be3-5284e9551189

📥 Commits

Reviewing files that changed from the base of the PR and between b0aecdc and ecb23eb.

📒 Files selected for processing (2)
  • messages/en.json
  • src/pages/app/[app].compatibility.vue
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread src/pages/app/[app].compatibility.vue Outdated
Comment thread src/pages/app/[app].compatibility.vue
Backend: rolling a channel back to the baseline of an unresolved
compatibility event used to raise a fresh mirror event (prev/cur swapped)
while only the original auto-resolved — so the recommended remediation
created an endless chain of unresolved events. decideCompatibilityEvents
now takes the channel's unresolved events and suppresses an event whose
current bundle is a known unresolved baseline for that channel+platform
(a revert); the original event still auto-resolves on the same pass.
Suppression is channel-scoped and only applies while the event is
unresolved (an accepted incompatibility later reverted still raises).

Frontend: rollback now shows a single toast and re-refreshes at 4s/10s so
the queued auto-resolution becomes visible without a manual reload.

3 new unit tests for the revert suppression.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@supabase/functions/_backend/triggers/compatibility_events.ts`:
- Around line 159-170: The revert suppression predicate
(isRevertToKnownBaseline) is too broad and can match stale unresolved events;
update the unresolvedEvents.some(...) check to also require that the unresolved
event's current_version_id equals the bundle id we're reverting from
(previous.bundle.id) so only true mirror rollbacks are suppressed—i.e., keep the
existing checks against newChannel.id, previous.platform and
previous_version_id/currentBundle.id and add a condition verifying
event.current_version_id === previous.bundle.id.

In `@tests/compatibility-events-decide.unit.test.ts`:
- Around line 215-285: Update the three newly added test cases that call it(...)
to use it.concurrent(...) instead: "suppresses the mirror event when reverting
to an unresolved event's baseline", "does NOT suppress when the unresolved
baseline belongs to another channel", and "does NOT suppress an incompatible
change to a bundle no unresolved event knows as a baseline". Leave test bodies
and fixtures (decideCompatibilityEvents, newChannel, bundle, CHANGE_AT,
PKG_V6/PKG_V7) unchanged—only replace the it(...) invocations with
it.concurrent(...) so they run in parallel.
🪄 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: ce0c5c21-dcff-4848-96c2-35d9b3b2cf64

📥 Commits

Reviewing files that changed from the base of the PR and between ecb23eb and 137e69a.

📒 Files selected for processing (5)
  • messages/en.json
  • src/pages/app/[app].compatibility.vue
  • supabase/functions/_backend/triggers/compatibility_events.ts
  • supabase/functions/_backend/triggers/on_channel_update.ts
  • tests/compatibility-events-decide.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread supabase/functions/_backend/triggers/compatibility_events.ts Outdated
Comment thread tests/compatibility-events-decide.unit.test.ts Outdated
…arded rollback

- Revert the on_channel_update / compatibility_events trigger changes from
  this branch; the mirror-event suppression now lives in the backend-only
  PR #2482 (gated on disable_auto_update_under_native) so it can merge and
  deploy first.
- Rollback confirm dialog now appends a warning when an affected channel
  has the downgrade guard off: devices already on a newer native build
  could receive the older bundle — suggest enabling the guard first.
- Track the 4s/10s post-rollback refresh timers and clear them on unmount
  (and on a subsequent rollback) instead of firing into a dead component.
- Per-channel rollback outcome: partial failures now name the failed
  channel(s) and acknowledge which channels did roll back, instead of one
  generic error implying nothing happened.
- Snapshot rollback targets at dialog-open and pass them to the handler,
  so the writes performed on confirm are exactly the changes the dialog
  displayed.
- Soften the rollback card copy: 'the bundle it served before the
  incompatible change' instead of overpromising 'last compatible bundle /
  users stay safe' for chained-incompatibility cases; same for the
  rollbackTargets comment.
- Pre-check channel.promote_bundle per rollback target (request-id
  guarded watch) and gate the 'Roll back for me' CTA + confirm dialog on
  the permitted subset, so users without permission are never offered an
  action that would fail.
- A late permission failure (revoked between dialog open and confirm) now
  skips just that channel and names it in the outcome toast instead of
  aborting the whole batch with an anonymous no-permission error.
Address review: interactive elements use DaisyUI primitives per
AGENTS.md; replace the custom Tailwind button styling on the rebuild,
rollback and manage-channels CTAs with d-btn variants.
@sonarqubecloud

Copy link
Copy Markdown

@WcaleNieWolny WcaleNieWolny merged commit c2e93f4 into main Jun 12, 2026
44 checks passed
@WcaleNieWolny WcaleNieWolny deleted the feat/dependencies-incompatible-fix-guidance branch June 12, 2026 07:08
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