Commit Graph

13822 Commits

Author SHA1 Message Date
Guilhem Lemouel 6512bafad6 feat(raw-apps): clear empty-render hint on renderAppeared retraction
Handle the preview's `renderAppeared` message (posted when a slow app
mounts #root after the empty-render grace window) by clearing the
"Nothing rendered" hint, so a legitimate late render doesn't leave a
false-positive overlay.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 14:09:23 +02:00
Guilhem Lemouel 3edd3e65ec feat(raw-apps): show hint when a build renders nothing into #root
Mirror the runtime-error overlay for the silent blank-screen case: when the
preview reports `emptyRender` (build ran cleanly but never mounted #root),
show an info Alert pointing at the missing createRoot(...).render(...) call.

The handler is dormant-safe until the builder tarball that emits `emptyRender`
is pinned, so it can land ahead of the artifact bump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 13:18:04 +02:00
Guilhem Lemouel aef4db4117 docs: clarify raw-app index.tsx must mount App to avoid blank screen
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 13:18:04 +02:00
Guilhem 286da005ef feat: AI chat background jobs tray with detach, approval and preview (#9982)
* feat(ai-chat): background jobs tray with detach, approval and preview

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-chat): route exec_datatable_sql through the jobs tray

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-chat): jobs tray — orange queued badge, 5-recent pagination, drop remove button

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): silence dev-only false-positive binding warnings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): silence dev-only false-positive binding warning in FlowEditorView

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-chat): auto-expand jobs tray on approval, close modal on resume

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-chat): let the AI set a per-call inline wait before jobs detach

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-chat): auto-resume the chat when a background job finishes while idle

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(ai-chat): merge jobs tray and edits bar into a segmented session bar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): address review — canceled-job handling, cross-chat poll guard, tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): gray chip dot for canceled-only jobs instead of green

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): keep jobs segment right-aligned when there are no edits

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): address /review — drain snapshot, live region, a11y, leading-ellipsis, remove dev harness

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): announce all same-tick job completions; drop redundant aria-live

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): guard poller re-entrancy; datatable error fallback (auto-review P2/nit)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): honor tool formatter on detached job completion; coalesce poller

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): persist tool result formatter so rehydrated detached jobs keep contract

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 10:11:29 +02:00
Ruben Fiszel d467161117 fix: bump bundled Go CLIs to patched versions to clear image CVEs (#9996)
The runtime image bundles several Go CLIs whose pinned versions were built
with an outdated Go toolchain (go1.21.7 for kubectl/helm), which image
scanners flag for fixable Go stdlib CVEs. Bump each to the latest release
built on the current patched Go 1.26.4:

- kubectl 1.28.7 (EOL) -> 1.36.2 (latest stable)
- helm 3.14.3 -> 3.21.2 (latest v3; staying on v3 to avoid the Helm 4
  breaking changes for a bundled CLI users depend on)
- crane v0.20.6 -> v0.21.7

crane is also updated in DockerfileSlim/DockerfileSlimEe (the slim images
don't bundle kubectl/helm). The docker client comes from the floating
docker:29-dind tag, which already rebuilds to a current Go toolchain.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 00:25:41 +02:00
Ruben Fiszel 1ed979a2de refresh picomatch in lockfile to unbreak npm ci (#9994)
The ai-evals CI job's `npm ci` (frontend) failed with:

    npm error `npm ci` can only install packages when your package.json
    and package-lock.json are in sync.
    Missing: picomatch@4.0.5 from lock file

`picomatch` is a floating transitive: svelte-check pulls it as an
`optional peer` at `^4.0.4`, and vite/vitest/tinyglobby at `^4.0.x`. The
lock pinned 4.0.3/4.0.4, but 4.0.5 was published upstream. On a cold-cache
CI runner npm re-resolves those ranges against the registry and picks the
latest (4.0.5), which isn't in the lock — so `npm ci`'s sync check fails.
It passes locally only because a warm npm cache still serves 4.0.4.

Fix: `npm update picomatch --package-lock-only` (npm 10.9.8, matching CI's
node 22) to refresh every picomatch node to 4.0.5 (and the 2.x line to
2.3.2). Lockfile-only, all semver-patch; no package.json change. Verified
`npm ci --dry-run` is back in sync with a cold cache.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 22:12:35 +02:00
Ruben Fiszel 223e1569ce chore(main): release 1.752.0 (#9974)
* chore(main): release 1.752.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.752.0
2026-07-07 22:10:10 +02:00
Ruben Fiszel f7efb646bf fix(pipelines): live materialize/dataset editing — stale graph, phantom drafts, stale Save-all deploys (#9990)
* fix(pipelines): live materialize/dataset edits reflect on the graph; no phantom draft after deploy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(pipelines): Save all deploys the open pane's live buffer, not the stale draft snapshot

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(pipelines): pin deployedFromPane to the shipped content so mid-deploy keystrokes still promote to a draft

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(pipelines): draft rename ping-pong loop, stale rename deploys, inactive-draft input lineage

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(pipelines): dedupe inferred-lineage overlay against accumulated edges; first draft teardown still captures reads

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(pipelines): record an authoritative empty read capture on uncaptured draft entries

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(pipelines): teardown skip compares lineage too, so access-only overrides still persist

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(pipelines): compress persist-back guard comments to the invariant

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 22:00:39 +02:00
AlexRV12 a6276b5900 feat: smooth bursty AI chat streaming with a typewriter reveal (#9991)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 21:49:51 +02:00
Guilhem 6b01caaf26 fix(ai): flow writer builds approval steps as scripts, not identity (#9985)
* fix(ai): flow writer builds approval steps as scripts with getResumeUrls, not identity

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(ai-evals): accept rawscript or script for approval step type

The flow-writer prompt allows an approval step to be `type: rawscript`
or `type: script`, but the topLevelStepTypes check pinned an exact
`rawscript` match, so a valid `type: script` approval would fail
deterministically. Let the check accept a list of allowed types.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 21:47:14 +02:00
Guilhem 804178f5e1 fix(sessions): auto-rename regression + preview-panel and fork nits (#9993)
* perf(sessions): don't mount preview tabs when side panel is collapsed

* fix(sessions): cap metadata max_tokens so Anthropic auto-rename works

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): drop redundant -fork suffix from auto-generated fork names

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): default 'also delete forked workspace' to false

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(copilot): apply metadata max_tokens cap on the OpenAI Responses path

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 21:46:10 +02:00
Guilhem 63e3e7735f lign copy button on fork-less workspace rows (#9992)
The hover-revealed copy button in the workspace picker sat flush against
the menu's right edge on fork-less rows, because only forked rows render
an expand chevron that insets the copy button. Reserve the chevron's slot
on fork-less rows so copy buttons align across rows and keep right padding.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:25:33 +00:00
Guilhem 4bb82ad6cd feat: open runs/schedules pages from AI chat in session preview tabs (#9976)
* feat(copilot): open runs/schedules pages in session preview tabs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(copilot): drop buggy in-place nav, always chip outside a session

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(copilot): open_page covers variables/resources/assets/audit-logs/settings, perm-gated

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(copilot): open_page adds folders, groups and all trigger kinds (EE-gated)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(copilot): close_page tool to close session preview tabs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(copilot): fail-closed on unavailable trigger_kind in open_page handler

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(copilot): gate open_page on operator_settings, keep open_preview mention preview-only

Gate the open_page page set on the workspace operator_settings for operators
(mirrors OperatorMenu) instead of hardcoding runs/assets, with an empty-enum
guard. Also move the open_preview cross-reference out of the always-on prompt
line into the preview-gated block so it isn't advertised when preview tools
are off.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(copilot): gate open_page on the session's operating workspace

A session chat targets its own (possibly forked) workspace while $workspaceStore
stays on the navigation workspace, so operator_settings must be read for the
operating workspace, not the global store. Thread it through GlobalToolHelpers
so both setSchema (advertised enum) and the handler guard gate on the same
workspace; the global side-panel chat still follows the live store.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:21:34 +00:00
Guilhem 7046dc6dfb fix(sessions): scope fork session Edits to session-edited items only (#9989)
* fix(sessions): scope fork session Edits to session-edited items only

A session chat with an undefined modified-items mask fell back to showing every draft in its (possibly forked) workspace, so the Edits bar/diff drawer listed all fork drafts instead of just what the session edited. Always track session chats: seed an empty mask for legacy chats in loadPastChat and guard the not-yet-persisted-chat case in initRuntime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: clarify session chats always persist their modified-items mask

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:21:11 +00:00
Ruben Fiszel 88c2d0e8e3 feat(cli): clarify fork-branch workspace auto-targeting in output (#9988)
* feat(cli): clarify fork-branch workspace auto-targeting in output

* fix(cli): auth comes from saved profile, not wmill.yaml, in fork notes

* fix(cli): consolidate workspace resolution logs, fork-target last-used profile

* chore: regenerate system prompts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(cli): fork-target interactively created profiles, dedupe workspace line

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:08:09 +00:00
Ruben Fiszel edfe7b415a fix(cli): auto-derive cascade triggers in --local pipeline graph (#9978)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:22:53 +00:00
Ruben Fiszel 7efeae26d8 feat: add fork_parent_workspace claim to OIDC tokens for fork workspaces (#9987)
* feat: add parent_workspace claim to OIDC job tokens for fork workspaces

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: rename claim to fork_parent_workspace for clarity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: update ee-repo-ref to e2df172596e00877068d4b0a98afaef62fe429d1

This commit updates the EE repository reference after PR #651 was merged in windmill-ee-private.

Previous ee-repo-ref: f73001ac6c038694cfc2604233a59be1c0daa40b

New ee-repo-ref: e2df172596e00877068d4b0a98afaef62fe429d1

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-07 16:21:32 +00:00
Ruben Fiszel ffb80d1ae3 regenerate prompts 2026-07-07 15:56:52 +00:00
hugocasa 87f8d46aaf fix(ai-agent): align agent_actions_success with agent_actions for mcp and websearch (#9983) 2026-07-07 16:19:37 +02:00
Guilhem 95031903eb feat(sessions): v2 unified sidebar with family/fork scoping and preview router (#9816)
* feat(sessions): prototype session-mode layout wrapper (design exploration)

Do not merge — design exploration of an optional full-page 'session mode' layout for AI sessions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): add full-screen toggle for the session panel

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): workspace-tree rail with browse mode and collapsible sidebar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): restore sessions page with iframe preview of current view

Roll back the session-mode layout wrapper: sessions is a dedicated /sessions
page again rather than a layout toggled over the live app. Opening a session
from a Windmill page captures that page as the session's preview target; the
page shows the chat beside a preview panel that iframes the target, with a
breadcrumb and full-screen toggle.

- Remove SessionShell wrapper and the sticky sessionLayout flag; +layout.svelte
  always renders the normal global sidebar. Sidebar components introduced
  alongside the wrapper are kept for the upcoming sidebar rework.
- sessionMode.svelte.ts: per-session preview-URL map (captureSessionView /
  sessionPreviewUrl) + withMenuHidden to drop the previewed page's own sidebar
  via the nomenubar flag.
- Drop the #content sidebar gutter (pl-12/pl-40) when the menu is hidden, so
  the nomenubar preview fills the panel edge-to-edge.
- SessionPicker: activate() navigates to /sessions; createAndOpen() seeds the
  new session's preview from the current page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): add exit (X) button to chat header

Add a close button at the top-right of the session chat header that leaves the
sessions page and navigates to the session's target (the previewed page), so
exiting lands on exactly what was being previewed, full-screen with the sidebar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sidebar): promote workspace picker and widen the sidebar

Replace the Windmill logo header with the workspace picker so the active
workspace is the sidebar's anchor: show the workspace name (not the id) in a
stronger weight, with a down-chevron and a bottom-aligned dropdown. Add the
same dropdown chevron to every other sidebar menu trigger (Favorites, User,
Settings, secondary/Help groups) via an opt-in MenuButton option, and widen
the expanded sidebar from w-40 to w-48 (content offset kept in sync).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): collapsible preview panel + sidebar session entry polish

Add a collapse control to the sessions preview panel (top-left, matching the
legacy editor's PanelRightClose), animated with an x-axis slide. The panes
carry no explicit size so Splitpanes auto-distributes — the chat fills the
width when the preview collapses and splits evenly when both are shown. When
collapsed, a floating "Open side panel" Button (top-right) brings it back.

Also gather the AI sessions section into the Favorites/Search container via a
new embedded mode on SessionPicker, replace the small "+" with a full sidebar
"New AI session" entry, and drop the chat header's exit (X) button.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): split family/fork picking with a global breadcrumb

Separate workspace-family selection from fork selection. The sidebar
workspace picker now lists families (roots) only and shows the active
family name even inside a fork. A persistent `family · fork` breadcrumb
lives in the global logged layout (WorkspaceBreadcrumb, rendered via a
new AiChatLayout topBar snippet): the fork segment opens the fork picker
popover, staging a pending fork on a draft session (the old in-chat
SessionWorkspaceBar semantics) or switching workspace directly elsewhere.
WorkspaceFamilyPicker gains onRequestCreateFork to route create-fork to
the global fork modal in non-session contexts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* revert(sessions): drop the global fork breadcrumb top bar

Remove WorkspaceBreadcrumb and its AiChatLayout topBar wiring; restore
the in-chat SessionWorkspaceBar for draft fork-picking and the original
WorkspaceFamilyPicker. The sidebar workspace picker stays family-only
(roots, no forks listed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): family/fork-scoped sidebar with scope header

Restructure the sidebar into a family-scoped region (workspace family
header → New AI session → session list) and a workspace-scoped region
(a Fork scope header → Favorites + Search → workspace items), split by a
full-width divider. The new WorkspaceScopeHeader is a full-width
root/fork picker: accent-styled on a fork (text + faded border), with a
bottom "<workspace> settings" link; picking a different fork from a
session navigates home. The family header keeps the root's color when
inside a fork, and drops "Fork current workspace" / "Workspace settings"
(now surfaced via the scope header and the bottom Settings dropdown).
The session preview header shows "family · fork <page path>".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): drop colon from "Workspace root" scope label

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): turn the preview breadcrumb into a page router

Every breadcrumb segment now opens a drill picker that lists workspace pages
(Home, Runs, Workspace settings, …) alongside scripts/flows/apps. Picking
either steers the preview iframe without leaving the sessions page. The
non-item case resolves to the page's real name (e.g. "Workspace settings").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): tabbed preview with mounted tabs + Home quick-access

The preview is now a tiny tabbed browser: the first tab is pinned to the
session's view, "+" opens the router picker to add more, and every tab stays
mounted (stacked + visibility-toggled) so switching preserves each page's
state. Per tab, the commanded `url` is decoupled from the observed `loc` so
in-iframe navigation never reloads the frame. Home is also pulled up as the
first quick-access item in the router picker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): persist preview tabs with the session in IndexedDB

Save the open preview tabs (+ active tab) onto the session record so reopening
a session restores its tabs. Write-behind is debounced since a tab's observed
location churns as the user browses; transient (unsent) sessions skip it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* perf(sessions): lazy-mount preview tab iframes

Only boot a tab's iframe the first time it's activated, then keep it mounted.
Restoring a session with N saved tabs now boots just the active tab instead of
N full Windmill apps at once.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): fold the breadcrumb picker into the preview tabs

Drop the separate family·fork/path breadcrumb bar. The active tab now doubles
as its own router picker (click it to re-point the tab); inactive tabs switch
on click. Removes the now-unused PreviewRouterSegment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(raw-apps): auto-compact the editor when it opens narrow

On the first measured layout, if the editor container is under 800px, drop to
the merged single-pane view and retract the file sidebar (e.g. when shown in
the narrow session preview pane). Applied once on open; the sidebar is set
without persisting so it never overrides the user's saved preference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): auto-refresh preview tabs after mutating chat tools

Add a tool-completion hook in the shared chat dispatcher; the sessions page
subscribes and debounced-reloads every mounted preview tab when a write/deploy/
delete tool finishes (matched by verb prefix, so read/test/navigate tools skip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): keep nav bar hidden across in-preview navigation

The sessions preview iframes load pages with `nomenubar=true`, but the
layout recomputed `menuHidden` from the current URL on every navigation,
so a client-side nav inside the preview (an in-page link or redirect)
dropped the flag and the global nav popped back in. Make the hidden state
sticky for the document's lifetime when running inside an iframe; the top
window is unaffected so the oauth-callback toggle still works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): persist hidden nav across full reloads in preview iframe

The in-memory sticky flag was lost on a full document load inside the
preview (a navigation that drops the `nomenubar` query param), so the
global nav — including the mobile burger — reappeared. Store the sticky
state in sessionStorage so it survives full reloads within the iframe's
browsing context. The top window is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): refuse to mount sessions UI inside a preview iframe

A preview tab navigating back to /sessions would mount another sessions
page with its own preview iframes, nesting endlessly. When the page
detects it is running inside an iframe, render a stub that breaks out to
the top-level window instead of mounting the full UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): Workspace ⇄ AI Sessions mode switch + workspace-decoupled session chat

Add a route-derived mode switch that flips the sidebar rail between the classic
workspace navigation and a dedicated AI-sessions sidebar, cleanly separating
sessions from the workspace nav.

- SessionModeSwitch (Workspace | AI Sessions) in the rail; session mode is
  exactly "on /sessions", so the switch just navigates in/out (sessionSwitch).
- Session chats target their own (possibly forked) workspace via
  AIChatManager.operatingWorkspace/workspaceResolver without mutating the global
  workspaceStore; "Acting on" header strip shown once a session has started.
- Flow editor AI button becomes "Open in AI session": saves the draft, then
  opens a new session targeting the current flow.
- New sessions: no default preview (empty state instead of iframing home, panel
  collapsed); preview-panel collapse persisted per-session on the record.
- Persist nav-rail collapse (manual toggle only) and drop the editor-route
  auto-collapse that fought it.
- Smaller fork picker; add a `preview` proxy so `vite preview` reaches the
  backend for production-build demos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ai-chat): replay assistant turns verbatim so thinking blocks validate

The global AI chat reconstructs each assistant turn from an OpenAI-shaped
message, keeping only the thinking/redacted_thinking blocks and re-injecting
them at the front of the content array. When a turn interleaves thinking with
the native web_search tool and ends in a tool call, this reorders the thinking
blocks and drops the server_tool_use / web_search_tool_result blocks. Anthropic
validates each thinking block's signature against the blocks that precede it in
the latest assistant message, so the replayed turn is rejected:

  400 invalid_request_error
  "messages.N.content.M: `thinking` or `redacted_thinking` blocks in the latest
   assistant message cannot be modified. These blocks must remain as they were
   in the original response."

Preserve the full `finalMessage.content` verbatim (`_anthropicContent`) and
re-emit it unchanged, instead of extracting and reordering thinking blocks. Skip
the standalone text message that the streamer emits for the same turn (its text
is already inside `_anthropicContent`). The previous thinking-only path is kept
as a fallback for sessions persisted before this change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(sessions): give empty-state preview picker its own open state

* feat(sessions): render preview editors as components, not iframes

Introduce a PreviewTabHost seam that routes each preview tab to either an
in-process editor (the session's script/flow/raw_app target, reusing the
existing *EditorView wrappers + shared runtime) or an iframe fallback for
pages and other items, behind a uniform reload(). resolvePreviewTab classifies
a tab from its URL + the session target.

Also intercept in-iframe navigation to an editor route (logged layout
beforeNavigate): post the target up to the sessions page, which promotes the
active tab to the live editor component, so an editor is never booted inside
an iframe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): drive open_preview tool through the multi-tab model

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: plan SessionPreviewTabs deep module for sessions preview tabs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(sessions): own preview tabs in a SessionPreviewTabs deep module

Collapse the three drifting preview-tab copies (page-local state, session
record, legacy previewUrls localStorage) into one live owner held on
SessionRuntime.previewTabs. Both the sessions page (renderer) and the
open_preview/get_preview_status tools cross it, so both sync effects and the
localStorage seed disappear; url/target writes become atomic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): gate the Workspace/Sessions switch behind the global-AI dev flag

The SessionModeSwitch is the only entry point into the AI-sessions
experience, so gate it on wm_dev_global_ai like the global chat and the
sessions page — otherwise the unfinished mode ships to prod.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): pin the settings footer and normalize row text in the fork dropdown

Split the family picker menu into a scrollable body + a pinned settings
footer so the workspace-settings link stays visible while the fork list
scrolls. Give every row a uniform text-primary font-normal style (rows
were inheriting a bold 600 weight; the settings link was text-secondary).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sidebar): fold theme switch into the settings dropdown and keep it in session mode

Move the Switch-theme toggle into the sidebar Settings dropdown and reorder
its entries (bottom-to-top: Instance, Workspace, User). The dropdown now
renders in both navigation and session modes; session mode hides only the
workspace-settings entry (the rail's global workspace doesn't map to a
session's forked workspace).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(fork): validate fork name/id length before creation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): title open-in-workspace button "Open in workspace"

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): keep AI chat working when the sessions dev flag is off

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): match burger drawer width and keep it open on mode toggle

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sessions): surface the dev-workspace badge across session workspace pickers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): dedup navigate, sanitize hydration, cap mounted tabs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(fork): support forks of forks via a base-workspace picker

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sidebar): family expansion, pinned menu actions, animated popovers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(sessions): persist unsent drafts, gate preview, loading state

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sidebar): group fork picker on top and unfold the session list

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sessions): capture splitter pointer so off-window release ends drag

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(sessions): retire the pinned preview tab (dot and no-close)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(sessions): shared open-in-AI-session button across editors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sessions): dedup page tabs, flush on hide, review cleanups

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(sessions): give unsent drafts a side panel, reset tabs on retarget

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sessions): keep the session fork icon neutral except when detached

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sessions): scope session-mode restore and transient reuse to family

* fix(sessions): preserve session mode across workspace switches

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sessions): reconcile open session with family on workspace switch

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sessions): lazy-load runtime in session switch to keep it node-testable

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(sidebar): add bottom brand mark and standalone workers/logs links

* feat(sidebar): add name+id copy tooltip to workspace picker

* style(sidebar): add spacing between settings and brand mark

* feat(forks): id-based fork creation, fork color theming, picker polish

* feat(forks): copy-id in session header, inert chip, fork form polish

* fix(sidebar): restore logs, help, user and leave-workspace menus

* feat(sidebar): carry active tick on collapsed family root

* feat(dev): add settings-menu kitchen sink page

* fix(sessions): fail closed for unbound persisted sessions in family scope

* fix(sidebar): keep workspace URL param in sync across switches

* feat(sessions): remove home page from preview tab navigation

* refactor(sidebar): dedupe shared helpers and address review findings

* feat(sessions): keep preview hosts alive across session switches

* feat: workspace settings links in session rail, acting badge and family picker

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: refresh session changes bar after out-of-window deploy

The session "Edits" bar re-fetched its draft list and existence checks
only on AI turn-end, tab visibilitychange, and drawer open. Deploying an
item from a full-page editor in a second browser window left the bar
stale: that tab never goes hidden, so visibilitychange never fires, and
the badge kept reading "1 draft" while opening the drawer showed no
pending change.

Add a window `focus` listener alongside visibilitychange so returning to
the session window re-syncs the bar, and refresh the dock when a badge is
clicked so the drawer always opens on fresh state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(sessions): show name/id copy tooltip on acting badge, drop inline copy

* fix: keep editor header cloud indicator visible at narrow widths

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(sessions): scope preview reload to the mutated item

Reloading every mounted preview tab on any mutating chat tool blank-
rebooted unrelated raw-app previews: a raw app that isn't the session's
live-editor target renders as an /apps_raw/edit iframe, and reloadAllTabs
hard-reloaded it (frame.location.reload) on every write/deploy elsewhere.

Pass the tool args through the completion listener and scope the reload:
an item-route iframe reloads only when its item was actually touched. The
changed item is args.path for workspace-path tools; the raw-app file tools
(write_app_file, …) pass a leading-'/' frontend file path and edit the
active session's target app, so scope to the target; anything else is
unresolved and reloads everything (safe fallback). Changed paths accumulate
across the 500ms debounce. Non-item pages still always reload; live-editor
slots still no-op.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(diff): honor side-by-side/unified toggle and widen draft drawer

Monaco forces inline view below its 900px renderSideBySideInlineBreakpoint, which overrode our SIDE_BY_SIDE_MIN_WIDTH gate and made the toggle a no-op in the ~800px draft drawer. Disable useInlineViewWhenSpaceIsLimited so our width logic wins, and widen the drawer default 1200->1500px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(diff): vertically center the element-header icon with its path

The path renders as ExternalEditLink's inline-flex <a> in production, which sat ~2px low on the wrapper's line-box baseline. Make the path wrapper flex+items-center so the icon and path align by box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(diff): reflect the auto-unified downgrade in the drawer view toggle

The side-by-side/unified downgrade lived inside each DiffEditor's width gate, so the drawer toggle still showed side-by-side when the narrow column rendered inline. Measure the diff column, make the drawer authoritative (force inline when narrow), and reflect it in the toggle (unified selected, side-by-side disabled) while preserving the user's preference for when it widens again. Shared SIDE_BY_SIDE_MIN_WIDTH via diffEditorTypes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(sidebar): make the nav rail resizable with rem scaling

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(diff): gate Monaco auto-inline behind a prop to keep narrow diffs unified

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ui): restore instant popover/dropdown default, opt sidebar and sessions in

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sidebar): restore delete-forked-workspace action in the settings menu

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(tooltip): add cursor anchoring option and use it for the name/id tooltip

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(dev): remove settings-menu kitchen sink scaffolding

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(docs): remove session-preview-tabs owner plan doc

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(dev): drop vite preview-server proxy scaffolding

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(sidebar): scroll nav as one block with fade hints, pin settings to bottom

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sidebar): guard against concurrent pointer drags leaking resize listeners

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: scope session preview, LLM proxy, and raw-app workspace switch

Address PR review findings: session preview iframes, the AI chat LLM
proxy client, and the raw-app workspace-switch guard all now resolve the
session's effective workspace instead of the global navigation workspace.

- withMenuHidden appends the session workspace as ?workspace= so preview
  iframes render fork-scoped pages against the fork, not the nav workspace.
- AIChatManager builds the proxy clients from operatingWorkspace so the
  LLM request hits the session workspace's /ai/proxy, not the global
  singleton (init'd only on global workspace changes).
- workspaceSwitchUrl adds /apps_raw/edit|get to EDIT_PAGES so switching
  workspace from a raw-app editor/viewer goes home like other item pages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: scope session model, preview picker, and open-in-workspace to session

Second-layer workspace-scoping fixes from PR review: three more paths
resolved the global navigation workspace instead of the session's
effective workspace.

- SessionWrapper loads copilot config (models/providers) for the session's
  acting workspace, so getCurrentModel/modelProvider match the workspace
  the chat writes to, not the nav workspace.
- PreviewRouterPicker takes a workspaceId prop; the sessions page passes the
  session's effective workspace so the breadcrumb/+ picker lists fork items
  and its drafts, not the nav workspace's.
- 'Open in workspace' appends ?workspace= via the new withWorkspaceParam so
  the full-page link opens the active preview under the session workspace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): only the active session loads global copilot config

Follow-up to the session-workspace copilot fix: SessionWrapper's
loadCopilot effect ran in every warm/hidden wrapper, and since
copilotInfo/copilotSessionModel are global, a background session in a
different workspace could finish loading after the active one and leave
the active chat on the wrong provider/model. Gate the load on
currentSessionId so only the active session writes the shared config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): guard copilot load race + scope app handoff to workspace

Two more session-vs-navigation workspace fixes from PR review:

- loadCopilot now applies only the most recent call's result via a
  monotonic token, so a stale async load from a just-switched-away session
  can't clobber the active session's global model/provider config.
- navigateEditorTo carries the session workspace on the low-code app
  handoff (goto /apps/edit) so the app opens in the fork the session acts
  on, not the navigation workspace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): scope live-editor breadcrumb picker to session workspace

The session preview's live script/flow/raw-app editors mount with a
session workspaceId, but their EditorHeader breadcrumb picker
(WorkspaceItemDrillPicker) still loaded items and drafts from the global
navigation workspace. Thread an optional workspaceId prop from each
builder's autosaveWorkspace through EditorHeader -> BreadcrumbSegment ->
WorkspaceItemDrillPicker; it falls back to $workspaceStore, so non-session
editors are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(sessions): fix stale setSessionTabs transient-persistence comment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): scope live-editor deploy/save/triggers to session workspace

The session preview's live script/flow/raw-app editors load and autosave
against the session's acting workspace, but their internal deploy,
save-draft, trigger-loading, fork-eligibility, worker-tags and
live-editor-draft operations read $workspaceStore directly. Since a session
deliberately leaves $workspaceStore on the navigation workspace, a
fork-scoped session deployed/saved to the wrong workspace (verified: deploy
POSTed to the nav workspace and 400'd).

Introduce an opWorkspace derived (autosaveWorkspace ?? $workspaceStore) in
each builder and route the operation reads through it. autosaveWorkspace is
only set by the session editor views, so opWorkspace equals $workspaceStore
for every non-session editor — no behavior change outside sessions. Verified
in-browser: a fork-session deploy now POSTs to the fork (201 Created) while a
normal editor still targets the navigation workspace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): staged pending fork chip uses default accent, not parent's color

A staged pending fork's effective workspace resolves to its parent
(setSessionPendingFork sets pending_workspace_id = parent_workspace_id),
so WorkspaceScopeTrigger read the parent workspace's color and painted the
'Acting on' chip in the parent's hue (e.g. yellow) instead of the neutral
fork accent. A real fork shows its own color; a not-yet-created one has
none, so fall back to the default fork accent unless the creation form
passes an explicit color preview.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): scope raw-app deploy/save/version to session workspace

The raw-app create/update/version/diff/save operations live in
RawAppEditorHeader (not RawAppEditor), and still read $workspaceStore — so
a fork-scoped session's raw-app deploy targeted the navigation workspace,
the same class of bug already fixed for scripts and flows. Route those
operation reads through opWorkspace (autosaveWorkspace ?? $workspaceStore);
the inSessionPane-guarded draft-cleanup blocks are intentionally
non-session and keep $workspaceStore. Verified in-browser: a fork-session
raw-app deploy POSTs update_raw to the session fork (200).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): key live-editor load cache on workspace, not just path

The script/flow/raw-app loaders returned early when loadedPath matched the
requested path, ignoring the workspace. Retargeting a session to the same
item path in a different fork kept the old workspace's loaded content while
the editor props switched to the new workspace — so save/deploy/autosave
could write stale old-workspace content into the new fork. Add
loadedWorkspace to the load slot and include it in the early-return guard so
a same-path/different-workspace retarget reloads. Verified in-browser: the
script re-fetches from the new fork on an acting-workspace switch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): drop stale content when a live editor retargets to a new workspace

Follow-up to keying the load cache on workspace: the loaders reloaded on a
workspace retarget but did not clear loadedPath during the fetch, so
SessionEditorTarget's loadedPath-keyed ready/notFound/stale gates still
treated the editor as ready on the old workspace's content — the outbound
draft sync (now wired to the new workspace) could write stale content into
the new fork, and a 404 kept rendering the old editor. Clear loadedPath on a
workspace change too (like a force reload), so the loading/not-found gates
and the draft-sync ready check resolve correctly. Same-workspace path swaps
are unaffected (loadedWorkspace still matches, so the old editor stays
visible during the swap).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): await committed-workspace copilot config before a session send

getCurrentModel() reads the global copilotInfo when the request builds, but
SessionWrapper's loadCopilot for the active session is fire-and-forget — so a
send right after switching to a session in another workspace could pick the
previous workspace's provider/model while the proxy clients and tools target
the new workspace. Track the workspace copilotInfo reflects (copilotWorkspace)
and, in the session beforeSend hook (awaited before the request builds), load
the committed workspace's config when it doesn't already match. Verified
in-browser: sending a session committed to a workspace whose copilot config
wasn't yet loaded fires get_copilot_info for it just before the LLM proxy call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): navigate to a fresh session on reset; dedupe preview page tabs

Two review findings:

- resetToNewSession (deleting/archiving the open session) and the sidebar
  delete of the active last session created/selected a fresh session but left
  the URL on the old session_name. The page derives the visible session from
  that query, not currentSessionId, so it showed the deleted session's
  not-found state (or stayed on the archived one). Navigate to the fresh
  session, matching how activate()/enterSessionMode already switch sessions.

- Preview page-tab dedupe: the iframe reports its location with the injected
  nomenubar/workspace params, but tabs dedupe the observed loc against the
  workspace-less canonical url, so reopening a page spawned a duplicate tab.
  Canonicalize the observed loc in observeLocation (dropping both params);
  covered by a new sessionPreviewTabs test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): don't persist preview-iframe workspace; scope fork ducklakes to base

Two review findings:

- A sessions-preview iframe runs the logged layout, which persisted its
  ?workspace= (the session's fork) to localStorage — shared with the
  top-level app, so opening a fork preview clobbered the navigation
  workspace and reloads restored into the fork. Skip the persist when
  embedded; $workspaceStore is still set in-memory for the iframe's own API
  calls. Verified: opening a fork /runs preview leaves localStorage.workspace
  on the top-level workspace.

- ForkDucklakeSection listed ducklakes from $workspaceStore while a
  fork-of-fork is created from the selected base, so it could show the root's
  lakes and submit shared_ducklakes the base doesn't have. Add a
  sourceWorkspace prop (base ?? $workspaceStore) like ForkDatatableSection,
  and pass baseWorkspaceId at the mount.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): keep preview iframe on session fork across reloads and open-in-workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sessions): scope worker-tag pickers to the session's effective workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 14:27:00 +02:00
Diego Imbert 927b8d064f fix: clear old path asset usage when renaming a script (#9979) 2026-07-07 14:23:31 +02:00
Diego Imbert e47aedac0a feat: add SQL migrations for data tables (#9693)
* feat: add datatable_migrations table

* feat: add route to run datatable migrations

* feat: sync datatable migrations as .up.sql/.down.sql files

* feat: add datatable migrate up/down commands and post-push run prompt

* feat: add datatable migrate new command to scaffold migrations

* feat: add datatable migrations management UI

* feat: prompt to create migration on DDL in datatable SQL editors

* feat: support running a single specific datatable migration

* feat: view migration content, run single migration, fix stacked modal

* feat: per-row revert button with out-of-order warning

* fix: avoid migrations list flicker on refresh after an action

* feat: generate initial datatable migration via pg_dump

* fix: surface datatable migration API error details in toasts

* fix: revert created migration if create-and-run fails to run

* fix: include postgres error detail in migration run/rollback failures

* feat: sync datatable migrations as files via the workspace export

* refactor: move datatable migrations to migrations/datatable/ path

* fix: drop redundant datatable_migration label in sync output

* fix: exclude datatable migration sql files from script metadata generation

* feat: run datatable migrations as user-permissioned labeled jobs

* feat: reject invalid datatable migrations on sync push

* feat: datatable migrate up/down default to all datatables, --datatable to target one

* fix: surface postgres error detail when datatable migrations fail to run

* chore: regenerate CLI docs for datatable migrate commands

* feat: default new datatable migration to a BEGIN/END transaction template

* fix: validate datatable migration name and datatable at the API boundary

* fix: ensure detected DDL ends with semicolon when wrapped in transaction

* fix: re-prompt instead of stripping DDL when new-migration modal is cancelled

* feat: refresh datatable schema after running a migration from the SQL REPL

* feat: record db manager DDL on data tables as migrations

* feat: make datatable migrations opt-in per data table

* fix: make migration view editor read-only so its code can scroll

* fix: don't re-prompt DDL guard when creating a migration without running

* feat: generate down migrations for db manager DDL (postgres)

* fix: correct down migration for db manager alters (no double-wrap, serial)

* feat: explain migrations purpose with a tooltip in the migrations modal

* compare paeg

* feat: add datatable_migration kind to workspace diff pipeline

* chore: point ee-repo-ref at datatable_migration git-sync companion

* fix: harden datatable migration version allocation and initial-migration bookkeeping, add tests

* feat: deploy and run datatable migrations on workspace merge

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Refactor + handle datatable setting delete/rename

* refactor: move datatable migration rename/delete cascade into module

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(windmill-utils-internal): bump to 1.7.1 for datatable migration deploy provider methods

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(db-manager): add Migrations button to top bar, make Refresh icon-only

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* BEGIN/END placeholder in down migration

* feat: autofocus migration name input and flag it red when empty

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(datatable-migrations): allow non-admins to create/run/revert migrations, gate only opt in/out

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* border nits

* refresh db manager schema on migrations

* BEGIN/END scaffold in CLI

* feat(cli): push local datatable migrations before running on migrate up

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: flag invalid migration name with red border, not just empty

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor: drop random slug from auto-generated migration names

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: offer revert-and-delete when deleting an installed migration

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: record fork merge as a migration when target datatable opts in

* nit

* clone migrations on fork

* windmill-utils-internal

* fix(datatable-migrations): serialize run/rollback with a per-db advisory lock

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(db-manager): fail closed when migrations-status check errors on DDL apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: fix generate_initial migration ordering comment to match code

* chore(datatable-migrations): remove unused update_datatable_migrations endpoint

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: run DDL migration guard on the script editor Test button

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* split

* ee-repo-ref

* chore(frontend): sync package-lock with package.json (@emnapi deps)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(datatable-migrations): never resolve instance credentials into migration job args

datatable_database_arg eagerly resolved instance data-table credentials
(including the shared instance-wide Postgres password) and passed them as the
migration job's plaintext `database` arg, landing in v2_job.args. Since the
run route has no admin gate, a non-admin could run a migration and read
args.database to recover the password, granting cross-workspace psql access to
all instance data-table DBs.

Pass a `datatable://<name>` reference for both resource-backed and instance
data tables instead; the pg executor already resolves it to real credentials
server-side at run time, so nothing sensitive is ever stored in the job args.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* nit

* fix: handle dollar-quoting and comments when splitting SQL statements

* feat: deploy datatable migrations on merge with explicit opt-in error

* fix(frontend): sync package-lock with npm 11 peer-dep resolution

npm ci failed with 'Missing: @emnapi/core@1.11.2 / @emnapi/runtime@1.11.2 from
lock file'. @napi-rs/wasm-runtime declares @emnapi/core|runtime ^1.7.1 as
peerDependencies while @rolldown/binding-wasm32-wasi pins them to exactly
1.10.0. Newer npm (bundled with node 24 in CI) installs the peer deps at the
highest match (1.11.2) alongside rolldown's nested 1.10.0, so the ideal tree
needs both versions; the committed lock only had 1.10.0.

Regenerate the lock with npm 11.18 so it carries both 1.11.2 (top-level, for
the peer deps) and 1.10.0 (nested, for rolldown's pin). Verified npm ci passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* nit npm publish

* fix: fail closed on migrations-status error in fork schema merge

* nit CI emnapi/core version

* prevent initial_datatable_migration if migrations already exist

* fix(datatable-migrations): validate persisted data table names as path segments

edit_datatable_config only validated rename segments, not the actual
settings.datatables keys, so a data table could be saved directly under a name
like '..' or one containing '/'. Since new tables default to
migrations_enabled = true, generate_initial_datatable_migration would then
insert a migration row and the sync export would build
migrations/datatable/<name>/... paths from that name, producing malformed or
directory-escaping export paths.

Validate every persisted data table name in edit_datatable_config (alongside
the existing rename checks) and add validate_datatable_path_segment to
generate_initial_datatable_migration for defense in depth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: scope datatable _wm_migrations by data table and cascade renames/deletes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(system_prompts): resolve nested local command groups in CLI docs generator

The CLI docs generator anchored on the first `new Command()` in a file and
never resolved locally-defined command groups passed as
`.command("name", localCmd)`. For datatable this flattened the nested
`migrate` group: it emitted `datatable new/up/down` plus a bare
`datatable migrate`, and mislabeled the datatable command with the migrate
group's description. jobs was broken the same way (its description was pull's,
and pull/push rendered empty).

Anchor block extraction on the `export default`ed command, recurse into
locally-defined `const x = new Command()` groups mounted as subcommands, and
render nested sub-subcommands. Regenerated docs now show
`datatable migrate new/up/down` and `jobs pull/push` with their real
options.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor: drop unreleased _wm_migrations legacy-upgrade handling

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: return datatable migration SQL from getItemValue for the diff drawer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(frontend): use windmill-utils-internal 1.8.2 for migration diff drawer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* nit

* nit

* fix: handle datatable migration renames on push and dedupe timestamps

* fix: reject rewriting an already-applied datatable migration on upsert

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): add missing @emnapi/core and @emnapi/runtime lockfile entries

Resolves npm ci EUSAGE failure: the optional cpu:wasm32 @rolldown/binding-wasm32-wasi
declares deps on @emnapi/core@1.11.2 and @emnapi/runtime@1.11.2 that had no resolved
lockfile entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(cli): datatable migrate up/down default to main datatable, not all

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: fail closed when applied status unreadable on datatable migration rewrite

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: surface full error detail in Database Manager DDL/query errors

* "See migration" button in the toast

* feat: add Enter shortcut to Create-a-migration in the DDL guard

* fix(frontend): warn before running a newly-created datatable migration out of order

The row-level Run action warns when earlier migrations are still pending, but
the create-and-run paths ran a just-created migration with `only` directly,
applying it ahead of older pending migrations without that confirmation.

Reuse the same "Run migration out of order" confirmation across all
create-and-run paths via a shared helper (datatableMigrationUtils):
- NewDataTableMigrationModal "Create and run" (and the DDL guard path)
- DatatableSchemaDiff fork→parent merge
- dbOps schema ops (DB manager create/alter/drop) — the pure factory throws a
  MigrationRunCancelled sentinel on decline, which DBTableEditor treats as a
  silent cancel

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: keep renamed datatable migrations visible in compare view

* fix: record per-migration deployment on datatable migrations disable

* fix(cli): run deployed datatable migrations after workspace merge

The merge command upserted datatable_migration definitions into the target
workspace and reported the item as successfully deployed, but never ran the
migrations. For forked datatables backed by separate databases, this left the
target schema unchanged until someone manually ran `wmill datatable migrate up`,
while the CLI reported a successful merge.

Collect the datatable migrations deployed (not deleted) into the target and,
after the deploy loop, offer to run them via the existing offerToRunNewMigrations
helper — the same post-deploy run prompt the push/sync path uses (interactive
only; `--yes`/non-TTY skip the mutating run, matching push behavior). Export
parseDatatableMigrationDeployPath so the merge path can parse the deployed items.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(backend): serialize datatable migration edits/deletes with the run lock

A migration run snapshots a migration's code_up from datatable_migrations and
only records its version in the data table's _wm_migrations after the job
succeeds. upsert_datatable_migration checked _wm_migrations before allowing an
edit but took no lock, so a concurrent edit could read "not applied yet",
rewrite code_up/code_down, and then the in-flight run would record the version
for the old SQL — leaving _wm_migrations pointing at SQL that was never applied
(migrate up then skips it; rollback runs a down that doesn't match).

Serialize definition rewrites and deletes with the same per-database advisory
lock the run/rollback paths use:
- Factor the connect+advisory-lock into lock_datatable_migration_runs and the
  applied-versions read into read_applied_versions_on_client.
- run_datatable_migrations now snapshots the definitions AFTER taking the lock,
  so code_up can't change between snapshot and version-record.
- upsert (when changing an existing def) and delete take the lock across the
  applied-check and the write; delete now rejects deleting an already-applied
  migration (would orphan its _wm_migrations record), symmetric with upsert.
  Both fail closed if the data table database is unreachable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): stack the out-of-order migration confirm above the DB editor preview

Creating a table on a migrations-enabled data table opened the DB table editor's
"Confirm running the following" preview modal, whose confirm triggers applyDdl,
which then asks for out-of-order confirmation. Both are ConfirmationModals with a
hardcoded z-[9999]; the out-of-order one lives in DBManagerContent (mounted before
the editor), so it rendered behind the still-open preview modal.

Add an optional zIndexClass prop to ConfirmationModal (default z-[9999],
backward-compatible) and give the DB-manager out-of-order confirm z-[10000] so it
stacks on top.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to 27672e37df5d9dfde94f19963d5ffcdf8dd5448c

This commit updates the EE repository reference after PR #623 was merged in windmill-ee-private.

Previous ee-repo-ref: 6c287041cd7edd4a77a4bc07ad0e156cec32cce4

New ee-repo-ref: 27672e37df5d9dfde94f19963d5ffcdf8dd5448c

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-07 08:25:16 +00:00
Ruben Fiszel c5c1eadeb1 feat: update base image to debian 13 (trixie) (#9973)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 08:13:55 +00:00
Guilhem 8df613b4d2 feat(raw-apps): runtime-error overlay + AI import-React instruction (#9966)
* feat(raw-apps): render runtime-error overlay + instruct AI to import React

Render the `runtimeError` message the raw-app preview frame now posts as a
prominent overlay, so an uncaught exception that blanks the app is visible
instead of silent. Cleared on the next successful build (via a shared
`feedPreviewIframe` helper so every preview-feed path resets it).

Add an AI app-generation instruction to begin React files with
`import React from 'react'`: raw apps bundle with the classic JSX transform,
so a missing import compiles fine but throws "React is not defined" at runtime.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(raw-apps): add the import-React rule to the shared raw-app prompt

The global AI chat and the raw-app CLI skill draw their raw-app authoring
reference from system_prompts/base/raw-app.md — a separate surface from the
app chat's inline prompt (core.ts). Add the same "always begin JSX files with
`import React`" rule there (esbuild's classic transform needs React in scope,
or JSX throws "React is not defined" at runtime) and regenerate the derived
prompt files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(raw-apps): bump ui_builder tarball to f8cecf9 (runtime-error overlay)

Pins the ui_builder artifact to windmill-code-ui-builder#15, which pushes
uncaught runtime errors from the preview iframe to the parent so the raw-app
editor can render them in the error overlay.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 09:32:43 +02:00
Ruben Fiszel 51e1eba1cc icon-only muted-read badge + lighter DuckDB template (#9972)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 22:11:11 +02:00
Ruben Fiszel 8dd5e48a68 chore(main): release 1.751.0 (#9965)
* chore(main): release 1.751.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.751.0
2026-07-06 19:00:48 +00:00
Guilhem f3da86512a theme-aware prose palette for markdown in dark mode (#9971)
* fix(frontend): theme-aware prose palette for markdown in dark mode

* chore(frontend): add markdown example to kitchen_sink showcase
2026-07-06 19:00:35 +00:00
Ruben Fiszel 97d14d979f bun bootstrap housekeeping on the migrator's held connection (#9970)
migrate() and fix_flow_versioning_migration re-acquired a second connection from the pool while already holding one (the migrator's checked-out, advisory-locked connection). That deadlocks any backend limited to one connection at a time — connection-constrained managed Postgres, PgBouncer transaction pooling, or an embedded single-connection dev database. Route those housekeeping queries onto the already-held connection via a new CustomMigrator::connection() accessor. Fewer connections during migration and, for fix_flow_versioning, the existence check and write now run on the same advisory-locked connection. Default multi-connection behavior is unchanged.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 18:59:25 +00:00
Guilhem 2e14302e4a feat(frontend): custom skills — detail modal, batch manage, shared validation (#9847)
* feat(frontend): simplify custom skills workspace settings UI

Collapse the "Custom skills" AI settings section into a single block. When
no skills exist, show two side-by-side zones: a drag-and-drop folder dropzone
(reusing FileInput) and a paste textarea whose add button appears only once
content is entered. When skills exist, an "+ Add skills" dropdown offers
"Import a folder of skills" (native picker) and "Paste a skill" (modal), above
the skills list. Folder ingestion is shared by both the picker and the dropzone
via processFolderFiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): custom skills detail modal + shared zod validation

Rework the Custom skills settings: header Add-skills dropdown, per-row
ellipsis menu (edit/delete), a Show more detail modal with a view/edit
toggle (rendered markdown in read mode), accent Save gated by dirty
detection and inline validation, and a folder-import conflict modal with
per-skill overwrite toggles. Extract skill parsing/validation into a
shared Zod-backed aiSkills module used by both the modal and the importer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): use Button for Show more; surface empty-body validation

Address review: swap the raw <button> Show-more affordance for the
design-system Button (per frontend component standards), and render the
Save/inline-error block whenever editing an existing skill so clearing
the body surfaces "body is required" instead of hiding both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(frontend): unit-test aiSkills; use themed border token

Add aiSkills.test.ts covering parseSkillMd (BOM, CRLF, malformed YAML),
validateSkill (code-point vs byte limits, name pattern), parseAndValidateSkill
(nameOverride precedence) and buildSkillMd round-trip. Replace the hardcoded
gray borders with the themed border-border-light token.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): cap custom skills list height and scroll

Constrain the skills list to max-h-96 with overflow-y-auto so a large
number of skills scrolls within the section instead of pushing the page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): manage-mode batch delete for custom skills

Add a "Manage skills" button (shown only with more than one skill) that
enters a multi-select mode: a checkbox per row plus a sticky select-all
(tri-state) header, and a batch Delete gated on the selection with a
confirmation. Manage mode auto-exits when the list drops to one skill.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): destructive delete, subtle manage button, Esc exits manage mode

Batch Delete uses the destructive accent variant, Manage skills uses the
subtle variant, and Escape leaves manage mode (mirroring Done) unless a
modal or menu is open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): add neutral 'info' type to ConfirmationModal; use for skill import

ConfirmationModal only had 'danger' and 'reload' semantics, so a
constructive confirmation like importing skills defaulted to danger
(red warning + destructive button). Add a neutral 'info' type (blue Info
icon, non-destructive accent confirm) and use it for the Import skills modal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 18:43:50 +00:00
hugocasa fd8e64d11f feat: add cosmetic dev/staging label for dev workspaces (#9959)
* feat: add cosmetic dev/staging label for dev workspaces

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: prefill dev fork name and use a link to switch its label

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style: reword the dev/staging label link copy

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style: preview the dev/staging label as a badge in the switch link

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show the dev/staging badge in the session diff drawer header

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 18:43:17 +00:00
Guilhem 6587019d26 fix: critical alerts modal mute toggles no longer close popover or fail to save (#9969)
* fix: mute toggles in critical alerts modal no longer close popover or fail to save

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: mark popover content root as dropdown-portal so padding clicks don't close modal

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: derive no-channels warning from mute state so it survives modal reopen

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 18:43:04 +00:00
Guilhem 45946d1185 fix(assets): responsive layout for small screens (#9961)
* fix(assets): handle card/header overflow on small screens

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(assets): keep filter row label on one line with min spacing from refresh

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(assets): wrap card header actions below title instead of collapsing docs to icon

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(assets): widen card basis to 340px so cards wrap sooner and header stays one row

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 18:23:31 +00:00
Guilhem 9821596251 fix(frontend): theme-aware code block background in prose markdown (#9968) 2026-07-06 18:22:31 +00:00
Ruben Fiszel 3dcd3949a1 feat(pipelines): auto-derive cascade edges from ducklake/s3 reads (+ muted-read badge) (#9963)
* feat(pipelines): auto-derive cascade trigger edges from ducklake/s3 reads

Within a `// pipeline`, a read of a ducklake table or s3 object now
auto-wires its cascade trigger edge straight from the FROM clause, so
`// on <asset>` is only needed for edges inference can't see (dynamic SQL)
or to carry per-edge opts. Two opt-outs: `// mute <asset>` suppresses a
single derived edge (a lookup / SCD input read every run but not cascaded
on), and `// mute all` opts the script out of derivation entirely (back to
explicit-`// on`-only). Explicit `// on` still wins the dedup.

Scoped to ducklake + s3 reads; resource/datatable/volume stay explicit.
Read-write (RW) and write inputs are excluded so a self-referential
merge can't loop-trigger itself; ambiguous (None) access is skipped.

- parser: `mute` / `mute_all` in PipelineAnnotations (Rust + TS mirror)
- deploy: derive_pipeline_asset_trigger_refs → script_trigger rows
- frontend: resolveGraph mirrors derivation for the live edit-mode canvas
- tests: shared parity corpus + derive-helper units + resolveGraph overlays

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipelines): mark auto-derived cascade edges with a persisted derived flag + "auto" badge

Persist script_trigger.derived (deploy: true for ducklake/s3-read derivation,
false for explicit // on) and return it from the asset-graph endpoint so the
canvas renders a Sparkles "auto" badge on auto-wired edges — the inference is
now visible on both the deployed graph and the live edit canvas, not just
implied. Dispatch (fetch_subscribers) ignores the flag, so a derived edge fires
identically to an explicit // on. Also copy derived in the workspace-clone
trigger copy, and backfill muteAssets/muteAll into two empty PipelineAnnotations
literals the base commit left stale (check:fast).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipelines): derive cascade edge from effective (alt-fallback) asset access

derive_pipeline_asset_trigger_refs gated on the raw parser access_type, but the
persisted asset.usage_access_type and the frontend canvas both use
access_type.or(alt_access_type). An ambiguous parse with a manual read override
was persisted/drawn as a read yet derived no edge, so the auto edge silently
vanished on deploy. Gate on the effective access type for parity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipelines): badge muted reads instead of auto-derived edges

Auto-derivation is the default now, so badging every derived cascade edge is
noise. Drop the "auto" badge and the persisted `script_trigger.derived` flag
(migration + insert param + graph field + clone copy) that only powered it, and
instead badge the exception: a ducklake/s3 asset a script reads but does NOT
cascade — `// mute <asset>` / `// mute all`. `computeMutedReadKeys` marks a
read-only ('r') supported read with no cascade trigger and no self-write; the
canvas renders a bell-off "muted" badge on that read edge.

Also fixes two review parity nits:
- TS `// on` parser now strips trailing `key=value` opts (e.g. `debounce=60s`)
  like the Rust `split_trailing_kv_opts`, so the ref dedups against inference.
- A `// materialize` producer reading its own target is upgraded to `rw`
  (deploy) / excluded via the materialize write refs (canvas), so it neither
  self-cascades nor shows as a muted read.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipelines): drop redundant // on for auto-derived reads; gate muted badge to pipeline scripts

- Templates no longer scaffold `// on <asset>` for a ducklake/s3 input the body
  reads — the read auto-wires the cascade now that derivation is the default.
  Kept for datatable/resource (not auto-derived) and native triggers. The
  discoverability hint now mentions `// mute` (the newly relevant annotation).
- computeMutedReadKeys only badges reads by `// pipeline` scripts. A plain
  script or flow reading a ducklake/s3 asset never had an auto trigger to
  suppress, so it must render as ordinary lineage, not "muted" (Codex review).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipelines): only drop template // on when the body actually reads the input

The redundant-`// on` removal assumed the generated body reads the ducklake/s3
input, but postgres/bash/generic bodies (and `data_upload`, which reads the
picker file) ignore `input` — dropping `// on` there left the asset-created
script with no cascade at all. Gate the drop on READS_INPUT_LANGS
(bun/deno/python/duckdb) so non-reading templates keep the explicit trigger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 18:12:12 +00:00
Ruben Fiszel 91e1b087a2 feat(auth): add runtime NO_AUTH mode for authentication bypass (#9962)
* feat(auth): add runtime NO_AUTH mode for authentication bypass

Adds a runtime `NO_AUTH` env flag that makes every request resolve as the
`admin@windmill.dev` superadmin with no login required, so self-hosted
deployments can front Windmill with their own authenticating gateway
without building a dedicated `oss` (compile-time `no_auth`) binary.

- `NO_AUTH` is honored in any build but is force-disabled when
  `CLOUD_HOSTED` is set, so the managed cloud always enforces real auth.
- The existing compile-time `no_auth` feature keeps its always-on behavior
  (`cfg!(feature = "no_auth") || *NO_AUTH`), so `oss` builds are unchanged.
- `Tokened` now yields a synthetic token in no-auth mode so handlers that
  require it (e.g. global_whoami, called by the frontend on load) resolve.
- A loud startup banner warns when the mode is on; `HIDE_NO_AUTH_BANNER`
  silences it once the operator has deliberately deployed behind a gateway.

Fixes WIN-2131

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(auth): dismissable NO_AUTH warning banner via global setting

Replaces the HIDE_NO_AUTH_BANNER env flag with a UI warning banner that
can be permanently dismissed for all users from within the running
instance (not exposed in instance settings).

- New `no_auth_banner_dismissed` global setting, only ever written by
  dismissing the banner itself.
- `GET /api/settings/no_auth_banner` returns whether to show the banner
  (true only when NO_AUTH is active and it hasn't been dismissed).
- NoAuthBanner.svelte renders a top-of-app warning in NO_AUTH mode; its
  dismiss button opens a confirmation modal, then writes the global
  setting via the existing setGlobal endpoint so it stays hidden for
  everyone.
- The server still logs the startup NO_AUTH warning unconditionally.

Fixes WIN-2131

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(auth): resolve NO_AUTH in AuthCache so all_runnables works

Codex/Pi review flagged that `/api/users/all_runnables` still failed in
NO_AUTH mode: `get_all_runnables` extracts `Tokened` and re-validates the
request token per workspace via `AuthCache::get_authed`, which rejected the
fabricated `"no_auth"` token (no matching DB row) with a 400.

Short-circuit `AuthCache::get_opt_job_authed` (the resolver behind
`get_authed`) to the admin superadmin in no-auth mode, so any direct cache
caller resolves without a real token. Single-source the mode check and the
synthetic identity via `is_no_auth()` / `no_auth_admin_authed()` and reuse
them across the extractor, resolver, and login paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* revert(auth): drop the NO_AUTH dismissable UI banner

The in-app banner added a GET /api/settings/no_auth_banner request to every
instance load for little benefit. The startup log warning already surfaces
that auth is bypassed to operators, so drop the banner, its endpoint, and the
no_auth_banner_dismissed global setting entirely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 17:02:51 +00:00
hugocasa cc2f638de6 fix(ai): centralize Anthropic Messages API routing across completion paths (#9960)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 16:36:21 +00:00
hugocasa dc6b99775b fix(cli): quote non-identifier property names in resource-type namespace (#9964)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:56:32 +00:00
Ruben Fiszel 5fe7e1f3e8 chore(main): release 1.750.0 (#9952)
* chore(main): release 1.750.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.750.0
2026-07-06 11:39:57 +02:00
Guilhem 056ebdb035 fix: read chat drafts via own-draft route so drawer-kind drafts deploy (#9913)
* fix: read chat drafts via own-draft route so drawer-kind drafts deploy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: cover trigger and resource chat-draft read/deploy regressions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: cover non-secret variable chat-draft read/deploy regression

Completes the drawer-kind matrix from the review notes on #9913: schedule,
trigger, and resource already had full write→read→deploy regressions; this
adds the variable one (non-secret — the secret flow deploys through the
ephemeral in-memory value and is pinned by the existing ephemeral tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ai_evals): mock getOwnDraft so eval draft hydration stays in-memory

The frontend eval adapter intercepts DraftService for benchmark workspaces,
but only updateDraft/getDraftForUser/listDrafts. Global eval output
collection hydrates draft values through getGlobalDraft, which reads via
getOwnDraft — so draft-producing global cases fell through to the real
generated client instead of the in-memory benchmark store. Adds a
getBenchmarkOwnDraft helper (null on miss, mirroring the 200/null route
semantics), wires it into the adapter mock, and pins it in
mockBackendDrafts.test.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-07-06 11:34:08 +02:00
Ruben Fiszel 98013483c8 restore auto-review & command gating for private org members (#9958)
* fix(ci): gate auto-review on non-fork PR not author_association (skips private members)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ci): authorize private org members for command workflows via app-token gate

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 11:26:55 +02:00
hugocasa ea19cc9dc4 fix(ai): test key routes Azure Foundry Claude models via Anthropic Messages API (#9956)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 11:26:24 +02:00
Ruben Fiszel 43044c2e28 feat(pipelines): wm_partition macro for grain-agnostic partition filters (#9950)
* wip: partial work before earlyoom-recovery relaunch

* fix(pipelines): scaffold the strftime {partition} filter idiom (frontend-only)

The DuckDB materialize scaffold and the AI pipeline prompt now teach the
grain-agnostic `WHERE strftime(<ts_col>, '<fmt>') = {partition}` filter instead
of the naive `= TIMESTAMP {partition}` cast. `{partition}` substitutes to the
partition IDENTITY string (`2026-07-05T23`, `2026-W27`, `2026-07`), which is not
a valid DuckDB TIMESTAMP literal for any non-daily grain — so the naive form
raises a `Conversion Error` for hourly/weekly/monthly (only daily parses).

Adds a frontend unit test asserting the hourly scaffold emits the strftime
idiom (`%Y-%m-%dT%H`) for every grain and never scaffolds the naive TIMESTAMP
cast as executable SQL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipelines): scope strftime partition idiom to time grains

Review nit: `dynamic` partitioning's identity is a caller-supplied key, not a
timestamp, so `strftime` doesn't apply. Scope the scaffold + AI prompt claim to
time grains and add a `dynamic` example that filters on the user's own key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipelines): wm_partition macro for grain-agnostic partition filters

The materialize runtime now injects a `wm_partition(ts)` temp macro as the first
setup statement of a time-partitioned script, so filtering the source to the
active slice is one grain-agnostic line — `WHERE wm_partition(<ts_col>) =
{partition}` — instead of a hand-written `strftime` format the author must keep
in lockstep with the resolver, or the `= TIMESTAMP {partition}` cast that only
parses for daily and Conversion-Errors for hourly/weekly/monthly.

The macro's format comes from `PartitionKind::default_time_format` in
windmill-parser, the same source the EE resolver reads to stamp the `{partition}`
identity, so the two can't drift. `dynamic` partitions get no macro (their
identity is a caller-supplied key → `WHERE <key_col> = {partition}`).

Replaces the earlier 9-line strftime comment block in the scaffold with the
single macro line; AI pipeline prompt and design doc updated to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(pipelines): verify wm_partition strftime parity vs chrono through real DuckDB

Runs the bundled DuckDB engine in-memory and asserts strftime renders every
grain format (daily/hourly/weekly `%G-W%V`/monthly) byte-for-byte identically to
chrono — the engine the resolver uses to stamp the `{partition}` identity —
across ISO-week year boundaries (2027-01-01 → 2026-W53 etc.). Also proves the
injected `wm_partition` macro buckets the whole slice and that the naive
`TIMESTAMP '<weekly|monthly identity>'` cast Conversion-Errors.

Closes the one cross-engine assumption the pure-Rust/frontend tests couldn't
reach (flagged by CI review for weekly ISO-week rendering).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to 0de2412ff0734b11e12ba378c9bcc373ff9ae800

This commit updates the EE repository reference after PR #649 was merged in windmill-ee-private.

Previous ee-repo-ref: ad6c6685689d7741058e7d2c9ecbe95d982e6268

New ee-repo-ref: 0de2412ff0734b11e12ba378c9bcc373ff9ae800

Automated by sync-ee-ref workflow.

* fix(pipelines): classify CREATE TEMP MACRO as a DuckDB prepare-path setup statement

The FFI prepare/diagnostics pass only EXECUTES statements recognized by
is_setup_statement (ATTACH/USE/INSTALL/…); everything else is merely prepared.
`CREATE [OR REPLACE] TEMP MACRO` wasn't recognized, so on a `-- prepare` run of a
partitioned materialize the injected `wm_partition` macro was never created on
the connection, and the later generated `CREATE TABLE … SELECT … WHERE
wm_partition(...)` failed to bind ("function does not exist"). The same latent
gap affected the workspace-macro splicer, which injects TEMP MACRO blocks too.

Classify CREATE [OR REPLACE] TEMP|TEMPORARY MACRO as setup so it's executed
before dependent blocks and excluded from the PrepareQueryResult count
(persistent CREATE MACRO stays a user statement). Adds a prepare-path test that
fails without the fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-07-06 10:26:53 +02:00
Ruben Fiszel a1c5b7aa3e feat(pipelines): require data uploads before running a pipeline (#9953)
* feat(pipelines): require data uploads before running a pipeline

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(pipelines): require every S3Object filled for data-upload readiness

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(pipelines): Run pipeline defaults schedule-triggered scripts to their schedule args

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: correct why only schedules default their args in Run pipeline

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(pipelines): close cascade double-start race and gate data-upload readiness on full-schema validity

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: expose ScriptEditor validity via callback, not banned bindable-with-default

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 10:26:19 +02:00
Ruben Fiszel 4b1f2207b8 ci: replace expiring-PAT org membership gate with author_association (#9957)
* ci for broken links + fix broken links

* ci: replace expiring-PAT org membership gate with author_association

The shared check-org-membership.yml reusable workflow authenticated to the
GitHub API with the ORG_ACCESS_TOKEN PAT to confirm org membership. That PAT
expired ~1 year after issuance, so the API could no longer see private org
members and check-membership emitted is_member=false — silently skipping every
auto-review, command-triggered review, /ai, /plan, and git-command job while
still reporting success.

Gate on the event payload's author_association (OWNER/MEMBER/COLLABORATOR)
instead, which comes from the built-in GITHUB_TOKEN and never expires. The
trusted internal bot and existing draft/fork/command guards are preserved; the
workflow_call paths stay open as trusted upstream. Deletes the now-unused
reusable workflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 10:08:25 +02:00
windmill-internal-app[bot] aaeb9524b3 chore: refresh vendored docs snapshot (#9955)
Co-authored-by: hugocasa <15649739+hugocasa@users.noreply.github.com>
2026-07-06 10:01:13 +02:00
Guilhem a6c0b3756b feat: chat-scoped session changes bar + unified diff drawer (#9762)
* feat(frontend): chat-scoped unified session changes bar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(frontend): drop diff-baseline toggle, show natural per-row diffs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): carry Draft marker to expanded raw-app file rows

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): show raw-app Draft badge once at tree root, not per file

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(frontend): reuse shared DraftBadge in session diff drawer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): show draft-author avatars in session diff badge, icon-only in sidebar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): keep badge pill around avatar in icon-only DraftBadge

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): small draft marker = indigo pen + avatar; correct itemKind label

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(frontend): drop package-lock churn from merge (match origin/main)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): dedup diff-button count for legacy fork sessions; test mask helper

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): collapsible session diff panel + per-row open-diff action

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): shared sessionDeployModel for review & deploy (S1)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): model-driven session review drawer, deploy inert (S2)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): wire session deploy + on-behalf/conflict gating (S3)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): behind banner + Update fork + deployment request (S4)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): session changes dock opens drawer by filter (S5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): session review UI polish (badge, dock, In parent, tree width)

- draft rows show only the avatar DraftBadge, not a duplicate state pill
- drop redundant dock Review button (same as "N to review")
- rename Done -> In parent with a "deployed in parent workspace" tooltip
- widen the file tree

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): existence-gate In parent rows; Badge filters; badge hover

- drop discarded mask-only items from the In-parent segment (existence check)
- use the Badge component for the drawer filter segments and the changes dock
- soften the blue Badge hover (blue-50 base was jumping to blue-200)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): polish session diff drawer (layout, badges, actions)

- remove empty fork-banner gap; uniform sidebar tree padding
- full-bleed diff list: drop card borders/side padding, separators between items
- clamp tree x-overflow; right-align tree badges (min-w-0 on the row button)
- brand-compliant selected filter badges; smaller draft badge
- hide per-row open-diff button when the panel is open
- rename "Delete draft" to "Discard draft" (destructive); remove header Review button
- larger sm deploy/discard action buttons; remove per-item diff-content collapse

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): session Edits dock — deploy gating + change-op tracking

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(frontend): session bar per-status badges; drop change-op tracking

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): unwrap raw apps into per-file tree in session diff sidebar

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): dot-parcours pipeline (badge-derived, melt tooltip) + discard confirm

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): behind-only session item reads as deployed, not bare

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(frontend): even sidebar tree margins; gutter-aware right padding

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): pass chat id as from_session; wire deploying flag

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): session diff drawer scroll-to-flush, ordering, spacer, deploy gating

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): preserve chat mask on compact; guard stale existence checks; clear poll timers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): refresh bar after drawer deploys; conflict hint over chip; plain conflict badge

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(frontend): diff drawer card layout with flash ring and aligned insets

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): hide stale deployed chip once row status badge reads deployed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(frontend): session dock to two states; drop parent deploy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): staged deploy animation in session edits drawer

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): stale-draft warning in session edits drawer

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): keep chat mask honest on deploy and discard

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): map trigger_email deploy kind; serialize mask persists

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): reset mask on new chat; close review-flagged races

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(frontend): rename session drawer title to Edited during session

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): keep mask persist queue alive after a failed save

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): sync session chatId on chat rotation; gate deploy on canWrite

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(frontend): keep compare handoff for deletion-only session edits

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(frontend): hold deploy success beat across re-keyed rows

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 09:23:54 +02:00
Ruben Fiszel 234a7254d5 chore: share DuckDB FFI build cache across worktrees (#9954)
The `duckdb` bundled feature compiles the whole DuckDB C++ library from
source (~2min), which dominates the FFI crate's build. A fresh git
worktree had an empty target dir and paid that cost every time.

build_dev.sh now builds into a per-user cache shared across worktrees,
keyed by Cargo.lock + build.rs so distinct DuckDB versions don't collide.
Uncommitted changes to the crate source fall back to an isolated
per-worktree ./target so active FFI development neither disturbs nor is
disturbed by the shared cache. Add a crate .gitignore for /target, and
note the shared cache in the AGENTS.md / backend CLAUDE.md build steps.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 08:55:02 +02:00
Ruben Fiszel bf9662172a fix: resolve extensionless bun relative imports on windows loader (#9949)
* fix: resolve extensionless bun relative imports on windows loader

* fix: resolve local module probe against importer dir not job root

* fix: keep node_modules-internal relative imports out of windmill resolver on windows
2026-07-06 03:49:51 +02:00
Ruben Fiszel ad6f23d6bf fix(cli): HD-1 test_edges + HD-2 scd2 _current write in --local pipeline graph (#9947)
* fix(cli): emit HD-1 test_edges + HD-2 scd2 _current write in --local pipeline graph

Close the remaining local-vs-deployed graph parity gaps in `wmill pipeline
show <folder> --local` so it matches the deployed graph (backend
`asset_graph`, windmill-api-assets):

- HD-1 `test_edges`: synthesize ordering-only producer → tested-script edges
  from parsed `// data_test` annotations. A `relationships` test references
  its `to_path` asset; a custom `// data_test <script>` resolves best-effort
  against that script's parsed reads. Each referenced asset is resolved to its
  in-pipeline producer via the write edges; self-edges and producer-less
  (external) assets are dropped — mirroring the backend set semantics.
  Routed through the asset node in boundedCascade's lineage DAG (asset →
  tested script) so a cold/bounded cascade orders the referenced dimension
  first, matching the frontend.

- HD-2 scd2 `<dim>_current` companion write: a managed `// materialize …
  history` (scd2 && !manual) also produces a `<dim>_current` view. Register it
  as a second write edge and mark the asset `derived_from` its base dimension,
  so a consumer reading only the view links back to the producer instead of
  orphaning. Gated exactly like the backend `MaterializeSpec::write_targets` /
  `scd2_current_target`.

The pinned `windmill-parser-wasm-asset` (1.740.0) predates the `scd2`
materialize flag, so `buildLocalPipelineGraph` takes an injectable parser and
the HD-2 test injects one that re-adds `scd2` for a `history` materialize —
exercising the already-shipped companion-write branch until a wasm carrying
`scd2` is republished (cf. #9926).

Extends cli/test/pipeline_local_graph_unit.test.ts with HD-1 (relationships,
no-producer, self-test, custom) and HD-2 coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(cli): pin windmill-parser-wasm-asset 1.749.0, drop HD-2 test parser seam

Now that windmill-parser-wasm-asset 1.749.0 (which serializes the `scd2`
materialize flag) is published, bump the CLI pin and retire the temporary
injection seam:

- Remove the `infer?` parameter from `buildLocalPipelineGraph`; it always uses
  the wasm-backed `inferScriptAssets` again.
- The HD-2 `<dim>_current` companion-write test drives the real wasm directly
  (drops the `inferWithScd2` wrapper that re-added `scd2` against the pinned
  1.740.0 build).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(frontend): pin windmill-parser-wasm-asset 1.749.0 to match CLI

Restore the CLI↔frontend lockstep on the asset parser wasm broken by the
previous commit: every other windmill-parser-wasm-* package is pinned to the
same version in both cli/package.json and frontend/package.json, so keep the
asset parser aligned too. The frontend derives materialize/scd2 from its own
TS annotation parser (`parsePipelineAnnotations`), so this bump only affects
body asset inference in the live graph — moving it in step with the CLI
`--local` graph and the deployed backend parser.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-06 03:08:54 +02:00
Ruben Fiszel 6eabb96ae7 fix(pipelines): make node & pipeline-level run affordances always visible (#9948)
* fix(pipelines): make node & pipeline-level run affordances always visible

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipelines): don't leave node Run disabled by stale form validity

The always-visible node Run button read `isValid` directly, but that flag is
only meaningful while PipelineRunForm is mounted to set it. On a same-path
re-resolve (the component is keyed on script.path) from an input-carrying
script to an input-less one, the form unmounts leaving `isValid=false`, which
wrongly kept the empty-args Run button disabled. Gate validity through a
`runValid` derived that is true whenever no form is rendered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pipelines): run with {} when node has no form, not stale args

The always-visible node Run button dispatched $state.snapshot(args)
unconditionally. `args` persists across a same-path re-resolve (component keyed
on script.path), so a script that once had inputs/partition args and is
re-resolved as input-less would run with the stale hidden args instead of {}.
Send {} whenever no form is rendered, matching the no-form run intent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 03:06:31 +02:00