Skip to content

feat(boost): implement AI Catalog browse page and entity extensions#3706

Open
rohitkrai03 wants to merge 1 commit into
redhat-developer:mainfrom
rohitkrai03:feat/boost-ai-catalog-browse-page
Open

feat(boost): implement AI Catalog browse page and entity extensions#3706
rohitkrai03 wants to merge 1 commit into
redhat-developer:mainfrom
rohitkrai03:feat/boost-ai-catalog-browse-page

Conversation

@rohitkrai03

@rohitkrai03 rohitkrai03 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Description

Implements Story 2 (RHIDP-15166) — the AI Catalog browse page with card grid, table view, search, filters, pagination, and entity page extensions for AI assets in the Boost workspace.

Browse page (/ai-catalog)

  • Card grid (4 columns desktop, responsive) and table view toggle
  • Type, Provider, Owner, Tag multi-select filters with URL query param sync
  • Debounced search (300ms) matching name, title, description, tags
  • Client-side pagination with BUI TablePagination
  • Sortable table columns (name, type, owner, provider)
  • Loading skeleton, empty state, filtered-empty state, error state with retry
  • Error boundary preventing RHDH shell crashes
  • Colored type badges with click-to-filter

Entity page extensions

  • SummaryCard: prose description with rationale for rules
  • AdoptionCard: copyable npx skills add install command for skills
  • VersionListCard: current version from rhdh.io/ai-asset-version annotation
  • UsageTab: TechDocs link, external links, or "contact owner" fallback

Dev app updates

  • Group entity fixtures for owner resolution
  • @backstage/plugin-org dependency for Group entity pages
  • Fixture MCP server schema fix (transporttype)

Design decisions (deviations from spec)

  • 4 columns instead of 3 — fits better with card design
  • No category grouping (tabs/section headers) — deferred; type filter serves the purpose
  • No lifecycle filter/badge — not a primary discovery dimension
  • Provider filter (renamed from "Source") — clearer terminology
  • Owner as Select multi instead of SearchAutocomplete — sufficient for Dev Preview

Fixed

Screeshots

Screen.Recording.2026-07-08.at.1.29.07.AM.mov

✔️ Checklist

  • A changeset describing the change and affected packages.
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

Made with Cursor

Browse page at /ai-catalog with:
- Card grid (4 columns desktop, responsive) and table view toggle
- Type/Provider/Owner/Tag multi-select filters with URL sync
- Debounced search (300ms) matching name, title, description, tags
- Client-side pagination with TablePagination
- Sortable table columns (name, type, owner, provider)
- Loading skeleton, empty state, filtered-empty state, error state
- Error boundary preventing RHDH shell crashes
- Colored type badges with click-to-filter

Entity page extensions:
- SummaryCard: prose description with rationale for rules
- AdoptionCard: copyable npx install command for skills
- VersionListCard: current version from annotation
- UsageTab: TechDocs link, external links, fallback message

Signed-off-by: Rohit Rai <rohitkrai03@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@rhdh-gh-app

rhdh-gh-app Bot commented Jul 7, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
app workspaces/boost/packages/app none v0.0.0
@red-hat-developer-hub/backstage-plugin-boost workspaces/boost/plugins/boost minor v0.2.0

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:47 PM UTC · Completed 7:52 PM UTC
Commit: 26407b3 · View workflow run →

@sonarqubecloud

sonarqubecloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.52417% with 163 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.57%. Comparing base (26407b3) to head (7ba6e81).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3706      +/-   ##
==========================================
+ Coverage   54.55%   54.57%   +0.02%     
==========================================
  Files        2349     2360      +11     
  Lines       89735    90085     +350     
  Branches    25135    25229      +94     
==========================================
+ Hits        48953    49164     +211     
- Misses      40482    40621     +139     
  Partials      300      300              
Flag Coverage Δ *Carryforward flag
adoption-insights 83.70% <ø> (ø) Carriedforward from 26407b3
ai-integrations 68.35% <ø> (ø) Carriedforward from 26407b3
app-defaults 69.79% <ø> (ø) Carriedforward from 26407b3
augment 46.39% <ø> (ø) Carriedforward from 26407b3
boost 73.07% <58.52%> (-1.62%) ⬇️
bulk-import 72.46% <ø> (ø) Carriedforward from 26407b3
cost-management 14.10% <ø> (ø) Carriedforward from 26407b3
dcm 61.81% <ø> (ø) Carriedforward from 26407b3
extensions 61.53% <ø> (ø) Carriedforward from 26407b3
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 26407b3
global-header 59.71% <ø> (ø) Carriedforward from 26407b3
homepage 50.23% <ø> (ø) Carriedforward from 26407b3
install-dynamic-plugins 56.77% <ø> (ø) Carriedforward from 26407b3
konflux 91.49% <ø> (ø) Carriedforward from 26407b3
lightspeed 68.81% <ø> (ø) Carriedforward from 26407b3
mcp-integrations 85.46% <ø> (ø) Carriedforward from 26407b3
orchestrator 42.97% <ø> (ø) Carriedforward from 26407b3
quickstart 65.63% <ø> (ø) Carriedforward from 26407b3
sandbox 79.56% <ø> (ø) Carriedforward from 26407b3
scorecard 82.67% <ø> (ø) Carriedforward from 26407b3
theme 61.26% <ø> (ø) Carriedforward from 26407b3
translations 7.25% <ø> (ø) Carriedforward from 26407b3
x2a 78.68% <ø> (ø) Carriedforward from 26407b3

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 26407b3...7ba6e81. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fullsend-ai-review

Copy link
Copy Markdown

Review — approve

PR: feat(boost): implement AI Catalog browse page and entity extensions
Scope: 40 files changed (+2070 / −207) within workspaces/boost/

Summary

This PR replaces placeholder stub components with a fully functional AI Catalog browse page and entity page extensions for the Boost plugin. The implementation is well-structured, follows existing project conventions, and includes meaningful test coverage for the main browse page and card components.

Key strengths:

  • Clean separation of concerns: URL filter state (useUrlFilters), data fetching (useAiAssets), filtering logic (entityHelpers), and presentation components are well-decoupled
  • Proper debounced search with URL query parameter synchronization
  • Comprehensive state handling: loading skeletons, empty state, filtered-empty state, error state with retry, and error boundary to prevent RHDH shell crashes
  • Good i18n support with translation keys for all user-facing strings
  • Refactored filter logic from inline useAiAssets to shared applyEntityFilters utility, adding title and provider as new search/filter dimensions
  • buildCatalogFilter moved to isAiAsset.ts alongside the type map it depends on — better co-location

Findings

1. clearFilters resets layout preferences along with filter criteria — low

File: workspaces/boost/plugins/boost/src/hooks/useUrlFilters.ts (line ~224)

clearFilters calls setSearchParams(new URLSearchParams()) which replaces all query parameters, including view (grid/table toggle) and pageSize. A user who switched to table view with 50 rows/page, then applied filters that produced no results, would lose their layout preference when clicking "Clear filters."

Suggested fix: Preserve view and pageSize params when clearing filters:

const clearFilters = useCallback(() => {
  setSearchParams(prev => {
    const next = new URLSearchParams();
    const view = prev.get('view');
    const pageSize = prev.get('pageSize');
    if (view) next.set('view', view);
    if (pageSize) next.set('pageSize', pageSize);
    return next;
  });
  setSearchInputValue('');
  setDebouncedSearch('');
}, [setSearchParams]);

2. Entity page extension components have no unit test coverage — low

File: workspaces/boost/plugins/boost/src/components/catalog/entity/

SummaryCard, AdoptionCard, VersionListCard, and UsageTab each contain conditional rendering logic (returning null when data is absent) and non-trivial behavior (clipboard copy with timeout state, TechDocs link construction). These components have no test files. The main AiCatalogPage and AiAssetCard are well-tested, but the entity extensions would benefit from regression tests — especially AdoptionCard's clipboard flow and UsageTab's link fallback chain.

3. Extension identifier renames modify public API surface — low

File: workspaces/boost/plugins/boost/src/plugin.tsx, report.api.md

entity-card:boost/ai-asset-summaryentity-card:boost/summary and entity-card:boost/download-adoptentity-card:boost/adoption. These are extension identifiers that consumers reference in app-config.yaml. Since this is a dev-preview plugin that hasn't been released, the rename is safe now but would be breaking post-release. The report.api.md correctly reflects the change.

Verified

  • ✅ Filter/sort/pagination flow: filter → sort → paginate ordering is correct in AiCatalogPageContent
  • ✅ Debounce implementation: cleanup timer on re-render prevents stale closures
  • hasActiveFilters check correctly handles undefined values from useUrlFilters
  • ✅ Page bound clamping: Math.max(0, ...) and Math.min(100, Math.max(1, ...)) prevent invalid URL param values
  • ✅ External links use target="_blank" rel="noopener noreferrer" (EmptyState, UsageTab)
  • ✅ Error boundary correctly implements getDerivedStateFromError / componentDidCatch pattern
  • ✅ No injection vectors: entity data rendered via React component tree (no dangerouslySetInnerHTML)
  • ✅ Translation keys are complete and consistent between ref.ts and component usage
  • ✅ Fixture schema fix (transporttype) matches the expected MCP server spec
  • buildCatalogFilter moved to isAiAsset.ts — stays in sync with AI_ASSET_SPEC_TYPES by co-location
  • ✅ Changeset correctly marks the change as minor

Comment thread workspaces/boost/plugins/boost/src/hooks/useUrlFilters.ts
Comment thread workspaces/boost/plugins/boost/src/plugin.tsx
@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Jul 7, 2026
@rhdh-qodo-merge

Copy link
Copy Markdown

PR Summary by Qodo

AI Catalog browse page with filters, views, and entity page extensions

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add AI Catalog browse page with grid/table views, search, filters, sorting, and pagination.
• Introduce resilient UX states (loading/empty/filtered-empty/error) and a page-level ErrorBoundary.
• Extend AI asset entity pages with Summary, Adoption, Version, and Usage UI extensions.
Diagram

graph TD
  U["User"] --> P["AiCatalogPage (/ai-catalog)"] --> UI["Browse UI (Grid/Table/Sidebar)"]
  P --> UF("useUrlFilters") --> QS["URL query params"]
  P --> AF("useAiAssets") --> CA{{"Backstage Catalog API"}}
  EE["Entity page extensions"] --> CA

  subgraph Legend
    direction LR
    _c["UI component"] ~~~ _h("Hook") ~~~ _e{{"External/API"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Leverage Backstage catalog list primitives
  • ➕ Reuse established patterns like EntityListProvider/useEntityList for filtering/search/sort
  • ➕ Potentially less custom URL/state management and fewer bespoke components
  • ➖ May not map cleanly to the AI-asset-specific UX (card grid + bespoke filters)
  • ➖ Integration effort could exceed current approach for this custom design
2. Server-side filtering/pagination via catalog queries
  • ➕ Scales better for large catalogs (avoid fetching all entities client-side)
  • ➕ URL params can map directly to backend query/filter semantics
  • ➖ Catalog API filtering may not support all desired semantics (multi-field search, tags contains, etc.) without extra backend work
  • ➖ Adds backend coupling/complexity; harder to iterate quickly for Dev Preview
3. Adopt a query/cache library (e.g., react-query) for catalog fetches
  • ➕ Built-in caching, retry, and request deduplication; simplifies loading/error flows
  • ➕ Clear separation between async data and UI state
  • ➖ Introduces additional dependency/patterns to maintain
  • ➖ May be overkill if this plugin keeps a single fetch-on-mount model

Recommendation: Current approach is reasonable for Dev Preview: fetch AI assets once, then apply fast client-side filtering/sorting/pagination with URL-synced state. If catalog size is expected to grow significantly, consider evolving toward server-side filtering/pagination (or Backstage list primitives) to avoid loading all entities into the browser.

Files changed (33) +2036 / -174

Enhancement (25) +1707 / -152
AiAssetCard.module.cssAdd styling for AI asset card layout and truncation +65/-0

Add styling for AI asset card layout and truncation

• Defines card sizing, badge styling, description line-clamp, and footer layout for owner/scope rendering.

workspaces/boost/plugins/boost/src/components/catalog/AiAssetCard.module.css

AiAssetCard.tsxIntroduce AiAssetCard for grid view browsing +120/-0

Introduce AiAssetCard for grid view browsing

• Implements a card component that renders category/type badge (click-to-filter), title/description, tags, owner, and provider/scope annotation with a link to the entity page.

workspaces/boost/plugins/boost/src/components/catalog/AiAssetCard.tsx

AiCatalogPage.module.cssAdd layout and skeleton grid styles for AI Catalog page +64/-0

Add layout and skeleton grid styles for AI Catalog page

• Adds page layout sizing, empty state height, loading sidebar/grid styles, and responsive skeleton grid rules.

workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.module.css

AiCatalogPage.tsxImplement AI Catalog browse page UX (filters/search/views/pagination) +192/-7

Implement AI Catalog browse page UX (filters/search/views/pagination)

• Replaces the stub page with a full browse experience using URL-synced filters, debounced search, grid/table view toggle, sortable table integration, and pagination. Adds explicit loading/empty/error states and wraps the page in an ErrorBoundary to avoid shell crashes.

workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.tsx

AiCatalogTable.tsxAdd sortable table view for AI assets +106/-0

Add sortable table view for AI assets

• Implements a table-based view with sortable columns (name/type/owner/provider) and row href navigation, mapping entities into display rows.

workspaces/boost/plugins/boost/src/components/catalog/AiCatalogTable.tsx

EmptyFilteredState.tsxAdd filtered-empty state with clear-filters CTA +38/-0

Add filtered-empty state with clear-filters CTA

• Renders a dedicated empty state when filters/search produce zero results, with a button to clear all filters.

workspaces/boost/plugins/boost/src/components/catalog/EmptyFilteredState.tsx

EmptyState.tsxAdd base empty state when no assets exist +46/-0

Add base empty state when no assets exist

• Provides a “no assets available” state when the catalog has zero AI assets and no filters are applied, including an external learn-more link.

workspaces/boost/plugins/boost/src/components/catalog/EmptyState.tsx

ErrorBoundary.tsxAdd ErrorBoundary wrapper to prevent shell crashes +73/-0

Add ErrorBoundary wrapper to prevent shell crashes

• Introduces a class-based error boundary that logs render errors and shows an inline Alert with retry to recover without reloading the shell.

workspaces/boost/plugins/boost/src/components/catalog/ErrorBoundary.tsx

ErrorState.tsxReplace stub summary card with catalog fetch error state +18/-13

Replace stub summary card with catalog fetch error state

• Reworks the component into an error alert view for the browse page, including translated messaging and a retry action.

workspaces/boost/plugins/boost/src/components/catalog/ErrorState.tsx

FilterSidebar.module.cssAdd sticky sidebar styling for filters +12/-13

Add sticky sidebar styling for filters

• Defines a sticky, fixed-width vertical sidebar layout used to host filter selects on the browse page.

workspaces/boost/plugins/boost/src/components/catalog/FilterSidebar.module.css

FilterSidebar.tsxAdd filter sidebar with multi-select Type/Provider/Owner/Tag +128/-0

Add filter sidebar with multi-select Type/Provider/Owner/Tag

• Builds filter option sets from loaded entities and wires multi-select controls to URL filter actions (category/provider/owner/tag).

workspaces/boost/plugins/boost/src/components/catalog/FilterSidebar.tsx

LoadingState.tsxAdd loading skeleton state for sidebar and grid +60/-0

Add loading skeleton state for sidebar and grid

• Renders skeleton placeholders for the filter sidebar and card grid while assets are being fetched.

workspaces/boost/plugins/boost/src/components/catalog/LoadingState.tsx

AdoptionCard.module.cssAdd styling for copyable install command block +11/-16

Add styling for copyable install command block

• Introduces monospace, scrollable command styling used by the AdoptionCard.

workspaces/boost/plugins/boost/src/components/catalog/entity/AdoptionCard.module.css

AdoptionCard.tsxAdd AdoptionCard with copy-to-clipboard install command for skills +75/-0

Add AdoptionCard with copy-to-clipboard install command for skills

• Shows an 'npx skills add <name>' command for skill entities and provides a copy button with transient “Copied” feedback.

workspaces/boost/plugins/boost/src/components/catalog/entity/AdoptionCard.tsx

SummaryCard.tsxAdd SummaryCard displaying description and rationale +49/-0

Add SummaryCard displaying description and rationale

• Renders an entity summary with description plus optional rationale (from spec) when present; hides itself when empty.

workspaces/boost/plugins/boost/src/components/catalog/entity/SummaryCard.tsx

UsageTab.tsxAdd Usage tab with TechDocs and external links handling +73/-0

Add Usage tab with TechDocs and external links handling

• Displays a TechDocs link when configured, lists external links when present, and otherwise shows a translated fallback message directing users to contact the owner.

workspaces/boost/plugins/boost/src/components/catalog/entity/UsageTab.tsx

VersionListCard.tsxAdd VersionListCard reading current version from annotation +50/-0

Add VersionListCard reading current version from annotation

• Shows the current version badge based on the 'rhdh.io/ai-asset-version' annotation and hides the card when absent.

workspaces/boost/plugins/boost/src/components/catalog/entity/VersionListCard.tsx

index.tsExport AiAssetCard from catalog component index +2/-0

Export AiAssetCard from catalog component index

• Adds AiAssetCard (and its props type) to the public component exports for the catalog components module.

workspaces/boost/plugins/boost/src/components/catalog/index.ts

useAiAssets.tsRefactor AI asset fetching to shared helpers and stable sorting +14/-77

Refactor AI asset fetching to shared helpers and stable sorting

• Moves catalog filter construction and client filtering into shared utilities, sorts entities by title/name for stable UI ordering, and supports multi-select owner/provider filters.

workspaces/boost/plugins/boost/src/hooks/useAiAssets.ts

useUrlFilters.tsAdd hook for URL-synced filters, debounced search, and pagination +243/-0

Add hook for URL-synced filters, debounced search, and pagination

• Introduces URL query param synchronization for search (debounced), multi-select filters, view mode, and pagination state; includes clear-filters behavior and page reset on filter changes.

workspaces/boost/plugins/boost/src/hooks/useUrlFilters.ts

plugin.tsxWire new entity extensions and rename extension IDs +12/-12

Wire new entity extensions and rename extension IDs

• Renames entity card extension IDs (summary/adoption) and updates lazy loaders to the new entity extension components; updates usage tab and version card import paths accordingly.

workspaces/boost/plugins/boost/src/plugin.tsx

ref.tsExpand translations for browse page UI states and controls +43/-12

Expand translations for browse page UI states and controls

• Adds translation strings for toolbar, filters, table headers, empty/filtered-empty states, error details, and new entity extension copy/version labels.

workspaces/boost/plugins/boost/src/translations/ref.ts

categoryMeta.tsAdd centralized category metadata (labels/icons/colors) +88/-0

Add centralized category metadata (labels/icons/colors)

• Defines the mapping from spec.type to category label/icon/color and provides helpers to retrieve meta and populate filter options.

workspaces/boost/plugins/boost/src/utils/categoryMeta.ts

entityHelpers.tsAdd shared entity helpers for href, spec access, filtering, and sorting +112/-0

Add shared entity helpers for href, spec access, filtering, and sorting

• Introduces utilities to build entity URLs, read spec fields, apply client-side filters (search/category/provider/owner/tags), and compute sort values for table sorting.

workspaces/boost/plugins/boost/src/utils/entityHelpers.ts

isAiAsset.tsExpose buildCatalogFilter alongside isAiAsset source-of-truth map +13/-2

Expose buildCatalogFilter alongside isAiAsset source-of-truth map

• Clarifies ownership of AI asset kind/type map and adds an exported buildCatalogFilter helper for catalogApi.getEntities OR-filter construction.

workspaces/boost/plugins/boost/src/utils/isAiAsset.ts

Tests (3) +230 / -10
AiAssetCard.test.tsxAdd unit tests for AiAssetCard rendering and linking +125/-0

Add unit tests for AiAssetCard rendering and linking

• Covers title/description rendering, category badge, tags, owner/scope display, and href to the entity detail page with fallbacks.

workspaces/boost/plugins/boost/src/components/catalog/AiAssetCard.test.tsx

AiCatalogPage.test.tsxExpand AiCatalogPage tests for loading, success, empty, and error states +104/-9

Expand AiCatalogPage tests for loading, success, empty, and error states

• Mocks CatalogApi to validate loading skeletons, rendering entity cards and counts, empty state messaging, error state with retry, and search field presence.

workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.test.tsx

useAiAssets.test.tsAdjust useAiAssets tests for new stable sorting behavior +1/-1

Adjust useAiAssets tests for new stable sorting behavior

• Updates expectations to reflect the new title/name sort applied to the fetched entity list.

workspaces/boost/plugins/boost/src/hooks/useAiAssets.test.ts

Documentation (2) +36 / -10
ai-catalog-browse-page.mdAdd changeset for AI Catalog browse page release +5/-0

Add changeset for AI Catalog browse page release

• Introduces a minor changeset entry for the boost plugin package describing the new AI Catalog browse page and entity extensions.

workspaces/boost/.changeset/ai-catalog-browse-page.md

report.api.mdUpdate public API report for new/renamed extensions and i18n keys +31/-10

Update public API report for new/renamed extensions and i18n keys

• Renames entity-card extension IDs (summary/adoption) and expands the translation key surface for the catalog page/table/filters/states.

workspaces/boost/plugins/boost/report.api.md

Other (3) +63 / -2
app-config.yamlAllow Group entities in local catalog ingestion rules +1/-1

Allow Group entities in local catalog ingestion rules

• Extends the local catalog location rules to allow Group entities so owner resolution can work with group fixtures.

workspaces/boost/app-config.yaml

ai-catalog-fixtures.yamlEnhance AI catalog fixtures with owner groups and asset locations +61/-1

Enhance AI catalog fixtures with owner groups and asset locations

• Adds Group entities used as owners for AI assets, adds git location metadata to sample assets, and fixes MCP server remote schema from transport to type.

workspaces/boost/fixtures/ai-catalog-fixtures.yaml

package.jsonAdd Backstage org plugin dependency +1/-0

Add Backstage org plugin dependency

• Adds @backstage/plugin-org to support rendering Group entity pages in the dev app.

workspaces/boost/packages/app/package.json

@rhdh-qodo-merge

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials

Grey Divider


Remediation recommended

1. Out-of-range page empties results 🐞 Bug ≡ Correctness
Description
AiCatalogPage slices entities using page * pageSize from the URL without clamping to the
available result count. If the URL contains an out-of-range page value, pagedEntities becomes
empty while totalCount > 0, and the UI shows neither results nor an empty state.
Code

workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.tsx[R92-97]

+  const totalCount = filteredEntities.length;
+  const pageStart = page * pageSize;
+  const pagedEntities = useMemo(
+    () => sortedEntities.slice(pageStart, pageStart + pageSize),
+    [sortedEntities, pageStart, pageSize],
+  );
Relevance

⭐⭐ Medium

No similar historical finding on pagination clamping; team often accepts edge-case guards (e.g.,
empty-input guard) in PR 3572.

PR-#3572

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The page offset is derived directly from page and pageSize, and the results slice is taken
without bounds checks. Rendering only switches to empty states when totalCount === 0, so an empty
slice with totalCount > 0 results in no rendered results content.

workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.tsx[92-98]
workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.tsx[162-181]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Pagination uses `page` from the URL to compute the slice offset, but there is no guard for `pageStart >= totalCount`. This can render an empty grid/table even though matches exist.

### Issue Context
Empty states are only shown when `totalCount === 0`, so an empty slice with `totalCount > 0` yields a blank results pane.

### Fix Focus Areas
- workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.tsx[92-98]
- workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.tsx[162-197]

### Suggested fix
- Add clamping/reset logic when `totalCount` or `pageSize` changes:
 - Compute `maxPage = Math.max(0, Math.ceil(totalCount / pageSize) - 1)`.
 - If `page > maxPage`, call `setPage(maxPage)` (or `setPage(0)`), which will also normalize the URL.
- Consider also handling the render case: if `totalCount > 0` but `pagedEntities.length === 0`, show a small message and/or auto-reset to page 0.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Invalid view param blanks 🐞 Bug ≡ Correctness
Description
useUrlFilters casts the view query param to ViewMode without validation, so any non-empty
invalid value (e.g. ?view=foo) becomes the state. AiCatalogPage only renders results when
viewMode is exactly 'grid' or 'table', which can leave the results area empty even when assets
exist.
Code

workspaces/boost/plugins/boost/src/hooks/useUrlFilters.ts[R74-90]

+  const rawSearch = searchParams.get('q') ?? '';
+  const category = useMemo(
+    () => readArray(searchParams, 'type'),
+    [searchParams],
+  );
+  const provider = useMemo(
+    () => readArray(searchParams, 'provider'),
+    [searchParams],
+  );
+  const owner = useMemo(() => readArray(searchParams, 'owner'), [searchParams]);
+  const tag = useMemo(() => readArray(searchParams, 'tag'), [searchParams]);
+  const viewMode = (searchParams.get('view') as ViewMode) || 'grid';
+  const page = Math.max(0, parseInt(searchParams.get('page') ?? '0', 10) || 0);
+  const pageSize = Math.min(
+    100,
+    Math.max(1, parseInt(searchParams.get('pageSize') ?? '20', 10) || 20),
+  );
Relevance

⭐ Low

Similar “don’t cast union type without validation” suggestion was rejected in PR 3574
(lifecycleStage cast).

PR-#3574

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The hook assigns viewMode directly from the URL using a type cast, allowing invalid string values;
the page render logic only handles 'grid' and 'table', so invalid values result in no results
component being rendered.

workspaces/boost/plugins/boost/src/hooks/useUrlFilters.ts[74-90]
workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.tsx[166-181]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`useUrlFilters` accepts any string for the `view` query param (via a TS cast), which can put the UI into an unsupported view mode.

### Issue Context
Unsupported `viewMode` values prevent both the grid and table render branches from running.

### Fix Focus Areas
- workspaces/boost/plugins/boost/src/hooks/useUrlFilters.ts[74-90]
- workspaces/boost/plugins/boost/src/components/catalog/AiCatalogPage.tsx[166-181]

### Suggested fix
- Parse `view` defensively:
 - `const viewParam = searchParams.get('view');`
 - `const viewMode: ViewMode = viewParam === 'table' ? 'table' : 'grid';`
- (Optional) If `viewParam` is present but invalid, normalize the URL by deleting `view` so links self-heal.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@rhdh-qodo-merge rhdh-qodo-merge Bot added documentation Improvements or additions to documentation enhancement New feature or request Tests Bug fix labels Jul 7, 2026

@gabemontero gabemontero 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.

@rohitkrai03 - saw my kagenti agent, and the models form my openai creds leveraged by llamastack !!!

didn't do a super detail validation, especially given I'll be changing the backend wrt kagenti / llamstack MCP servers ... and if my upstream model server as API entity merges soon like it appears it might, will switch that too

Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug fix documentation Improvements or additions to documentation enhancement New feature or request ready-for-merge All reviewers approved — ready to merge Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants