mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 08:01:25 +00:00
c19441bc8cb2da064e4ad44d77dc04ab8bbb22ec
13318 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c19441bc8c |
perf(python): add --compile-bytecode to uv pip install (#9393)
Python jobs under nsjail experience slow imports (~465ms for `import requests`) because dependency directories are mounted read-only. Without pre-compiled `.pyc` files, Python recompiles `.py` source to bytecode in-memory on every import in every fresh nsjail process, paying the cost repeatedly. Add `--compile-bytecode` to both `uv pip install` invocations (the Rust-driven install in python_executor.rs and the nsjail download_deps.py.sh script) so `.pyc` files are generated at install time and available at runtime even through read-only mounts. The files are included in both the local cache and S3 piptar uploads. The flag is supported by the uv version (0.9.24+) shipped in the Dockerfile. The existing `__pycache__` skip only filters top-level dirs for dedup logic, not subdirs within packages, so there is no conflict. Fixes WIN-2001 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5c20d6b4f7 |
feat: add global ai chat test tools (#9391)
* feat: add global ai chat test tools
* fix: avoid session id in flow test preview
* test: cover global flow preview ids
* test: require script and flow test tools
* fix: harden global flow test fallback
* Revert "fix: harden global flow test fallback"
This reverts commit
|
||
|
|
075faabf3b |
feat(frontend): surface local drafts in drawer editors with an unsaved-changes banner (#9335)
* feat(frontend): surface local drafts in drawer editors with an unsaved-changes banner Drawer-based editors (the 11 trigger types, plus resource and variable) restore unsaved edits from browser localStorage on open using the same mechanism as flows/scripts, but only showed a transient "Reset to deployed" toast with no way to review the diff. Add a persistent "You have unsaved changes" banner below the drawer header with Show diff / Discard actions, shown whenever the form diverges from the deployed baseline. Replaces the toast for these editors; flows/scripts/apps (full-page) keep their existing toast. - new shared LocalDraftBanner.svelte (Alert-styled bar + DiffDrawer) - DrawerContent: optional `banner` snippet rendered below the header - useTriggerDraftSync: reactive `hasDraft`, `deployed`/`current` getters and `resetToDeployed`; drop the restore toast (banner supersedes it) - wire the banner into all 11 trigger editors + variable; resource lifts its dirty state up to ResourceEditorDrawer via a callback + accessors - fix ScheduleEditorInner.openNew not resetting initialConfig (reused editor instance kept a stale baseline, wrongly flagging a new schedule dirty) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(frontend): address PR review on local-draft drawer banners - LocalDraftBanner: snapshot diff sides at click time so the diff drawer doesn't keep updating as the user types behind it. - VariableEditor / ResourceEditor: scope the banner and its Discard action to the selected workspace; the cross-workspace dirty state stays surfaced by the existing otherDirty Alert. Forward can_write via a new onCanWriteChange callback so the resource banner hides Discard in read-only mode (matching the trigger editors). - useTriggerDraftSync: drop the now-unused path arg from maybeRestore and update all 11 trigger editor call sites. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(frontend): deep-clone fallback in UserDraft.discard to avoid baseline aliasing When a caller passed a live $state proxy as `fallback` (Variable/Resource editors handed `initialStates[selected]` to the banner's Discard), the handle's draft cell ended up sharing the same proxy as the caller's baseline. Subsequent form edits mutated both sides in lock-step and the dirty check kept reporting equal, so the banner never reappeared and the Update button stayed disabled until the drawer was reopened. Cloning the fallback inside `discard` (via `snapshotDraftValue`) gives the handle a fresh tree and decouples the two reactive graphs. Trigger editors already cloned at their call site (resetToDeployed); this just makes the API self-contained for all callers. Also switch the variable form's "Audit log for each access" alert from warning to info — it's informational, not a warning. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(frontend): honor disabled prop in LocalDraftBanner's diff drawer The banner's `disabled` prop hid the inline Discard button but the diff drawer's "Discard changes" action was still wired unconditionally, so a read-only user could bypass the hidden inline action via Show diff. Gate the diff-drawer button on the same flag so both surfaces agree. Flagged by cubic and Codex on PR #9335. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e4213c1ab8 |
feat(flow-ai): constrain flow-group colors to the NoteColor palette (#9343)
The flow AI chat's set_flow_json tool lets the model set a `color` on each semantic flow group, but nothing told it which colors are valid, so it would sometimes emit hex codes / arbitrary CSS color names. Those render with default styling at best and break the group color picker at worst. - core.ts: the set_flow_json schema `.describe()` and the `groups` system-prompt bullet now spell out that `color` MUST be one of the palette names (yellow, blue, green, purple, pink, orange, red, cyan, lime, gray) — no hex, no CSS colors — and that omitting it lets the editor auto-assign one. - helperUtils.ts: validateFlowGroups now rejects any color outside that palette, sourced from the NoteColor enum so the two can't drift. - helperUtils.test.ts: tests for reject-unknown / accept-known / accept-omitted. Split out of the sessions branch (gl/layout-ai), where it had been bundled into the large feature commit; it's an independent flow-AI improvement. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
eadeac248b |
feat: sessions page with isolated AI chat + flow editor (#9034)
* feat(sessions): chat + editor side-by-side with multi-session state
Introduces the Sessions feature: a workspace where the AI chat and an
editor (flow / script / app / raw-app) sit side-by-side, with each session
having its own AIChatManager instance, history, and target item. Sessions
are persisted across reloads and can be staged into forks for review.
Key pieces:
- sessions/ — SessionWrapper (the split-pane shell), SessionPicker
(sidebar list), SessionForkBar, SessionWorkspaceBar, FlowEditorView /
ScriptEditorView / AppEditorView / RawAppEditorView, ForkDiffDrawer,
sessionRuntime (per-session AIChatManager + draft state),
sessionState (in-memory + persisted index), sessionUnread, sessionScope,
appDraftCodec / flowDraftCodec, forkEditUrl, /sessions route.
- WorkspaceItemDrillPicker refactor — extracts WorkspaceItemRow + adds
surfaceAI drafts, stale-while-revalidate. workspacePicker.ts drops
explicit invalidate() in favor of always re-fetching in the background.
- ForkDiffDrawer + WorkspaceItemDiffViewer — per-kind diff bodies
reusable from the compare page. FlowGraphDiffViewer / FlowGraphV2 gain
inlineDiff forwarding + onHeight callback for equal-height layout.
- Global AI chat sessions plumbing — AIChatManager exports the class +
adds disabledModes, beforeSend hook, scoped instance context. AIChat /
AIChatDisplay accept session-only props (wideLayout, emptyHint,
inputPreface, hideHeader, hideModeSelector, forceDisabled). Chat
preserved across /flows/add → /flows/edit, /scripts/add → /scripts/edit.
- Draft-first loaders — sessions open drafts when present, otherwise
seed a draft from the last deployed value via globalDraftStore.
RawAppEditor / AppEditor / AppEditorHeaderDeploy get newApp prop +
fixes so draft-only apps can deploy.
- Compare page (/forks/compare) — bigger overhaul to plug into the new
drawer.
- Sidebar — Sessions entry + unread badge + status dot in
SidebarContent / MenuButton / SideBarNotification.
- Misc fixes — chat group color palette constraint, deploy_workspace_item
confirmation dropped, open_preview tool, picker drafts surfacing,
fork archive/delete buttons on compare page.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sessions): bypass UserDraft inside session panes + sessionUnread crash
After merging main's UserDraft PR (#9121) into the sessions branch, two
integration issues surfaced:
1. AppEditor.svelte calls `UserDraft.use<App>('app', path)` at the
component level — keyed by ($workspaceStore, 'app', path). Sessions
that haven't materialized a fork yet stay at the user's main
workspace, so a session targeting an app at the same path as a
regular /apps/edit tab shared the same LS key. The session would
read the regular tab's autosave and write its fork-edits back over
it.
Gate UserDraft.use on `!getContext('aiChatManager')` — sessions
inject the manager via setContext, so inside a session pane the
handle is `undefined`, stateApp falls through to the `app` prop
the session loaded, and the auto-save $effect bails. Same gate on
the four UserDraft.remove call sites in AppEditorHeader and
RawAppEditorHeader so save/deploy from a session pane doesn't wipe
the LS draft of a non-session tab at the same path.
2. sessionUnread.svelte.ts called useLocalStorageValue at module
scope. Main's PR added a deep-mutation $effect inside that helper,
which now requires component-initialization context — every page
crashed at import time with `Svelte error: effect_orphan`.
Replaced with a plain module-level $state + manual localStorage
persist; same reactivity contract for callers.
3. ScriptEditorView.svelte was passing a `replaceStateFn` prop that
ScriptBuilder dropped on main. Removed.
Verified end-to-end with Playwright:
- /flows/edit/{path} regression: UserDraft handle still created, no
console errors
- /sessions loads, sessionUnread doesn't crash
- Session targeting non-raw app `u/admin/userdraft_collision_test`
displays the fork content (FORK_ONLY_MARKER) even with an LS
poison at `userdraft/w/local/app/{path}` containing a
POISONED_BY_REGULAR_TAB_AUTOSAVE marker; poison remains untouched
after the session loads and renders
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sessions): stop fork-create retry loop on first user message
Removed the SessionWrapper $effect that retroactively committed the
session's workspace from the in-memory chat history. When opening a
session whose previous commit attempt had failed (or whose response was
lost) the effect ran in a tight retry loop, flooding the user with
`workspace_pkey` violations from `create_workspace_fork`.
The send path already commits through `AIChatManager.beforeSend` →
`commitSessionWorkspace`, which is the deterministic moment-of-action.
The $effect was a redundant reactive bridge that turned every backend
failure into an infinite retry.
Also hardens `materializeFork`/`commitSessionWorkspace` so the most
common cause of the duplicate-key error self-heals:
- `materializeFork` short-circuits when `fork.id` is already in
`$userWorkspaces` (the previous create actually succeeded, we just
lost the response). On a `workspace_pkey` catch, refresh the workspace
list and adopt the existing row instead of toasting an error.
- On a real `materializeFork` failure, `commitSessionWorkspace` now
drops `pending_fork` so the session falls through to the
workspace-pick fallback instead of looping on the same broken intent.
* feat(sessions): show EditorHeader breadcrumb in the not-found state
When a session's target item has been deleted or moved, the editor pane
used to render a bare "Script not found at path X" line — leaving the
user with no way to navigate to a different target without backing out
of the session.
Each editor view now renders a `SessionItemNotFound` shell instead: a
real `EditorHeader` (read-only summary, no pen popover) with a
breadcrumb keyed to the missing kind+path, plus the "not found" copy
below. Clicking any breadcrumb segment opens the workspace picker
scoped to that level — pick a replacement and the session swaps target
via the existing `onNavigate` callback.
`SessionItemNotFound` maps `raw_app` to `EditorHeader`'s `kind: 'app'
+ raw_app: true` so the picker routes through `/apps_raw/...`; the
local label still says "Raw app not found" (not "App not found") so
the user knows which surface is missing.
* fix(picker): stop self-feeding fetch effect that OOM'd the tab
The drill picker's $effect watched `scope` and called `ensureLoaded`
on every change. `ensureLoaded` reads `loaded[kind]` synchronously
(to decide whether to show a spinner), so the effect ended up
subscribed to the very signal it fills. Each fetch result wrote
`loaded[kind] = items`; Svelte 5's $state proxy notifies on every
property set even when the reference is unchanged from cache, which
refired the effect, which called `ensureLoaded` again, which awaited
the cached fetch, which wrote `loaded[kind]` again... runaway loop.
In `/scripts/edit/...` the picker's lifecycle stabilised quickly
enough to mask the loop, but in a session pane (multiple warm
sessions, picker kept alive by the surrounding state) the cycle
spun freely — 29.8 million iterations in <100 ms during testing,
enough to OOM Firefox / kill the Chromium tab.
Two changes:
- Replace the scope-watching $effect with an explicit `setScope()`
helper called from `drill()`, `goUp()`, and `onMount`. Fetch is
now a callback reaction to user navigation, never a reactive
consequence of one. No closed feedback cycle is possible.
- Untrack the `loaded[kind]` read inside `ensureLoaded`. The search
$effect (which loads every kind on first keystroke) is still a
reactive caller; the untrack stops it from subscribing to the
signal `ensureLoaded` fills, so the same loop can't form there.
* feat(script-editor): wire initialTestPanelCollapsed through ScriptBuilder
The `initialTestPanelCollapsed` prop was already declared on
`ScriptBuilderProps` (used by the session preview to start the editor
with the run/test pane closed) but never destructured in
`ScriptBuilder.svelte`, so the value silently dropped on the floor
and the test pane always opened.
- `ScriptBuilder.svelte` — destructure the prop and forward it to
`<ScriptEditor>`.
- `ScriptEditor.svelte` — accept the prop and seed `rawTestPanelSize`
to 0 when true, while keeping `storedTestPanelSize` at the default
30 so the user's first toggle expands the pane to a sensible width
rather than 0.
Regular `/scripts/edit/...` doesn't pass the prop → default `false`
→ panel still opens by default.
* fix(sessions): resolve aiChatManager via context in AskUserQuestionDisplay
Inside a session the chat uses a per-pane AIChatManager injected via context. AskUserQuestionDisplay imported the global singleton, so answers clicked in a session dispatched to the singleton's callback map and the AI loop stalled. Resolve via getContext with singleton fallback, matching ChatMode / ToolExecutionDisplay.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(raw_apps): let preview start in single-view on the preview tab
Add a defaultSplitWithPreview prop (default true). When false (session preview), the editor boots in single view with the preview tab selected: gate the onMount default-file activation, the setActiveDocument auto-activation, and iframeShouldMount so the UI Builder bundler iframe still mounts when preview is the active tab. RawAppEditorView passes defaultSplitWithPreview={false}.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(copilot): add get_preview_status tool and make open_preview idempotent
So the assistant can tell whether the session preview already shows the item it just edited, instead of re-opening or re-offering it. Mirrors the open_preview handler plumbing (setGetPreviewStatusHandler) and the session runtime registers it alongside open_preview. open_preview now returns 'already open' when the requested target matches the active session's current target. The system prompt steers the AI to check status before offering. Unit tests cover the no-arg schema, the session-only error, and handler dispatch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sessions): make script preview reactive to AI draft writes
ScriptEditorView read the draft via static UserDraft.get inside an effect, which only subscribes to UserDraft's reactive cell when a live entry exists. None did for the preview path, so the chat's writes (UserDraft.save) only touched localStorage and the open preview never updated. Hold a live handle via UserDraft.useMany (reactive getter so it re-acquires when open_preview swaps the path without remounting) and read inbound through handle.draft, materializing the shared $state cell that bridges the chat's writes to the editor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sessions): make raw-app preview reactive to AI draft writes
Mirror of the script-preview fix. RawAppEditorView read the draft via static UserDraft.get inside an effect, which only subscribes to UserDraft's reactive cell when a live entry exists. None did for the preview path, so the chat's raw-app writes (UserDraft.save / setDraftAndMeta, from write_app_file / patch_app_file / write_app_runnable) only touched localStorage and the open preview never updated. Hold a live handle via UserDraft.useMany (reactive getter so it re-acquires when open_preview swaps the path without remounting) and read inbound through handle.draft. Verified in-browser: an external UserDraft.save live-updates the bound summary in the open preview.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sessions): make flow preview reactive to AI draft writes
Mirror of the script/raw-app preview fixes, completing two-way binding for all three session editor kinds. FlowEditorView read the draft via static UserDraft.get inside an effect, which only subscribes to UserDraft's reactive cell when a live entry exists — none did, so the chat's writes (write_flow / patch_flow_json / set_flow_module_code) only touched localStorage and the open preview never updated. Hold a live handle via UserDraft.useMany (reactive getter so it re-acquires when open_preview swaps the path without remounting) and read inbound through handle.draft. Verified in-browser both directions: an external UserDraft.save live-updates the flow header summary and rebuilds the module graph; a preview edit propagates through the debounced save to both UserDraft.get and the chat's getGlobalDraft adapter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(sessions): surface local-storage drafts in fork diff & compare page
Augments the backend fork-vs-parent comparison with browser-local (UserDraft) drafts so a session's uncommitted AI/user changes are visible in the Fork Diff Viewer and the /forks/compare page. Adds forkDraftDiff.ts (augmentForkComparisonWithLocalDrafts + getForkItemValue), a 'local changes detected' / new-draft warning surface (checkbox-slot warning icon, no-op-baseline filtering, dedup), a 'Local draft <> fork' tab in DiffDrawer, and selectTooltip/nonSelectableTooltip plumbing in Row/WorkspaceDeployLayout.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Revert "feat(sessions): surface local-storage drafts in fork diff & compare page"
This reverts commit
|
||
|
|
32b4771f19 |
chore(main): release 1.713.1 (#9389)
* chore(main): release 1.713.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.713.1 |
||
|
|
9d9c5038ce |
fix(api): handle multi-version scripts when removing granular ACL (#9388)
* fix(api): handle multi-version scripts when removing granular ACL
remove_granular_acl built a CTE whose `old` subquery selects one row per
matching table row, then read it back via the scalar subquery
`(SELECT old_write FROM old)` in the RETURNING clause. For the `script`
table the PK is (workspace_id, hash), so a path with multiple deployed
versions yields several rows sharing the same (workspace_id, path). When
two or more versions carried the ACL key, `old` returned multiple rows and
PostgreSQL rejected the scalar subquery with "more than one row returned by
a subquery used as an expression", making it impossible to remove an ACL
entry from a script's permissions panel.
All versions share the same extra_perms value (the UPDATE applies to every
matching row), so any single row's old_write is representative. Add
`LIMIT 1` to the scalar subquery. Other kinds are unaffected because they
have a unique constraint on (workspace_id, path/name).
Introduced by
|
||
|
|
b16828d480 |
chore(main): release 1.713.0 (#9369)
* chore(main): release 1.713.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.713.0 |
||
|
|
edf340c4d4 |
fix(security): re-pin cached hub scripts to CVE-patched versions (+ HUB_BASE_URL override for cache mode) (#9387)
* feat(cache): allow overriding hub base url via env in `cache` mode The `windmill cache hubPaths.json` prebuild step (run in the Dockerfile) never connects to the DB, so HUB_BASE_URL stays at its compiled default (https://hub.windmill.dev) — unlike server/worker modes which load it from the DB global setting. This made it impossible to point the prebuild cache step at a private or staging hub. Read HUB_BASE_URL from the environment at the start of cache_hub_scripts and store it into the existing HUB_BASE_URL ArcSwap (the same static the hub fetch functions read). No effect unless the env var is set and non-empty; server and worker modes are unchanged (they still use the DB setting). This also enables validating hub-script dependency changes end-to-end against a local fake-hub before pushing to the real hub. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(security): re-pin cached hub scripts to patched versions windmill-integrations#133 was merged and pushed to the hub, minting new versions with regenerated (CVE-free) lockfiles. Bump the hubPaths.json pins so the prebuild cache step (`windmill cache`) fetches the patched lockfiles instead of the old vulnerable ones (the hub serves each version_id immutably, so the old pins keep returning the vulnerable deps until bumped). - slackErrorHandler 19741 -> 28241 - slackRecoveryHandler 9080 -> 28239 - slackSuccessHandler 28220 -> 28240 - smtpReport 9086 -> 28242 - appReport 28076 -> 28243 (puppeteer screenshot script) - gitInitRepo 28219 -> 28229 (already-fixed hub version; pin was stale) Validated end-to-end against the real hub: `windmill cache` with these pins produces a clean cache_nomount/bun (axios 1.16.1, form-data 4.0.5, follow-redirects 1.16.0, nodemailer 8.0.10, ws 8.21.0, svelte 5.55.8, devalue 5.8.1; basic-ftp and ip-address no longer pulled). No vulnerable versions remain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d23d8374dd |
use libgnutls30 in slim images (#9286)
* Harden slim image security surface * Limit slim hardening to libgnutls pin * fix(docker): pin libgnutls30 to exact +deb12u7 in slim images Replace the +deb12u* wildcard with the exact current security build so builds fail loudly when Debian ships a newer patch, prompting an explicit review/bump rather than silently floating forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(docker): drop libgnutls30 version pin, keep explicit listing The version pin (exact or wildcard) was not the load-bearing part of this change. Naming libgnutls30 explicitly in apt-get install is what forces apt to upgrade it past the base image's older pre-installed version — transitive deps from wget/curl/git would otherwise leave it in place because their version constraints are already satisfied. Dropping the version specifier so each rebuild picks up the current security build automatically, matching the PR's stated intent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(docker): drop libgnutls30 comment Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ruben Fiszel <ruben@windmill.dev> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f0301b1605 |
feat(flows): preserve step/subflow worker tags under a custom-tagged flow (#9375)
* feat(flows): preserve step/subflow worker tags under a custom-tagged flow A flow running on a custom worker tag force-propagates that tag to every descendant step, script and nested sub-flow, overriding their own declared tags. This made it impossible to route a specific step or sub-flow to a different worker group. The new opt-in FlowValue.preserve_step_tags lets a step that declares its own non-empty tag run on it; untagged steps still inherit the flow tag. Defaults off to preserve existing behavior. * chore: regenerate system prompts for preserve_step_tags Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(flows): nest preserve_step_tags toggle under flow worker tag setting The toggle only affects routing when the flow has a custom worker tag, so show it as a sub-setting of the Worker Group tag picker, visible only once a tag is set, instead of as a standalone option. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(flows): allow step worker tag picker when preserve_step_tags is enabled When a flow defines a worker tag, the per-step tag picker was replaced by a read-only "Flow's WG" label. With preserve_step_tags enabled the step's own tag is honored, so the picker must remain editable in that case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(flows): propagate preserve_step_tags to branch and loop bodies payload_from_modules built the synthetic RawFlow for branch/loop bodies with a default FlowValue, dropping preserve_step_tags. Tagged steps inside a branch or loop therefore still inherited the parent flow tag even with the flag enabled. Thread the flag through to the synthetic FlowValue so the behavior is consistent for nested containers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(flows): clear preserve_step_tags when flow worker tag is removed Avoids the flag lingering as invisible state after the flow tag (and its toggle) are removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(flows): repair preserve_step_tags propagation to branch/loop bodies The previous commit added flow.preserve_step_tags at the payload_from_modules call sites but the parameter and FlowValue field were not actually threaded through (a failed edit left the function unchanged), so the crate did not compile. This completes the change: payload_from_modules takes preserve_step_tags and sets it on the synthetic FlowValue for branch/loop bodies. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(flows): complete preserve_step_tags propagation to branch/loop bodies Previous two commits left windmill-worker uncompilable: payload_from_modules received flow.preserve_step_tags at its call sites but the parameter and the synthetic FlowValue field were not actually added. This adds the parameter, sets preserve_step_tags on the synthetic FlowValue, and threads flow.preserve_step_tags through all five call sites. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(flows): clear preserve_step_tags whenever the flow worker tag is removed The flag was only reset when the Worker Group toggle was switched off, not when the tag was cleared directly in the picker (or via the YAML editor), leaving preserve_step_tags=true as invisible state with the advanced badge still reporting it active. Move the cleanup into the reactive block that already tracks the flow tag so every clear path is covered. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2ddf93de96 | fix(auth): enforce monotonic privilege on user token lifecycle endpoints (#9371) | ||
|
|
def01b8ff6 |
fix(frontend): sanitize user markdown to prevent stored XSS (#9386)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fddbe4a51c |
docs(skills): fix //native marker + import rules for bunnative, remove legacy nativets skill (#9382)
* docs(skills): document mandatory //native marker for bunnative and nativets * docs(skills): clarify windmill-client is the only allowed library in native mode * docs(skills): remove legacy nativets skill in favor of bunnative Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(skills): fix bunnative import rule - any bundleable lib, not just windmill-client Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ruben Fiszel <ruben@windmill.dev> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2c0c2c467f |
fix(apps): make public apps opt into cross-origin isolation via wm_coep (GIT-884) (#9374)
* fix(apps): make public apps opt into cross-origin isolation via wm_coep
Public app pages served at /public/* and custom paths /a/* were not
getting the COEP/COOP/CORP headers, so they were blocked when embedded
as an iframe inside a cross-origin-isolated page (e.g. another raw app,
which sets Cross-Origin-Embedder-Policy: require-corp). A nested
document loaded into a require-corp context must itself set COEP for
the iframe to load.
Rather than applying the isolation headers to all public pages (which
would also force COEP on classic apps and break subresources without
CORP, e.g. external image URLs or embeds), public apps now opt in via
a `wm_coep` query param on the embed URL:
<iframe src="https://<domain>/public/<ws>/<secret>?wm_coep=on">
The app publish drawer gains a URL/Embed toggle: "URL" shows the plain
shareable link (param-free), "Embed" shows a ready-to-copy iframe
snippet with wm_coep baked in, so the flag is discoverable exactly when
embedding and absent otherwise.
`wm_coep` is consumed internally and stripped from the app `query`
context so it doesn't collide with app-defined params. Only params we
own are stripped (an explicit set), not the whole `wm_` prefix.
Fixes GIT-884
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* nit
* nit
* fix(apps): only bake wm_coep into embed snippet for raw apps
AppEditorHeaderDeploy is shared by the classic (AppEditorHeader) and raw
(RawAppEditorHeader) deploy drawers. The embed snippet unconditionally
appended ?wm_coep=on, which for a classic/low-code app forces COEP
require-corp on the document and breaks no-CORP cross-origin subresources
(external <img> in AppImage/AppStatCard/AppNavbar, {@html} embeds in
AppHtml, CDN import() in AppCustomComponent) — the exact regression the
opt-in design avoids.
Add a `rawApp` prop (default false); the raw header passes rawApp. The
flag is appended only for raw apps; classic apps get a plain iframe
snippet, and the wm_coep helper text is shown only for raw apps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
f300a716a9 | test: add global chat resource variable schedule evals (#9379) | ||
|
|
b0c3b01d31 |
fix(cli): preserve user drafts on sync push and permissioned-as (#9381)
CLI deploys (sync push, set-permissioned-as) went through the same create/update endpoints as a UI "deploy from draft", which delete the draft at that path. That silently wiped teammates' in-progress drafts on every push. Add a transient skip_draft_deletion deploy flag (mirroring deployment_message) that the CLI sets; the backend then skips the DELETE FROM draft for scripts, flows, and apps. UI deploys are unchanged. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4b06881918 |
fix(ai): validate token_url for SSRF in OAuth credentials flow (#9385)
get_token_using_oauth resolved the AI OAuth resource's token_url and POSTed to it without any SSRF validation, while base_url is validated in get_base_url. A workspace member with resources:write could point token_url at an internal/metadata address (e.g. 169.254.169.254), turning the server into an authenticated blind SSRF probe. Validate the resolved token_url with validate_url_for_ssrf before the request, gated behind the same ALLOW_PRIVATE_AI_BASE_URLS opt-in as base_url so private AI deployments keep working consistently for both URL fields. ALLOW_PRIVATE_AI_BASE_URLS is now pub so windmill-api can reuse it instead of re-parsing the env var. |
||
|
|
3345837574 | chore: add gpt-5.5 eval model (#9377) | ||
|
|
04a08976ae |
fix: batch encryption-key rotation into one git-sync job (#9355)
* fix: trigger git sync for re-encrypted secrets on encryption key change When changing a workspace encryption key, the secret variables get re-encrypted with the new key, but the git sync was only dispatched for the encryption_key.yaml metadata file. Repos with Secrets sync enabled were left with stale ciphertexts until the next per-variable deployment. Now, after the transaction commits, we also dispatch a Variable git sync event for each re-encrypted secret so the new encrypted values are pushed to the configured repos. Errors are logged but don't roll back the key rotation. Fixes WIN-1994 Fixes #9344 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: batch encryption-key rotation into one git-sync job Workspace encryption key rotation now re-encrypts every secret variable and then dispatches a single batched git-sync job carrying the Key event plus one Variable item per re-encrypted secret. Repos with Secrets sync enabled receive every new ciphertext in one commit instead of nothing (previously only `encryption_key.yaml` was pushed) — and instead of N separate jobs the debouncer might or might not merge. Wires through the new `handle_deployment_metadata_batch` entry point added in the companion EE PR; OSS has a no-op shim so the build stays green. Adds an integration test (`workspace_encryption_key_git_sync`) asserting that rotating the key with 3 secret variables in scope produces exactly one deployment-callback job whose `items` array contains the Key event + all 3 variable entries and `skip_secret=false`. Fixes WIN-1994 Fixes #9344 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref for git-sync helper simplification Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: cover non-debouncing git-sync fallback on key rotation Adds a regression test exercising a workspace whose sync script predates hub version 28103: the rotation must still queue a legacy-format deployment-callback job per item (encryption_key + each re-encrypted secret) instead of silently skipping the repo. Bumps ee-repo-ref to the EE fallback fix. Addresses the P1 raised in the PR review (Codex/Pi/Claude): batch path dropped git sync entirely for repos without sync-job debouncing support. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: add sqlx offline cache for encryption-key git-sync test queries The cargo_test CI job builds with SQLX_OFFLINE=true; the two new sqlx::query!/query_as! calls in windmill-api-integration-tests/tests/workspace_encryption_key_git_sync.rs had no cached entries, failing the build with E0282. Regenerated and added only the two new query caches (no EE/feature cache loss). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: bump ee-repo-ref to updated EE companion PR (08e3b9b) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
96a8eb63d4 |
disable redirect following on AI proxy client to close SSRF (#9370)
* fix(ai): disable redirect following on AI proxy client to close SSRF The AI proxy validates the configured base_url against SSRF rules but the shared HTTP client followed up to 10 redirects without revalidating the hops, so a public base_url could 3xx the server into a private/internal address (e.g. the Docker socket or cloud metadata). Disable redirect following so the validated host is the only one the server connects to. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(ai): remove heavy redirect SSRF integration test Drop the integration-test-level regression for redirect following; it spins up a full API server + DB for a one-line client-config change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
bb90f4ce83 |
fix(api): authorize and harden log-file reading endpoints (#9368)
* fix(api): don't follow symlinks when reading service log files Defense in depth on top of the existing `..` path-traversal check in the get_log_file handler: reject the request if the final path component is a symlink, so a planted symlink in the logs directory cannot be used to read arbitrary files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(api): authorize and harden the jobs_u get_log_file endpoint The unauthenticated jobs_u get_log_file endpoint served any job's log file to anyone who knew the job UUID, with no authorization. Gate it the same way as get_job_logs: look up the job (the log directory name is the job id) filtered by workspace and the caller's scope tags, and only allow non-logged-in callers to read logs of jobs created by the anonymous user. Also add defense in depth: refuse to read through a symlink so a planted symlink in the logs directory cannot be used to exfiltrate arbitrary files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
12e06bfa6b |
bump default sync script to hub/28238 (windmill-cli@1.712.0) for fork branch push (#9372)
The fork-branch deployment callback runs the hub script sync-script-to-git-repo-windmill, which imports `windmill-cli` as a pinned npm dependency and runs it in-process (it does NOT shell out to a PATH wmill). hub/28236 pinned windmill-cli@1.706.1, whose `git-deploy --only-create-branch` path returns early without pushing — so the fork branch was checked out locally but never published to the remote. #9366 fixed the CLI and shipped it as windmill-cli@1.712.0, but without a hub-script bump the running callback still used 1.706.1. Bump LATEST_GIT_SYNC_SCRIPT_PATH to hub/28238, which is identical to 28236 except it pins windmill-cli@1.712.0 (content + lockfile). This fixes test_workspace_fork_creates_branch and production fork-branch creation. Also add backend/windmill-common/src/workspaces.rs to the git-sync-test path-gate so future script-path bumps trigger the e2e (the bump alone is not otherwise covered by the gate). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2bf11dcb15 |
feat(oauth): support per-provider sandbox URLs (#9358)
* feat(oauth): support per-provider sandbox URLs in registry + instance settings * fix(oauth): polish sandbox review nits (cc lookup, header label, ee ref) * refactor(oauth): drop dead build_oauth_clients duplicate in windmill-oauth * refactor(oauth): derive sandbox-capable provider list from registry * chore(docker): copy oauth_connect.json into frontend build stage * test(oauth): cover sandbox helpers (as_sandbox, canonical_name, resolve) * chore: update ee-repo-ref to 9297d8f790346e6a6ad540c7bca1a67f91ec11a2 This commit updates the EE repository reference after PR #595 was merged in windmill-ee-private. Previous ee-repo-ref: 3ab3eca9ac15ebab6db991e7964bc5e48ce21f42 New ee-repo-ref: 9297d8f790346e6a6ad540c7bca1a67f91ec11a2 Automated by sync-ee-ref workflow. --------- Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
889101b7f0 |
chore(main): release 1.712.0 (#9340)
* chore(main): release 1.712.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.712.0 |
||
|
|
2553fbfe31 | feat: add deepseek fim support (#9365) | ||
|
|
9a659b636d | fix(frontend): prevent duplicate asset node ids crashing flow graph (#9367) | ||
|
|
2fdc51e629 |
fix(git-sync): publish fork branch on only_create_branch from the CLI (#9366)
* [ee] fix(git-sync): publish fork branch on only_create_branch from the CLI Fixes WIN-1997. Forking a git-sync-configured workspace must push a `wm-fork/<branch>/<id>` branch to the repo, but the integration test `test_workspace_fork_creates_branch` failed: the fork callback job succeeded yet no branch appeared. Root cause: the fork-branch callback runs the sync script with `only_create_branch: true` and no items. The hub sync script delegates branch checkout to `wmill sync git-deploy --only-create-branch` and runs its own in-process commit+push ONLY for the `!only_create_branch` path (`if (!only_create_branch) git_push(...)`). #9284 had moved commit+push out of the CLI to the caller for the GPG-cache-warmth invariant (WIN-1974) — but it also dropped the CLI's push for the branch-only case. A branch-only publish has no commit, so no signing is involved and the GPG concern does not apply; with neither the CLI nor the hub script pushing, the empty fork branch was never published. Restore the CLI push for the `only_create_branch` path (a bare `git push --porcelain` of the checked-out branch ref). Adds a deterministic CLI regression test that runs `git-deploy --only-create-branch` for a fork workspace and asserts the branch reaches the remote with no caller-side push. EE companion: format the fork-branch commit message with Display instead of Debug (no more `Some("...")` leak). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to a30079e75dc5b7d7413aa8ee20e40e80bfea9cbd This commit updates the EE repository reference after PR #597 was merged in windmill-ee-private. Previous ee-repo-ref: 8b02336fcebdfae4b9d2795cbb74fa7046530bcb New ee-repo-ref: a30079e75dc5b7d7413aa8ee20e40e80bfea9cbd Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> |
||
|
|
a7d85a39ff |
refactor: clean up ai provider proxy logic (#9360)
* refactor: clean up ai provider proxy logic * docs: remove completed ai refactor plan * fix: audit failed google global proxy calls |
||
|
|
045d12043e |
feat(queue): duration-weighted fairness admission (#9334)
* [ee] feat(queue): duration-weighted fairness admission atomic Add the `WORKSPACE_FAIRNESS_ADMISSION_PPM` atomic that the EE `workspace_fairness_ee::refresh_overloaded` writes on each refresh (see companion EE PR). The atomic is read on every pull by `should_admit_capped` to decide whether the dispatch goes down the standard or fairness path. Defaults to 10_000 (= admit all) so the pre-fairness behaviour is preserved until the first refresh fires. OSS stub in `workspace_fairness.rs` continues to return `true` unconditionally, so non-EE builds are bit-identical. * docs(queue): consolidate full fairness algorithm into workspace_fairness.rs Move the algorithm doc — what "overloaded" means in worker-seconds, the duration-weighted admission derivation, coordinated refresh structure, audit emission, the SQL perf constraints (no params CTE, drive running side from v2_job_runtime), and EE gating — into the OSS surface module where it is readable without EE access. The EE file becomes implementation only. Also bump ee-repo-ref to the EE commit that strips the duplicate doc. * docs(queue): clarify ADMISSION_PPM default is "admit all", not count-based Addresses CI review (claude[bot]): the `10_000` initial value is the "admit all" no-op default that applies before the first refresh classifies an overloaded set — not the count-based value (which would be `target * 10_000`). The count-based form is the empty-bucket fallback inside `compute_admission_ppm`, a different thing. * chore(queue): point ee-repo-ref at EE main (fairness admission merged via #593) * fix(queue): duration-weighted admission uses unclamped service-time window Bumps ee-repo-ref to the EE fix (windmill-ee-private#596) that sources D_c/D_u for the admission probability from a separate 60s service-time window of true `duration_ms`, instead of the occupancy aggregation whose per-job contributions are clamped to the 10s occupancy window. The clamp truncated D_c for capped jobs longer than the window, under-admitting the duration skew (true 34s jobs → ~86% effective share instead of the target 65%). Occupancy worker-seconds still drive overload classification. Updates the algorithm doc in workspace_fairness.rs accordingly. Note: ee-repo-ref points at the EE feature branch; re-point to EE main once #596 merges. |
||
|
|
aea00611c4 |
fix(frontend): prevent MultiSelect crash on undefined value (#9364)
MultiSelect read `value.length` directly while `value` is a bindable prop with no default, so a parent passing `undefined` (e.g. an enum-array approval form field with no initial value via ArgInput) threw a TypeError that blanked the entire approval page. Guard all reads behind a `value ?? []` derived. Fixes WIN-1996 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9e7eaf3684 | feat: inject active editor into global chat (#9361) | ||
|
|
a9e5140995 |
feat: warn when custom instance db is shared across workspaces (#9359)
* feat: warn when custom instance db is shared across workspaces * Fix leaking workspace names * sqlx prepare |
||
|
|
c2b5ba8871 |
fix(cli): stop re-prompting on wmill refresh prompts (#9357)
referencesIncludeLine required the include token to be the entire trimmed line. The wmill-default CLAUDE.md template is `Instructions are in @AGENTS.md` — include mid-sentence — so the migration prompt fired every run on files wmill itself wrote. Accept the include as a whitespace-separated token on any non-comment line. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4efc37212a |
fix: infer script arg schema when deploying via AI chat (#9356)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
da882c54b2 |
fix(frontend): close other sidebar menus when hovering Help (#9354)
The Help menu lived in a separate Menubar from the Settings/Workers/ Folders/Logs group, so melt-ui's hover-to-switch logic (which only spans menus within the same Menubar) did not close the Help popup when the cursor moved to a sibling group, causing menus to stack. Merge both bottom Menubars into a single Menubar, wrapping each group in its own flex container to preserve the visual spacing. Fixes WIN-1993 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
dec58e6c4f |
feat: deploy raw apps from global chat (#9349)
* feat: deploy raw apps from global chat * fix: require raw app bundle protocol * chore: bump ui builder artifact * docs: explain app custom path deploy handling |
||
|
|
f947b1dfdf | fix (frontend): schedule "View runs" url (#9350) | ||
|
|
e29dfbaa87 |
test: add global chat eval coverage (#9320)
* test: improve global chat eval parity * test: add human-style global chat evals |
||
|
|
88056f8d4c |
fix(cli): redact encryption_key diff in stdout by default (#9347)
* fix(cli): redact encryption_key diff in stdout by default Sync diff output previously printed the full encryption_key contents on stdout whenever the workspace key changed locally or on the remote, which made it easy to leak the key via shell history, CI logs, etc. Now the diff is replaced with a redacted notice for any encryption_key change in both prettyChanges and showConflict. Pass --show-encryption-key-diff (also configurable via wmill.yaml's showEncryptionKeyDiff) to opt back into the full diff. Fixes WIN-1992 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli): redact encryption_key diff with fixed-length mask Drop the --show-encryption-key-diff opt-in and always redact: the diff now keeps the first 5 chars of the key so rotations are still visible (different prefixes), then replaces every remaining char with `*` so the length of the key is preserved without leaking it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ae2222febf | prevent path component from wrapping (#9345) | ||
|
|
9752f0f909 | docs: remove stale planning notes (#9342) | ||
|
|
59ab038d77 |
fix(monitor): cleanup stale server_heartbeat background_task_state rows (#9338)
`announce_server_started` writes a `server_heartbeat:{INSTANCE_NAME}` row
on each startup. INSTANCE_NAME is a fresh random string per process, so
the row is never updated again and a new row is inserted on every
restart, growing background_task_state unboundedly.
Add an hourly monitor task that deletes server_heartbeat:* rows older
than 7 days. Older rows cannot influence check_any_server_started (which
only considers heartbeats refreshed after the restart was initiated), so
they are safe to prune.
Fixes WIN-1990.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
8d72a7a4a4 |
chore(main): release 1.711.0 (#9337)
* chore(main): release 1.711.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.711.0 |
||
|
|
6f770346fb | fix(cli): handle __flow suffix when deriving the flow's Windmill path (#9333) | ||
|
|
42d2121af9 |
fix(queue): duration-weighted workspace fairness signal (#9329)
* fix(queue): bump EE ref to include worker_ping fairness signal The current ee-repo-ref.txt pointed to 31cda7c (an unrelated merge commit on the asset-graph-view-ee branch) instead of ddc9e80, which contains the workspace-fairness fix that switches the active-share signal from v2_job_queue.running=true to worker_ping. As a result cloud was still computing overload off the legacy signal, so a workspace with many in-flight/suspended flows (lancom01-prod, with 799 suspended flows × 3 v2_job_queue bookkeeping rows each = 2397 running-true rows) was flagged as 95% of cluster activity despite consuming zero worker slots. Bumping to ddc9e80 picks up the worker_ping-based signal, which naturally excludes (a) suspended jobs (no worker pinging them), (b) zombie running-rows from dead workers, and (c) flow/flownode orchestration rows that never run on a worker in the first place. * test(queue): seed v2_job rows + realistic durations for fairness helpers The new duration-weighted fairness algorithm joins v2_job_queue and v2_job_completed to v2_job for the `kind` filter (excluding flow bookkeeping) and reads `duration_ms` for the completed contribution. Update the test helpers to mirror that schema: * `insert_completed` now inserts a matching v2_job row (kind=script) and writes `duration_ms = 1000` with a 1-second [started_at, completed_at] interval, so each completed row contributes ~1 worker-second when fully inside the refresh window. * `insert_queued` likewise pre-inserts v2_job, sets `started_at` to NOW() - 1s when running=true (so running rows contribute ~1 worker-second by the time the refresh runs), and seeds v2_job_runtime.ping so the running side accrues real-time worker seconds (the algorithm bounds end-of-interval by ping). The zombie/suspended insert helpers are intentionally left without v2_job rows — the new algorithm's INNER JOIN excludes them, so they still correctly contribute zero worker-seconds. * chore(queue): bump EE ref to duration-weighted fairness algorithm Companion to windmill-ee-private#<TBD>: switch the EE workspace fairness aggregation from a count-based UNION (worker_ping snapshot + v2_job_completed count) to a worker-seconds aggregation sourced directly from v2_job_queue and v2_job_completed, with kind/suspend filters mirroring handle_zombie_jobs and per-row defenses against zombie inflation on both halves. * chore(queue): bump EE ref for fairness perf fix (inline window_start) * chore(queue): bump EE ref for fairness perf rewrite (driver-side flip) * update ee ref |
||
|
|
979b086b08 | refactor(cli): fold flow test-step into flow preview --step (#9330) | ||
|
|
36f574ff95 |
feat(cli): add object-storage commands and flow test-step (#9326)
* feat(cli): add object-storage commands and flow test-step * docs(cli): clarify flow test-step doesn't recurse into aiagent tools * fix(cli): correct failure step id in docs, handle bare flow.yaml path |
||
|
|
a6c51b146d |
ignore flaky fairness regression tests in CI (#9328)
`fairness_ignores_zombie_running_rows` and `fairness_ignores_concurrency_suspended_rows` panic intermittently in CI (both Linux and Windows runs). Mark them `#[ignore]` until the underlying flakiness is resolved. |
||
|
|
85a128cc34 | prevent windows backend tests from running out of disk space (#9325) |