Skip to content

feat(frontend): bundle dependency compatibility view#2375

Merged
WcaleNieWolny merged 4 commits into
mainfrom
wolny/bundle-compatibility-view
May 31, 2026
Merged

feat(frontend): bundle dependency compatibility view#2375
WcaleNieWolny merged 4 commits into
mainfrom
wolny/bundle-compatibility-view

Conversation

@WcaleNieWolny
Copy link
Copy Markdown
Contributor

@WcaleNieWolny WcaleNieWolny commented May 30, 2026

Summary

Adds a bundle dependency compatibility view to the Dependencies tab. It compares the bundle you're viewing (the candidate — what would ship over-the-air) against a baseline bundle you pick, and tells you whether the update is OTA-safe or needs an app-store update — the same verdict as the CLI capgo bundle compatibility.

Builds on the compare-picker fix already merged in #2373 (which gated the picker on native_packages). This PR is a single clean commit on top of main.

What it does

Status-aware dependency diff — every native package shown with a colour-coded status and version(s):

Status Colour Display
changed blue old → new
added green New · <version>
removed red Removed · <version>
unchanged gray Unchanged · <version>

Counts row: Changed / Added / Removed / Unchanged.

Compatibility verdict banner — ✅ Compatible ("can be delivered over-the-air to devices running {baseline}") or ❌ Not compatible ("N packages require an app store update"), with per-package reasons. Logic is ported from the CLI's getCompatibilityDetails: version-range intersection (@std/semver) plus iOS/Android native checksum changes; a new native plugin blocks OTA, a removed one is safe.

Deep-linkable — selecting a baseline reflects in the URL as ?compare=<versionId> (history-friendly router.replace); loading such a URL pre-selects the baseline.

Picker hygiene — the compare dropdown excludes soft-deleted bundles, and falls back past deleted deployments when choosing preferred (deploy-history) baselines, so a channel whose latest prior deploy was deleted still gets a usable baseline.

Direction (by design)

The viewed bundle is the candidate; the picked bundle is the installed baseline — matching the CLI. So an added native plugin (green in the diff) is also what triggers a ❌ verdict (new native code → app-store update); the banner names offenders so the two readings never contradict.

Files

  • New src/services/bundleCompatibility.ts — pure, unit-tested comparison + verdict util.
  • New tests/bundle-compatibility.unit.test.ts — 14 tests.
  • src/pages/app/[app].bundle.[bundle].dependencies.vue — status-aware table, verdict banner, ?compare sync.
  • src/components/bundle/BundleCompareSelect.vue — exclude deleted bundles; deleted-deployment fallback.
  • messages/en.json — new i18n keys (source locale; others fall back to en).
  • Design spec under docs/superpowers/specs/.

No backend/schema/API changes.

Verification

  • ✅ 14/14 unit tests
  • ✅ Lint: oxlint src and eslint src/**/*.{vue,ts,js} both clean
  • ✅ Typecheck clean (only a pre-existing unrelated gsap resolution note)
  • ✅ Verified against prod data (me.wcaleniewolny.test.ionic.vue2): 1.0.7-c vs 0.0.0 → 3 changed packages, ❌ Not compatible; deleted 1.0.4-* bundles excluded from the picker; deploy-history fallback exercised (channel with 4 deleted + 10 live deploys).

Test plan

  • Open a bundle's Dependencies tab, pick a baseline → verdict banner + colour-coded rows render; dropdown closes on select; URL gains ?compare=.
  • Load a ?compare=<id> URL directly → baseline pre-selected, comparison shown.
  • Soft-deleted bundles do not appear in the compare picker (dependencies and manifest tabs).
  • Compare two identical bundles → all unchanged, ✅ Compatible.

Summary by CodeRabbit

  • New Features

    • OTA compatibility checking for native dependencies with a verdict banner, per-package compatibility reasons, status summary cards, and a status-aware comparison table.
  • User Experience

    • Baseline selection is URL-driven, offers richer history choices, and dropdown behavior is more reliable.
  • Documentation

    • Added a design spec for bundle dependency compatibility.
  • Localization

    • Updated dependency/compatibility UI copy and added new message keys.
  • Tests

    • Added unit tests covering compatibility logic and edge cases.

Review Change Stack

Adds an OTA compatibility verdict and a status-aware dependency diff to the
bundle Dependencies tab, comparing the viewed bundle (candidate) against a
selected baseline bundle.

- Per-package status with colours: changed (blue, old -> new), added (green),
  removed (red), unchanged (gray).
- Compatibility verdict banner (compatible vs requires-app-store-update) using
  logic ported from the CLI's getCompatibilityDetails: version-range
  intersection + native checksum changes; new native plugins block OTA.
- Comparison is deep-linkable via ?compare=<versionId>.
- Compare picker excludes deleted bundles and falls back past deleted
  deployments when choosing preferred baselines.
- New unit-tested util src/services/bundleCompatibility.ts.

Builds on the compare-picker fix already merged in #2373.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 07acfb38-4db0-432e-87dd-a25d9c22dbe1

📥 Commits

Reviewing files that changed from the base of the PR and between e77a2f9 and 06f36f6.

📒 Files selected for processing (2)
  • src/services/bundleCompatibility.ts
  • tests/bundle-compatibility.unit.test.ts

📝 Walkthrough

Walkthrough

Adds a shared bundle compatibility service and tests, updates design docs and i18n, improves compare-version candidate selection, and refactors the dependencies page to show baseline-driven compatibility verdicts, per-package statuses, and localized incompatibility reasons.

Changes

Bundle Dependency OTA Compatibility Feature

Layer / File(s) Summary
Compatibility service and tests
src/services/bundleCompatibility.ts, tests/bundle-compatibility.unit.test.ts
New shared module exports NativePackage, IncompatibilityReason, and PackageStatus types. Implements comparePackages to classify per-package status and emit reasons (version mismatch, iOS/Android checksum changes) and summarizeCompatibility to produce an OTA verdict with incompatible count and offending packages. Unit tests cover statuses, semver intersection, checksum detection per platform, multi-reason cases, and deterministic ordering.
Design specification and i18n
docs/superpowers/specs/2026-05-30-bundle-dependency-compatibility-design.md, messages/en.json
Design spec documents the directional compatibility model, verdict UX, reason mapping, and test plan. Messages add keys for incompatibility reasons (compat-reason-version-mismatch, -android-changed, -ios-changed, -new-plugin, -removed-plugin), verdict banners (compat-verdict-compatible, -incompatible, -detail), and simplified dependency status/summary labels (dependencies-status-added, -changed, -removed, -unchanged, plus summary keys).
Compare version candidate selection enhancements
src/components/bundle/BundleCompareSelect.vue
Blurs active element after selection to close the DaisyUI dropdown, filters out deleted bundle versions from candidate queries, and refactors preferred-version derivation to use a per-channel lookback (PREFERRED_LOOKBACK) with per-channel candidate lists and cross-channel deduplication.
Dependencies page baseline comparison and verdict UI
src/pages/app/[app].bundle.[bundle].dependencies.vue
Refactors comparison flow to use shared compatibility service and URL-driven baseline selection via ?compare=<versionId>. Replaces local compare state with baselinePackages cache and adds computed comparisons, compatibilitySummary, and statusCounts. Implements URL sync and Supabase restoration with race-guarded loading. Updates the template to show four status summary cards, a compatibility verdict banner with offending packages, and a status-aware comparison table rendering localized incompatibility reasons and baseline→candidate version formatting.

Sequence Diagram

sequenceDiagram
  participant User
  participant DepsPage
  participant URL
  participant Supabase
  participant CompatService
  User->>DepsPage: select baseline version
  DepsPage->>URL: write ?compare=versionId
  DepsPage->>Supabase: fetch baseline native_packages
  Supabase-->>DepsPage: baselinePackages
  DepsPage->>CompatService: comparePackages(candidate, baseline)
  CompatService-->>DepsPage: comparisons[]
  DepsPage->>DepsPage: compute statusCounts + compatibilitySummary
  DepsPage-->>User: render verdict banner + status cards + table
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Cap-go/capgo#2373: Modifies src/components/bundle/BundleCompareSelect.vue and the compare candidate/query logic; related at candidate selection/query level.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% 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 clearly and concisely summarizes the main feature addition: a bundle dependency compatibility view for the frontend.
Description check ✅ Passed The description provides a comprehensive summary, test plan with checkboxes, and verification details, though the explicit checklist items are not checked off.
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.

✨ 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 May 30, 2026
@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/bundle-compatibility-view (06f36f6) with main (df527e2)

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: 90c3f20377

ℹ️ 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 src/pages/app/[app].bundle.[bundle].dependencies.vue
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 `@src/services/bundleCompatibility.ts`:
- Around line 85-90: Remove the redundant Boolean() wrappers around the checksum
comparisons: assign iosChanged and androidChanged directly from the logical
expressions using candidate.ios_checksum && baseline.ios_checksum &&
candidate.ios_checksum !== baseline.ios_checksum and candidate.android_checksum
&& baseline.android_checksum && candidate.android_checksum !==
baseline.android_checksum respectively so the expressions themselves produce the
boolean values (refer to iosChanged and androidChanged in the current diff).
🪄 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: a83a2cfa-42af-410e-986e-cd2d040e3077

📥 Commits

Reviewing files that changed from the base of the PR and between df527e2 and 90c3f20.

📒 Files selected for processing (6)
  • docs/superpowers/specs/2026-05-30-bundle-dependency-compatibility-design.md
  • messages/en.json
  • src/components/bundle/BundleCompareSelect.vue
  • src/pages/app/[app].bundle.[bundle].dependencies.vue
  • src/services/bundleCompatibility.ts
  • tests/bundle-compatibility.unit.test.ts

Comment thread src/services/bundleCompatibility.ts Outdated
…ar nits

- restoreCompareFromQuery now requires deleted=false and (dependencies mode)
  native_packages IS NOT NULL, matching the compare picker's eligibility. A
  ?compare= link to a deleted or package-less bundle is scrubbed instead of
  comparing against an empty baseline that reports every dep as added. (Codex P2)
- Replace the void operator on router.replace with a .catch() (Sonar S3735).
- Flip the negated ternary in the sort comparator (Sonar S7735).
Use explicit != null guards so iosChanged/androidChanged are still typed as
boolean (bare && would infer string | boolean). Addresses CodeRabbit nitpick on #2375.
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.

Caution

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

⚠️ Outside diff range comments (1)
src/services/bundleCompatibility.ts (1)

98-109: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align statusFor with checksum-based incompatibility so “Unchanged” never blocks OTA

  • evaluateReasons flags ios_code_changed / android_code_changed / both_platforms_changed when checksums differ (null-safe), but statusFor only marks changed on candidate.version !== baseline.version; unit tests already cover same-version/different-iOS-checksum => status: 'unchanged' with compatible: false.
  • dependencies.vue renders the pill from entry.status while rendering incompatibility reasons only when !entry.compatible, so users can see a gray “Unchanged” row alongside red “native code changed” text while the verdict banner reports blocking OTA.
  • Update statusFor to incorporate the same criteria as evaluateReasons (checksum diffs, and consider aligning the version-mismatch condition to versionsIntersect rather than string inequality).
🤖 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/services/bundleCompatibility.ts` around lines 98 - 109, statusFor
currently marks packages 'changed' only when candidate.version !==
baseline.version, which misses checksum-based native-code changes used by
evaluateReasons; update statusFor (function statusFor) to return 'changed' if
any platform checksum differs (null-safe compare candidate.iosChecksum vs
baseline.iosChecksum, candidate.androidChecksum vs baseline.androidChecksum, or
both) or if versions do not intersect (use versionsIntersect(candidate.version,
baseline.version) rather than plain string inequality), otherwise return
'unchanged' or the existing 'added'/'removed' cases; align logic with
evaluateReasons so checksum diffs cause status 'changed' and reflect
compatibility consistently.
🤖 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.

Outside diff comments:
In `@src/services/bundleCompatibility.ts`:
- Around line 98-109: statusFor currently marks packages 'changed' only when
candidate.version !== baseline.version, which misses checksum-based native-code
changes used by evaluateReasons; update statusFor (function statusFor) to return
'changed' if any platform checksum differs (null-safe compare
candidate.iosChecksum vs baseline.iosChecksum, candidate.androidChecksum vs
baseline.androidChecksum, or both) or if versions do not intersect (use
versionsIntersect(candidate.version, baseline.version) rather than plain string
inequality), otherwise return 'unchanged' or the existing 'added'/'removed'
cases; align logic with evaluateReasons so checksum diffs cause status 'changed'
and reflect compatibility consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 753a6029-a457-4ccc-af40-b09e2ba3a0eb

📥 Commits

Reviewing files that changed from the base of the PR and between 6a3c42d and e77a2f9.

📒 Files selected for processing (1)
  • src/services/bundleCompatibility.ts

…nchanged'

statusFor marked a package 'changed' only on a version-string difference, but
evaluateReasons also flags incompatibility when a native checksum changes at the
same version. That produced a contradictory row: a gray 'Unchanged' pill next to
red 'native code changed' text while the verdict reported incompatible.

Derive status from the computed reasons too, so a same-version bundle whose
native code changed is shown as 'changed' and stays consistent with the verdict.
Addresses CodeRabbit review on #2375.
@WcaleNieWolny
Copy link
Copy Markdown
Contributor Author

Addressed the CodeRabbit "outside diff range" finding on bundleCompatibility.ts (statusFor vs evaluateReasons): a same-version package whose native checksum changed was classified unchanged while the verdict flagged it incompatible — a contradictory gray-pill-plus-red-reason row. Fixed in 06f36f6 by deriving status from the computed reasons, so any incompatibility-causing change (version or native checksum) renders as changed. Updated 2 tests + added a same-version/same-checksum guard test (15 total, all green). No inline thread existed to resolve (the finding was posted outside the diff range).

@sonarqubecloud
Copy link
Copy Markdown

@WcaleNieWolny WcaleNieWolny merged commit a376daf into main May 31, 2026
42 checks passed
@WcaleNieWolny WcaleNieWolny deleted the wolny/bundle-compatibility-view branch May 31, 2026 04:44
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