Skip to content

fix(FR-2526): block file browser access while a revision is being added - #9517

Draft
yomybaby wants to merge 3 commits into
mainfrom
fix/FR-2526-block-folder-access-during-revision-add
Draft

fix(FR-2526): block file browser access while a revision is being added#9517
yomybaby wants to merge 3 commits into
mainfrom
fix/FR-2526-block-folder-access-during-revision-add

Conversation

@yomybaby

@yomybaby yomybaby commented Sep 7, 2026

Copy link
Copy Markdown
Member

Resolves #6603 (FR-2526)

The issue was filed against ServiceLauncherPageContent, which was deleted in #7325 (FR-2852) together with the modify_endpoint path it guarded. The request itself is still undelivered: the model-service update path today is DeploymentAddRevisionModal, and it embeds the same two file-browser entry points with no in-flight gate.

While addModelRevision is running, the modal body stays fully interactive — confirmLoading only spins the OK button (BAIModal passes it to the OK button's isLoading and renders no mask). So a user can still open the folder explorer, start a filebrowser session, and edit files in a folder the new replicas are about to mount. A revision references a model folder, it does not snapshot its contents, so those edits land in what the new replicas serve.

What changed

  • DeploymentAddRevisionModal gets a single isSubmitInFlight flag (isAddInFlight || isResolvingImage — the same state the submit button already shows, so the image-name resolution that precedes the mutation is covered too) and uses it for the submit button, confirmLoading, and the new gates.
  • Both "Open Folder" icon buttons (Preset body and Custom body) are disabled while it is true, and their tooltip switches to an explanation rather than the button silently going dead.
  • VFolderTable gains isFolderLinkDisabled, which renders the folder-name cells as BAILink type="disabled" — an aria-disabled, non-navigating link — instead of a route to the folder explorer. The additional-mounts form item carries the same explanation in its extra slot.
  • New i18n key deployment.FolderAccessDisabledWhileAddingRevision, translated in all 21 locale files. th is not in i18n.config.js's lngs so make i18n skips it; it is added by hand, the way the rest of th.json is kept in sync.

Access is restored automatically when the mutation settles, success or error. The create path is untouched — this modal only adds revisions to an existing deployment.

Design decisions

  • A prop on VFolderTable, not a tooltip per row. Astryx's disabled Link sets pointer-events: none, so a Tooltip wrapping the cell would never fire. The "why" is therefore shown once, in the form item's extra, while the cells themselves just stop navigating.
  • The gate is not extended to "create folder" / "refresh" in the same button group: neither mutates the contents of a mounted folder, and the issue asks specifically about file-browser access.

Scope — what this does not close

Both limits below are deliberate; flagging them so a reviewer can confirm the narrower scope is acceptable for FR-2526 rather than discover it after merge.

  • Only the two entry points are gated, not sessions already open. A FolderExplorerModal already open via the global ?folder= query param, or a filebrowser session already launched in another browser tab, keeps working when the submit starts. Revoking those means force-closing a modal the user may be mid-upload in (FileUploadManager), and a tab this modal has no handle on at all — a product call, not a mechanical one. The gate closes the routes this form owns.
  • The rest of the modal stays editable during the mutation. The deleted ServiceLauncherPageContent gated its whole <Form disabled=…>; this PR does not, because BAIModal's confirmLoading renders no mask and blanket-disabling every field mid-submit is a wider UX change than the issue asks for. Edits made in that window are discarded when the modal closes on success, so they do not reach the submitted revision.

Tests

  • New react/src/components/VFolderTable.test.tsx (2 cases): the folder name is a real link by default; with isFolderLinkDisabled there is no link role and the name sits inside an aria-disabled element. pnpm exec vitest run src/components/VFolderTable.test.tsx — 2 passed.
  • pnpm exec vitest run src/components/DeploymentAddRevisionModal.test.tsx — 3 passed (no regression).

Verification

bash scripts/verify.sh=== ALL PASS ===

Review notes

  • Open a deployment → Add revision, pick a model folder, submit, and watch the "Open Folder" button during the request: it should be disabled with the explanatory tooltip, and enabled again once the modal settles.
  • In Custom mode, expand the additional-mounts table while submitting: folder names should be grey and non-clickable, with the explanation under the field.
  • The gate uses the same flag as the submit spinner, so anything that makes the button spin also closes the folder routes — there is no second source of truth to keep in sync.

Checklist: (if applicable)

  • Documentation
  • Minium required manager version
  • Specific setting for review (eg., KB link, endpoint or how to setup)
  • Minimum requirements to check during review
  • Test case(s) to demonstrate the difference of before/after

https://claude.ai/code/session_011RFmSEBxxxvCXyquSPtqVJ

The Add-revision modal is the model-service update path, and while its
`addModelRevision` mutation is in flight both file-browser entry points on the
form stay live: the "Open Folder" icon buttons and the folder-name links in the
additional-mounts table. A filebrowser session started in that window can edit
files in a folder the new replicas are about to mount, so what gets served does
not match what the user submitted. `confirmLoading` only spins the OK button —
it does not mask the modal body.

Gate both on a single `isSubmitInFlight` flag (the mutation plus the image-name
resolution that precedes it, i.e. the same state the submit button already
shows):

- the two "Open Folder" icon buttons become disabled, and their tooltip
  explains why instead of the button going silently dead;
- `VFolderTable` gains `isFolderLinkDisabled`, which renders the folder-name
  cells as disabled (non-navigating) links, and the mounts form item carries
  the same explanation in its `extra` slot.

Access is restored automatically when the mutation settles. The create path is
untouched — this modal only adds revisions to an existing deployment.

Claude-Session: https://claude.ai/code/session_011RFmSEBxxxvCXyquSPtqVJ
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for react-coverage (./react)

Status Category Percentage Covered / Total
🔵 Lines 15.91% 5793 / 36395
🔵 Statements 13.33% 7048 / 52867
🔵 Functions 14.23% 923 / 6482
🔵 Branches 9.65% 4885 / 50574
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
react/src/components/DeploymentAddRevisionModal.tsx 31.34% 16.91% 24.24% 34.68% 224-273, 285, 297-310, 328-340, 356-361, 375-377, 716, 735-743, 784-825, 876-965, 969-1019, 1028-1208, 1225-1226, 1234-1235, 1245, 1247-1248, 1262, 1264-1280, 1289-1293, 1316-1324, 1335-1350, 1359-1367, 1380-1710, 1733-1734, 1750-1763, 1874-1875, 1900-1944, 2007-2014, 2079-2102, 2175-2198, 2277-2341, 2365-2467, 2485-2494, 2517-2528, 2631-2724
react/src/components/VFolderTable.tsx 59.2% 42.85% 47.27% 59.67% 167-170, 182, 190-197, 282, 302, 327, 331, 349-354, 369-370, 402, 440-557, 608, 616, 651-732
Generated in workflow #766 for commit 93e5a1a by the Vitest Coverage Report Action

Review found the new `deployment.FolderAccessDisabledWhileAddingRevision`
string shipped untranslated.

- Replace the raw `make i18n` `__NOT_TRANSLATED__` placeholder with a real
  translation in the 18 scanner locales (de, el, es, fi, fr, id, it, ja, mn,
  ms, pl, pt, pt-BR, ru, tr, vi, zh-CN, zh-TW). Because the key existed,
  i18next resolved it instead of falling back to en, so a non-English user
  hovering the disabled Open Folder button literally saw
  `__NOT_TRANSLATED__`. `origin/main` carries zero occurrences of the
  placeholder; this restores that.
- Add the key to `resources/i18n/th.json` by hand. `th` is not in
  `i18n.config.js`'s `lngs`, so `make i18n` never writes it, but Thai is a
  user-selectable language in `UserSettingsPage` and th.json is kept in sync
  with the other locales by hand.

Each translation keeps "Revision" as the untranslated product term, matching
the surrounding `deployment.*` strings in the same file (e.g.
`AddRevision`, `FailedToRollback`).

`bash scripts/verify.sh` ends with `=== ALL PASS ===`.

Claude-Session: https://claude.ai/code/session_011RFmSEBxxxvCXyquSPtqVJ

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Modal dismissal can re-enable folder access during an active request, and the submission-state wiring lacks regression coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Blocks folder-browser entry points while adding a deployment revision.

Changes:

  • Reuses submission state to disable folder access with explanatory feedback.
  • Adds disabled-link support to VFolderTable.
  • Adds link-state tests and translations across all locales.
  • Requires fixes for modal dismissal during submission and missing modal-level regression coverage; also includes a prop-naming nit.
File summaries
File Description
resources/i18n/zh-TW.json Adds Traditional Chinese feedback.
resources/i18n/zh-CN.json Adds Simplified Chinese feedback.
resources/i18n/vi.json Adds Vietnamese feedback.
resources/i18n/tr.json Adds Turkish feedback.
resources/i18n/th.json Adds Thai feedback.
resources/i18n/ru.json Adds Russian feedback.
resources/i18n/pt.json Adds Portuguese feedback.
resources/i18n/pt-BR.json Adds Brazilian Portuguese feedback.
resources/i18n/pl.json Adds Polish feedback.
resources/i18n/ms.json Adds Malay feedback.
resources/i18n/mn.json Adds Mongolian feedback.
resources/i18n/ko.json Adds Korean feedback.
resources/i18n/ja.json Adds Japanese feedback.
resources/i18n/it.json Adds Italian feedback.
resources/i18n/id.json Adds Indonesian feedback.
resources/i18n/fr.json Adds French feedback.
resources/i18n/fi.json Adds Finnish feedback.
resources/i18n/es.json Adds Spanish feedback.
resources/i18n/en.json Adds English feedback.
resources/i18n/el.json Adds Greek feedback.
resources/i18n/de.json Adds German feedback.
react/src/components/VFolderTable.tsx Supports disabled folder links; prop naming should follow repository conventions.
react/src/components/VFolderTable.test.tsx Tests enabled and disabled folder-link states.
react/src/components/DeploymentAddRevisionModal.tsx Gates folder access during submission, but dismissal can bypass the gate and modal-level regression coverage is missing.
Review details
  • Files reviewed: 24/24 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread react/src/components/DeploymentAddRevisionModal.tsx
}
onCancel={() => onRequestClose()}
confirmLoading={isAddInFlight || isResolvingImage}
confirmLoading={isSubmitInFlight}
Comment thread react/src/components/VFolderTable.tsx
Copilot asked for modal-level regression coverage of the folder-access
gate: `VFolderTable.test.tsx` only proves the downstream prop, and
`DeploymentAddRevisionModal.test.tsx` never held `addModelRevision`
pending, so the wiring that drives the gate could regress while the table
tests still passed.

Add a `DeploymentAddRevisionModal folder-access gate (FR-2526)` suite that
drives the preset form to a submittable state, leaves the mutation pending,
and asserts the Open Folder entry point is `aria-disabled` *with* the
explanatory tooltip in that window — then that the gate lifts once the
mutation settles, on success and on error alike.

To make the form drivable the two select mocks become form probes that write
through their injected `onChange`, and the mocked ids are real global ids
because the submit path runs them through `toLocalId` (atob).

Custom mode is not covered: its submit path needs a resolvable image id and
a populated resource block, both of which come from children this test file
mocks out.

`bash scripts/verify.sh` ends with `=== ALL PASS ===`.

Claude-Session: https://claude.ai/code/session_011RFmSEBxxxvCXyquSPtqVJ

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Prevent modal dismissal while submission is in flight so reopening cannot bypass the folder-access gate.

Review details

Suppressed comments (1)

react/src/components/DeploymentAddRevisionModal.tsx:1945

  • confirmLoading only affects the OK button, so Cancel, the header close button, Escape, and backdrop clicks still call onRequestClose() while the mutation is pending. Both callers wrap this modal in BAIUnmountAfterClose; after dismissal the local Relay in-flight state is lost and the modal can be reopened with both folder entry points enabled even though the server-side add may still be running. Keep the modal mounted by disabling Cancel and ignoring dismissal until isSubmitInFlight clears.
      onCancel={() => onRequestClose()}
      confirmLoading={isSubmitInFlight}
  • Files reviewed: 25/25 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

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

Labels

area:i18n Localization area:ux UI / UX issue. frontend model-service size:L 100~500 LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Disable file browser access during model service update

2 participants