Skip to content

feat(nvcf-ui): import the self-hosted UI into the monorepo - #662

Open
balajinvda wants to merge 6 commits into
mainfrom
feat/import-nvcf-ui
Open

feat(nvcf-ui): import the self-hosted UI into the monorepo#662
balajinvda wants to merge 6 commits into
mainfrom
feat/import-nvcf-ui

Conversation

@balajinvda

@balajinvda balajinvda commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Why

Closes #634. The self-hosted NVCF UI is developed outside this repository today.
Now that every first-party service builds from the root Bazel module here, the
UI should live alongside them so it shares one build, one CI matrix, one
dependency audit, and one release path.

Source: ngc/apps/nvcf-ui at 2c6b0b711b46c964cd4b3a119c5dd467540efbfc.

What changed

Imports 267 of the upstream's 271 tracked files into src/uis/nvcf-ui, grouped
by artifact kind alongside src/clis/nvcf-cli rather than under control-plane
services, since the UI is a shipped user-facing artifact and not a
control-plane service. Layout is ui/ (React/TypeScript, pnpm), backend/
(Go), helm/, and spec/.

Excluded from the import:

Path Reason
.gitlab-ci.yml superseded by this repository's release path
node_modules/.vite/.../results.json committed vitest cache artifact
.vscode/ editor-local settings

Rewrites bare uppercase SIS to describe the cluster API in prose. All five
occurrences were comments or documentation, so nothing structural changed. The
OSS snapshot tooling has historically dropped entire files containing those bare
tokens, which would have silently removed backend/cmd/server/main.go from the
public mirror. Worth noting for the reviewer: .github/CODEOWNERS on main
already contains SIS today, so that gate may no longer be active on this path.
The rewrite is cheap and reads fine either way, but I did not want to overstate
it as a confirmed near-miss.

Adds /src/uis/ @NVIDIA/nvcf-control-plane-dev to CODEOWNERS as requested in
review. Note the team slug: nvcf-control-plane-services-dev returns 404 from
the org API, and nvcf-control-plane-dev is what every other entry uses, so I
went with the one that exists. A slug that does not resolve silently disables
the code-owner gate rather than erroring, so it is worth confirming this is the
intended team.

Customer Release Notes

Not customer visible. This moves source only; nothing is built or published
differently yet.

Plan Summary

Not applicable.

Usage

Not applicable.

Testing

tools/ci/check-license passes: 291 files checked, MPL audit in sync, NOTICE up
to date after regenerating it to absorb the UI's own NOTICE. All 182 imported
.ts/.tsx/.go source files already carry SPDX headers, confirmed by count
rather than by sampling.

No Bazel build of the subtree runs in this PR, by design. See below.

Notes

The subtree keeps the upstream MODULE.bazel for now and is listed in the root
.bazelignore, so the root module does not try to load packages whose labels
are still relative to the upstream module root.

This is deliberately temporary and runs against the recent consolidation of all
17 nested modules into the root. Merging the JS rules into the root
MODULE.bazel means rewriting every label in the UI's BUILD files
(//ui:... becomes //src/uis/nvcf-ui/ui:...) and validating with a real
build that needs Node and pnpm. That is its own reviewable change, and doing it
here would bury a 267-file import under a build migration.

Good news relative to the issue text: #634 assumed no JavaScript ruleset existed
anywhere. The UI already builds under Bazel with aspect_rules_js 3.2.2 and
rules_nodejs 6.7.3, and already ships LICENSE, NOTICE, THIRD-PARTY.txt,
and license-header.txt. Its MODULE.bazel deps are close to a superset of the
root module's, so the remaining work is a merge rather than a design exercise.

Follow-ups, tracked in #634:

  • merge the JS rules into the root MODULE.bazel, rewrite BUILD labels, drop
    the .bazelignore entry, and add a bazel.yml matrix row
  • add an npm collector to tools/collect-dependencies. It has collectors for
    Go, Helm, Java, Python, and Rust but none for npm, so until it exists the
    UI's third-party tree does not reach dependencies.md
  • decide whether helm/ stays in the subtree or moves to deploy/helm/nvcf-ui
  • security and quality scanning registration

References

Closes #634

Related Merge Requests/Pull Requests

None

Dependencies

No new dependencies are added to the root module by this PR. The UI's own
third-party tree arrives with the import and is enumerated in its
THIRD-PARTY.txt and NOTICE; wiring it into this repository's
dependencies.md is blocked on the npm collector above and should gate the
dependency review.

Github commit:
feat(nvcf-ui): import the self-hosted UI into the monorepo

Closes #634

Co-authored-by: Balaji Ganesan bganesan@nvidia.com

Summary by CodeRabbit

  • New Features
    • Added the NVCF UI with dashboard statistics, account switching, compute-cluster views, and function listings.
    • Added detailed cluster and function pages with health, GPU utilization, deployment configuration, filtering, sorting, searching, and pagination.
    • Added responsive data tables, loading states, error recovery, copy-to-clipboard controls, theme preferences, and not-found handling.
    • Added Kubernetes deployment support with Helm, autoscaling, gateway routing, health monitoring, and secure API connectivity.
  • Documentation
    • Added setup, development, testing, security, and licensing guidance.
  • Tests
    • Added broad UI and backend test coverage.

Imports ngc/apps/nvcf-ui at 2c6b0b711b46c964cd4b3a119c5dd467540efbfc into
src/uis/nvcf-ui, grouped by artifact kind alongside src/clis/nvcf-cli rather
than under control-plane services: the UI is a shipped user-facing artifact,
not a control-plane service.

267 of the upstream's 271 tracked files come across. Excluded:

- .gitlab-ci.yml, superseded by this repository's release path
- node_modules/.vite/.../results.json, a committed vitest cache artifact
- .vscode/, editor-local settings

Rewrites bare uppercase SIS to describe the cluster API in prose instead. All
five occurrences were comments or documentation, so nothing structural changed.
The OSS snapshot tooling has historically dropped whole files containing those
bare tokens, which would have silently removed backend/cmd/server/main.go from
the public mirror and broken the build with no error pointing at the cause.

Adds /src/uis/ to CODEOWNERS under nvcf-control-plane-dev, per review request.

The subtree keeps the upstream MODULE.bazel for now and is listed in
.bazelignore, so the root module does not try to load packages whose labels are
still relative to the upstream module root. That is deliberately temporary and
runs against the recent consolidation of every nested module into the root;
merging the JS rules into the root MODULE.bazel and rewriting the labels needs
a real build to validate and belongs in its own change.

Not included, and tracked in #634:

- root MODULE.bazel merge and the bazel.yml matrix row
- an npm collector for tools/collect-dependencies; until it exists the UI's
  third-party tree does not reach dependencies.md

NOTICE is regenerated to absorb the UI's own NOTICE. All 182 imported source
files already carry SPDX headers, and tools/ci/check-license passes.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
@balajinvda
balajinvda requested review from a team as code owners August 4, 2026 17:03
@balajinvda
balajinvda requested a review from sbaum1994 August 4, 2026 17:03
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9e16dc71-44a8-47f8-abb5-278e759684f0

📥 Commits

Reviewing files that changed from the base of the PR and between 596c004 and f426c95.

📒 Files selected for processing (2)
  • src/clis/nvcf-cli/internal/client/client_test.go
  • src/clis/nvcf-cli/internal/state/state.go

📝 Walkthrough

Walkthrough

The pull request imports the self-hosted NVCF UI into the monorepo. It adds the React frontend, Go backend, control-plane monitoring, token watching, OpenAPI specifications, Helm chart, Bazel and OCI build integration, tests, mocks, documentation, and licensing metadata.

Changes

NVCF UI monorepo import

Layer / File(s) Summary
Repository and build integration
.bazelignore, MODULE.bazel, go.work.bazel, .github/workflows/bazel.yml, src/uis/nvcf-ui/BUILD.bazel, src/uis/nvcf-ui/ui/BUILD.bazel, rules/oci/*
Adds Bazel workspace support, Node.js dependency translation, Go targets, UI build and test targets, workflow coverage, source packaging, and multi-architecture OCI image creation.
Backend services and monitoring
src/uis/nvcf-ui/backend/**
Adds the HTTP server, reverse proxies, static SPA serving, middleware, Kubernetes client utilities, token rotation watcher, control-plane health monitor, heartbeat liveness checks, and Go tests.
Deployment and API contracts
src/uis/nvcf-ui/helm/**, src/uis/nvcf-ui/spec/**, src/uis/nvcf-ui/tools/workspace_status.sh
Adds Helm resources for the UI and control-plane monitor, OpenBao and Vault integration, RBAC, Gateway API resources, autoscaling, health configuration, OpenAPI contracts, and workspace stamping.
Frontend application
src/uis/nvcf-ui/ui/src/**, src/uis/nvcf-ui/ui/orval.config.ts, src/uis/nvcf-ui/ui/package.json
Adds the React application shell, routing, account switching, dashboard, cluster and function views, shared components, data tables, query utilities, generated API configuration, and MSW-backed mock data.
Validation and project metadata
src/uis/nvcf-ui/ui/src/**/*.test.*, src/uis/nvcf-ui/README.md, src/uis/nvcf-ui/SECURITY.md, src/uis/nvcf-ui/THIRD-PARTY.txt, dependencies.md, src/uis/nvcf-ui/.claude/**
Adds frontend tests, mock scenarios, development and testing guidance, security documentation, third-party notices, dependency records, ownership, and project configuration.

Estimated code review effort: 5 (Critical) | ~120 minutes

Suggested reviewers: kristinapathak, sbaum1994

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR completes the import and license work but does not complete the release and security or quality scanning requirements in issue #634. Complete or explicitly defer the remaining #634 requirements, including release integration and security or quality scanning, before closing the issue.
Out of Scope Changes check ⚠️ Warning The PR includes unrelated nvcf-cli state-manager changes in src/clis/nvcf-cli outside the nvcf-ui import objectives. Remove the nvcf-cli changes or move them into a separate pull request.
Docstring Coverage ⚠️ Warning Docstring coverage is 38.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses a valid scoped Conventional Commit and accurately describes the UI import.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/import-nvcf-ui

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

@berkomu berkomu self-assigned this Aug 4, 2026
Two CI failures from the import.

check-nested-modules rejected src/uis/nvcf-ui because a new nested MODULE.bazel
is unclassified. Recorded as `ledger`, not `exception`: the category means the
migration is outstanding rather than excused, and the entry leaves the file
entirely once the subtree builds from the root module. This does re-open the
consolidation backlog, which had reached zero services awaiting migration.

dependencies.md was stale because the UI's Go backend pulls in
github.com/rs/xid. Regenerated with the real generator rather than hand-applying
the diff CI printed, since the two have disagreed before.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
@balajinvda
balajinvda requested a review from a team as a code owner August 4, 2026 17:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 20

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (38)
src/uis/nvcf-ui/.claude/skills/add-feature/SKILL.md-39-39 (1)

39-39: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to the fenced tree block.

markdownlint reports Line 39 because the fence has no language. Use text for this file-tree example.

Proposed fix
-```
+```text
🤖 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/uis/nvcf-ui/.claude/skills/add-feature/SKILL.md` at line 39, Update the
fenced tree block in SKILL.md to specify the text language tag, changing the
untagged fence to a text-tagged fence while preserving the file-tree example
content.

Source: Linters/SAST tools

src/uis/nvcf-ui/AGENTS.md-9-9 (1)

9-9: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language identifiers to both Markdown fences.

  • src/uis/nvcf-ui/AGENTS.md#L9-L9: add text or another suitable language.
  • src/uis/nvcf-ui/README.md#L55-L55: add text or another suitable language.
🤖 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/uis/nvcf-ui/AGENTS.md` at line 9, Add a suitable language identifier,
such as text, to the Markdown fence in src/uis/nvcf-ui/AGENTS.md at lines 9-9
and src/uis/nvcf-ui/README.md at lines 55-55.

Source: Linters/SAST tools

src/uis/nvcf-ui/AGENTS.md-56-60 (1)

56-60: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the subtree build and test commands.

This file documents lint and code-generation commands, but it does not document the commands that build the subtree or run its tests. Add the exact native commands and their scope, such as task build and task test, plus backend-specific commands when they differ.

As per path instructions, each AGENTS.md must stay under 400 lines and include subtree build commands, test commands, code style, and subtree-specific conventions.

Also applies to: 73-79

🤖 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/uis/nvcf-ui/AGENTS.md` around lines 56 - 60, Update the Testing section
of AGENTS.md to document the exact native commands for building and testing the
subtree, including their scope and any backend-specific variants where
applicable. Retain the existing testing guidance and ensure the file continues
to include subtree build commands, test commands, code style, and conventions
within the 400-line limit.

Source: Path instructions

src/uis/nvcf-ui/README.md-63-64 (1)

63-64: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align CI and release documentation with the imported scope.

The imported subtree does not contain the CI and release automation described by these documents. The PR defers that integration.

  • src/uis/nvcf-ui/README.md#L63-L64: remove .gitlab-ci.yml from the tree or identify the external pipeline; mark the image-publishing statement at Lines 46-47 as external or deferred.
  • src/uis/nvcf-ui/SECURITY.md#L190-L195: identify the external semantic-release owner, or mark the release process as planned or deferred.
🤖 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/uis/nvcf-ui/README.md` around lines 63 - 64, Update
src/uis/nvcf-ui/README.md lines 63-64 to remove .gitlab-ci.yml from the
repository tree or identify the external pipeline, and mark the image-publishing
statement at lines 46-47 as external or deferred. Update
src/uis/nvcf-ui/SECURITY.md lines 190-195 to identify the external
semantic-release owner or explicitly mark the release process as planned or
deferred.
src/uis/nvcf-ui/README.md-5-10 (1)

5-10: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the Go toolchain requirement for Task commands. backend/go.mod requires Go 1.26.2. Keep Go 1.24+ only if the README states that automatic toolchain switching must download Go 1.26.2; otherwise, raise the prerequisite to Go 1.26.2.

🤖 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/uis/nvcf-ui/README.md` around lines 5 - 10, Update the Prerequisites
section to align the Go requirement with backend/go.mod: either change Go to
1.26.2 or explicitly document that Task commands automatically download and
switch to Go 1.26.2 while retaining Go 1.24+.
src/uis/nvcf-ui/ui/src/features/accounts/getActiveNcaId.ts-25-27 (1)

25-27: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Enable stale account-list revalidation.

Pass revalidateIfStale: true to ensureQueryData. This triggers a background refresh when the cached account list is stale.

🤖 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/uis/nvcf-ui/ui/src/features/accounts/getActiveNcaId.ts` around lines 25 -
27, Update the ensureQueryData call in getActiveNcaId to pass revalidateIfStale:
true alongside getGetCloudAccountsQueryOptions(), enabling background refreshes
for stale cached account lists.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/features/clusters/ClustersListPending.tsx-21-28 (1)

21-28: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a user-visible test for the pending view.

ClustersListPending is a new route component. The supplied list test covers only resolved data and empty data. Add a Vitest and Testing Library test that delays the clusters request and asserts the pending UI.

As per coding guidelines: “When adding or modifying a component, add or update its corresponding tests.”

🤖 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/uis/nvcf-ui/ui/src/features/clusters/ClustersListPending.tsx` around
lines 21 - 28, Add a Vitest/Testing Library test for ClustersListPending that
delays the clusters request, renders the pending route state, and asserts the
visible ClustersPageHeading and DataTable.Skeleton UI. Extend the existing
clusters list test coverage without changing the component implementation or
resolved/empty-state assertions.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/features/clusters/ClustersList.tsx-42-51 (1)

42-51: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not create a detail link without a cluster ID.

Line 45 replaces an absent clusterId with "". The response model permits an absent clusterId, and src/uis/nvcf-ui/ui/src/features/clusters/ClustersList.test.tsx uses that case. The table then creates a detail link with no resource identifier.

Render plain text when clusterId is absent. Create Link only when the ID is non-empty. Add assertions for both ID-present and ID-absent rows.

Proposed fix
-		cell: ({ row, getValue }) => (
-			<Anchor asChild>
-				<Link
-					params={{ clusterId: row.original.clusterId ?? "" }}
-					to="/clusters/$clusterId"
-					viewTransition
-				>
-					{getValue() ?? row.original.clusterId ?? "—"}
-				</Link>
-			</Anchor>
-		),
+		cell: ({ row, getValue }) => {
+			const clusterId = row.original.clusterId;
+			const label = getValue() ?? clusterId ?? "—";
+
+			if (!clusterId) return label;
+
+			return (
+				<Anchor asChild>
+					<Link
+						params={{ clusterId }}
+						to="/clusters/$clusterId"
+						viewTransition
+					>
+						{label}
+					</Link>
+				</Anchor>
+			);
+		},
🤖 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/uis/nvcf-ui/ui/src/features/clusters/ClustersList.tsx` around lines 42 -
51, Update the cluster column renderer in ClustersList so rows without a
non-empty row.original.clusterId render plain text instead of an Anchor/Link
with an empty parameter; retain the detail link for rows with an ID and add test
assertions in ClustersList.test.tsx covering both present and absent cluster
IDs.
src/uis/nvcf-ui/ui/src/features/dashboard/components/ClusterStatsPanel.tsx-30-45 (1)

30-45: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the Not Ready drill-down consistent with the aggregate.

clustersSelect counts every status except READY and UNHEALTHY as Not Ready. The link filters only NOT_READY, CORDON, and CORDON_AND_DRAIN. A PAUSED, FAILED, ABANDONED, or DELETED cluster increases the dashboard count but is absent after the user selects Not Ready.

Derive the aggregate and link values from one shared status set. Add coverage for an omitted status.

Also applies to: 112-124

🤖 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/uis/nvcf-ui/ui/src/features/dashboard/components/ClusterStatsPanel.tsx`
around lines 30 - 45, Update clustersSelect and the Not Ready drill-down filter
to use one shared status set containing NOT_READY, CORDON, CORDON_AND_DRAIN,
PAUSED, FAILED, ABANDONED, and DELETED. Ensure every status counted in the Not
Ready aggregate is included by the link, and add coverage for at least one
previously omitted status such as PAUSED.
src/uis/nvcf-ui/ui/src/features/dashboard/Dashboard.test.tsx-283-285 (1)

283-285: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the five-function limit.

The test proves that fn-newest is present and fn-oldest is absent. A regression that renders one to four functions still passes. Assert all five expected functions, or assert the rendered recent-function count.

🤖 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/uis/nvcf-ui/ui/src/features/dashboard/Dashboard.test.tsx` around lines
283 - 285, Strengthen the dashboard test around the recent-functions rendering
assertion: verify that all five expected functions are present, including the
existing fn-newest case, or assert that the rendered recent-function collection
has a count of five. Keep the fn-oldest absence assertion to preserve the
recency boundary check.
src/uis/nvcf-ui/ui/src/features/dashboard/components/ReadyClustersPanel.tsx-45-50 (1)

45-50: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clamp the selected GPU page after GPU data changes.

If page two is selected and the same cluster later has only two GPU types, visibleGpus becomes empty. The pagination control is then hidden, so the user cannot return to page one.

Derive a page value clamped to the current GPU-entry count before slicing. Pass that value to Pagination. Add a refresh or rerender test for this transition.

Also applies to: 84-96

🤖 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/uis/nvcf-ui/ui/src/features/dashboard/components/ReadyClustersPanel.tsx`
around lines 45 - 50, Clamp the selected GPU page in the ReadyClustersPanel
component to the valid range derived from gpuEntries.length before calculating
visibleGpus, ensuring reduced GPU data falls back to the last available page.
Use the clamped page for both slicing and the Pagination component, and add a
test covering a transition from page two to a dataset with only two GPU entries.
src/uis/nvcf-ui/ui/src/features/dashboard/components/RecentFunctionsList.tsx-36-36 (1)

36-36: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use ~/ aliases for imports from ui/src.

Replace all relative UI-source imports with their ~/ equivalents.

  • src/uis/nvcf-ui/ui/src/features/dashboard/components/RecentFunctionsList.tsx#L36-L36: use ~/features/functions/components/StatusBadge.
  • src/uis/nvcf-ui/ui/src/features/dashboard/components/StatPanelBoundary.tsx#L22-L22: use ~/features/dashboard/components/StatPanelSkeleton.
  • src/uis/nvcf-ui/ui/src/features/dashboard/routes.tsx#L54-L54: use import("~/features/dashboard/Dashboard").
  • src/uis/nvcf-ui/ui/src/features/functions/FunctionDetail.test.tsx#L31-L31: use ~/features/functions/routes.
  • src/uis/nvcf-ui/ui/src/features/functions/FunctionDetail.tsx#L42-L44: use ~/features/functions/components/StatusBadge, ~/features/functions/constants, and ~/features/functions/routes.
  • src/uis/nvcf-ui/ui/src/features/functions/FunctionsList.test.tsx#L26-L26: use ~/features/functions/routes.
  • src/uis/nvcf-ui/ui/src/features/functions/FunctionsList.tsx#L24-L28: use ~/features/functions/components/FunctionCard, ~/features/functions/components/FunctionsPageHeading, ~/features/functions/constants, ~/features/functions/hooks/useFunctionsWithDeployments, and ~/features/functions/types.
  • src/uis/nvcf-ui/ui/src/features/functions/FunctionsListPending.tsx#L19-L20: use ~/features/functions/components/FunctionCard and ~/features/functions/components/FunctionsPageHeading.
  • src/uis/nvcf-ui/ui/src/features/functions/components/FunctionCard.test.tsx#L21-L22: use ~/features/functions/types and ~/features/functions/components/FunctionCard.

As per coding guidelines, “Use the ~/ path alias for imports from ui/src/.”

🤖 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/uis/nvcf-ui/ui/src/features/dashboard/components/RecentFunctionsList.tsx`
at line 36, Replace the listed relative imports with the ~/ path aliases: in
src/uis/nvcf-ui/ui/src/features/dashboard/components/RecentFunctionsList.tsx
lines 36-36 use ~/features/functions/components/StatusBadge; in
src/uis/nvcf-ui/ui/src/features/dashboard/components/StatPanelBoundary.tsx lines
22-22 use ~/features/dashboard/components/StatPanelSkeleton; in
src/uis/nvcf-ui/ui/src/features/dashboard/routes.tsx lines 54-54 use
~/features/dashboard/Dashboard; in
src/uis/nvcf-ui/ui/src/features/functions/FunctionDetail.test.tsx lines 31-31
use ~/features/functions/routes; in FunctionDetail.tsx lines 42-44 use the
specified ~/features/functions aliases for StatusBadge, constants, and routes;
in FunctionsList.test.tsx lines 26-26 use ~/features/functions/routes; in
FunctionsList.tsx lines 24-28 use the specified ~/features/functions aliases for
FunctionCard, FunctionsPageHeading, constants, useFunctionsWithDeployments, and
types; in FunctionsListPending.tsx lines 19-20 use the component aliases; and in
components/FunctionCard.test.tsx lines 21-22 use the types and FunctionCard
aliases.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/features/dashboard/routes.tsx-35-50 (1)

35-50: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle rejected cache warmups. ensureQueryData can reject when a cache miss request fails. Attach .catch(() => undefined) to each fire-and-forget call to prevent unhandled-rejection reporting while preserving the query error for panel boundaries.

🤖 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/uis/nvcf-ui/ui/src/features/dashboard/routes.tsx` around lines 35 - 50,
Update each fire-and-forget ensureQueryData call in the dashboard route loader
to attach a catch handler that resolves to undefined. Apply this to the calls
using getGetClustersQueryOptions, getGetControlPlaneStatusQueryOptions,
getGetAllFunctionsQueryOptions, and getGetAllFunctionDeploymentsQueryOptions,
while preserving the original query errors for panel boundaries.
src/uis/nvcf-ui/ui/src/features/dashboard/components/RecentFunctionsList.tsx-151-168 (1)

151-168: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reset the error boundary when ncaId changes.

If Account A fails, the boundary remains in its fallback after switching to Account B. Add resetKeys={[ncaId]} to ErrorBoundary so the new account can render.

🤖 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/uis/nvcf-ui/ui/src/features/dashboard/components/RecentFunctionsList.tsx`
around lines 151 - 168, Add resetKeys={[ncaId]} to the ErrorBoundary in
RecentFunctionsList so changing accounts resets the fallback and allows the new
account’s functions to render.
src/uis/nvcf-ui/ui/src/hooks/useLocalStorage.ts-42-44 (1)

42-44: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle cross-document localStorage.clear() events.

When another document calls clear(), StorageEvent.key is null. The current handler ignores this event, so the hook can retain a stale value. Notify subscribers when event.key is null or matches key, and add a regression test for the clear event.

🤖 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/uis/nvcf-ui/ui/src/hooks/useLocalStorage.ts` around lines 42 - 44, Update
the onStorage handler in useLocalStorage to call onChange when event.key is null
or equals key, covering cross-document localStorage.clear() events while
preserving the existing matching-key behavior. Add a regression test that
dispatches or simulates a clear event with a null key and verifies subscribers
are notified.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/features/functions/routes.tsx-82-88 (1)

82-88: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use ensureQueryData for the optional deployment preload.

Replace prefetchQuery with ensureQueryData and revalidateIfStale: true. Catch rejected promises because deployment loading is optional.

🤖 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/uis/nvcf-ui/ui/src/features/functions/routes.tsx` around lines 82 - 88,
Update the optional deployment preload around
getGetFunctionDeploymentQueryOptions to use queryClient.ensureQueryData with
revalidateIfStale enabled instead of prefetchQuery. Handle rejected promises so
a failed optional deployment load does not propagate as an unhandled rejection.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/components/DataTable/Filters.tsx-23-44 (1)

23-44: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the missing DataTable control tests.

DataTable.test.tsx covers filtering, row-count labels, and page clamping. Add coverage for pagination page changes, debounced search timing, and the Search dismissal path. Run (cd src/uis/nvcf-ui/ui && pnpm test) and record the result before merge.

🤖 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/uis/nvcf-ui/ui/src/components/DataTable/Filters.tsx` around lines 23 -
44, Extend DataTable.test.tsx with coverage for pagination page changes in
Pagination.tsx (lines 32-86), debounced search timing and the Search dismissal
path in Search.tsx (lines 24-62), and retain existing filter coverage for
Filters.tsx (lines 23-44) and row-count coverage for ItemCount.tsx (lines
21-38); no direct component changes are required. Run the UI test suite with the
specified pnpm test command and record its result before merging.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/mocks/store/handlers.ts-43-92 (1)

43-92: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add mock-handler tests.

Cover getStoreHandlers tenant isolation and unknown-record fallbacks. Add coverage for all-statuses; existing tests cover only empty-list and no-deployment. Run task test in src/uis/nvcf-ui/ui and record the result before merge.

🤖 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/uis/nvcf-ui/ui/src/mocks/store/handlers.ts` around lines 43 - 92, Add
tests covering getStoreHandlers tenant isolation and unknown-record fallback
responses for functions, deployments, and clusters; include the all-statuses
scenario alongside the existing empty-list and no-deployment scenarios. In
src/uis/nvcf-ui/ui/src/mocks/server.ts:18-21,
src/uis/nvcf-ui/ui/src/mocks/scenarios/functions/all-statuses.ts:24-42,
src/uis/nvcf-ui/ui/src/mocks/scenarios/functions/empty-list.ts:18-20, and
src/uis/nvcf-ui/ui/src/mocks/scenarios/functions/no-deployment.ts:18-31, update
only what is necessary to expose or exercise these cases; the handler
implementation at src/uis/nvcf-ui/ui/src/mocks/store/handlers.ts:43-92 requires
no direct change unless tests identify incorrect behavior. Run task test from
src/uis/nvcf-ui/ui and record the result before merge.

Source: Coding guidelines

src/uis/nvcf-ui/ui/index.html-20-24 (1)

20-24: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the Vite base URL for the favicon. Replace /favicon.ico with %BASE_URL%favicon.ico. The root route already defines the "NVCF" document title.

🤖 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/uis/nvcf-ui/ui/index.html` around lines 20 - 24, Update the favicon link
in the HTML head to use Vite’s %BASE_URL% prefix instead of the root-relative
/favicon.ico path, preserving the existing favicon filename and document title.
src/uis/nvcf-ui/ui/scripts/vendor-kui-css.js-22-22 (1)

22-22: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add Node version constraint and fetch timeout.

import.meta.dirname (line 22) requires Node 20.11 or later. The package.json declares no engines field, so older Node versions produce join(undefined, ...) and an unclear TypeError instead of a version error. Add an engines.node constraint to ui/package.json.

The fetch call (line 40) has no timeout. If the CDN is unresponsive, the script hangs indefinitely with no output. Pass signal: AbortSignal.timeout(30_000) to the fetch options.

♻️ Proposed timeout
 	files.map(async (file) => {
-		const res = await fetch(`${cdn}/${file}`);
+		const res = await fetch(`${cdn}/${file}`, {
+			signal: AbortSignal.timeout(30_000),
+		});
 		if (!res.ok) throw new Error(`${file}: ${res.status}`);
🤖 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/uis/nvcf-ui/ui/scripts/vendor-kui-css.js` at line 22, Add an engines.node
constraint to the UI package metadata requiring Node 20.11 or newer, matching
the use of import.meta.dirname in the vendor script. Update the fetch call in
the same script to pass AbortSignal.timeout(30_000) through its options so
unresponsive CDN requests terminate.
src/uis/nvcf-ui/ui/src/rootRoute.tsx-39-42 (1)

39-42: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the ~/ alias for UI source modules.

These module specifiers resolve under ui/src/ but bypass the required source-root alias.

  • src/uis/nvcf-ui/ui/src/rootRoute.tsx#L39-L42: Replace each ./... UI-source import with its ~/... path.
  • src/uis/nvcf-ui/ui/src/router.tsx#L19-L32: Replace component, feature, library, and root-route relative imports with ~/... paths.
  • src/uis/nvcf-ui/ui/src/mocks/store/index.ts#L18-L19: Replace relative re-export module specifiers with ~/mocks/store/... paths.
  • src/uis/nvcf-ui/ui/src/components/DataTable/Skeleton.tsx#L28-L28: Import Toolbar through ~/components/DataTable/Toolbar.
  • src/uis/nvcf-ui/ui/src/components/DataTable/Sort.tsx#L25-L26: Import DataTable modules through ~/components/DataTable/....
  • src/uis/nvcf-ui/ui/src/components/DataTable/index.ts#L18-L32: Import compound-component modules through ~/components/DataTable/....
  • src/uis/nvcf-ui/ui/src/components/DataTable/utils/columns.test.ts#L20-L20: Import the utility through ~/components/DataTable/utils/columns.

As per coding guidelines, use the ~/ path alias for imports from ui/src/.

🤖 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/uis/nvcf-ui/ui/src/rootRoute.tsx` around lines 39 - 42, Replace all
relative UI-source imports with the ~/ alias: update imports in
src/uis/nvcf-ui/ui/src/rootRoute.tsx lines 39-42,
src/uis/nvcf-ui/ui/src/router.tsx lines 19-32, and
src/uis/nvcf-ui/ui/src/mocks/store/index.ts lines 18-19; update the DataTable
imports in src/uis/nvcf-ui/ui/src/components/DataTable/Skeleton.tsx line 28,
Sort.tsx lines 25-26, index.ts lines 18-32, and utils/columns.test.ts line 20
using the specified ~/ paths.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/components/AsyncBoundary.tsx-33-47 (1)

33-47: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add behavior coverage for AsyncBoundary.

No test covers its loading fallback or query-error reset behavior. Add a Vitest and Testing Library test, or explain why existing higher-level coverage makes a component test unnecessary.

🤖 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/uis/nvcf-ui/ui/src/components/AsyncBoundary.tsx` around lines 33 - 47,
Add focused Vitest and Testing Library coverage for AsyncBoundary, verifying
that fallback renders while children suspend and that the query-error reset path
retries rendering after recovery. If equivalent higher-level tests already cover
both behaviors, document that coverage instead of adding a duplicate component
test.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/rootRoute.tsx-116-118 (1)

116-118: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make cloud accounts a route-loader dependency.

Replace the discarded prefetchQuery() call in beforeLoad with a root loader that returns ensureQueryData({ ...getGetCloudAccountsQueryOptions(), revalidateIfStale: true }). Add tests for successful loading and route errors.

🤖 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/uis/nvcf-ui/ui/src/rootRoute.tsx` around lines 116 - 118, Replace the
discarded prefetchQuery call in the root route’s beforeLoad with a loader that
returns ensureQueryData using getGetCloudAccountsQueryOptions() and
revalidateIfStale: true, making cloud accounts a route-loader dependency. Add
tests covering successful loader resolution and propagated route errors.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/components/CopyButton.tsx-61-66 (1)

61-66: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Show the success state only after the copy operation succeeds.

The handler discards the copyToClipboard promise. It then sets data-copied before the operation completes. A rejected copy therefore displays a false success state and can produce an unhandled rejection.

Await the operation. Set data-copied only after success. Handle the failure without showing the check icon.

🤖 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/uis/nvcf-ui/ui/src/components/CopyButton.tsx` around lines 61 - 66,
Update the onClick handler in CopyButton to await copyToClipboard(value) before
setting data-copied. Handle rejected copy operations so failures do not produce
unhandled rejections or set the success state, while preserving the existing
success-state timeout behavior.
src/uis/nvcf-ui/ui/src/components/DataTable/Content.tsx-63-77 (1)

63-77: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Expose the sort state with aria-sort.

Map "asc" and "desc" to "ascending" and "descending" on TableHeaderCell, and omit the attribute when unsorted. Add a user-visible test for both states. TableHeaderCell forwards native <th> attributes.

🤖 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/uis/nvcf-ui/ui/src/components/DataTable/Content.tsx` around lines 63 -
77, Update the TableHeaderCell in the sortable header rendering to set aria-sort
to "ascending" or "descending" based on the existing sorted state, and omit the
attribute when unsorted. Add a user-visible test covering both ascending and
descending states.
src/uis/nvcf-ui/ui/src/utils/formatters.test.ts-18-28 (1)

18-28: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Cover the remaining exported formatters.

src/uis/nvcf-ui/ui/src/utils/formatters.ts Line 29 and Line 45 add user-visible formatting behavior, but this file only tests toTitleCase. Add cases for compact numbers, a valid local ISO timestamp, and the null and undefined date fallback.

As per coding guidelines, “Code changes must include tests, or the Pull Request must explain why tests are unnecessary.”

🤖 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/uis/nvcf-ui/ui/src/utils/formatters.test.ts` around lines 18 - 28, Extend
the formatter test suite in the existing describe block to cover the remaining
exported functions from formatters.ts: verify compact-number formatting,
formatting of a valid local ISO timestamp, and identical fallback behavior for
null and undefined dates. Reuse the established Vitest table-driven style and
assert the expected user-visible strings.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/utils/resize-observer.ts-24-33 (1)

24-33: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reset scheduler state before callbacks run.

A callback that throws at Line 29 bypasses Line 31 and Line 32. rafId then remains non-null, so Line 25 blocks every later resize update. Snapshot and clear the pending state before invoking callbacks. Add a regression test where one callback throws and a later resize still schedules.

Proposed fix
 	rafId = requestAnimationFrame(() => {
-		for (const element of pending) {
+		const elements = [...pending];
+		pending.clear();
+		rafId = null;
+
+		for (const element of elements) {
 			callbacks.get(element)?.();
 		}
-		pending.clear();
-		rafId = null;
 	});

As per coding guidelines, “Code changes must include tests, or the Pull Request must explain why tests are unnecessary.”

🤖 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/uis/nvcf-ui/ui/src/utils/resize-observer.ts` around lines 24 - 33, Update
scheduleUpdate so it snapshots and clears pending and resets rafId before
invoking any callbacks, ensuring a thrown callback cannot block future resize
scheduling. Preserve callback execution for the captured entries, and add a
regression test proving a throwing callback does not prevent a later resize
update from scheduling.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/components/DataTable/utils/tableSearchSchema.ts-21-22 (1)

21-22: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Require integer pagination values.

page and pageSize flow directly into TanStack Table pagination state. Add .int() before .min(1) and add regression tests for decimal values falling back to 1 and 10.

🤖 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/uis/nvcf-ui/ui/src/components/DataTable/utils/tableSearchSchema.ts`
around lines 21 - 22, Update the page and pageSize fields in the table search
schema to require integer values by applying the integer constraint before the
existing minimum validation, while preserving their current defaults and
fallback behavior. Add regression coverage verifying decimal inputs fall back to
1 for page and 10 for pageSize.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/utils/clipboard.test.ts-32-44 (1)

32-44: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Restore document.execCommand after this test.

Line 38 replaces a global DOM method by direct assignment. vi.restoreAllMocks() at Line 23 does not undo that assignment. Later tests can inherit the stub. Save and restore the original property, or use a restorable mock.

🤖 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/uis/nvcf-ui/ui/src/utils/clipboard.test.ts` around lines 32 - 44, Update
the test that stubs document.execCommand to preserve and restore the original
DOM method after the test completes. Use a restorable mock or explicit cleanup
around the assignment so later tests do not inherit the stub, while keeping the
existing fallback and textarea cleanup assertions unchanged.
src/uis/nvcf-ui/ui/src/components/OverflowGroup.test.tsx-21-21 (1)

21-21: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the required ~/ alias for UI-source imports.

  • src/uis/nvcf-ui/ui/src/components/OverflowGroup.test.tsx#L21-L21: replace ./OverflowGroup with ~/components/OverflowGroup.
  • src/uis/nvcf-ui/ui/src/utils/clipboard.test.ts#L19-L19: replace ./clipboard with ~/utils/clipboard.

As per coding guidelines, use the ~/ path alias for imports from ui/src/.

🤖 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/uis/nvcf-ui/ui/src/components/OverflowGroup.test.tsx` at line 21, Update
the import in src/uis/nvcf-ui/ui/src/components/OverflowGroup.test.tsx at lines
21-21 to use the ~/components/OverflowGroup alias instead of the relative path;
likewise update src/uis/nvcf-ui/ui/src/utils/clipboard.test.ts at lines 19-19 to
import clipboard through ~/utils/clipboard.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/components/NotFound.tsx-22-39 (1)

22-39: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add behavior tests for the new route components.

The supplied import contains no corresponding tests or test exemption for these user-visible components.

  • src/uis/nvcf-ui/ui/src/components/NotFound.tsx#L22-L39: test the not-found status and dashboard navigation action.
  • src/uis/nvcf-ui/ui/src/components/RouteErrorFallback.tsx#L22-L41: test error-message fallback and retry invalidation.
  • src/uis/nvcf-ui/ui/src/components/RouteSpinner.tsx#L21-L27: test the accessible loading description.

As per coding guidelines, when adding or modifying a component, add or update its corresponding tests.

🤖 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/uis/nvcf-ui/ui/src/components/NotFound.tsx` around lines 22 - 39, Add
behavior tests for NotFound in
src/uis/nvcf-ui/ui/src/components/NotFound.tsx:22-39 covering the not-found
status and dashboard navigation; add tests for RouteErrorFallback in
src/uis/nvcf-ui/ui/src/components/RouteErrorFallback.tsx:22-41 covering the
error-message fallback and retry invalidation; and add tests for RouteSpinner in
src/uis/nvcf-ui/ui/src/components/RouteSpinner.tsx:21-27 verifying its
accessible loading description.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/components/OverflowGroup.tsx-110-153 (1)

110-153: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hide +N after expansion.

When expanded is true, hiddenChildren still contains the previously hidden entries. Lines 110-119 still create +N, and Lines 143-153 render it beside all children and See Less. Render the overflow trigger only before expansion. Add an assertion that +N is absent after expansion.

As per coding guidelines, update the component's corresponding test.

Proposed fix
-	const overflowButton = hiddenChildren.length > 0 && (
+	const overflowButton = !expanded && hiddenChildren.length > 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/uis/nvcf-ui/ui/src/components/OverflowGroup.tsx` around lines 110 - 153,
Update OverflowGroup’s overflowButton rendering so the +N trigger is created and
displayed only when expanded is false, while preserving the existing popover
behavior before expansion and rendering all children with See Less after
expansion. Add or update the corresponding component test to assert that +N is
absent when expanded.

Source: Coding guidelines

src/uis/nvcf-ui/ui/src/utils/clipboard.ts-26-41 (1)

26-41: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Report success only after a successful copy.

Line 38 discards the document.execCommand("copy") result. If writeText rejects and the fallback returns false, this helper resolves successfully. The downstream handler in src/uis/nvcf-ui/ui/src/components/CopyButton.tsx Lines 61-67 marks the value as copied before this promise settles. Return a failure result, await it in each consumer, and add a failed-fallback test.

Proposed coordinated fix
- export async function copyToClipboard(text: string): Promise<void> {
+ export async function copyToClipboard(text: string): Promise<boolean> {
    try {
      await navigator.clipboard.writeText(text);
+     return true;
    } catch {
      // ...
      try {
-       document.execCommand("copy");
+       return document.execCommand("copy");
      } finally {
        document.body.removeChild(textarea);
      }
    }
  }
- onClick={() => {
-   void copyToClipboard(value);
+ onClick={async () => {
+   const copied = await copyToClipboard(value).catch(() => false);
+   if (!copied) return;
    // Set the copied state here.
  }}
🤖 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/uis/nvcf-ui/ui/src/utils/clipboard.ts` around lines 26 - 41, Update
copyToClipboard to return or propagate the document.execCommand("copy") result
instead of discarding it, and make each CopyButton consumer await and inspect
that result before marking the value as copied. Add coverage for a failed
fallback copy, preserving success behavior for navigator.clipboard.writeText and
successful fallback copies.
src/uis/nvcf-ui/backend/internal/utils/utils.go-40-52 (1)

40-52: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

The fatal message loses the variable name because logLevel is shadowed.

Line 43 declares a zerolog.Level named logLevel, which shadows the const on line 29 for the rest of the function. Line 45 therefore formats the parsed level, not "LOG_LEVEL". On a parse error the level is NoLevel, which renders as an empty string, so the operator sees Invalid with no variable name and no rejected value. The code still compiles, because the short variable declaration takes effect only after line 43.

Rename the const and include the rejected value. Compare against zerolog.NoLevel instead of the empty string.

🐛 Proposed fix
-const logLevel = "LOG_LEVEL"
+const logLevelEnv = "LOG_LEVEL"
@@
 func ConfigLogger() zerolog.Logger {
 	logger := zerolog.New(os.Stdout).With().Caller().Timestamp().Logger()
 	// To disable logging entirely, pass [zerolog.Disabled]
-	logLevel, err := zerolog.ParseLevel(os.Getenv(logLevel))
+	raw := os.Getenv(logLevelEnv)
+	level, err := zerolog.ParseLevel(raw)
 	if err != nil {
-		logger.Fatal().Err(err).Msgf("Invalid %s", logLevel)
+		logger.Fatal().Err(err).Msgf("Invalid %s=%q", logLevelEnv, raw)
 	}
-	if logLevel.String() == "" {
-		logLevel = zerolog.InfoLevel
+	if level == zerolog.NoLevel {
+		level = zerolog.InfoLevel
 	}
 
-	return logger.Level(logLevel)
+	return logger.Level(level)
 }

The rename also requires an update in src/uis/nvcf-ui/backend/internal/utils/utils_test.go at line 77:

t.Setenv(logLevelEnv, tt.level)
🤖 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/uis/nvcf-ui/backend/internal/utils/utils.go` around lines 40 - 52, Update
ConfigLogger to use distinct names for the LOG_LEVEL environment-variable
constant and parsed zerolog.Level, then report both the variable name and
rejected value in the parse-error log. Replace the empty-string check with a
comparison against zerolog.NoLevel, and update utils_test.go to use the renamed
environment-variable constant.
src/uis/nvcf-ui/backend/internal/middleware/middleware.go-60-75 (1)

60-75: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Re-panic on http.ErrAbortHandler; remove the write guard.

hlog.AccessHandler already suppresses duplicate WriteHeader calls and records the upstream status. ReverseProxy uses http.ErrAbortHandler for interrupted streams, but PanicRecovery currently swallows it. Re-panic before logging.

🤖 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/uis/nvcf-ui/backend/internal/middleware/middleware.go` around lines 60 -
75, Update PanicRecovery to detect http.ErrAbortHandler immediately after
recover and re-panic it before logging or writing a response. Remove the
unconditional w.WriteHeader(http.StatusInternalServerError) from the recovery
handler, while preserving error logging for other panics.
src/uis/nvcf-ui/helm/templates/vault-agent-configmap.yaml-24-34 (1)

24-34: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Make the token JSON valid when a secret lookup is skipped. If sis-api is absent, the trailing comma causes json.Unmarshal to fail. The watcher exits on an invalid initial file and ignores the invalid reload. Use conditional comma placement so every rendered combination is valid JSON.

🤖 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/uis/nvcf-ui/helm/templates/vault-agent-configmap.yaml` around lines 24 -
34, Update the Vault template JSON block around the secret lookups for
nvcfApiToken, nvctApiToken, and sisApiToken so commas are emitted conditionally
based on whether another token field will be rendered. Ensure every combination
of available or skipped secrets produces valid JSON, including when sis-api is
absent.

Source: Linters/SAST tools

src/uis/nvcf-ui/tools/workspace_status.sh-43-48 (1)

43-48: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Validate OCI tag inputs before emitting stamp keys. src/uis/nvcf-ui/BUILD.bazel uses //rules/oci, whose push template consumes STABLE_VERSION and STABLE_OCI_TAG. NVCF_VERSION and exact Git tags are emitted verbatim, so /, +, leading invalid characters, or excessive length can make the push fail. STABLE_GIT_BRANCH is not used by the OCI template and must remain unchanged for CLI metadata.

🤖 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/uis/nvcf-ui/tools/workspace_status.sh` around lines 43 - 48, Update the
stamp generation in workspace_status.sh to validate or normalize NVCF_VERSION
and exact Git-tag-derived values before emitting STABLE_VERSION and
STABLE_OCI_TAG, ensuring OCI-compatible characters, starting character, and
length while preserving the existing commit/dirty composition. Leave
STABLE_GIT_BRANCH unchanged because it remains CLI metadata only.
src/uis/nvcf-ui/helm/templates/deployment.yaml-51-60 (1)

51-60: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Security Misconfiguration (CWE-250)

Reachability: External · Exploitability: Difficult

Harden both UI containers by default

The default container context already enforces non-root execution and drops all capabilities. Add allowPrivilegeEscalation: false, seccompProfile.type: RuntimeDefault, and readOnlyRootFilesystem: true to the defaults for both deployments. Add writable volumes only where application writes require them.

🤖 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/uis/nvcf-ui/helm/templates/deployment.yaml` around lines 51 - 60, Harden
the default container security contexts in both
src/uis/nvcf-ui/helm/templates/deployment.yaml:51-60 and
src/uis/nvcf-ui/helm/templates/control-plane-deployment.yaml:47-56 by adding
allowPrivilegeEscalation: false, seccompProfile.type: RuntimeDefault, and
readOnlyRootFilesystem: true alongside the existing non-root and capability
settings. Add writable volume mounts and volumes only for paths each UI
container must write to, preserving read-only root filesystems otherwise.

Source: Linters/SAST tools

Comment on lines +17 to +19
```bash
VITE_MOCK=true task dev
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the included UI task name.

src/uis/nvcf-ui/Taskfile.yml does not define a root dev task. Both commands fail with a missing-task error.

Replace task dev with task ui:dev in mocked mode and real-cluster mode.

Proposed fix
-VITE_MOCK=true task dev
+VITE_MOCK=true task ui:dev
@@
-task dev
+task ui:dev

Also applies to: 33-35

🤖 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/uis/nvcf-ui/.claude/skills/run/SKILL.md` around lines 17 - 19, Update
both mocked-mode and real-cluster-mode commands in the run skill to invoke the
defined UI task name, replacing task dev with task ui:dev while preserving the
existing VITE_MOCK setting and command context.

Comment on lines +121 to +124
server := http.Server{
Addr: fmt.Sprintf("0.0.0.0:%d", port),
Handler: handler,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Set timeouts on the HTTP server.

The server has no ReadHeaderTimeout, ReadTimeout, WriteTimeout, or IdleTimeout. A slow client can hold connections open and exhaust server resources. Static analysis flags the same problem.

🛡️ Proposed fix
 	server := http.Server{
-		Addr:    fmt.Sprintf("0.0.0.0:%d", port),
-		Handler: handler,
+		Addr:              fmt.Sprintf("0.0.0.0:%d", port),
+		Handler:           handler,
+		ReadHeaderTimeout: 10 * time.Second,
+		ReadTimeout:       30 * time.Second,
+		WriteTimeout:      60 * time.Second,
+		IdleTimeout:       120 * time.Second,
 	}
🤖 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/uis/nvcf-ui/backend/cmd/server/main.go` around lines 121 - 124, Update
the http.Server initialization in main to configure nonzero ReadHeaderTimeout,
ReadTimeout, WriteTimeout, and IdleTimeout values, using the project's
established timeout constants or appropriate durations so slow clients cannot
hold connections indefinitely.

Source: Linters/SAST tools

Comment on lines +145 to +170
func newProxy(target *url.URL, token func() (string, bool), logger zerolog.Logger) http.Handler {
proxy := &httputil.ReverseProxy{
Rewrite: func(pr *httputil.ProxyRequest) {
pr.SetURL(target)
pr.SetXForwarded()
pr.Out.Host = target.Host
},
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
if errors.Is(err, context.Canceled) { // client went away; nothing to send
return
}
logger.Error().Err(err).Msgf("upstream %s error", target.Host)
w.WriteHeader(http.StatusBadGateway)
},
}

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t, ok := token()
if !ok || t == "" {
w.WriteHeader(http.StatusInternalServerError)
return
}
r.Header.Set("Authorization", "Bearer "+t)
proxy.ServeHTTP(w, r)
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Look for authentication/authorization in the backend middleware and the Helm route definitions.
set -euo pipefail

fd -t f 'middleware.go' | xargs -r rg -n -C4 'Auth|auth|Token|Bearer|Forbidden|Unauthorized'

fd -t f -e yaml -e yml . src/uis/nvcf-ui/helm | xargs -r rg -n -C4 'auth|oidc|jwt|filters|extensionRef|requestAuthentication'

Repository: NVIDIA/nvcf

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- server routing and middleware ---'
sed -n '1,210p' src/uis/nvcf-ui/backend/cmd/server/main.go
printf '%s\n' '--- backend files and deployment manifests ---'
git ls-files src/uis/nvcf-ui | grep -E '(^|/)(.*(helm|chart|route|ingress|gateway|middleware|auth).*)\.(go|yaml|yml|tpl)$' | head -200
printf '%s\n' '--- exact route and authentication references ---'
rg -n -C3 --glob '!vendor/**' --glob '!**/node_modules/**' \
  '(/v2/nvcf/accounts|/v1/nvct/accounts|/v1/si/accounts|newProxy|Authorization|authentication|RequestAuthentication|AuthorizationPolicy|oauth|oidc|jwt|Bearer)' \
  src/uis/nvcf-ui

Repository: NVIDIA/nvcf

Length of output: 49354


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- HTTPRoute ---'
cat -n src/uis/nvcf-ui/helm/templates/httproute.yaml
printf '%s\n' '--- Helm values relevant to exposure ---'
cat -n src/uis/nvcf-ui/helm/values.yaml | sed -n '1,180p'
printf '%s\n' '--- deployment and service exposure ---'
cat -n src/uis/nvcf-ui/helm/templates/deployment.yaml
cat -n src/uis/nvcf-ui/helm/templates/service.yaml
printf '%s\n' '--- documented trust boundary ---'
cat -n src/uis/nvcf-ui/SECURITY.md | sed -n '88,175p'

Repository: NVIDIA/nvcf

Length of output: 25925


Missing Caller Authentication Before Privileged Credential Injection (CWE-306): Missing Authentication for Critical Function

Enforce caller authentication before injecting the service token.

The BFF has no caller authentication or authorization. The default HTTPRoute has no authentication filter, and the route is optional. Ensure every exposed deployment places an authenticating gateway before /v2/nvcf/accounts/, /v1/nvct/accounts/, and /v1/si/accounts/; otherwise any reachable caller can access upstream APIs with the BFF's credentials. Replace the hard-coded http upstream URLs with TLS/mTLS or enforce equivalent network encryption.

🤖 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/uis/nvcf-ui/backend/cmd/server/main.go` around lines 145 - 170, Update
the deployment and routing configuration around newProxy so every exposed
deployment authenticates and authorizes callers before requests reach the
protected account paths. Ensure the gateway covers /v2/nvcf/accounts/,
/v1/nvct/accounts/, and /v1/si/accounts/ even when the HTTPRoute is optional,
and replace plaintext upstream targets passed to newProxy with TLS/mTLS or an
equivalent encrypted network path.

Comment on lines +137 to +152
func (w *Watcher) load() error {
data, err := os.ReadFile(w.tokensPath)
if err != nil {
return err
}
var t apiTokens
if err := json.Unmarshal(data, &t); err != nil {
return err
}

w.mu.Lock()

// Mark all tokens valid and cancel any in-flight expiry goroutines.
w.nvcf.value, w.nvcf.valid = t.NvcfApiToken, true
w.nvct.value, w.nvct.valid = t.NvctApiToken, true
w.sis.value, w.sis.valid = t.SisApiToken, true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

load marks a token valid even when the value is empty.

json.Unmarshal leaves a missing or empty field as "". Lines 150-152 then set valid = true for that empty value. startExpiryWatcher fails to parse the empty string, logs a warning, and returns, so the token stays "valid" for the process lifetime. The proxy in cmd/server/main.go receives ("", true) and forwards the request with an empty credential instead of failing fast with a clear error.

Set valid only when the value is non-empty, and report the missing token.

🐛 Proposed fix
+	logger := zerolog.Ctx(w.ctx)
+	set := func(name, value string) bool {
+		if value == "" {
+			logger.Error().Msgf("%s token missing from %s", name, w.tokensPath)
+			return false
+		}
+		return true
+	}
+
 	w.mu.Lock()
 
-	// Mark all tokens valid and cancel any in-flight expiry goroutines.
-	w.nvcf.value, w.nvcf.valid = t.NvcfApiToken, true
-	w.nvct.value, w.nvct.valid = t.NvctApiToken, true
-	w.sis.value, w.sis.valid = t.SisApiToken, true
+	// Mark non-empty tokens valid and cancel any in-flight expiry goroutines.
+	w.nvcf.value, w.nvcf.valid = t.NvcfApiToken, set("nvcf", t.NvcfApiToken)
+	w.nvct.value, w.nvct.valid = t.NvctApiToken, set("nvct", t.NvctApiToken)
+	w.sis.value, w.sis.valid = t.SisApiToken, set("sis", t.SisApiToken)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (w *Watcher) load() error {
data, err := os.ReadFile(w.tokensPath)
if err != nil {
return err
}
var t apiTokens
if err := json.Unmarshal(data, &t); err != nil {
return err
}
w.mu.Lock()
// Mark all tokens valid and cancel any in-flight expiry goroutines.
w.nvcf.value, w.nvcf.valid = t.NvcfApiToken, true
w.nvct.value, w.nvct.valid = t.NvctApiToken, true
w.sis.value, w.sis.valid = t.SisApiToken, true
func (w *Watcher) load() error {
data, err := os.ReadFile(w.tokensPath)
if err != nil {
return err
}
var t apiTokens
if err := json.Unmarshal(data, &t); err != nil {
return err
}
logger := zerolog.Ctx(w.ctx)
set := func(name, value string) bool {
if value == "" {
logger.Error().Msgf("%s token missing from %s", name, w.tokensPath)
return false
}
return true
}
w.mu.Lock()
// Mark non-empty tokens valid and cancel any in-flight expiry goroutines.
w.nvcf.value, w.nvcf.valid = t.NvcfApiToken, set("nvcf", t.NvcfApiToken)
w.nvct.value, w.nvct.valid = t.NvctApiToken, set("nvct", t.NvctApiToken)
w.sis.value, w.sis.valid = t.SisApiToken, set("sis", t.SisApiToken)
🤖 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/uis/nvcf-ui/backend/internal/token-watcher/watcher.go` around lines 137 -
152, Update Watcher.load so each token’s valid flag is set only when its
unmarshaled value is non-empty, and report missing or empty tokens through the
existing error-reporting mechanism. Ensure empty credentials are not exposed as
valid to the proxy while preserving valid-token loading and expiry-watcher
behavior.

Comment on lines +16 to +22
# Helm hook Job that provisions the OpenBao JWT signing roles the nvcf-ui BFF
# server needs to mint cluster, NVCF API, and NVCT tokens. Runs pre-install and
# pre-upgrade so the auth role and sign policies exist before the server pods
# come up and the Vault Agent tries to log in.
{{- $bao := .Values.nvcfUi.openbaoMigrations }}
apiVersion: batch/v1
kind: Job

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Render the migration Job only when it is enabled.

This template always runs the pre-install and pre-upgrade Job. An installation without the external ServiceAccount or root-token Secret will fail before the application resources are installed.

Add an openbaoMigrations.enabled value. Wrap the complete Job with that condition. The PR context describes this hook as opt-in.

🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 20-20: syntax error: expected the node content, but found '-'

(syntax)

🤖 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/uis/nvcf-ui/helm/templates/hook-openbao-migrations.yaml` around lines 16
- 22, Add an enabled guard around the complete Job manifest in the
hook-openbao-migrations template, using the existing
.Values.nvcfUi.openbaoMigrations configuration via $bao. Introduce or consume
openbaoMigrations.enabled so the pre-install and pre-upgrade migration Job
renders only when explicitly enabled, while preserving the existing Job content
unchanged.

Comment on lines +64 to +79
return useSuspenseQueries({
queries: [
{
...getGetAllFunctionsSuspenseQueryOptions(ncaId),
...queryOptions,
},
{
...getGetAllFunctionDeploymentsSuspenseQueryOptions(ncaId),
...queryOptions,
},
],
combine(results) {
const base = combine(results);
return (select ? select(base) : base) as T;
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline src/uis/nvcf-ui/ui/src/features/functions/FunctionsList.test.tsx --items all
rg -n -C 5 'useSuspenseQueries|allSettled|getGetAllFunctionDeployments' \
  src/uis/nvcf-ui/ui/src/features/functions

Repository: NVIDIA/nvcf

Length of output: 7170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- hook ---'
cat -n src/uis/nvcf-ui/ui/src/features/functions/hooks/useFunctionsWithDeployments.ts

printf '%s\n' '--- route ---'
sed -n '35,90p' src/uis/nvcf-ui/ui/src/features/functions/routes.tsx

printf '%s\n' '--- function-list tests ---'
cat -n src/uis/nvcf-ui/ui/src/features/functions/FunctionsList.test.tsx

printf '%s\n' '--- related consumers and query option types ---'
rg -n -C 4 'useFunctionsWithDeployments|SharedQueryOptions|BaseCombineResult|functionDeployments' \
  src/uis/nvcf-ui/ui/src/features/functions \
  src/uis/nvcf-ui/ui/src/generated/api

Repository: NVIDIA/nvcf

Length of output: 12294


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- hook ---'
cat -n src/uis/nvcf-ui/ui/src/features/functions/hooks/useFunctionsWithDeployments.ts

printf '%s\n' '--- route ---'
sed -n '35,90p' src/uis/nvcf-ui/ui/src/features/functions/routes.tsx

printf '%s\n' '--- function-list tests ---'
cat -n src/uis/uis/nvcf-ui/ui/src/features/functions/FunctionsList.test.tsx

printf '%s\n' '--- related consumers and query option types ---'
rg -n -C 4 'useFunctionsWithDeployments|SharedQueryOptions|BaseCombineResult|functionDeployments' \
  src/uis/nvcf-ui/ui/src/features/functions \
  src/uis/nvcf-ui/ui/src/generated/api

Repository: NVIDIA/nvcf

Length of output: 5280


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- package versions ---'
rg -n '"`@tanstack/react-query`"|"`@tanstack/react-router`"' \
  package.json '**/package.json' '**/pnpm-lock.yaml' '**/yarn.lock' '**/package-lock.json' 2>/dev/null || true

printf '%s\n' '--- generated query options ---'
rg -n -A 35 -B 8 \
  'getGetAllFunctionDeployments(Suspense)?QueryOptions|getGetAllFunctions(Suspense)?QueryOptions' \
  src/uis/nvcf-ui/ui/src/generated/api/function-deployment/function-deployment.ts \
  src/uis/nvcf-ui/ui/src/generated/api/function-management/function-management.ts

printf '%s\n' '--- deployment mocks and function-list test setup ---'
rg -n -C 8 \
  'getGetAllFunctionDeploymentsMockHandler|function-deployment|deployments' \
  src/uis/nvcf-ui/ui/src/mocks \
  src/uis/nvcf-ui/ui/src/features/functions/FunctionsList.test.tsx

printf '%s\n' '--- all direct hook tests/usages ---'
rg -n -C 5 'useFunctionsWithDeployments' src/uis/nvcf-ui/ui/src

Repository: NVIDIA/nvcf

Length of output: 49260


🌐 Web query:

TanStack Query React useSuspenseQueries error behavior throwOnError query state data undefined v5

💡 Result:

In TanStack Query v5, useSuspenseQueries is designed to strictly adhere to the Suspense pattern, which means the throwOnError option is not supported for individual queries and cannot be passed as an argument [1][2][3]. Regarding error behavior and data state: 1. Default Error Handling: By default, useSuspenseQueries (like useSuspenseQuery) only throws errors to the nearest React Error Boundary if no previously cached data is available [4][5]. If stale data exists in the cache, the hook will return that data instead of throwing, prioritizing the display of stale information over an error state [4][6][5]. 2. data Undefined State: Because useSuspenseQueries guarantees that data is defined in the successful rendering path, it will not return undefined data while in the success state [1][2][5]. If you encounter data is undefined, it typically indicates that your queryFn returned undefined or failed to return a value, which is not allowed [7]. 3. Manual Error Propagation: If you need to force an error to be thrown to an Error Boundary even when stale data is present, you must manually throw the error in your component [4][8][5]. A common pattern is: tsx const results = useSuspenseQueries({ queries: [...], }); // Manual throw for Error Boundary results.forEach((result) => { if (result.error && !result.isFetching) { throw result.error; } }); Because throwOnError is disallowed by design in useSuspenseQueries to ensure the hook's contract (data is always defined) remains consistent with Suspense, you cannot disable this behavior or configure it via the hook options [9][3]. If your use case requires granular control over whether to show stale data versus an error boundary, you may need to use useQueries with suspense: false and handle the loading/error states manually [5][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- UI dependency manifests ---'
fd -i -t f 'package.json|pnpm-lock.yaml|yarn.lock|package-lock.json' . | while read -r file; do
  if rg -q '`@tanstack/react-query`' "$file"; then
    printf '%s\n' "--- $file"
    rg -n -C 2 '`@tanstack/react-query`' "$file"
  fi
done

printf '%s\n' '--- query client defaults ---'
rg -n -C 8 'QueryClient|defaultOptions|retry|throwOnError|suspense' \
  src/uis/nvcf-ui/ui/src/lib src/uis/nvcf-ui/ui/src/testing \
  src/uis/nvcf-ui/ui/src 2>/dev/null | head -n 240

printf '%s\n' '--- route error boundary and test harness ---'
rg -n -C 8 'RouteErrorFallback|defaultErrorComponent|renderWithRouter' \
  src/uis/nvcf-ui/ui/src

Repository: NVIDIA/nvcf

Length of output: 50367


Keep deployment enrichment non-blocking.

If the deployment request fails before data is cached, useSuspenseQueries throws and does not call combine. FunctionsList then cannot render functions without deployment enrichment, which conflicts with functionsListRoute treating deployment failures as best effort. Use a non-suspense deployment query, or make deployments mandatory in the route. Add a regression test for this failure path.

🤖 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/uis/nvcf-ui/ui/src/features/functions/hooks/useFunctionsWithDeployments.ts`
around lines 64 - 79, The useFunctionsWithDeployments hook currently suspends
the entire result when the deployment query fails, preventing FunctionsList from
rendering base functions. Update the deployment query in
useFunctionsWithDeployments to use a non-suspense, best-effort path while
preserving the suspense behavior for function data and existing select/combine
handling; add a regression test covering an uncached deployment failure and
confirming functions still render without enrichment.

Source: Coding guidelines

Comment on lines +36 to +42
const body = [204, 205, 304].includes(res.status)
? null
: await res.json().catch(() => null);

if (!res.ok) throw new HttpError(res, body);

return body as T;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject malformed JSON from successful responses.

Lines 36-42 convert a JSON parsing failure into null and cast it to T. The generated clusters client uses customFetch. ClustersList.tsx then converts this null into an empty data set at Line 116.

This hides a malformed successful response as “No data.” Other callers can dereference null while TypeScript reports T. For successful responses that are not explicit no-content responses, rethrow the JSON parsing failure. Keep best-effort parsing only for non-2xx HttpError bodies. Update fetch.test.ts Lines 52-59 to expect rejection.

Proposed fix
-	const body = [204, 205, 304].includes(res.status)
-		? null
-		: await res.json().catch(() => null);
+	let body: unknown = null;
+
+	if (![204, 205, 304].includes(res.status)) {
+		try {
+			body = await res.json();
+		} catch (error) {
+			if (res.ok) throw error;
+		}
+	}
 
 	if (!res.ok) throw new HttpError(res, body);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const body = [204, 205, 304].includes(res.status)
? null
: await res.json().catch(() => null);
if (!res.ok) throw new HttpError(res, body);
return body as T;
let body: unknown = null;
if (![204, 205, 304].includes(res.status)) {
try {
body = await res.json();
} catch (error) {
if (res.ok) throw error;
}
}
if (!res.ok) throw new HttpError(res, body);
return body as T;
🤖 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/uis/nvcf-ui/ui/src/lib/fetch.ts` around lines 36 - 42, Update customFetch
so JSON parsing failures for successful, non-no-content responses are rethrown
instead of converted to null or cast to T. Preserve null for explicit 204, 205,
and 304 responses, and retain best-effort parsing for non-2xx responses before
throwing HttpError. Update the corresponding fetch.test.ts case to assert
rejection.


import { ThemeProvider } from "@nvidia/foundations-react-core";
import { QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

ReactQueryDevtools comes from a devDependency but renders unconditionally.

@tanstack/react-query-devtools is listed under devDependencies in ui/package.json, and src/main.tsx imports and renders it on every code path. A production install that omits dev dependencies then fails to build. The devtools panel and its bundle also ship to end users.

Guard the render on import.meta.env.DEV, or move the package to dependencies if the panel is intentional in production.

Also applies to: 55-55

🤖 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/uis/nvcf-ui/ui/src/main.tsx` at line 20, Update the ReactQueryDevtools
usage in main.tsx so it is rendered only when import.meta.env.DEV is true,
keeping the devtools package out of production builds and runtime paths while
preserving its development behavior.

Comment on lines +42 to +60
enableMocking().then(() => {
const rootElement = document.getElementById("app");
if (rootElement && !rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<ThemeProvider
density="standard"
global
target="html"
theme={getStoredTheme()}
>
<QueryClientProvider client={queryClient}>
<RouterProvider context={{ queryClient }} router={router} />
<ReactQueryDevtools />
</QueryClientProvider>
</ThemeProvider>,
);
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Render the app even when mock setup fails.

enableMocking() can reject. The dynamic import of ./mocks/browser can fail, and worker.start() rejects when service-worker registration is refused, for example on an insecure origin. The chain has no rejection handler, so .then() never runs, the root stays empty, and the user sees a blank page with only an unhandled rejection in the console.

Log the failure and continue to render.

🐛 Proposed fix
-enableMocking().then(() => {
+function renderApp() {
 	const rootElement = document.getElementById("app");
 	if (rootElement && !rootElement.innerHTML) {
 		const root = ReactDOM.createRoot(rootElement);
@@
 		);
 	}
-});
+}
+
+enableMocking()
+	.catch((error) => {
+		console.error("Failed to start request mocking", error);
+	})
+	.finally(renderApp);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
enableMocking().then(() => {
const rootElement = document.getElementById("app");
if (rootElement && !rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<ThemeProvider
density="standard"
global
target="html"
theme={getStoredTheme()}
>
<QueryClientProvider client={queryClient}>
<RouterProvider context={{ queryClient }} router={router} />
<ReactQueryDevtools />
</QueryClientProvider>
</ThemeProvider>,
);
}
});
function renderApp() {
const rootElement = document.getElementById("app");
if (rootElement && !rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<ThemeProvider
density="standard"
global
target="html"
theme={getStoredTheme()}
>
<QueryClientProvider client={queryClient}>
<RouterProvider context={{ queryClient }} router={router} />
<ReactQueryDevtools />
</QueryClientProvider>
</ThemeProvider>,
);
}
}
enableMocking()
.catch((error) => {
console.error("Failed to start request mocking", error);
})
.finally(renderApp);
🤖 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/uis/nvcf-ui/ui/src/main.tsx` around lines 42 - 60, Update the
enableMocking() startup chain so rejected mock initialization is logged and
still proceeds to the existing app-rendering logic. Add a rejection handler or
equivalent fallback around enableMocking(), preserving the current rootElement
check and ReactDOM.createRoot rendering path.

Comment on lines +22 to +26
@import "@nvidia/foundations-tailwind-plugin";
@source "../node_modules/@nvidia/foundations-react-core";

#style-root {
@tailwind utilities;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Confirm the installed Tailwind version and locate the Stylelint rule configuration.
rg -n -C 2 '"tailwindcss"|`@tailwindcss`' src/uis/nvcf-ui/ui/package.json
rg -n --hidden --glob '!node_modules/**' --glob '!dist/**' -C 2 \
  'at-rule-no-unknown|ignoreAtRules|`@tailwind`|`@source`' src/uis/nvcf-ui

Repository: NVIDIA/nvcf

Length of output: 14892


Use the Tailwind v4 entrypoint and allow @source in Stylelint. Tailwind resolves to v4.2.4. Replace @tailwind utilities with @import "tailwindcss" and configure Stylelint to accept @source.

🧰 Tools
🪛 Stylelint (17.14.0)

[error] 23-23: Unexpected unknown at-rule "@source" (scss/at-rule-no-unknown)

(scss/at-rule-no-unknown)


[error] 26-26: Unexpected unknown at-rule "@tailwind" (scss/at-rule-no-unknown)

(scss/at-rule-no-unknown)

🤖 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/uis/nvcf-ui/ui/src/styles.css` around lines 22 - 26, Update the
`#style-root` block in styles.css to use Tailwind v4’s `@import` "tailwindcss"
entrypoint instead of `@tailwind` utilities, and adjust the Stylelint
configuration to recognize the existing `@source` directive.

Sources: Learnings, Linters/SAST tools

balaji-g and others added 2 commits August 4, 2026 13:00
The import landed source-only: the subtree kept its upstream MODULE.bazel and
sat in .bazelignore, so nothing in this repository built, tested or linted its
185 TypeScript and 29 Go files. Excluding the upstream .gitlab-ci.yml had also
removed the six quality gates it used to run, replacing them with none. This
finishes the migration so the code is actually verified here.

check-nested-modules now reports `awaiting migration: 0` again, so the import no
longer works against the consolidation.

The merge, in the order the failures surfaced:

- Lifted aspect_rules_js and rules_nodejs plus the node and npm extensions into
  the root MODULE.bazel, repointed at //src/uis/nvcf-ui/ui:pnpm-lock.yaml.
- Registered the backend in go.work.bazel and rewrote subtree-relative labels.
- Dropped the subtree's MODULE.bazel, .bazelrc, rules/ and platforms/; the root
  already provides distroless_static, rules/oci and every Go dependency the
  backend imports.
- Lifted the subtree's .bazelignore entries into the root. aspect_rules_js
  refuses to evaluate npm_translate_lock unless every nested node_modules pnpm
  will create is ignored, so this is required, not tidiness.
- Ported go_web_oci_image into the root rules/oci. The root copy was not
  equivalent; it had no such rule.
- Dropped that rule's repo_tag parameter. Its own doc said it existed because
  package_name() is empty at a repo root; at src/uis/nvcf-ui it is not, so the
  root's derived load tag applies and the workaround is obsolete.
- Removed //platforms:sources and //rules/oci:sources from the container's
  source bundle. Those are root build infrastructure, not UI source.
- Aligned the backend's controller-runtime from v0.24.1 to v0.22.5 to match the
  rest of the repository. A version-specific replace pins client-go to v0.34.2
  while MVS was selecting controller-runtime v0.24.1 from this module, and 0.24
  needs client-go APIs that 0.34 does not have.

Merges main for the pnpm dependency collector added in #657. Without it the UI's
npm tree never reached dependencies.md; the regenerated file now records 531
Node.js packages.

Adds the nvcf-ui matrix row so the subtree builds and tests on its own changes.

Verified locally: `bazel build //src/uis/nvcf-ui/...` covers 31 targets and
`bazel test //src/uis/nvcf-ui/...` passes 5 of 5, including the frontend vitest
suite.

The controller-runtime downgrade is proven at compile and unit-test level only.
The token-watcher is the component using it, and its tests do not exercise
cache behaviour, so a reviewer from the UI team should confirm 0.22.5 is
acceptable at runtime.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
rules/oci/private/go_web.bzl (1)

75-77: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reject an empty binaries list before indexing it.

mandatory = True does not reject an explicitly empty list, and allow_empty defaults to True. Set allow_empty = False or validate binaries before accessing binaries[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 `@rules/oci/private/go_web.bzl` around lines 75 - 77, Validate that binaries is
non-empty before the fallback in the entrypoint assignment accesses binaries[0],
or configure the corresponding attribute with allow_empty = False so an
explicitly empty list is rejected while preserving the existing entrypoint
behavior.
🤖 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 @.github/workflows/bazel.yml:
- Around line 150-151: Update the workflow’s affected-target scheduling logic so
root dependency or shared Bazel rule changes also schedule the go-root nvcf-ui
row, rather than limiting the java_shared_changed path to java-* rows. Preserve
the existing nvcf-ui build-container configuration and ensure its build and
tests run for shared-root changes.

---

Outside diff comments:
In `@rules/oci/private/go_web.bzl`:
- Around line 75-77: Validate that binaries is non-empty before the fallback in
the entrypoint assignment accesses binaries[0], or configure the corresponding
attribute with allow_empty = False so an explicitly empty list is rejected while
preserving the existing entrypoint behavior.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0bd0103c-f1c3-47c1-921a-976ee2cee2a8

📥 Commits

Reviewing files that changed from the base of the PR and between 0aca7a9 and c33bee3.

⛔ Files ignored due to path filters (3)
  • MODULE.bazel.lock is excluded by !**/*.lock, !**/MODULE.bazel.lock
  • src/uis/nvcf-ui/backend/go.sum is excluded by !**/*.sum
  • tools/collect-dependencies/go.sum is excluded by !**/*.sum
📒 Files selected for processing (18)
  • .bazelignore
  • .github/workflows/bazel.yml
  • MODULE.bazel
  • NOTICE
  • dependencies.md
  • go.work.bazel
  • rules/oci/defs.bzl
  • rules/oci/private/go_web.bzl
  • src/uis/nvcf-ui/BUILD.bazel
  • src/uis/nvcf-ui/backend/BUILD.bazel
  • src/uis/nvcf-ui/backend/cmd/control-plane/BUILD.bazel
  • src/uis/nvcf-ui/backend/cmd/server/BUILD.bazel
  • src/uis/nvcf-ui/backend/go.mod
  • src/uis/nvcf-ui/backend/internal/control-plane/BUILD.bazel
  • src/uis/nvcf-ui/backend/internal/middleware/BUILD.bazel
  • src/uis/nvcf-ui/backend/internal/token-watcher/BUILD.bazel
  • src/uis/nvcf-ui/backend/internal/utils/BUILD.bazel
  • src/uis/nvcf-ui/ui/BUILD.bazel
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/uis/nvcf-ui/backend/go.mod
  • .bazelignore
  • NOTICE
  • src/uis/nvcf-ui/backend/cmd/server/BUILD.bazel
  • src/uis/nvcf-ui/backend/BUILD.bazel
  • src/uis/nvcf-ui/ui/BUILD.bazel
  • src/uis/nvcf-ui/backend/internal/utils/BUILD.bazel
  • src/uis/nvcf-ui/backend/internal/control-plane/BUILD.bazel
  • src/uis/nvcf-ui/BUILD.bazel
  • src/uis/nvcf-ui/backend/cmd/control-plane/BUILD.bazel
  • src/uis/nvcf-ui/backend/internal/token-watcher/BUILD.bazel

Comment on lines +150 to +151
image-credential-helper|src/compute-plane-services/image-credential-helper|false|go-root|build-container
nvcf-ui|src/uis/nvcf-ui|false|go-root|build-container'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Schedule nvcf-ui for root-owned dependency changes.

nvcf-ui is a go-root row, so the root row excludes it at Line 327-332. However, MODULE.bazel, go.work.bazel, and shared Bazel rules trigger the root row. The java_shared_changed branch schedules only java-* rows. A change to a root dependency or rule can therefore skip the UI build and tests.

Add a shared-root trigger for affected go-root rows, or include nvcf-ui in the corresponding validation.

🤖 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 @.github/workflows/bazel.yml around lines 150 - 151, Update the workflow’s
affected-target scheduling logic so root dependency or shared Bazel rule changes
also schedule the go-root nvcf-ui row, rather than limiting the
java_shared_changed path to java-* rows. Preserve the existing nvcf-ui
build-container configuration and ensure its build and tests run for shared-root
changes.

balaji-g and others added 2 commits August 4, 2026 14:12
`bazel (root)` failed while `bazel (nvcf-ui)` passed. Adding the backend to
go.work.bazel changes dependency resolution for every Go module in the
repository, not just this subtree, and MVS took k8s.io/apiextensions-apiserver
from v0.34.1 to v0.36.0 because this module still required k8s.io at v0.36.0.
A version-specific replace pins client-go to v0.34.2, so the v0.36 apiextensions
code referenced symbols that version does not have.

Pins k8s.io/api, apimachinery and client-go to v0.34.2 and
apiextensions-apiserver to v0.34.1, matching the rest of the repository. Applied
with `go mod edit` and deliberately no `go mod tidy`: tidy re-resolves these
upward from the transitive graph, which is what silently undid the equivalent
fix for controller-runtime earlier.

Verified with a full `bazel build //...` rather than the subtree alone. The
subtree-only build is what let this through: it cannot observe a change in
workspace-wide version resolution.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
TestLoadConfigAuthCredentials failed in CI on this branch while passing on
main, and the difference was not the code. On main the test is a remote-cache
hit on every run and has never actually executed in CI. Changing
go.work.bazel here invalidated the cache key, so this is its first real run.

The test is not hermetic. state.DefaultStateManager is constructed at package
init, capturing $HOME before any test can change it, so the test's
t.Setenv("HOME", t.TempDir()) does not move the state path. LoadConfig falls
back to that state file for credentials, so a stray ~/.nvcf-cli.state supplies
an API key: "nothing set" then finds credentials, and "complete oauth2 setup"
resolves to bearer instead of oauth2. CI runs tests with HOME=/tmp, which is
shared and writable.

Adds state.ResetDefaultStateManager so a test can rebuild the manager after
pointing HOME somewhere private, and calls it at both sites that override HOME.
Production behaviour is unchanged: a CLI process's home directory does not
change after start, and nothing outside tests calls it.

Reproduced before fixing by seeding $HOME/.nvcf-cli.state with an apiKey and
running against main, which fails exactly as CI did; the same command passes
with this change. Full suite: 21 of 21 pass with --nocache_test_results.

Worth noting beyond this test: cache hits can mask tests that would fail if
they ran. A periodic uncached run on main would surface others like it.

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
@berkomu

berkomu commented Aug 5, 2026

Copy link
Copy Markdown

@kristinapathak Will we need to update the root SECURITY.md and .security-triage.yaml as well?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(nvcf-ui): import the self-hosted UI into the monorepo

3 participants