Repository navigation
feat(frontend): add the on-canvas per-execution warehouse picker - #8551
Conversation
The workspace selector for which warehouse a run writes to (apache#7817), mirroring the computing-unit picker: owner avatar per entry, per-row delete, a create entry, and a refresh on every dropdown open, preselecting the latest execution's warehouse and falling back to the first one. With the feature enabled a run needs a warehouse, so the Run button becomes "Create Warehouse" while none is selected, mirroring the Connect flow, and the picked warehouseId rides the execute request. The pick lives in WarehouseService, where ExecuteWorkflowService reads it at execution time.
Automated Reviewer SuggestionsBased on the
|
There was a problem hiding this comment.
🟡 Changes recommended
Warehouse state races and fail-open behavior can select the wrong warehouse or allow executions without one.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds a feature-gated warehouse picker to the workspace and attaches the selected warehouse to workflow executions.
Changes:
- Adds warehouse selection, creation, deletion, and preselection.
- Gates Run when no warehouse is selected.
- Includes
warehouseIdin execution requests and expands test coverage.
File summaries
| File | Description |
|---|---|
execute-workflow.service.ts |
Adds warehouse ID to execution requests. |
execute-workflow.service.spec.ts |
Tests request serialization. |
computing-unit-selection.component.ts |
Implements warehouse picker state and actions. |
computing-unit-selection.component.spec.ts |
Tests picker behavior. |
computing-unit-selection.component.scss |
Styles the picker. |
computing-unit-selection.component.html |
Renders picker and creation modal. |
menu.component.ts |
Adds missing-warehouse Run gating. |
menu.component.spec.ts |
Tests Run behavior. |
workflow-executions-entry.ts |
Exposes execution warehouse ID. |
workflow-execution-history.component.spec.ts |
Updates execution fixtures. |
warehouse.service.ts |
Stores the current warehouse selection. |
warehouse.service.spec.ts |
Tests selection state. |
Review details
Suppressed comments (2)
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts:480
- A status failure is treated as feature-disabled, then the selected ID is cleared. Consequently
warehouseRequiredButMissingbecomes false andrunWorkflow()proceeds withwarehouseId: undefined; until #7751 lands, an enabled deployment silently writes that run to shared storage. Keep an explicit failed/loading state and fail closed (disable or retry Run) unless the status request successfully reportsenabled: false.
error: (err: unknown) => {
// The pick lives in the root-scoped service, so hiding the picker is not
// enough: a stale id from a previous workflow would still ride the next
// execution request. Clear it whenever the picker cannot be shown.
this.warehouseEnabled = false;
this.warehouses = [];
this.warehouseService.selectWarehouse(undefined);
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.html:126
- Adding
role="button"to an<i>does not make it keyboard-operable: this delete action cannot receive focus or be activated with Enter/Space. Prefer a real<button type="button">, or addtabindex="0"plus equivalent keyboard handlers.
role="button"
aria-label="Delete warehouse">
- Files reviewed: 12/12 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #8551 +/- ##
============================================
- Coverage 92.93% 92.92% -0.02%
Complexity 4885 4885
============================================
Files 1231 1235 +4
Lines 51468 51966 +498
Branches 6327 6389 +62
============================================
+ Hits 47833 48287 +454
- Misses 2073 2105 +32
- Partials 1562 1574 +12
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- The menu re-snapshots the Run button whenever the warehouse pick changes; every relevant transition (load, preselect, create, disable, failure) ends in a selectWarehouse call, so the pick stream covers them all. - Warehouse refreshes flow through one switchMap'd stream, so an older response cannot restore a deleted row or clobber a newer answer. - The previous workflow's last-execution warehouse is cleared on workflow change, so a workflow without history falls back to the first warehouse instead of the old workflow's. - The warehouse rows carry unique DOM ids.
There was a problem hiding this comment.
🟡 Changes recommended
Status failures can bypass warehouse gating, and asynchronous preselection can overwrite a manual choice.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.html:127
- This icon has
role="button"but is neither focusable nor keyboard-operable, so keyboard users cannot invoke the destructive action. Add tab focus plus Enter/Space handlers (including propagation prevention so the menu row is not selected).
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts:541 - A manual choice does not invalidate the in-flight latest-execution lookup. If the user selects a row before either lookup callback runs,
applyWarehousePreselect()later overwrites that explicit choice with the historical/default warehouse. Track a workflow/request generation (or whether the user has selected since lookup start) and ignore preselection callbacks after a manual pick.
- Files reviewed: 12/12 changed files
- Comments generated: 1
- Review effort level: Balanced
A status transport failure no longer drops warehouseEnabled: the flag initializes from the boot-time GUI config and only a status answer moves it, so Run stays gated on an enabled deployment instead of silently writing to the shared default storage. Explicit picks (dropdown or create) are tracked and preselection never overrides one; switching workflows clears the pick immediately, so nothing of the old workflow rides an execution while the new preselect is still pending.
There was a problem hiding this comment.
🟡 Changes recommended
Several execution entry points bypass the warehouse gate and can still submit without a warehouse.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.html:101
- This class has no corresponding style rule and does not set Ng Zorro's menu-item selection state, so the open menu exposes no visual or ARIA indication of the current warehouse. Bind the menu item's
nzSelectedinput instead.
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.html:127 - This delete control is mouse-only:
role="button"does not make the icon focusable or activate it from the keyboard. Add focusability and Enter/Space handlers (including propagation prevention so deletion does not select the row).
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts:605 - After deletion succeeds, this callback starts another HTTP request but leaves the deleted warehouse selected until that request returns. During that window Run remains enabled and can submit the now-invalid ID; if the refresh stalls, the stale selection persists. Clear the selected ID synchronously when the deleted warehouse was selected, then refresh to choose the fallback.
- Files reviewed: 12/12 changed files
- Comments generated: 1
- Review effort level: Balanced
The error path re-asserts the boot-time flag instead of the field being config-initialized at construction: identical gating after a failure, without a "Create Warehouse" flash on the Run button during the initial load window.
The menu's Run button was the only gate, but the form view, run-up-to-operator, and replay all call the execution service directly and would fall through to the shared default storage. The guard now lives in sendExecutionRequest — the one point every path funnels through — refusing with a toast while the deployment requires a warehouse and none is picked. (apache#7751 adds the backend-side rejection.)
There was a problem hiding this comment.
🟡 Changes recommended
Warehouse preselection, deletion timing, execution-state resets, and keyboard accessibility have unresolved defects.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.html:126
role="button"does not make this<i>keyboard-focusable or activate it with Enter/Space, so keyboard-only users cannot delete a warehouse. Use a real button, or addtabindex="0"plus Enter/Space handlers that invoke the same confirmation without selecting the row.
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts:388- This lookup is still coupled to the computing-unit list becoming nonempty. If the workflow has a remembered CU that was terminated and the user currently has zero units, the
filter(units => units.length > 0)above never emits, so the latest execution is never queried and the warehouse refresh permanently leaves the first warehouse selected instead of the latest execution's warehouse. Start warehouse preselection independently of the CU-list wait.
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts:601 - After a selected warehouse is successfully deleted, this callback starts an asynchronous refresh but leaves the deleted ID selected until that request returns. A run during that interval sends a warehouse ID that is guaranteed not to exist. Clear the selection synchronously in the delete-success callback before refreshing.
- Files reviewed: 12/12 changed files
- Comments generated: 1
- Review effort level: Balanced
The public entry points now check the warehouse requirement before resetExecutionState/resetStatus, so a refused click keeps the results already on screen; sendExecutionRequest keeps the same check as the shared belt for direct callers.
There was a problem hiding this comment.
🔵 Needs a closer look
Unsaved workflows can lose automatic preselection, and the delete control is not keyboard accessible.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.html:127
- This delete control has
role="button"but is not focusable or keyboard-activatable, so keyboard-only users cannot delete a warehouse. Add focus and Enter/Space activation (or use a native button) while continuing to stop propagation to the parent menu item.
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts:352 - Switching to a new unsaved workflow (
wid === DEFAULT_WORKFLOW.wid) clears the warehouse selection and then skips every preselection path. If the warehouse list was already loaded, the Run button therefore offers “Create Warehouse” even when the user already has warehouses, until they manually reopen the dropdown. Apply the first-warehouse fallback immediately for the default workflow.
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Ma77Ball
left a comment
There was a problem hiding this comment.
Review summary
This is the workspace-side half of per-user warehouses (#6870): a warehouse picker inside ComputingUnitSelectionComponent beside the computing-unit dropdown, Run-button gating that redirects to a create-warehouse modal when a warehouse is required but missing, and a warehouseId on the execute request. The change is well structured and mirrors the existing computing-unit patterns closely; the concurrency handling (a single switchMap'd refresh stream, preselect re-running after whichever of the list/latest-execution responses lands last, fail-closed on a status error) is careful and matches the extensive tests. The pick lives in the root-scoped WarehouseService and is cleared on workflow switch, on status failure, and when nothing is selectable, so a stale id cannot ride the next request. I verified the contract holds on the backend side: /executions/{wid}/latest serializes whId, and WorkflowExecuteRequest already declares warehouseId: Option[Int], so nothing here breaks execution. No blocking issues.
The one real gap is behavioral: the Run-button gate sits ahead of the execution-state switch, so it can replace Pause/Kill while a workflow is actively running (see below). Flag-off behavior is a clean no-op as described.
Action items: Nothing blocking. Consider the P2 (don't let the warehouse gate hide Pause/Kill mid-run) before merge; the P3 is optional.
Findings
2 total: 0 P0, 0 P1, 1 P2, 1 P3.
Non-blocking (P2)
- Don't let the warehouse gate override Pause/Kill during an active execution -
frontend/src/app/workspace/component/menu/menu.component.ts:427- see inline.
Nit (P3)
- Run-gate reads a different "enabled" signal than the picker -
frontend/src/app/workspace/service/execute-workflow/execute-workflow.service.ts:252- see inline.
Verification
- P2 (button gate vs active execution): with the feature enabled and a workflow Running (or Paused), deleting the currently-selected warehouse via the picker's delete icon calls
confirmDeleteWarehouse->refreshWarehouses->applyWarehousePreselect; if it was the only/last warehouse,selectWarehouse(undefined)runs, sowarehouseRequiredButMissingflips to true.getRunButtonBehaviorchecks that before theexecutionStateswitch, so the button becomes "Create Warehouse" and the user loses Pause/Kill on the in-flight run. Narrow trigger, real loss of control. - P3 (dual enabled source): confirmed both
status.enabled(picker) andconfig.env.warehouseEnabled(execute-service gate) derive from the sameStorageConfig.warehouseEnabledtoday, so they cannot diverge on a real deployment - hence P3, not a bug. - Not run: I did not execute the Vitest suites; findings are from static reading of the PR at head plus the backend contract at the same ref.
The warehouse gate on the Run button now applies only in the states whose button would start a run; deleting the last warehouse during a Running/Paused execution no longer replaces Pause/Resume with "Create Warehouse".
- Create and delete update the picker's list locally, like the dashboard tab, instead of refetching; deleting the picked warehouse re-runs the preselect. - A status transport failure now reports the error and keeps the last known list and pick; clearing is reserved for an authoritative answer (enabled:false, or a list that no longer holds the pick). The flag still falls back to the boot-time config so Run stays gated. - The latest-execution lookup is one method: selectFromLastExecution takes a selectUnit flag for the remembered-unit path instead of a duplicate warehouse-only copy. - The refresh path always re-runs the preselect; its manual-pick guard is the single condition, so the overlapping one at the call site is gone. - sendExecutionRequest drops the warehouse belt: every production caller is one of the two public entry points, which already refuse before resetting state.
…tton The form view's run button enumerates every state a run cannot start from — invalid workflow, empty workflow, connecting, no computing unit, a read-only unit — so that, in its own words, the reader is never sent to press a button that does nothing. The warehouse requirement added in apache#8551 was missing from that list: with the feature enabled and nothing picked, the button read "Run" and looked ready, and the click was refused deeper down, in ExecuteWorkflowService, with a toast. It now names what is missing and stays disabled, after the computing unit as the canvas orders them. The condition read is the one that service refuses on — the boot-time flag and the picked warehouse, both root-scoped — so the button predicts the refusal exactly. Closes apache#8591.
… and name the picker in its tooltip (apache#8589) ### What changes were proposed in this PR? A follow-up to the warehouse picker (apache#8551). - **The run button's label overflowed.** `#run-button` is a fixed 140px, which fits `Empty Workflow` with about a pixel to spare; `Create Warehouse` ran roughly 13px past it and spilled over the execution timer. The label is now `Warehouse` — the same word the picker's own empty state already shows, mirroring how the computing-unit flow repeats `Connect` in both places — and the button keeps its fixed width, so nothing else on the toolbar moves. - **The trigger's tooltip now reads `Warehouse: <name>`.** Two pickers sit side by side showing nothing but a name, and the trigger ellipsises that name at 220px; one tooltip says which picker this is and carries the name in full, in the same shape every time — short names included, so there is nothing to learn about when it appears. It replaces "Warehouse this execution writes to", which named the picker but not the warehouse. Flag off (the default): the picker never renders and the run button is untouched. ### before: <img width="715" height="208" alt="Screenshot 2026-09-17 at 11 28 13 PM" src="https://github.com/user-attachments/assets/59f58e27-259d-413d-aca2-ce2bbf2aec24" /> <img width="761" height="191" alt="Screenshot 2026-09-17 at 11 29 10 PM" src="https://github.com/user-attachments/assets/c0cd7e1e-ea60-4f0f-b5c0-48b445c42d12" /> ### after: <img width="657" height="165" alt="Screenshot 2026-09-17 at 11 30 12 PM" src="https://github.com/user-attachments/assets/e007fcaf-7771-4030-a961-49e361cf14aa" /> <img width="758" height="278" alt="Screenshot 2026-09-17 at 11 30 45 PM" src="https://github.com/user-attachments/assets/2771ae7d-0713-40a1-af96-7f0324996089" /> ### Any related issues, documentation, discussions? Follow-up to apache#8551. Part of apache#6870. The remaining divergences are on the computing-unit side and are tracked separately in apache#8587; a warehouse status badge needs a backend signal first (apache#8588). ### How was this PR tested? - Label widths measured in a browser against the button's real clipping width (140px minus padding, border and icon leaves ~106px for text) across the font stack's macOS, Windows and Linux faces: `Warehouse` 72px, `Create Warehouse` 119px, and main's own `Empty Workflow`/`Invalid Workflow` 105px. - Vitest: the run-button label test updated, a tooltip test added covering both a long and a short name and pinning one tooltip per control; the workspace suite passes in full (3323 tests). - Failure paths verified rather than assumed: the label and the tooltip's name were each reverted on purpose and the suite confirmed to fail for the expected reason before being restored. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (claude-opus-5, claude-fable-5)
…) and 18 other commits Brings apache#8528, the last merged Form View PR staging lacked, with the rest of main since the last sync: the warehouse run picker (apache#8551, apache#8589), RustFS CORS for presigned URLs (apache#8562), and the curated-image PRs staging already carried as open branches (apache#8485, apache#8518, apache#8546), now as merged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XnSFFbnmkjX9gxTFW1j7Wj # Conflicts: # computing-unit-managing-service/src/test/scala/org/apache/texera/service/resource/CuratedImageResourceSpec.scala # frontend/src/app/common/component/computing-unit-create-modal/computing-unit-create-modal.component.spec.ts # frontend/src/app/common/component/computing-unit-create-modal/computing-unit-create-modal.component.ts # frontend/src/app/dashboard/component/dashboard.component.spec.ts
…tton The form view's run button enumerates every state a run cannot start from — invalid workflow, empty workflow, connecting, no computing unit, a read-only unit — so that, in its own words, the reader is never sent to press a button that does nothing. The warehouse requirement added in apache#8551 was missing from that list: with the feature enabled and nothing picked, the button read "Run" and looked ready, and the click was refused deeper down, in ExecuteWorkflowService, with a toast. It now names what is missing and stays disabled. The condition read is the one that service refuses on — the boot-time flag and the picked warehouse, both root-scoped — so the button predicts the refusal exactly. The read-only check comes before the warehouse one: picking a warehouse is the only blocked state a reader can act on here, so naming it first would send someone without write access to fix the wrong thing. Closes apache#8591.
… and let its button be clicked (apache#8590) ### What changes were proposed in this PR? The warehouse picker (apache#8551) now sits beside the computing-unit picker, and the pair showed nothing but a name each. Fixes apache#8587. - **The trigger says what it is.** It gains the `deployment-unit` icon its own dashboard tab uses — the warehouse trigger already carries `cloud-server` — and its tooltip reads `Computing Unit: <name>`, the shape the warehouse trigger uses: one tooltip on the control rather than one on the badge and another on the name. The status stays where it already lives, the badge's colour here and its words in the dropdown rows. - **With nothing selected, both the trigger and the run button name what is missing** — `Computing Unit`, as the warehouse side says `Warehouse` — instead of `Connect`, so the two pickers read the same way. The form view's run button follows, since it mirrors the canvas's disable conditions. - **That button was disabled in exactly the state it exists for.** The run button's guard ended in `selectedComputingUnit?.accessPrivilege !== Privilege.WRITE`; with no unit selected that compares against `undefined` and is always true, so the button offered to connect and refused every click, leaving `runWorkflow()`'s create-unit branch unreachable — the branch the form view's own comments describe as the canvas's click target, and which its test reaches only by calling the method directly. The privilege is now asked only of a unit that exists; a unit shared read-only still refuses to run. - **The two triggers stop looking like different controls.** Sized to content between a min and a max they came out visibly different widths, so both are now 200px; a name too long for it is ellipsised and read from the tooltip, which carries it in full. The avatar, shrunk with a `transform` that changes only what is drawn, kept its 32px box inside a 32px-tall button and inflated it, so it is sized for real instead. The form view matches both pickers to its Run button's height, not just the computing-unit one that rule predates. And the gap between the pickers no longer widens whenever a unit is running: the container gap and the auto margin that exist to push the metrics block away now apply to the metrics block itself. - **The badge stops claiming work that is not happening.** `computeStatus()` returned `"processing"` with nothing selected, which ant renders as a pulsing blue dot; it is now `"default"`. ### before: <img width="1066" height="289" alt="Screenshot 2026-09-18 at 12 23 44 AM" src="https://github.com/user-attachments/assets/ae6ac7ec-2e2b-4d5e-b676-220e4a0a861e" /> <img width="706" height="180" alt="Screenshot 2026-09-18 at 12 24 07 AM" src="https://github.com/user-attachments/assets/6667d342-87f8-43fa-a85b-cd4217a9d9c8" /> <img width="696" height="177" alt="Screenshot 2026-09-18 at 12 24 40 AM" src="https://github.com/user-attachments/assets/2750dfa8-e026-4fda-983c-f7f0271d1dac" /> ### after: <img width="805" height="246" alt="Screenshot 2026-09-18 at 12 33 45 AM" src="https://github.com/user-attachments/assets/e93b7e1b-251a-4347-bfbd-a949761229de" /> <img width="747" height="234" alt="Screenshot 2026-09-18 at 12 32 57 AM" src="https://github.com/user-attachments/assets/8249e697-d136-465d-b81c-d2e9867233b5" /> <img width="751" height="192" alt="Screenshot 2026-09-18 at 12 34 00 AM" src="https://github.com/user-attachments/assets/a77aa290-dac4-4861-b472-2c1581565152" /> ### Any related issues, documentation, discussions? Closes apache#8587. Follows apache#8551 and apache#8589 (the warehouse side of the same pair). Part of apache#6870. ### How was this PR tested? - Vitest: new tests for the trigger's tooltip (long name, short name, nothing selected), for the no-unit button being clickable, and for a read-only unit still disabling it; the existing badge and label assertions updated. The workspace suite passes in full: 3326 tests. - Label width measured in a browser against the run button's clipping width (~106px for text): `Computing Unit` is 101px on macOS, 96px on Windows and Linux faces. - The sizing and spacing changes are CSS and out of reach of jsdom; they were checked in a running workspace and form view, with and without a selection on each picker and with a unit running. - Failure paths verified rather than assumed: the privilege guard, the badge state, the tooltip and the label were each reverted on purpose and the suite confirmed to fail for the expected reason before being restored. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (claude-opus-5, claude-fable-5)
…tton The form view's run button enumerates every state a run cannot start from — invalid workflow, empty workflow, connecting, no computing unit, a read-only unit — so that, in its own words, the reader is never sent to press a button that does nothing. The warehouse requirement added in apache#8551 was missing from that list: with the feature enabled and nothing picked, the button read "Run" and looked ready, and the click was refused deeper down, in ExecuteWorkflowService, with a toast. It now names what is missing and stays disabled. The condition read is the one that service refuses on — the boot-time flag and the picked warehouse, both root-scoped — so the button predicts the refusal exactly. The read-only check comes before the warehouse one: picking a warehouse is the only blocked state a reader can act on here, so naming it first would send someone without write access to fix the wrong thing. Closes apache#8591.
…tton (apache#8592) ### What changes were proposed in this PR? The form view's run button enumerates every state a run cannot start from — invalid workflow, empty workflow, connecting, no computing unit, a read-only unit — so that, in its own words, "the reader is never sent to press a button that does nothing". The warehouse requirement added in apache#8551 was missing from that list: with the feature enabled and nothing picked, the button read `Run` and looked ready, and the click was refused deeper down in `ExecuteWorkflowService`, with a toast. It now names what is missing, ordered after the computing unit as the canvas orders them, and stays disabled — the warehouse is picked in the embedded selector, the same reasoning the no-unit case already gives. The condition it reads is the one `ExecuteWorkflowService` refuses on — the boot-time flag and the picked warehouse, both root-scoped — so the button predicts that refusal exactly. Flag off (the default): `warehouseRequiredButMissing` is never true, so the button behaves exactly as before. ### Any related issues, documentation, discussions? Closes apache#8591. Follows apache#8551. Part of apache#6870. ### How was this PR tested? - Two Vitest cases added: the button names the missing warehouse and stays disabled, and returns to `Run` once one is picked. The workspace suite passes in full: 3324 tests. - Failure path verified rather than assumed: the new case was removed on purpose and the suite confirmed to fail for the expected reason before being restored. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (claude-opus-5, claude-fable-5)
…abled (apache#8586) ### What changes were proposed in this PR? With per-user warehouses enabled, an execution carrying no `warehouseId` silently wrote into the shared default warehouse: `resolveLakekeeperWarehouseName` mapped `None` to `None` whenever the flag was on. That made "a run writes into the user's own warehouse" a UI convention rather than a system property — the frontend gates Run on a pick (apache#7817), but nothing enforced it below, and one path already violated it: agent-driven runs hardcoded `warehouseId = None`, bypassing the picker entirely. **The requirement.** `resolveLakekeeperWarehouseName` refuses an execution that names no warehouse while the feature is enabled, the same way an execution needs a computing unit. Not a privilege change: an explicit `whid` was, and still is, checked against the caller's `uid`. It is resolved before anything destructive happens — both in `initExecutionService`, ahead of the teardown of the execution in flight, and in the sync endpoint, ahead of its own shutdown — so a request that will be refused never disturbs a run already going. **The agent path**, which had no way to carry a pick at all. `SyncExecutionRequest` accepts a `warehouseId` and `SyncExecutionResource` forwards it; the workspace attaches the current pick to each prompt, the agent service applies it before running, and the execute request carries it to the backend. Per prompt rather than at agent creation on purpose: an agent created before the picker had loaded would otherwise carry no warehouse for its whole life, with every run refused and nothing in the agent panel able to correct it. It also means a warehouse chosen after the agent exists takes effect, and an absent pick clears a previous one rather than leaving a stale id to be sent. With the feature off (the default) nothing changes: no pick still means the shared default warehouse, and an explicit pick is still refused loudly (apache#6930). ### Any related issues, documentation, discussions? Closes apache#7751. Part of apache#6870; the last of its Phase 0 items, on top of the dashboard tab (apache#8005) and the on-canvas picker (apache#8551). ### How was this PR tested? - Scala: `WorkflowServiceWarehouseSpec` (5) covers the requirement alongside the existing ownership and flag-off cases, and `WorkflowServiceSpec` (11) pins the new ordering — a refused request leaves the previous execution attached. `sbt 'WorkflowExecutionService/testOnly *WorkflowServiceSpec *WorkflowServiceWarehouseSpec'` - `agent-service`: 309 tests (`bun test`, on CI's bun 1.3.3), covering the delegate update and the execute request's body in both the picked and unpicked cases. - Frontend: 87 tests in `agent.service.spec.ts`, pinning the pick attached to each prompt. - Failure paths verified rather than assumed: the requirement, the pre-teardown ordering, the per-prompt carry, the delegate update and the request field were each broken on purpose and the suites confirmed to fail for the expected reason before being restored. - `scalafmtCheck` (both source sets), `tsc --noEmit` and `prettier --check` for `agent-service`, eslint for the frontend files: all clean. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (claude-opus-5, claude-fable-5)
What changes were proposed in this PR?
The workspace-side half of per-user warehouses (#6870): a warehouse picker on the canvas, beside the computing-unit selector it mirrors, choosing which warehouse the next execution writes to.
ComputingUnitSelectionComponent, like the computing-unit dropdown it sits beside) — shown only while the deployment enables the feature; each entry carries the owner avatar and a per-row delete, plus a create entry opening the shared create dialog. The list refreshes on every dropdown open, and preselection picks the latest execution's warehouse, falling back to the user's first one — so a run needs no explicit pick. A status failure clears the pick rather than letting a stale id ride the next request.warehouseIdrides the execute request (ExecuteWorkflowServicereads the pick fromWarehouseService, where it lives); executions exposewhIdso the preselect can read the latest run's warehouse.Flag off (the default): the picker never renders, the Run button is untouched, and requests carry no warehouseId — no user-visible change.
demo
Screen.Recording.2026-09-16.at.3.18.52.PM.mov
Any related issues, documentation, discussions?
Closes #7817. Part of #6870, on top of the dashboard tab (#8005); the backend enforcement (#7751) follows.
How was this PR tested?
warehouseIdon the execute request, 1 on the selection state. One pre-existing assertion modernized: the remembered-unit test asserted the latest-execution lookup's absence, which the warehouse preselect now legitimately performs — it asserts the unit choice directly instead.Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (claude-opus-5, claude-fable-5)