Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has required the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Coverage Report for react-coverage (./react)
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Pull request overview
This PR prevents users from selecting model vfolders on storage hosts they cannot mount, avoiding deployments that are accepted at creation time but later fail during replica spawn due to missing MOUNT_IN_SESSION permission.
Changes:
- Added
useMountableVFolderHostshook that queriesmyStorageHostPermissionsand derives the set of mountable storage hosts for the current user. - Updated
DeploymentAddRevisionModal’s model folder selectors (preset + custom forms) to apply a server-side filter combiningusage_mode == "model"with an OR-list of mountablehost == ...clauses. - Added the Relay generated query artifact for the new hook.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| react/src/hooks/useMountableVFolderHosts.ts | New Relay-backed hook to fetch mountable storage hosts via myStorageHostPermissions. |
| react/src/components/DeploymentAddRevisionModal.tsx | Applies host-based filtering to both model folder pickers to exclude non-mountable hosts. |
| react/src/generated/useMountableVFolderHostsQuery.graphql.ts | Relay generated query/types for useMountableVFolderHosts. |
Files not reviewed (1)
- react/src/generated/useMountableVFolderHostsQuery.graphql.ts: Generated file
The model folder picker in the deployment "add revision" modal previously listed every `usage_mode == "model"` vfolder, including folders on storage hosts the user cannot mount. The backend defers the host mount-permission check (MOUNT_IN_SESSION) to session start / replica spawn rather than deployment creation, so picking such a folder lets the deployment be created but its replicas later fail with a host-permission error (HTTP 400) — a worse, deferred failure than the legacy model-service flow. Filter both selectors (preset and custom forms) to folders whose host the current user is allowed to mount. The mountable host set is read from the `myStorageHostPermissions` GraphQL field (Strawberry V2), which resolves the union of `allowed_vfolder_hosts` across the domain, the user's projects, and the keypair resource policy server-side — avoiding the legacy keypair → allowed_vfolder_hosts request waterfall — and a new `useMountableVFolderHosts` hook keeps the MOUNT_IN_SESSION host names. The host clause is composed into the existing GraphQL `filter` string (`host` is server-side filterable on `vfolder_nodes`), so pagination is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Resolves #4234 (FR-1436)
Problem
The model folder picker on the deployment "add revision" modal (
DeploymentAddRevisionModal, both the preset and custom forms) listed everyusage_mode == "model"vfolder, regardless of whether the folder's storage host is mountable by the user. Picking a non-mountable-host folder (e.g. a left-over model-store project folder) reproduces the customer report: the request fails.In the deployment flow this is actually worse than the legacy model-service flow. The backend defers the storage-host mount-permission check (
MOUNT_IN_SESSION) to session start / replica spawn, not deployment creation:So a deployment built from a non-mountable-host model folder is accepted, looks created, and then its replicas silently fail to spawn with
InsufficientStoragePermission(HTTP 400). Front-end filtering is the right place to prevent this up front.Change
Filter both model-folder selectors to folders whose host the current user may actually mount:
useMountableVFolderHostsreads themyStorageHostPermissionsGraphQL field (Strawberry V2) and returns the host names carryingMOUNT_IN_SESSION.filterstring asusage_mode == "model" & (host == "h1" | host == "h2" | …).hostis server-side filterable onvfolder_nodes, so this keeps the selector's pagination/count intact. When the user has no mountable host, the filter matches nothing (rather than every folder).Why
myStorageHostPermissions(no waterfall)The legacy client-side pattern (
VFolderTable,useMergedAllowedStorageHostPermission) first resolves the keypair's resource-policy name, then issues a second query for domain/group/keypairallowed_vfolder_hosts, then merges three JSON blobs client-side — a request waterfall.myStorageHostPermissionsreturns the union of domain + the user's projects + keypair permissions in a single query, already intersected with the registered volumes, server-side. No keypair lookup, no client merge.No
@sincegate is needed: this modal is already gated to managers ≥ 26.4.3, andmyStorageHostPermissionswas added in 26.4.2.Scope notes
ownership_type == "group") folders are intentionally not excluded here. The backend explicitly accepts group-ownedMODELfolders at deployment creation, so excluding them would contradict backend intent; the customer-reported folders are correctly handled by the host filter (their host is simply not mountable). (The original FR-1435 group-exclusion lives in the now-refactoredServiceLauncherPageContent; whether to reinstate it under the new modal is tracked separately.)myStorageHostPermissionsis scoped to the user (the union across all of the user's projects), so it is a slight superset of the hosts mountable in one specific project. This is strictly better than today (no filter) and acceptable; strict per-project scoping is out of scope here.VFolderTable,useMergedAllowedStorageHostPermission+ its file-browser / SFTP / folder-explorer consumers) ontomyStorageHostPermissionsis tracked as a separate Jira issue and will be stacked on top of this PR.Verification
bash scripts/verify.sh→ Relay / Lint / Format / TypeScript PASS.Test plan
🤖 Generated with Claude Code