Commit Graph
231 Commits
Author SHA1 Message Date
hugocasaandClaude Opus 4.8 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
GuilhemandClaude Opus 4.8 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
Ruben FiszelandClaude Fable 5 39eb9de1bc feat(pipelines): fork data environments for ducklake materialization (dev data) (#9915)
* feat(pipelines): fork-scoped ducklake namespaces with read-defer to parent

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

* feat(pipelines): fork graph indicator + fork ducklake namespace cleanup endpoint

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

* feat(pipelines): fork_views-keyed view transition, fork lineage clone, design doc

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

* fix(pipelines): review hardening - fork DATA_PATH last-wins, registry cache TTL, defer tests

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

* feat(pipelines): per-lake isolated/shared choice at fork creation

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

* fix(pipelines): chain-aware defer discovery + per-location fork namespace registry

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

* fix(pipelines): lake-scoped fork schemas, catalog identity in registry, chain-aware graph chips

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

* fix(pipelines): cleanup deletes fork data from the registered storage identity

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

* fix(pipelines): collapse fork data-path segment to one component (slash-safe ids)

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

* fix(pipelines): per-catalog ancestor checks, ancestor extra_args passthrough, test compile fix

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

* fix(pipelines): invalidate fork ancestor-chain cache on lineage mutations

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

* fix(pipelines): sweep descendant ancestor-chain caches on delete/reparent

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

* fix(pipelines): run fork ducklake cleanup inline in delete_workspace

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

* fix(pipelines): resolve fork cleanup credentials pre-commit, destroy post-commit

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

* fix(pipelines): shared dev-workspace authz gate for namespace drop, invalidatable registration cache, segment-boundary delete filter

- extract require_prod_admin_for_dev_workspace, used by both delete_workspace
  and drop_forked_ducklake_namespaces so the gates cannot drift
- key FORK_DUCKLAKE_REGISTERED per workspace and invalidate it in
  cleanup_fork_ducklake_namespaces so a same-id fork recreated within the TTL
  re-registers its namespaces
- filter listed object locations to the segment boundary before deletion

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

* fix(pipelines): keep orphaned wm-fork-* workspaces ducklake-isolated

parent_workspace_id is ON DELETE SET NULL, so a fork can outlive its
parent with an empty ancestor chain while its cloned config still points
at the shared lake. Key the isolation gate on the wm-fork- prefix as well
as the chain (mirroring workspace_is_fork): orphaned forks get the write
redirect, registration and cleanup with zero ancestors (no defer), and
keep their 'fork' graph chips.

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

* fix(pipelines): attach orphaned wm-fork-* ancestors at their fork namespace

Chain position alone classified the last ancestor as a root, but an
orphaned wm-fork-* ancestor (its own parent deleted, SET NULL) ends the
chain the same way while its data lives in its fork namespace — its
descendants' defer views bound the dead root's lake instead. Key the
root-vs-fork decision on the wm-fork- prefix too, matching the
resolution gate.

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

* fix(pipelines): never inherit shared lake opt-out; durable cleanup ledger for failed fork deletions

- fork creation strips cloned fork_behavior stamps before applying the
  request's shared_ducklakes list: sharing is a per-creation choice, a
  fork of a shared fork defaults back to isolated
- fork_ducklake_namespace loses its ON DELETE CASCADE FK: rows are the
  durable cleanup ledger and outlive the workspace when physical cleanup
  fails post-commit; fork creation retries leftover rows for the reused
  id and refuses to create while a metadata schema still cannot be
  dropped (data-file leftovers alone are inert once the schema is gone
  and are swept by the next successful same-prefix cleanup)

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

* fix(pipelines): make orphaned-namespace cleanup retries independent of deleted fork resources

- ledger rows gain a schema_dropped phase flag: set when the schema drop
  succeeded but data cleanup failed, so later retries skip the schema
  phase and need no catalog credentials at all; registration resets it
  on re-attach (ON CONFLICT DO UPDATE) since attaching recreates the
  schema
- retry-path $res: resolution falls back to the workspace being forked
  (the deleted fork's resources were clones of a parent's); live paths
  (delete_workspace prepare, drop endpoint) pass no fallback

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

* fix(pipelines): fork tables from failed-after-commit runs stay fork-owned in defer and graph

A failed materialization must not disguise a physically existing fork
table as deferred: CREATE VIEW IF NOT EXISTS silently yields to the
table, so reads hit fork data while the graph claims parent defer.

- record_mat upsert preserves the last committed snapshot_id on failure
- defer discovery and graph chips treat fork rows with a committed
  snapshot as fork-owned even when status is failed
- inspect_fork_catalog also lists live fork tables (same round trip) and
  the defer list is filtered against them — covers rows recorded before
  this fix and tables created by raw SQL
- drop stale FK-cascade wording in the design doc and sidebar comment

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

* feat(pipelines): fork-mode ducklake settings — per-lake isolated/shared chips + banner, fork_behavior round-trip

The workspace-settings ducklake editor had no fork awareness: no
reminder of each lake's isolated/shared choice and no warning about
what edits mean in a fork. It also rebuilt each lake explicitly on
save, silently dropping fork_behavior — any settings save in a shared
fork flipped the lake back to isolated.

- fork detection mirrors the backend gate (parent link or wm-fork- prefix)
- info banner explaining isolated vs shared semantics in a fork
- per-lake chip (emerald 'isolated' / amber 'shared with parent') with
  tooltips, matching the pipeline graph chip colors
- fork_behavior added to DucklakeSettingsType and preserved through
  convertDucklakeSettingsToBackend

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 09:58:06 +02:00
hugocasaandClaude Opus 4.8 7c7d7474cc feat: support workspace forks on cloud using parent workspace limits (#9864)
* feat: support workspace forks on cloud using parent workspace limits

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

* docs: clarify count_paid_seats approximates rather than mirrors billing seats

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

* fix: non-admin fork UI, attach cap, and fork-count for cloud forks

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

* fix: cloud fork billing cache on rename, usage display, attach cap edge

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

* feat: fork count in cloud quotas + fork billing points to parent

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

* fix: invalidate billing/fork caches on fork deletion for id reuse

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

* fix: gate fork usage remap on CLOUD_HOSTED, not just the cloud feature

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

* docs: note cloud feature vs CLOUD_HOSTED gating in backend guide

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

* fix: reserve fork-cap slots for an attach candidate's whole subtree

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

* fix: invalidate team-plan cache on delete, raise fork depth cap

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

* feat: cap fork nesting depth (MAX_FORK_DEPTH, default 5)

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

* fix: fork count/height robust to cycles and deleted intermediates

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

* fix(frontend): reset fork button loading state on creation error

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

* fix: invalidate billing cache for attached fork subtree; helper auth docs

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-02 23:59:02 +02:00
Ruben FiszelandClaude Opus 4.8 76a9523009 feat: use derived username instead of email for non-member superadmins (#9857)
* feat: use derived username instead of email for non-member superadmins

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

* fix: address review - drop redundant username cache, guard whoami membership by email

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

* refactor: use explicit non_member boolean instead of role string for superadmin banner

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

* fix: resolve email from password table for non-member superadmin permissioned_as

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

* fix: resolve non-member superadmin drafts via shared username->email resolver

Adds resolve_username_to_email (usr, then super_admin password fallback for both derived-username and email modes) and uses it in get_email_from_permissioned_as and the drafts get/list endpoints, so a non-member superadmin's drafts resolve and no email leaks into the drafts payload.

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

* test: superadmin-not-in-workspace schedule uses derived username as permissioned_as

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

* fix: resolve non-member superadmin identity in draft owner-circles, username_to_email, and home filter

Applies the password-fallback username resolution to the script/flow/app/draft owner-circle subqueries and the username_to_email endpoint (was an admins-workspace 'username == email' hack), and switches the home items-list user-folder filter to the non_member flag instead of the now-broken username-contains-@ heuristic.

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

* fix: backfill non-member superadmin favorites from email to derived username

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

* fix: propagate DB errors in username resolution instead of leaking email (CI review)

Addresses cubic-dev-ai P2: get_instance_username_or_fallback_to_email now returns Result and only falls back to the email for a genuine 'no derived username'; a query error propagates so callers fail closed rather than leaking the raw email as the acting username.

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

* fix: clarify non-member superadmin popover (username used + admin permissions)

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

* fix: keep username_to_email endpoint member-only to not disclose non-member superadmin email (CI review)

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

* fix: forbid disabling automate_username_creation once usernames assigned (CI review)

Makes the setting effectively one-way once instance-wide usernames exist, so the global-uniqueness invariant that keeps stored u/<username> identities (schedules/triggers/drafts/superadmin ownership) unambiguous can never be dropped back to workspace-local uniqueness. Re-saving false on an already-disabled instance stays a no-op.

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-01 14:54:07 +00:00
hugocasaandClaude Opus 4.8 b4b0c6a93e feat: add dev workspaces paired with a lockable prod workspace (#9793)
* feat: add dev workspaces paired with a lockable prod workspace

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

* fix: gate dev-workspace prod-lock on admin and prevent attach cycles

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

* feat: redirect locked-prod edits into the dev workspace

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

* fix: make dev-workspace settings tab available on CE (was EE-gated)

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

* feat: lock prod against forking too and funnel edits to the dev workspace

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

* feat: open dev item page on edit and tailor dev-workspace lock messages

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

* feat: prevent nested dev workspaces and hide dev option when one exists

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

* chore: drop the redundant already-has-dev hint on the fork form

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

* feat: badge dev workspaces and sort them ahead of forks in the tree/switcher

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

* feat: label dev workspaces as 'Dev workspace of X' instead of 'Fork of X'

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

* feat: label edit as 'Edit in <dev>', cover editor headers, auto-expand dev in tree

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

* feat: split prod lock into separate block-deploy and prevent-forking toggles

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

* feat: make resources/variables workspace-specific from compare page

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

* feat: steer AI-chat sessions to the dev workspace

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

* feat: refine session fork options and lock guidance for dev/prod

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

* fix: session picker reads prod's real rules, default to current ws

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

* feat: copy members into forks and clarify dev-workspace root labeling

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

* style: place the workspace id field under the fork name

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

* fix: address dev-workspace review findings and harden fork detection

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

* chore: regenerate sqlx offline cache

Restores entries dropped during the origin/main merge and adds the
dev-workspace queries (is_dev_workspace, ws_specific, has_parent).

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

* fix: address second-round dev-workspace review findings

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

* fix: address Pi and Codex review findings on dev-workspace endpoints

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

* fix: gate locked-dev git-branch fork on admin and validate ws_specific path

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

* fix: clear prod dev-lock when deleting an attached dev workspace

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

* refactor: consolidate dev-workspace migration and scope all-group join to attach

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

* fix: restore dev-workspace CHECK into consolidated migration and scope all-group join

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

* refactor: drop copy_members from the dev-workspace attach path

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

* fix: dev-workspace lifecycle/auth fixes from Codex review round

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

* feat: explicit create-in-other for workspace-specific items

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

* fix: make create-in-other strictly create-only (never overwrite target)

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

* fix: return 403 (not 401) for dev-workspace permission denials

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

* feat: allow attaching a same-family fork as a dev workspace

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

* style: emphasize the go-to-dev action in the no-direct-deploy alert

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

* fix: seed a resource's linked variables when creating it in the other workspace

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

* fix: judge workspace deploy/fork locks against the user's identity in that workspace

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

* style: clarify create-in help text in workspace-specific panel

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

* fix: admin-gate dev-workspace creation and harden lock/seed edges

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

* fix: preserve a staged fork's source on picker create-mode re-entry

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

* fix: clear dev flag on archive and check dev existence server-side

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

* fix: make create-in-other atomically create-only via direct create

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

* fix: create-only resource insert, ws-specific list scopes, archive lock guard

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

* fix: reserve the dev_workspace_lock protection-rule name from the public API

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

* docs: reattach create_protection_rule doc comment to its function

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

* refactor: make dev-archive pairing teardown atomic with the archive

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

* fix: follow deploy_to on root rename; show dev pairing to non-member prod admins

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

* fix: copy creator metadata on fork; invalidate fork routing cache on rename

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

* fix: accept g/ paths in set_ws_specific; gate copy_members to dev workspaces

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 17:01:07 +00:00
GuilhemandClaude Opus 4.8 42c5e7a3fc feat: scope AI sessions per workspace root with lifecycle reconcile (#9734)
* feat: scope AI sessions per workspace family with lifecycle reconcile

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

* refactor: centralize session reconcile trigger + extract pure lifecycle decision

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

* perf: remove unused workspace family index

* refactor: scope sessions by workspace root id, drop family_id column

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

* fix(sessions): preserve user-archived sessions when archiving their workspace

archiveSessionsForWorkspace tagged every session archivedByWorkspace, including ones the user had already archived by hand, so a later workspace unarchive auto-restored them. Skip already-archived sessions so only workspace-archived ones are tagged, matching decideSessionLifecycle.

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

* fix: archived-session banner with unarchive, suppress workspace-gone banner while archived

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

* fix: re-root sub-fork sessions on reconcile when an ancestor is deleted

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

* feat: group AI sessions by workspace family with show-all-workspaces filter

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

* chore: revert unrelated AIProviderPicker cosmetic changes

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

* fix: hide per-session unarchive when workspace is gone, show move/discard instead

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

* fix: GC attached files on lifecycle delete + reconcile on sidebar fork delete

Addresses Codex review: deleteSessionsForWorkspace/reconcile delete now GC linked files (deleteItemsForSession), matching deleteSession; sidebar deleteFork now reconciles so surviving child forks re-root off the deleted ancestor. Also de-flaked post-rehydrate reads in the IndexedDB tests via vi.waitFor.

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

* fix: don't strand user if post-delete reconcile throws; refresh stale warmSessions comment

Addresses auto-review P2s: wrap reconcileAfterWorkspaceChange in deleteFork so the parent switch + navigation always runs even on reconcile failure; correct the warmSessions comment which no longer holds under 'Show all workspaces'.

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

* fix: don't fail/strand fork archive+delete when client session cleanup throws

Addresses cubic P1/P2 on forks/compare: the workspace archive/delete is authoritative; wrap the best-effort session cleanup + reconcile so a local IndexedDB failure neither falsely reports failure nor blocks navigation away from the gone fork. Mirrors the SidebarContent fix.

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

* docs: drop drafting-history aside from reconcileAfterWorkspaceChange comment

Addresses auto-review P2: keep the refresh-before-reconcile invariant, drop the 'which they did inconsistently' narration per AGENTS.md (comments record constraints, not drafting history).

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

* fix: clean up sessions on fork-id reuse + make all workspace-mutation cleanup best-effort

Addresses Codex P1s: (1) CreateWorkspaceInner 'permanently delete existing fork' (id-reuse) now drops local sessions for that id so they don't resurface on the recreated fork; (2) workspace_settings archive/delete and SidebarContent child-delete loop + main delete now treat post-mutation session cleanup as best-effort, so a local IndexedDB failure can't strand the user or abort remaining deletes (matching the compare-page fix).

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

* fix: make fork-reuse session cleanup fire-and-forget (non-blocking)

Addresses cubic P2: don't await the best-effort cleanup so a slow IndexedDB op can't block the delete/reuse flow.

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

* fix: drop previous user's transient drafts on user change

Addresses Pi P1: hydrateSessions preserved transient (unsent) drafts across user changes, so user A's draft + its pending fork/workspace state bled into user B's list and got reused by createSession. onUserChange now drops transients when the email changes; reconcile (intra-user) still preserves them. Regression test added.

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-06-24 14:41:18 +02:00
Ruben FiszelandClaude Opus 4.8 6e96f90065 fix(frontend): destroy old WebsocketProvider on workspace switch in MultiplayerMenu (#9719)
Switching workspaces created a new WebsocketProvider without destroying
the old one. The leaked provider kept reconnecting, causing alternating
websocket traffic between old and new workspace rooms and flickering in
the Live Activity sidebar.

Add a disconnectWorkspace() cleanup that destroys the provider and resets
connected/awareness state, call it at the start of connectWorkspace()
before creating a new provider (matching ScriptEditor.svelte), and run it
from an onDestroy hook so the provider is torn down on unmount.

Fixes WIN-2086

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:20:08 +02:00
Diego ImbertandClaude Opus 4.8 7cb5c6e749 fix(frontend): reset deleteWorkspaceForkModal on confirm in SidebarContent (#9619)
The on:confirmed handler for the delete-fork ConfirmationModal never reset
deleteWorkspaceForkModal to false. Since SidebarContent persists across
workspace switches, the stale true state caused the delete-fork modal to
immediately reappear when a new fork workspace was created. Reset the state
before calling deleteFork(), matching the on:canceled handler and the pattern
in forks/compare/+page.svelte and SessionWrapper.svelte.

Fixes WIN-2057

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 17:07:13 +02:00
Ruben FiszelandClaude Fable 5 57e627eabf fix(frontend): stop live activity flickering when user has multiple tabs (#9543)
* fix(frontend): stop live activity flickering when user has multiple tabs

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

* fix(frontend): use hasOwnProperty instead of Object.hasOwn for ts lib compat

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 00:11:24 +02:00
GuilhemandClaude Opus 4.7 eadeac248b feat: sessions page with isolated AI chat + flow editor (#9034)
* feat(sessions): chat + editor side-by-side with multi-session state

Introduces the Sessions feature: a workspace where the AI chat and an
editor (flow / script / app / raw-app) sit side-by-side, with each session
having its own AIChatManager instance, history, and target item. Sessions
are persisted across reloads and can be staged into forks for review.

Key pieces:

- sessions/ — SessionWrapper (the split-pane shell), SessionPicker
  (sidebar list), SessionForkBar, SessionWorkspaceBar, FlowEditorView /
  ScriptEditorView / AppEditorView / RawAppEditorView, ForkDiffDrawer,
  sessionRuntime (per-session AIChatManager + draft state),
  sessionState (in-memory + persisted index), sessionUnread, sessionScope,
  appDraftCodec / flowDraftCodec, forkEditUrl, /sessions route.

- WorkspaceItemDrillPicker refactor — extracts WorkspaceItemRow + adds
  surfaceAI drafts, stale-while-revalidate. workspacePicker.ts drops
  explicit invalidate() in favor of always re-fetching in the background.

- ForkDiffDrawer + WorkspaceItemDiffViewer — per-kind diff bodies
  reusable from the compare page. FlowGraphDiffViewer / FlowGraphV2 gain
  inlineDiff forwarding + onHeight callback for equal-height layout.

- Global AI chat sessions plumbing — AIChatManager exports the class +
  adds disabledModes, beforeSend hook, scoped instance context. AIChat /
  AIChatDisplay accept session-only props (wideLayout, emptyHint,
  inputPreface, hideHeader, hideModeSelector, forceDisabled). Chat
  preserved across /flows/add → /flows/edit, /scripts/add → /scripts/edit.

- Draft-first loaders — sessions open drafts when present, otherwise
  seed a draft from the last deployed value via globalDraftStore.
  RawAppEditor / AppEditor / AppEditorHeaderDeploy get newApp prop +
  fixes so draft-only apps can deploy.

- Compare page (/forks/compare) — bigger overhaul to plug into the new
  drawer.

- Sidebar — Sessions entry + unread badge + status dot in
  SidebarContent / MenuButton / SideBarNotification.

- Misc fixes — chat group color palette constraint, deploy_workspace_item
  confirmation dropped, open_preview tool, picker drafts surfacing,
  fork archive/delete buttons on compare page.

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

* fix(sessions): bypass UserDraft inside session panes + sessionUnread crash

After merging main's UserDraft PR (#9121) into the sessions branch, two
integration issues surfaced:

1. AppEditor.svelte calls `UserDraft.use<App>('app', path)` at the
   component level — keyed by ($workspaceStore, 'app', path). Sessions
   that haven't materialized a fork yet stay at the user's main
   workspace, so a session targeting an app at the same path as a
   regular /apps/edit tab shared the same LS key. The session would
   read the regular tab's autosave and write its fork-edits back over
   it.

   Gate UserDraft.use on `!getContext('aiChatManager')` — sessions
   inject the manager via setContext, so inside a session pane the
   handle is `undefined`, stateApp falls through to the `app` prop
   the session loaded, and the auto-save $effect bails. Same gate on
   the four UserDraft.remove call sites in AppEditorHeader and
   RawAppEditorHeader so save/deploy from a session pane doesn't wipe
   the LS draft of a non-session tab at the same path.

2. sessionUnread.svelte.ts called useLocalStorageValue at module
   scope. Main's PR added a deep-mutation $effect inside that helper,
   which now requires component-initialization context — every page
   crashed at import time with `Svelte error: effect_orphan`.
   Replaced with a plain module-level $state + manual localStorage
   persist; same reactivity contract for callers.

3. ScriptEditorView.svelte was passing a `replaceStateFn` prop that
   ScriptBuilder dropped on main. Removed.

Verified end-to-end with Playwright:
- /flows/edit/{path} regression: UserDraft handle still created, no
  console errors
- /sessions loads, sessionUnread doesn't crash
- Session targeting non-raw app `u/admin/userdraft_collision_test`
  displays the fork content (FORK_ONLY_MARKER) even with an LS
  poison at `userdraft/w/local/app/{path}` containing a
  POISONED_BY_REGULAR_TAB_AUTOSAVE marker; poison remains untouched
  after the session loads and renders

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

* fix(sessions): stop fork-create retry loop on first user message

Removed the SessionWrapper $effect that retroactively committed the
session's workspace from the in-memory chat history. When opening a
session whose previous commit attempt had failed (or whose response was
lost) the effect ran in a tight retry loop, flooding the user with
`workspace_pkey` violations from `create_workspace_fork`.

The send path already commits through `AIChatManager.beforeSend` →
`commitSessionWorkspace`, which is the deterministic moment-of-action.
The $effect was a redundant reactive bridge that turned every backend
failure into an infinite retry.

Also hardens `materializeFork`/`commitSessionWorkspace` so the most
common cause of the duplicate-key error self-heals:

- `materializeFork` short-circuits when `fork.id` is already in
  `$userWorkspaces` (the previous create actually succeeded, we just
  lost the response). On a `workspace_pkey` catch, refresh the workspace
  list and adopt the existing row instead of toasting an error.
- On a real `materializeFork` failure, `commitSessionWorkspace` now
  drops `pending_fork` so the session falls through to the
  workspace-pick fallback instead of looping on the same broken intent.

* feat(sessions): show EditorHeader breadcrumb in the not-found state

When a session's target item has been deleted or moved, the editor pane
used to render a bare "Script not found at path X" line — leaving the
user with no way to navigate to a different target without backing out
of the session.

Each editor view now renders a `SessionItemNotFound` shell instead: a
real `EditorHeader` (read-only summary, no pen popover) with a
breadcrumb keyed to the missing kind+path, plus the "not found" copy
below. Clicking any breadcrumb segment opens the workspace picker
scoped to that level — pick a replacement and the session swaps target
via the existing `onNavigate` callback.

`SessionItemNotFound` maps `raw_app` to `EditorHeader`'s `kind: 'app'
+ raw_app: true` so the picker routes through `/apps_raw/...`; the
local label still says "Raw app not found" (not "App not found") so
the user knows which surface is missing.

* fix(picker): stop self-feeding fetch effect that OOM'd the tab

The drill picker's $effect watched `scope` and called `ensureLoaded`
on every change. `ensureLoaded` reads `loaded[kind]` synchronously
(to decide whether to show a spinner), so the effect ended up
subscribed to the very signal it fills. Each fetch result wrote
`loaded[kind] = items`; Svelte 5's $state proxy notifies on every
property set even when the reference is unchanged from cache, which
refired the effect, which called `ensureLoaded` again, which awaited
the cached fetch, which wrote `loaded[kind]` again... runaway loop.

In `/scripts/edit/...` the picker's lifecycle stabilised quickly
enough to mask the loop, but in a session pane (multiple warm
sessions, picker kept alive by the surrounding state) the cycle
spun freely — 29.8 million iterations in <100 ms during testing,
enough to OOM Firefox / kill the Chromium tab.

Two changes:

- Replace the scope-watching $effect with an explicit `setScope()`
  helper called from `drill()`, `goUp()`, and `onMount`. Fetch is
  now a callback reaction to user navigation, never a reactive
  consequence of one. No closed feedback cycle is possible.

- Untrack the `loaded[kind]` read inside `ensureLoaded`. The search
  $effect (which loads every kind on first keystroke) is still a
  reactive caller; the untrack stops it from subscribing to the
  signal `ensureLoaded` fills, so the same loop can't form there.

* feat(script-editor): wire initialTestPanelCollapsed through ScriptBuilder

The `initialTestPanelCollapsed` prop was already declared on
`ScriptBuilderProps` (used by the session preview to start the editor
with the run/test pane closed) but never destructured in
`ScriptBuilder.svelte`, so the value silently dropped on the floor
and the test pane always opened.

- `ScriptBuilder.svelte` — destructure the prop and forward it to
  `<ScriptEditor>`.
- `ScriptEditor.svelte` — accept the prop and seed `rawTestPanelSize`
  to 0 when true, while keeping `storedTestPanelSize` at the default
  30 so the user's first toggle expands the pane to a sensible width
  rather than 0.

Regular `/scripts/edit/...` doesn't pass the prop → default `false`
→ panel still opens by default.

* fix(sessions): resolve aiChatManager via context in AskUserQuestionDisplay

Inside a session the chat uses a per-pane AIChatManager injected via context. AskUserQuestionDisplay imported the global singleton, so answers clicked in a session dispatched to the singleton's callback map and the AI loop stalled. Resolve via getContext with singleton fallback, matching ChatMode / ToolExecutionDisplay.

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

* fix(raw_apps): let preview start in single-view on the preview tab

Add a defaultSplitWithPreview prop (default true). When false (session preview), the editor boots in single view with the preview tab selected: gate the onMount default-file activation, the setActiveDocument auto-activation, and iframeShouldMount so the UI Builder bundler iframe still mounts when preview is the active tab. RawAppEditorView passes defaultSplitWithPreview={false}.

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

* feat(copilot): add get_preview_status tool and make open_preview idempotent

So the assistant can tell whether the session preview already shows the item it just edited, instead of re-opening or re-offering it. Mirrors the open_preview handler plumbing (setGetPreviewStatusHandler) and the session runtime registers it alongside open_preview. open_preview now returns 'already open' when the requested target matches the active session's current target. The system prompt steers the AI to check status before offering. Unit tests cover the no-arg schema, the session-only error, and handler dispatch.

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

* fix(sessions): make script preview reactive to AI draft writes

ScriptEditorView read the draft via static UserDraft.get inside an effect, which only subscribes to UserDraft's reactive cell when a live entry exists. None did for the preview path, so the chat's writes (UserDraft.save) only touched localStorage and the open preview never updated. Hold a live handle via UserDraft.useMany (reactive getter so it re-acquires when open_preview swaps the path without remounting) and read inbound through handle.draft, materializing the shared $state cell that bridges the chat's writes to the editor.

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

* fix(sessions): make raw-app preview reactive to AI draft writes

Mirror of the script-preview fix. RawAppEditorView read the draft via static UserDraft.get inside an effect, which only subscribes to UserDraft's reactive cell when a live entry exists. None did for the preview path, so the chat's raw-app writes (UserDraft.save / setDraftAndMeta, from write_app_file / patch_app_file / write_app_runnable) only touched localStorage and the open preview never updated. Hold a live handle via UserDraft.useMany (reactive getter so it re-acquires when open_preview swaps the path without remounting) and read inbound through handle.draft. Verified in-browser: an external UserDraft.save live-updates the bound summary in the open preview.

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

* fix(sessions): make flow preview reactive to AI draft writes

Mirror of the script/raw-app preview fixes, completing two-way binding for all three session editor kinds. FlowEditorView read the draft via static UserDraft.get inside an effect, which only subscribes to UserDraft's reactive cell when a live entry exists — none did, so the chat's writes (write_flow / patch_flow_json / set_flow_module_code) only touched localStorage and the open preview never updated. Hold a live handle via UserDraft.useMany (reactive getter so it re-acquires when open_preview swaps the path without remounting) and read inbound through handle.draft. Verified in-browser both directions: an external UserDraft.save live-updates the flow header summary and rebuilds the module graph; a preview edit propagates through the debounced save to both UserDraft.get and the chat's getGlobalDraft adapter.

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

* feat(sessions): surface local-storage drafts in fork diff & compare page

Augments the backend fork-vs-parent comparison with browser-local (UserDraft) drafts so a session's uncommitted AI/user changes are visible in the Fork Diff Viewer and the /forks/compare page. Adds forkDraftDiff.ts (augmentForkComparisonWithLocalDrafts + getForkItemValue), a 'local changes detected' / new-draft warning surface (checkbox-slot warning icon, no-op-baseline filtering, dedup), a 'Local draft <> fork' tab in DiffDrawer, and selectTooltip/nonSelectableTooltip plumbing in Row/WorkspaceDeployLayout.

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

* Revert "feat(sessions): surface local-storage drafts in fork diff & compare page"

This reverts commit 3cfd858e36.

* fix(sessions): leave for home when switching workspace from the session page

An AI session is scoped to its (forked) workspace, so it makes no sense to keep showing it after the user picks a different workspace. The workspace switcher's link href now points home on the session route (the link navigation wins over onClick's preventDefault), and toggleSwitchWorkspace also redirects home there as a fallback. Session-switching uses a separate path (syncWorkspaceTo), so it's unaffected — which is why reacting at the switcher is more robust than watching workspaceStore.

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

* fix(sessions): clear session highlight off the session page; default delete-fork on

Two SessionPicker fixes: (1) only highlight the active session while on the /sessions route — currentSessionId lingers after navigating away, so the row stayed selected in the sidebar; gate the highlight on the route. (2) The 'Also delete forked workspace' toggle in the delete-session modal now defaults to on (the fork is tied to the session and would be orphaned otherwise); resets keep it defaulted-on for the next open. User can still untick it.

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

* fix(copilot): say "local storage" instead of "draft" in write-tool status

The global chat's write tools persist to the browser's localStorage (UserDraft), not a workspace draft. The tool status / result messages now say the item was saved to local storage (and discard says it was discarded from local storage) so users aren't misled into thinking a workspace draft was created. Covers the shared script/flow/trigger/resource/variable helpers and the app tools.

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

* fix(sessions): sidebar collapse, new-session chat, fork delete & not-found nits

- Hide the collapse chevron and make the section header non-interactive when there are no sessions; reset the persisted collapsed state while the list is empty so the first session always appears expanded.
- Stop grafting a recent past chat onto a freshly created session: ensureChatIdsSeeded now skips transient sessions, so the seed only pairs untagged chats with pre-existing sessions.
- After deleting a fork from a session (SessionPicker / SessionWrapper), fall back to the fork's parent workspace when the deleted fork was the active one, instead of stranding the user on a deleted workspace.
- Show a 'Session not found' message (with a New session action) when the URL names a session that doesn't exist, rather than rendering a blank page.

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

* feat(copilot): expose preview tools only to session chats

open_preview and get_preview_status drive a session's side-panel editor, so they only make sense inside an AI session. They were always present in the global tool list and just errored when called outside a session. Now AIChatManager carries an isSessionChat flag (set by sessionRuntime.createRuntime); the GLOBAL-mode branch uses globalToolsFor({ sessionPreview }) to drop the two tools for the regular side-panel chat, and prepareGlobalSystemMessage omits their guidance unless previewTools is set. The module-level handlers + in-tool error guards stay as defense in depth.

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

* chore(flow-editor): move intra-editor chat preservation to its own PR

The beforeNavigate / preserveChatOnDestroy guard that keeps the global FLOW
chat alive across same-flow editor remounts is a standalone global-chat fix,
unrelated to sessions. Split out to #9339; FlowEditor reverts to the plain
session-guarded saveAndClear lifecycle here.

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

* fix(raw-app): pre-boot session editor hidden so files open instantly

In single-view (sessions) the UI Builder iframe was mounted inside a
display:none wrapper while the Preview tab was active, so the VS Code
workbench booted at 0x0, threw in its LayoutService ("Unable to figure
out browser width and height"), and wedged the editor on "Loading
editor" with no recovery when later revealed.

Keep the iframe mounted at the editor area's real width and hide it with
visibility instead of collapsing it: Monaco boots correctly while hidden,
and revealing a file is an instant un-hide (no reload, no relayout, no
latency).

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

* chore(flow-ai): move flow-group color-palette work to its own PR

The flow-group color-palette guidance + validateFlowGroups guard + tests are
an independent flow-AI improvement, not part of sessions. Split out to #9343;
these three flow files revert to their main state here.

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

* fix(sessions): hide the in-editor Flow AI Chat button in the session preview

The flow preview pane in a session already sits next to the session's own AI
chat, so FlowBuilder's in-editor "Flow AI Chat" toggle (which opens the global
singleton chat) is redundant and confusing there. Pass
customUi={{ topBar: { aiBuilder: false } }} from FlowEditorView, reusing the
existing showFlowAiButton gate (!disableAi && customUi?.topBar?.aiBuilder !=
false) that flows down to FlowStickyNode — no new prop needed.

Verified in-browser: the button (WandSparkles) renders in the regular
/flows/edit route but is absent in the session preview for the same flow.

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

* fix(sessions): mirror /scripts/add for never-saved scripts in editor preview

An AI-created script with no backend version yet left savedScript undefined
in the session preview, which disabled Save draft and hid Show diff. Open it
as a new script (empty initialPath) like /scripts/add so Save draft is enabled
and creates it on first save; seed the path as already-chosen
(initialPathChosen) so the summary->path auto-slug does not rename the
AI-assigned path. On first save ScriptBuilder writes savedScript back through
the bind and flips into edit mode (Save draft + Show diff) without navigating
away.

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

* fix(sessions): refresh fork diff count after an editor draft save

The fork-bar diff count reads a cached comparison refreshed only on AI-turn-end or tab refocus. A 'Save draft' in the session editor registers in the backend fork tally asynchronously (~300ms after the create returns), so the count stayed stale until one of those triggers fired. Add SessionRuntime.scheduleForkComparisonRefresh() (re-fetches at 700ms + 2200ms to clear the async tally) and wire it to onSaveDraft in ScriptEditorView and FlowEditorView.

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

* fix(sessions): don't auto-open the settings drawer in script preview

When the AI's open_preview tool previews a never-saved script, ScriptEditorView
passes initialPath='' so ScriptBuilder behaves like /scripts/add. That empty
path also triggered ScriptBuilder's auto-open of the settings drawer, which is
unwanted in the session preview where the AI manages metadata. Pass
neverShowMeta so the drawer stays closed on mount; the Settings button still
opens it manually.

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

* fix(sessions): don't host legacy drag-and-drop apps in the editor preview

The session preview pane only hosts code-based items (flow, script, raw
app). Drop the legacy 'app' kind from SessionTarget and the open_preview
tool, and route a legacy app picked in the drill picker to the standalone
/apps/edit editor instead. Removes the now-dead AppEditorView and its
runtime load path.

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

* fix(sessions): don't prompt to discard raw-app changes on navigation

In a session the raw-app editor's content is continuously persisted to the
UserDraft (localStorage), so tearing the editor down on navigation loses
nothing. Skip the UnsavedConfirmationModal (and its beforeNavigate guard)
when the editor is mounted inside a session pane; the standalone /apps_raw
editor still shows it.

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

* fix(sidebar): pin Help to the bottom instead of floating

The bottom of the sidebar stacked the User/Settings cluster and the Help
block with a fixed ~40px gap between them, plus a bottom margin that kept
Help from sitting flush — so Help appeared to float. Drop those fixed
margins so the cluster and Help stay glued at the bottom with a small gap
and Help is flush, and let mt-auto own the flexible space above the group.
Add pt-4 so the cluster keeps a minimum gap from the Triggers section when
the sidebar runs out of room and that flexible space collapses.

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

* fix(sessions): surface diff/discard for AI script drafts in preview, refresh diff on deploy

loadScript built the editor's scriptStore by aliasing and mutating savedScript.val, so the deployed baseline got overwritten with the draft content and the diff compared draft-vs-draft. Clone the baseline before layering the AI draft on top. On load, when the local draft diverges from the saved baseline, surface a toast ('AI saved a local draft') with Show diff (opens the diff drawer with a Discard-draft button) and Discard local draft — mirroring the regular /scripts/edit affordance the session's parallel loader omitted. Also wire onDeploy (alongside onSaveDraft) to scheduleForkComparisonRefresh so the fork diff count refreshes after a deploy, not just on an AI turn or tab refocus.

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

* fix(sessions): script preview restore/deploy feedback; drop on-load draft toast

- Implement real restoreDeployed/restoreDraft for the diff drawer: the shared loadScript-based handler was a no-op (loadScript early-returns on the loaded path and would re-read the local draft). Reset the live UserDraft handle to the chosen baseline (deleting the backend draft for 'restore to deployed') so the inbound effect syncs the editor.
- Show a 'Deployed' toast on deploy: the default Deploy takes ScriptBuilder's no-toast branch (the editor navigates away instead); the session stays put, so surface the success toast.
- Remove the on-load 'AI saved a local draft' toast: unnecessary in a session, where the user already expects their changes to be present. Diff/discard remain reachable via ScriptBuilder's Show diff + the diff drawer's restore buttons.

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

* fix(sessions): gate breadcrumb picker draft-merge behind the dev flag

The WorkspaceItemDrillPicker merges localStorage UserDrafts into its
navigable items so in-flight session/chat drafts are reachable. That
merge was ungated, so with the sessions dev flag off it also surfaced
the standalone editors' autosave drafts — they appeared as navigable
rows that 404 on the backend draft fetch. Gate aiDraftsForKind on
isGlobalAiEnabled() so it is a no-op without the flag (no sessions
exist then anyway); inside sessions the merge still works.

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

* fix(editor): reload script/flow editor on client-side breadcrumb nav

Picking a different item in the editor-header breadcrumb picker calls
goto() for a client-side navigation. SvelteKit reuses the same +page
instance across a path-param change, but the script and flow editor
routes captured `draftPath` and the `UserDraft.use()` handle once at
mount and never remounted ScriptBuilder/FlowBuilder. The URL and title
updated while the editor kept showing the previous item's breadcrumb,
summary and content; only a full reload showed the navigated-to item.

Mirror the pattern the app / raw-app editors already use:
- Derive the draft path from the URL and key the handle off it via
  `UserDraft.useMany` (a stable proxy onto the current handle), so the
  reload reads/writes the navigated-to item's draft instead of the
  previous one's — fixing the stale draft-comparison too.
- Gate the builder subtree on a `renderEditor` flag flipped false when a
  navigation kicks off the reload and true once the data is ready, so
  the builder cleanly unmounts and remounts once against stable data. A
  synchronous `{#key}` swap instead races Monaco's async init against
  the torn-down container.
- Flows also reset `nobackenddraft` per navigation so a fresh load
  reconsiders the backend draft.

The unsaved-changes guard is unaffected (it runs in beforeNavigate,
before the remount). The app and raw-app editors already handled this
and are unchanged.

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

* feat(sessions): sync preview with the deployed version on editor + chat deploy

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

* feat(sessions): reload the preview after a chat raw-app deploy

The deploy-reload-preview callback added previously only fired for script and
flow. Now that the merged deploy_workspace_item tool can deploy raw apps
(bundle + createAppRaw/updateAppRaw), wire raw apps in too. A raw app deploys
under type 'app' but the session preview addresses it as 'raw_app', so the
deploy handler maps 'app' -> 'raw_app'; the runtime open-check gains the
loadedRawAppPath case. syncPreviewWithDeployed already handled 'raw_app'
(discard the local draft + force-reload via loadRawApp), so no runtime change
was needed there.

Adds a unit test asserting deploy_workspace_item(type:'app') notifies the
session handler with { kind: 'raw_app', path }.

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

* fix(sessions): address Claude PR review (3 P1 + 3 P2 + test)

P1:
- Drop the hardcoded placeholder default sessions (u/guilhempw/...). New users
  (empty/cleared/private-browsing localStorage) now start with no sessions and
  see the empty state instead of unresolvable "session not found" rows.
- Scope the preview/deploy tool handlers to the *calling* session. open_preview,
  get_preview_status and the deploy reload handler dispatched via the global
  currentSessionId, so a backgrounded session's tool call mutated the UI-active
  session. The calling session id is now carried in the per-manager tool
  `helpers` (AIChatManager.sessionId, set in createRuntime) and threaded through
  the tool ctx to the handlers, which dispatch to it (falling back to the active
  id only when absent). Keeps backgrounded sessions isolated.
- beforeSend now aborts the send on failure: commitSessionWorkspace throwing used
  to be swallowed, letting the message go out against the wrong workspace
  silently. Now it toasts and returns. Also guarded the unguarded
  listUserWorkspaces refresh in materializeFork's duplicate-key self-heal so a
  second network failure can't rethrow past the toast-and-return contract.

P2:
- disposeRuntime now clears the fork-comparison refresh timers (700ms/2200ms)
  via a new runtime.dispose(), so an evicted/deleted runtime can't fire a stray
  refreshForkComparisonNow/compareWorkspaces after teardown.
- Convert Svelte 4 on:click -> Svelte 5 onclick on the Button components in
  SessionWrapper, SessionForkBar, ForkDiffDrawer, SessionPicker, sessions/+page.
- WorkspaceItemRow's <a href> branch gains role="option" + aria-selected to match
  the <button> branch, for consistent listbox semantics.

Tests:
- core.test.ts: deploy_workspace_item(type:'app') threads the calling session id
  through helpers to the deploy handler ({ sessionId, kind:'raw_app', path }).
- New sessionState.test.ts unit-tests deriveForkStatus + isForkSession across
  all branches (root/fork/unavailable/draft, ahead/behind/diverged/in_sync).

svelte-check 0 errors; 57 frontend unit tests pass.

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

* refactor(copilot): collapse deploy preview-reload dispatch to a type→kind map

Replace the if/else-if that mapped deploy type to preview kind with a single Partial<Record<WorkspaceItemType, ...>> lookup + one if. Non-previewable types map to undefined → no dispatch.

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

* refactor(copilot): use getAiChatManager() instead of inlining the context fallback

Six chat components still inlined
`getContext<AIChatManager>('aiChatManager') ?? singletonAiChatManager` even
though aiChatManagerContext.ts already exports getAiChatManager() for exactly
this (the resolve-scoped-instance-or-fall-back-to-singleton pattern, already
used by AIChatDisplay/AIChatInput/AIChatMessage/CodeDisplay). Adopt it in
DatatableCreationPolicy, ChatMode, ToolExecutionDisplay, AIChat,
AskUserQuestionDisplay and flow/FlowAIChat, and drop the now-unused getContext /
AIChatManager / singletonAiChatManager imports (FlowAIChat keeps getContext for
its FlowEditorContext/FlowCopilotContext lookups).

No behavior change — getAiChatManager() is the same resolution.

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

* fix(sessions): consistent script deploy → preview sync; trim session deploy menu

Two related session deploy fixes + clarifying comments.

1. Hide the extra deploy-dropdown options in the session script preview. The
   editor always "stays" and is already scoped to a fork, so Deploy & Stay here,
   Fork, Edit in workspace fork, Exit & See details and Export as YAML/JSON make
   no sense there — only "Show diff" is kept. ScriptBuilder gains
   `inSessionPane = !!getContext('aiChatManager')` (same pattern ScriptEditor
   uses) and gates those items. (They were correctly absent for never-deployed
   session scripts but leaked for deployed ones.)

2. Fire onDeploy on every successful script deploy. ScriptBuilder previously
   skipped onDeploy for "Deploy & Stay here" and lib scripts (it just re-pinned
   parent_hash + toasted), so a session preview wouldn't sync after those. Now
   onDeploy always fires with a `stay` flag; route consumers skip navigation when
   stay (behaviour identical to before — stay → toast only, primary → navigate),
   and the session ignores stay and always syncs. With (1) hiding Deploy & Stay,
   this now covers the lib-script-in-session case.

3. Comments: RawAppEditorHeader / AppEditorHeader note that the
   `if (!inSessionPane) UserDraft.remove` guards are intentional — the editor
   doesn't own the localStorage draft in a session (the runtime does, keyed by
   the fork); the session-side equivalent is the View's onDeploy →
   runtime.syncPreviewWithDeployed (discard fork draft + reload to deployed).

svelte-check 0 errors; session dropdown verified to show only "Show diff" for a
deployed script, route deploy menu unchanged.

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

* fix(sidebar): single Menubar so bottom menus hover-switch (WIN-1993)

The bottom sidebar group split Settings/Workers/Folders/Logs and Help across
two separate <Menubar> components. melt-ui's hover-to-switch (open menu closes
when another trigger in the same Menubar is hovered) only coordinates within a
single Menubar, so hovering between the two groups left both menus open
(stacked) instead of switching. Collapse them into one Menubar, wrapping each
group in its own flex container to preserve spacing.

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

* refactor(editor): gate external code sync behind opt-in syncExternalCode prop

The unconditional `code` prop->Monaco sync effect added for sessions
live-preview ran for every <Editor> caller (14 call sites). Most either
bind:code with their own external-sync (e.g. ScriptEditor) or treat code as
init-only, so a blanket setValue risked clobbering them. Gate the effect on a
new opt-in `syncExternalCode` prop (default off) and enable it only at the two
flow inline-rawscript editors — the case that actually needs external updates
(AI chat editing a flow module's content reflecting live in the preview).

Verified in-browser: AI-driven external edit to a flow step now reflects live
in Monaco, and typing keeps the caret intact (round-trip guard).

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

* fix(sessions): address P1 review findings (commit-abort, render-stuck, workspace sync)

From the cubic/Claude PR review:

1. beforeSend now aborts the send when the workspace isn't committed. The earlier
   fix only caught a *thrown* error, but commitSessionWorkspace returns undefined
   (never throws) when a staged fork fails to materialise — so the first message
   + its tool calls shipped to get(workspaceStore) (the parent). beforeSend now
   throws on undefined so AIChatManager's catch toasts + aborts.

2. The script/flow edit reload effect set renderEditor=false then called
   loadScript()/loadFlow(); a rejected fetch left renderEditor stuck false, so the
   editor pane vanished and never remounted. Both calls now .catch → toast +
   renderEditor=true (token-safe), so the pane always remounts.

3. SessionWrapper.moveAndActivate now syncWorkspaceTo(target) — moving a session
   off an unavailable workspace was leaving the app pointed at the old one
   (mismatch with moveSessionToNewFork / handleConfirmedDelete).

Test: sessionState.test.ts pins commitSessionWorkspace's failure contract
(returns undefined + drops pending_fork when the fork fails) — the invariant the
beforeSend abort relies on. svelte-check 0 errors; 58 frontend unit tests pass.

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

* fix(sessions): address P2 review findings (cubic)

Draft round-trip:
- appDraftCodec: carry custom_path through runtimeRawAppToDraft /
  applyDraftToRuntimeRawApp (+ seed it in loadRawApp) so a session round-trip
  no longer erases a raw-app draft's custom URL.
- sessionRuntime.loadScript "no draft" path: structuredClone the baseline before
  setting parent_hash — it could alias `result` (= savedScript.val) and corrupt
  the pristine deployed baseline the diff drawer reads.
- FlowEditorView: include `summary` in the inbound/outbound dedup sigs so
  summary-only changes propagate/persist.

Workspace-state on navigation:
- SidebarContent (post-delete) and workspace_settings (post-archive): guard the
  listUserWorkspaces() refresh so a transient failure can't strand the user on
  the just-removed workspace, and refresh the list before switching to parent.
- WorkspaceMenu: keep ?workspace=<id> in the session-page workspace href so a
  modifier/middle click (which bypasses onClick) lands in the right workspace.

UI/keyboard:
- WorkspaceItemRow: indent adds to the px-3 base (calc) instead of replacing it.
- ForkDiffDrawer: ArrowLeft maps a 2-segment file path (f/foo) to its scope
  folder (folder:f/foo) instead of a nonexistent folder:f.
- flows/edit: defer flowBuilder setup (primary schedule, draft triggers,
  loadFlowState) until after the builder remounts (renderEditor=true + tick),
  so reload-time state restoration isn't skipped on the unmounted builder.

svelte-check 0 errors; 58 frontend unit tests pass.

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

* test(sessions): unit-test the P1/P2 review fixes (extract pure helpers)

Extract the pure logic touched by the review fixes into small tested helpers
(behaviour-preserving) and add unit tests:

- appDraftCodec.test.ts — custom_path survives the runtime↔draft round-trip (A1).
- forkDiffNav.ts/.test.ts — parentFolderKey (extracted from ForkDiffDrawer):
  ArrowLeft parent resolution incl. the 2-segment-path case (C2).
- workspaceMenuHref.ts/.test.ts — extracted from WorkspaceMenu: session-route
  href keeps ?workspace=<id>; off-session swaps the param (B2).
- flowDraftSig.ts/.test.ts — extracted from FlowEditorView (dedups 3 sig sites):
  the dedup signature includes summary, so summary-only changes propagate (A3).

(commitSessionWorkspace failure-contract test for the beforeSend P1 landed with
the P1 commit.) svelte-check 0 errors; 75 frontend unit tests pass.

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

* fix(sessions): address second-round review (Pi + Codex)

Three findings flagged post-push (cubic was fully addressed in the prior
commits; this commit covers the new ones):

- [P1] commitSessionWorkspace non-fork branch — when a session created
  inside a fork defaults pending_workspace_id to the family root, commit
  set s.workspace_id but never synced workspaceStore. First send's
  logAiChat + tool calls then ran against the wrong (still-fork)
  workspace. Fix: syncWorkspaceTo(ws) after the commit, mirroring the
  pending_fork branch's switchWorkspace(newId).

- [P1] Warm-session live-editor slot hijack — /sessions keeps up to 3
  warm-mounted sessions; UserDraft stores one live editor per
  (workspace, kind). Each editor view unconditionally claimed the slot,
  so a hidden warm session in the same workspace+kind could overwrite
  the visible session's claim — chat actions like discard /
  "the open editor" then resolved to the wrong session. Fix: thread
  isActiveSession from SessionWrapper into Script/Flow/RawAppEditorView
  and gate setLiveEditorDraft on it.

- [P2] ForkDiffDrawer stale per-item raw diff cache — loadedDiffs /
  summaries persist for the drawer's lifetime; fetchComparison refetched
  on each open() but loadDiffFor short-circuited on cached keys, so an
  edit-then-reopen showed fresh counts but stale expanded content. Fix:
  clear both records at the top of fetchComparison.

Tests:
- sessionState.test.ts: 2 tests pinning commitSessionWorkspace's
  workspaceStore sync (mismatch and matching).
- userDraft.test.ts: 3 tests pinning the live-editor slot collision
  (regression), the active-session gate, and cleanup ordering.
- forkDiffCache.test.ts (new): 2 tests for the drawer cache
  invalidation contract via fetchComparison simulation.

Verified end-to-end in browser: P2 (close+reopen drawer triggered an
identical second batch of per-item get fetches), P1#1 (new-session send
from a fork synced localStorage.workspace to root and posted chat to
/api/w/local/...), P1#2 (raw_app slot for workspace=local correctly
follows the visible session across A→B→A switches while both stay
warm-mounted). svelte-check 0 errors; touched test suites green.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:22:50 +02:00
Ruben FiszelandClaude Opus 4.7 da882c54b2 fix(frontend): close other sidebar menus when hovering Help (#9354)
The Help menu lived in a separate Menubar from the Settings/Workers/
Folders/Logs group, so melt-ui's hover-to-switch logic (which only
spans menus within the same Menubar) did not close the Help popup
when the cursor moved to a sibling group, causing menus to stack.

Merge both bottom Menubars into a single Menubar, wrapping each
group in its own flex container to preserve the visual spacing.

Fixes WIN-1993

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 20:30:34 +00:00
Ruben Fiszel 8c1f6ccc5d fix: prevent undefined user flickering in multiplayer presence list (#9231) 2026-05-19 07:30:27 +00:00
Diego ImbertandClaude Opus 4.7 20ecd904e7 feat(operators): allow operators to access assets page (#9095)
* feat(operators): allow operators to access assets page

Adds the "assets" key to workspace operator_settings (defaulting to true
for existing and new workspaces) and toggles the frontend default so the
assets page is visible to operators by default.

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

* nit: remove settings btn when not available

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:36:34 +00:00
Diego ImbertandClaude Opus 4.5 e43a958c5c feat(forks): prompt to delete forked children when deleting a fork (#9097)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-11 12:30:37 +00:00
hugocasaandClaude Opus 4.7 4ba46c0e1c make workspace picker and run-page Edit button openable in new tabs (#9041)
Workspace rows in the sidebar picker and the Edit button on the run detail
page now render as `<a href>`, so middle-click and Ctrl/Cmd-click open
them in a new tab. Plain clicks keep their existing in-tab behavior
(workspace store switch / args prefill via `$initialArgsStore`).

The Edit href carries `?workspace=<current>` so the new tab loads the
editor in the same workspace as the run.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:35:13 +00:00
Ruben FiszelandClaude Opus 4.7 4483d0cab9 fix(workspaces): split get_settings into admin-only + public endpoint (#8990)
* fix: redact GitHub App tokens and Slack OAuth secret for non-admins

`GET /workspaces/get_settings` returned the full `git_app_installations`
JSONB to any workspace member. That column caches the GitHub App JWT and
installation token used by git-sync; the installation token is refreshed
on every git-sync action and valid for ~55 minutes, so the value sitting
in the DB is essentially always live. Null it out for non-admins,
matching the existing `slack_oauth_client_secret` redaction.

The tarball export's v2 settings format (added in #8935) included
`slack_oauth_client_secret` with no admin gating, regressing the same
redaction. Mirror the admin check there.

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

* refactor: split get_settings into admin-only + public endpoint

Adds `WorkspacePublicSettings` and `GET /workspaces/get_public_settings`,
which returns only fields safe for any workspace member to read
(workspace_id, slack/teams team identity, mute_critical_alerts, deploy_ui,
large_file_storage, datatable). `get_settings` is now admin-only via
`require_admin`.

Migrates frontend callers: every caller that read non-sensitive fields
(deploy_ui on trigger pages, mute_critical_alerts on the root layout, slack
team identity for handler pickers, etc.) now uses `getPublicSettings`. The
admin-managed settings UI, git-sync admin context, operator settings,
checkout polling, and full settings page stay on `getSettings`.

This replaces the field-level redactions added in the previous commit:
the type system itself defines the public surface, so adding a sensitive
column to `workspace_settings` no longer defaults to leaking — it stays
out of `WorkspacePublicSettings` unless explicitly added.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:02:54 +00:00
d6c642b170 feat: add Azure Event Grid triggers (#8888)
* feat: add Azure Event Grid triggers (EE)

Introduces a new enterprise trigger kind `azure` that supports three
modes via a single unified trigger type:
- basic_push: Azure Event Grid basic — custom topics, system topics
  (Storage, Resource Manager, Key Vault, etc.), domains (push only)
- namespace_push: Event Grid Namespace topics (CloudEvents over HTTP push)
- namespace_pull: Event Grid Namespace topics (HTTP pull with lock-token
  ack/reject for dead-lettering)

Auth uses a Service Principal resource (tenant_id, client_id,
client_secret, subscription_id). Subscriptions are created in
CloudEvents 1.0 schema so the push webhook handler and the pull listener
share one payload parser.

Backend
- New crate `windmill-trigger-azure` (OSS stubs + EE impl symlinked from
  windmill-ee-private)
- Migration `azure_trigger` table with CHECK constraints enforcing
  mode/columns coherence
- `TriggerKind::Azure`, `JobTriggerKind::Azure`,
  `DeployedObject::AzureTrigger` variants
- Push route `/api/azure/w/{workspace}/*path` handles classic
  Event Grid SubscriptionValidation handshake and CloudEvents 1.0
  abuse-protection OPTIONS handshake
- Optional inbound JWT validation (audience check only for v1)
- Feature flag `azure_trigger` propagated through windmill-api,
  windmill-store (resource helper), and added to ee_core

Frontend
- `triggers/azure/` editor with mode toggle (basic/namespace-push/
  namespace-pull) and per-mode config (topic ARM id / namespace +
  topic name / subscription / filters / push auth / pull options)
- Registered in icon map, display names, save functions, badge,
  wrapper, editor, add-trigger menu

OpenAPI
- `AzureTrigger`, `AzureTriggerData`, `AzureMode`,
  `AzureSubscriptionMode`, `AzureDeliveryConfig`, `TestAzureConnection`
  schemas; `/azure_triggers/*` endpoints; client regenerated

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

* chore: update ee-repo-ref to eaa7c3a9cb37a9ccc93f10a2535d929365acd2d8

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

Previous ee-repo-ref: 9689014e8c12c36c1059fd8fa5758d550b8b8bc9

New ee-repo-ref: eaa7c3a9cb37a9ccc93f10a2535d929365acd2d8

Automated by sync-ee-ref workflow.

* feat(azure-trigger): secret-auth push, ARM discovery, capture isolation, CLI + parity

Frontend:
- Split mode selector into Namespace/Basic + Pull/Push
- ARM resource dropdowns (namespaces, Basic topics, namespace topics)
  populated from the service principal; cascade with stale-selection
  reset on SP / edition change
- Remove stale authenticate toggle + audience input (server-managed
  push_auth_config has replaced them)
- Azure listing page: "Create from template" button; "Also delete Azure
  subscription" toggle in the delete modal; simplified trigger label
  falling back to path
- AzureCapture.svelte: "Test subscription name" with -wm-capture suffix
- CompareWorkspaces.svelte: wire Azure for fork/compare
- Drop Trigger-deployed/event-loss warning (capture subscription is
  isolated with -wm-capture)

Backend:
- Shared-secret push auth (see EE crate for detail)
- JSONB push_auth_config column (renamed from delivery_config), #[serde(skip)]
  so clients/CLI/exports never see it
- Drop redundant enabled column; mode supersedes
- Azure capture infra: AzureTriggerConfig + set_azure_trigger_config +
  azure_payload route + TriggerKind::Azure arm; PT15M queue TTL on
  capture subscriptions so they bound storage after tab close
- Granular ACLs, users offboarding, trash, git-sync deployed-object:
  all include azure_trigger

CLI:
- Add azure to TRIGGER_TYPES, pushObj dispatch, getTypeStrFromPath,
  trigger commands (get/update/create/list/template), sync delete
  switch + regex; e2e test for `trigger new --kind azure`
- system_prompts: SCHEMA_MAPPINGS + schema_names include AzureTrigger;
  auto-generated/* regenerated

Skill:
- .claude/skills/adding-a-trigger/ checklist covering every file that
  needs editing when wiring a new trigger type (learned from this PR)

ee-repo-ref bumped to b0e490cbf3724b7b64c6a5b010e3bdf24acd873c.

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

* fix(azure-trigger): ci — ShareModal Kind + regenerated system_prompts

- frontend/src/lib/components/ShareModal.svelte: add 'azure_trigger'
  to the Kind type so the listing page's "Permissions" action compiles
  (ts2345 — caught by npm_check on CI, missed by fast-check locally).
- system_prompts/auto-generated/: regenerate to drop the stale
  delivery_config / AzureDeliveryConfig fields from the Azure schema
  (check-freshness on CI).

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

* refactor(azure-trigger): use workspace constant_time_eq crate

Drop hand-rolled constant-time compare in favour of the workspace
constant_time_eq crate (same one used by http_trigger_auth).

ee-repo-ref bumped to 9659382d47286e7f7f66d01b6f5dd8d4ed34848b.

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

* fix(azure-trigger): pass placeholder + disabled via inputProps

`TextInput`'s `placeholder` and `disabled` go through its `inputProps`
prop — CI's `npm run check` caught the stale top-level passing that
`npm run check:fast` missed. Align with the DefaultEmailConfigSection
pattern.

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

* fix(azure-trigger): correct LATEST_GIT_SYNC_SCRIPT_PATH version to 28213

The hub deploy of the azure-aware sync-script is version 28213, not
28214. Backend was pinning a non-existent hub script, which broke the
git_sync_e2e suite (every deploy's sync step 404'd).

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

* fix(azure-trigger): add azure_triggers to token scope selector + skill

- windmill-api/src/token.rs: `build_trigger_scope_domains` was missing
  `("azure_triggers", "Azure Event Grid")`, so the CreateToken UI's scope
  selector didn't surface azure_triggers:read/write. Backend already had
  `ScopeDomain::AzureTriggers` wired (scopes.rs), this just exposes it.
- .claude/skills/adding-a-trigger/SKILL.md: capture both scope-related
  files under the hardcoded-arrays section so future triggers don't miss
  the UI surface.

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

* docs(adding-a-trigger-skill): clarify token.rs scope effect

Not a regression — nothing was working before. Skipping TRIGGER_DOMAINS
just means the scope works via API/CLI but has no UI checkbox.

* docs(adding-a-trigger-skill): trim token.rs bullet

* fix(azure-trigger): regen openapi-deref + swap textarea for TextInput

- Run build_openapi.sh to regenerate openapi-deref.{yaml,json} with the
  12 azure_triggers paths + schemas. These files are served by the
  runtime (include_str! in windmill-api/src/lib.rs) to external SDK
  consumers; without this regen the new endpoints wouldn't be advertised.
- Replace the raw <textarea> for event type filters with the
  design-system TextInput in textarea mode (frontend/CLAUDE.md bans raw
  HTML elements).

Addresses cubic + claude PR review items.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-04-23 16:30:18 +00:00
3d4f4c6c38 feat: Fork datatables (#8339)
* export_datatable_schema

* Propose to fork the datatable on ws fork

* dump datatable

* Dockerfile

* Fix import_datatable_dump

* datatable schema fork works!

* Option to copy both schema and data

* Datatable fork behavior

* nit ui

* use psql instead

* remove fork_datatable route

* feat: add fork_pg_database and export_pg_schema routes with DB Manager UI

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

* style: pluralize "schema" to "schemas" in DB Manager export/import UI

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

* feat: add import mode select (schema only vs schema + data) to DB Manager import

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

* Select schema or schema+data when important database

* fix: prepend $res: prefix to resource paths in DB Manager import/export

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

* style: dynamic import button label based on selected mode

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

* nits

* feat: add warning alert when schema+data import mode is selected

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

* nit hide on cloud hosted

* refactor: remove fork_behavior from datatable settings

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

* refactor: split CreateWorkspace into layout wrapper and CreateWorkspaceInner

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

* feat: instantiate CreateWorkspaceInner in globalForkModal

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

* nit icons

* Data table fork UI

* feat: pass per-datatable fork behaviors from UI to backend during workspace fork

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

* Fix fork overwriting all datatables

* UI nits

* custom instance db refactor

* custom instance db wizard btn for all in dropdown

* nit

* Delete custom instance database button

* Disable forking for resource datatables

* Big import buttons when db empty

* Revert "Disable forking for resource datatables"

This reverts commit 9561cc8fd4.

* feat: add non_diffable flag to resource table

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

* feat: add resource-type datatable fork with CREATE DATABASE

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

* feat: tag forked datatables with nonDiffable and forkedFrom

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

* feat: diff datatable and ducklake settings individually on workspace merge

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

* feat: skip non_diffable resources and datatables in workspace diff

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

* feat: default datatable fork behavior to keep_original

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

* fix: make grant permissions non-fatal in instance datatable fork

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

* fix: make datatable and ducklake diffs visible in workspace comparison

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

* refactor: remove datatable fork logic from workspace fork route

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

* fix: correct ahead/behind logic for datatable and ducklake diffs

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

* Revert "fix: correct ahead/behind logic for datatable and ducklake diffs"

This reverts commit 6b50884dc6.

* revert: remove datatable and ducklake settings diffing logic

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

* feat: add datatable clone UI with step-by-step confirmation modal

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

* refactor: extract datatable fork UI into ForkDatatableSection component

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

* nit

* fix: run datatable cloning before workspace fork creation

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

* nit disable fork admins

* nit fix switching workspace prematurely

* fix: use source workspace for forkPgDatabase calls during fork

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

* feat: update forked workspace datatable settings after fork creation

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

* feat: add forked_from field to DataTable and set it for instance forks

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

* nit onFinish

* fix: add forked_from to DataTableSettings OpenAPI schema

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

* feat: track datatable table DDL changes in workspace_diff

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

* Revert "feat: track datatable table DDL changes in workspace_diff"

This reverts commit 7526dd68b9.

* feat: add get_datatable_full_schema endpoint and snapshot schema on fork

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

* fix duplicate migration key

* fix: set forked_from on datatable config for both instance and resource types

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

* nits

* feat: drop forked databases on workspace deletion with confirmation UI

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

* refactor: extract drop_forked_datatable_databases from delete_workspace

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

* fix: cast pg char columns to text in FK schema query

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

* fix: show dbname instead of resource type in fork deletion modal

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

* ui nit

* refactor: extract drop_custom_instance_database into windmill-common

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

* feat: add datatable schema diff section to merge UI

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

* UI

* feat: add review drawer with YAML diff and SQL migration runner

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

* refactor: use Monaco DiffEditor for YAML diff in review drawer

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

* nit

* Revert "refactor: use Monaco DiffEditor for YAML diff in review drawer"

This reverts commit a86008ba4c.

* Revert "feat: add review drawer with YAML diff and SQL migration runner"

This reverts commit 0a0deb5ddb.

* feat: add review drawer with DiffEditor and SQL migration runner

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

* ui nits

* fix: show diff between forked_from schema and changed side

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

* fix: re-fetch target live schema after migration for correct baseline

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

* revert

* nit auto next

* feat: add confirmation modal before deploying migration to parent

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

* fix: handle missing columns/foreignKeys in schema conversion

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

* nits

* refactor: use temp file on disk for pg_dump instead of in-memory string

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

* Don't replace postgres dbname

* fix: add validation to drop_custom_instance_database and use source db for CREATE/DROP

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

* refactor: type DataTable.forked_from as DataTableForkedFrom struct

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

* refactor: simplify fork_pg_database to take source + target_dbname

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

* dead code

* feat: enforce schema_and_data admin-only and extract create_custom_instance_database

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

* refactor: rename fork_pg_database to import_pg_database with source/target/override params

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

* nit

* refactor: remove original_dbname/original_resource from forked_from, resolve from parent

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

* nit

* fix: resolve forked dbname from fork workspace when dropping resource databases

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

* nits

* fix: always clean up global_settings even if database doesn't exist

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

* fix: check datatable resource_type from config instead of URL prefix

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

* fix: wrap PG default value expressions in braces to prevent CAST quoting

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

* Revert "fix: wrap PG default value expressions in braces to prevent CAST quoting"

This reverts commit 77f5a2c4e8.

* refactor: reuse columnDefToTableEditorValuesColumn for default value handling

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

* fix: store raw API schema in forked_from to avoid double transformation

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

* Revert "fix: store raw API schema in forked_from to avoid double transformation"

This reverts commit e326197a20.

* Revert "refactor: reuse columnDefToTableEditorValuesColumn for default value handling"

This reverts commit bd8f071d9f.

* fix: validate dbname with strict regex to prevent SQL injection

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

* fix default value

* always validate dbname

* refactor: move get_datatable_full_schema structs and logic to query_builders.rs

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

* refactor: split import_pg_database into create_pg_database + import_pg_database

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

* refactor: extract drop_forked_datatable_databases into its own route

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

* fix: require admin when using $res: resource paths in import_pg_database

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

* fix: use UserDB for $res: resource access and restrict dbname creation

- resolve_pg_source_checked uses UserDB (row-level security) for $res: paths
- transform_json_unchecked is now pub(crate) to prevent misuse
- Non-superadmins can only create databases with wm_fork_ prefix
- datatable:// remains accessible to everyone

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

* fix: refuse to drop forked databases unless name starts with wm_fork_

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

* refactor: remove resolve_pg_source, use resolve_pg_source_checked everywhere

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

* Fix permissions

* sqlx prepare

* compilation nits

* sqlx prepare

* sqlx prepare

* wrong route syntax

* fix: allow workspace owner to edit datatable config for fork setup

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

* Revert "fix: allow workspace owner to edit datatable config for fork setup"

This reverts commit ab683e637b.

* refactor: move datatable fork setup into create_workspace_fork backend

Instead of updating datatable settings from the frontend after fork
creation (which required admin/owner access), pass forked_datatables
info to create_workspace_fork and handle it atomically in the same
transaction. Removes applyPostForkDatatableUpdates from frontend.

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

* fix: snapshot schema in backend during fork instead of frontend

The schema snapshot is now taken by the backend in apply_forked_datatable
via snapshot_datatable_schema, which connects to the parent workspace's
datatable and runs pg_get_full_schema. This removes the need for the
frontend to call getDatatableFullSchema and pass the schema through.

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

* fix: use get_resource_value_interpolated_internal for $res: to resolve $var: references

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

* nit

* sqlx prepare

* fix: add permission check to drop_forked_datatable_databases, validate dbnames, restrict temp file perms

- drop_forked_datatable_databases: same permission as delete_workspace
  (fork owner or super admin)
- validate_dbname on target_dbname_override and ForkedDatatableInfo.new_dbname
- Enforce wm_fork_ prefix on forked datatable new_dbname
- DumpFile: set /tmp/windmill/ to 0700 and create files with 0600

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

* nit CLI

* Rename to ws_specific

* sqlx prepare

* nit always validate dbname

* fix: include foreign keys in CREATE TABLE migration for added tables

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

* fix: detect nextval defaults and use SERIAL/BIGSERIAL types in CREATE TABLE

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

* Update frontend/src/lib/components/DBManagerDrawer.svelte

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Update backend/windmill-common/src/lib.rs

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Update backend/windmill-common/src/lib.rs

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* fix: sort foreign keys by constraint name for deterministic schema output

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

* sqlx prepare

* rename migration to update timestamp

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-04-07 21:03:06 +00:00
Ruben FiszelandClaude Opus 4.6 3959fe8297 feat: add workspace-level service accounts (#8560)
* feat: add workspace-level service accounts (EE)

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

* sqlx

* sqlx

* chore: update ee-repo-ref

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:23:03 +00:00
Ruben FiszelandClaude Opus 4.6 cb8b264dee add signed request authentication to multiplayer websocket (#8534)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:23:47 +00:00
Ruben FiszelandClaude Opus 4.6 372023e995 feat: add ws_base_url instance setting for WebSocket URL override (#8405)
* feat: add ws_base_url instance setting to override WebSocket base URL

Allow deployments behind reverse proxies to route WebSocket traffic
(LSP, debugger, multiplayer) to a different host/port than the main
frontend via a new instance setting.

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

* feat: move ws_base_url to Advanced section with toggle and connectivity test

- Move setting from Core to Advanced > WebSocket section
- Render as toggle "Custom websocket base url from frontend to
  multiplayer/lsp/debugger" with conditional URL text field
- Add Test connectivity button (always visible) that checks HTTP health
  and WebSocket ping for all three services (LSP, Multiplayer, Debugger)
- Add /ws/ping and /ws/health endpoints to LSP service
- Add /ws_mp/health HTTP and __ping__ WS handlers to multiplayer service
- Add /ping WS handler to debugger service
- Add CORS headers to health endpoints for cross-origin testing

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

* fix: toggle enabled check and testWs promise resolution

- Fix enabled derived to check only for null (not empty string),
  otherwise the toggle never turns on since toggleEnabled sets ''
- Fix testWs onclose handler to resolve(false) so the promise
  doesn't hang if the server closes without sending a message

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

* fix: make connectivity test work with existing services

- HTTP test: accept plain text "ok"/"okay" (old services) in addition
  to JSON {"status": "ok"} (new services), reject HTML (SPA fallback)
- WS test: resolve on onopen (connection established) instead of
  waiting for a specific pong message, so the test works even with
  services that don't have the new /ping handler yet

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 12:48:59 +00:00
Diego ImbertandClaude Opus 4.6 5d79f33590 Final Svelte 5 migration (#8211)
* Remove $$props.field usage

* Rename slots to ensure no hyphen

* _props

* _trigger

* OnSelectedIteration type correct capitalization

* rename _content

* Remove afterUpdate

* Migrate everything to svelte 5

* array bind

* Fix popover

* type never

* nit fixes

* Fixed many trivial errors

* onClick

* Fix errors

* use let:

* nit typing

* fix: wrap state_referenced_locally vars with untrack()

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

* Add untrack import

* Fix all syntax errors due to untrack migration

* Fix undefined errors

* Fix more undefined errors

* untrack(() => initialOpen)

* svelte-ignore

* Fix state_descriptors_fixed error in Chart.svelte

Use $state.snapshot() to pass plain copies of data/options to Chart.js
instead of $state proxies. Chart.js's listenArrayEvents tries to define
property descriptors on data arrays, which Svelte 5 proxies reject.

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

* nit typing

* Merge issue

* Fix "path is not set" error in resource picker / editor

* Fix InputTransformForm error when rerunning some flows

* fix npm run check

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-05 18:11:40 +01:00
Diego Imbert 4bf827bea4 feat: persistent Db manager state in URI (#8134)
* DB Manager state in URL

* Fix state not saving

* shorted uri params

* infer db_type from prefix

* Revert "infer db_type from prefix"

This reverts commit 7415fbed3d.

* dbm syntax

* infer database type

* Omit main and public

* remove legacy #dbmanager:

* Preserve hash

* nit

* Fix remaining dbManagerDrawer objects
2026-03-04 10:46:34 +00:00
GuilhemandClaude Opus 4.6 0935bf9fc4 feat: add light mode for navigation sidebar (#8057)
* feat: add light mode support for navigation sidebar

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

* feat: use custom selected background for sidebar items

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

* fix: darken sidebar section separators in light mode

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

* refactor: lighten sidebar bg to #F3F3F7 and use border-light for separators

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

* fix: use text-secondary for sidebar labels and text-hint for icons

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

* refactor: extract dark sidebar background color to constant

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:25:46 +00:00
Ruben Fiszel dbec70aedd internal: instant npm run check 2026-02-19 09:33:33 +00:00
Ruben FiszelandClaude Opus 4.6 82e5f6de48 feat: add Kubernetes operator and instance settings YAML editor (#7836)
* Add windmill-operator crate for Kubernetes CRD-based instance config

Introduces a new `windmill-operator` crate that enables declarative
management of Windmill instance configuration via a Kubernetes
`WindmillInstance` CRD. The operator watches CRD resources and performs
full declarative sync of global_settings and worker configs to the
database, supporting GitOps workflows for instance-level configuration.

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

* Add tests for windmill-operator CRD and db_sync

- 9 unit tests for CRD serialization, deserialization, metadata, and
  status field behavior
- 15 integration tests for db_sync using #[sqlx::test] with full
  declarative sync coverage: upsert, delete, protected keys,
  idempotency, worker config prefix handling, and end-to-end sync

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

* Replace untyped BTreeMap CRD fields with typed structs for schema validation

GlobalSettings, SmtpSettings, IndexerSettings, and WorkerGroupConfig now
have explicit typed fields with serde(flatten) catch-all for forward
compatibility. The generated CRD YAML includes a full OpenAPI v3 schema
that Kubernetes validates on kubectl apply.

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

* Type opaque serde_json::Value CRD fields with real structs

Replace most remaining serde_json::Value fields in WindmillInstance CRD
with properly typed structs derived from the codebase:
- oauths: BTreeMap<String, OAuthClient>
- otel: OtelSettings
- otel_tracing_proxy: OtelTracingProxySettings with ScriptLang enum
- critical_error_channels: Vec<CriticalErrorChannel> (untagged enum)
- critical_alerts_on_db_oversize: DbOversizeAlert
- ducklake_settings: DucklakeSettings with nested catalog/storage types
- custom_instance_pg_databases: CustomInstancePgDatabases
- autoscaling (worker config): AutoscalingConfig with integration struct
- custom_tags, default_tags_workspaces: Vec<String>
- default_tags_per_workspace: bool

Still opaque (serde_json::Value): object_store_cache_config (kube-core
can't generate schemas for internally-tagged enums), secret_backend
(EE-private), slack, teams (no clear struct definitions).

Regenerated CRD YAML with full OpenAPI schema (352→703 lines).

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

* Unify instance config types and add bulk GET/PUT API

Move all typed settings (GlobalSettings, WorkerGroupConfig, etc.) from
windmill-operator/crd.rs into windmill-common/instance_config.rs so both
the API server and operator share a single source of truth. Add diff/apply
logic (Merge mode for UI, Replace mode for operator) and InstanceConfig::from_db().

Add GET/PUT /settings/instance_config endpoints so the frontend loads all
settings in 1 call instead of 42, and saves with a single bulk PUT. The
backend handles the diff internally, running pre-write hooks for changed keys.

Refactor windmill-operator/db_sync.rs to use the shared diff+apply functions
and slim crd.rs down to the CRD wrapper with re-exports.

Includes 32 unit tests and 30 integration tests covering serialization,
diff logic, DB roundtrips, protected settings, and edge cases.

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

* feat: add Form/YAML toggle to instance settings UI

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

* fix: show Form/YAML toggle regardless of hideTabs prop

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

* refactor: replace toggle button group with simple YAML toggle

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

* feat: mask sensitive fields in YAML view with show/hide toggle

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

* feat: hide internal settings and mask sensitive fields in YAML view

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

* feat: hide jwt_secret and min_keep_alive_version from API and config exports

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

* all

* feat: add secretKeyRef support for sensitive fields in operator CRD

Allow sensitive fields (license_key, hub_api_secret, scim_token,
smtp_password, OAuthClient.secret, custom PG user_pwd) to reference
Kubernetes Secrets via the standard secretKeyRef pattern instead of
inlining values as plaintext YAML. The reconciler resolves all refs
by reading K8s Secrets before syncing to the database.

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

* all

* all

* all

* fix: merge main and update dev environment docs

Resolve merge conflicts from origin/main, fix duplicate
UV_INDEX_STRATEGY_SETTING import, and add Playwright MCP
testing instructions to CLAUDE.md.

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

* all

* fix: init tracing for CLI subcommands and deduplicate setting side-effects

Initialize tracing subscriber before early-return CLI paths (sync-config,
operator) so tracing calls are not silently dropped. Refactor
set_global_setting_internal to call run_setting_pre_write_hook instead of
duplicating the side-effect logic.

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

* feat: add `wmill instance get-config` CLI command

Dumps the current instance config (global settings + worker configs) as
YAML. Supports --output-file to write to a file instead of stdout.

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

* all

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:42:01 +00:00
Guilhem dd419ade94 fix(frontend): redesign instance settings (#7916)
* handle dirty config

* Move instance update in drawer actions

* Put windmill version in drawer header

* Use sidebar instead of tabs

* Rework user section

* improve user table

* Handle EE

* Add settings section header

* test-1

* option 2

* create new settings group

* harmonize all settings inputs

* improve members/users table styling

* refactor instance setup

* show user count

* nit

* Create setting card component

* harmonize instance settings and workspace settings

* fix python version loader

* nit

* clean code

* nit

* add email validation

* fix reactivity issue on default value

* fix dirty config check

* fix object storage dirty config check

* Fix object storage settings reactivity

* fix indexer dirty reactivity

* Add validation for indexer

* fix sso dirty issues

* clean

* nit
2026-02-12 09:50:01 +00:00
wendrul 2851b6b7ca feat: restriction rulesets for workspaces (#7879) 2026-02-10 16:49:25 +00:00
Ruben Fiszel 309de58134 Revert "feat: restriction rulesets for workspaces (#7791)" (#7820)
This reverts commit a1cd02d7f8.
2026-02-05 18:16:40 +00:00
wendrulandwindmill-internal-app[bot] a1cd02d7f8 feat: restriction rulesets for workspaces (#7791)
* Add frontend for the workspace proctection rules

* api to add and update workspace protection rules

* Fix bypasser selection

* Fix Select bug on refocus

* Change rulesets to use bitflags

* Messages for protection rules errors

* claude remove ui for rules

* Hide edit buttons when rule

* No edit refactor pt1

* Update edit buttons to be disabled when rule is active

* Merge deploy ui and rulsets in one tab

* Remove not cleaned line in migration

* multiple fixes

* Remove old protection rule logic

* Add prrotection rule for deploying through Merge UI

* Add Alert on legacy Deploy UI

* Add backend enforcing of workspace rules

* Finish backend blocking on rulsets

* Last changes to api ruleset blocks

* Prepare sqlx

* Remove unused import and argument

* Update SQLx metadata

* fix npm run check

* Re trigger CI

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-02-05 18:02:41 +00:00
Diego Imbert a3d75ba10a feat: favorite datatable and ducklake tables + asset page nits (#7795)
* update cf worker hostname

* set remote_url cookie from param

* ephemeral backends v1

* nit

* Run queue server

* ntis

* timeout

* better db process management

* commit hash and worktree

* nit use map

* nit

* err handling

* Revert "err handling"

This reverts commit 19de00c0c0.

* nits

* auto cleanup

* Ephemeral backend command action

* remove checkout

* checkout ee repo

* nits

* process.env.GIT_EE_DEPLOY_KEY_FILE

* resumeURLs logic

* nit

* use windmill flow for ephemeral backend action

* fixes

* new token

* worktree pools

* Delete GH secret on cleanup

* linux deploy

* nit

* nit

* unhandled promises

* nit

* fix docker bridge IP on linux

* pass cf_frontend_url to wmill flow

* git fetch

* release worktree when binary started

* send error

* logger

* logging

* logging 2

* delete log files periodically

* redirect to raw app with logs

* CORS

* MANAGER_AUTH_TOKEN

* Check organization membership

* nit

* bwrap

* nit

* return timeoutAt in resumeUrl

* nit

* Change password

* nit remove https

* Settings icon instead of plain text

* doc link

* favorite icon

* Better favorites state logic

* Asset favorites

* nit fixes and better label display

* global db manager drawer

* Favorites menu open db manager

* favorites in assets page + backend refactor

* Delete favorite dropdown

* parseFavoriteAsset

* git fail

* nit

* git fail

* nit

* nits

* git fail

* Table2 instead of pyramid
2026-02-04 18:48:51 +00:00
Diego Imbert 56c88361b8 nit frontend undefined fixes (#7772) 2026-02-03 13:05:59 +00:00
Guilhem 861b167a14 replace hard hat icon with server cog (#7771) 2026-02-03 12:26:05 +00:00
Guilhem 8df6134859 fix: improve detail page layout (#7693)
* fix workspace logo centerig

* Add min h for run form

* nit
2026-01-27 09:19:33 +00:00
Ruben Fiszel cba6121207 fix: improve modal button on aggrid table actions 2026-01-26 23:07:35 +00:00
dieriba 6418c4bcc6 feat: nextcloud native triggers (#6797) 2026-01-26 16:49:52 +00:00
Ruben FiszelandClaude Opus 4.5 203f6785c4 fix: isolate SvelteKit-specific imports for library usage
Split SvelteKit-specific code into separate files to allow
windmill-components to be used as a library in non-SvelteKit
contexts (e.g., windmill-react-sdk):

- Split logout.ts into logout.ts and logoutKit.ts
- Split svelte5Utils.svelte.ts into svelte5Utils.svelte.ts and
  svelte5UtilsKit.svelte.ts (for runed/kit useSearchParams)
- Fix triggers/utils.ts type-only import resolution
- Update FlowRestartButton to use callback instead of direct navigation
- Update all route files to import from logoutKit

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 00:25:32 +00:00
Diego Imbert 668b1b0a89 Fix wonky operator menu (#7528)
* Fix wonky operator menu

* fix on close
2026-01-09 04:30:46 +00:00
Guilhem 7ca09455ea fix(frontend): auto-add invite workspace settings (#7522)
* fix auto add invite workspace settings

* nit
2026-01-08 06:24:23 +00:00
Guilhemandclaude[bot] 69b44f3b68 fix(frontend): improve workspace page (#7502)
* nit

* Improve workspace view by showing a tree

* implement search for workspaces

* Add collapse expand button

* improve unarchive button

* nit

* move search

* nit

* add max h

* Add keyboard navigation

* clean code

* Show admin workspaces with other workspaces

* Update frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* nit

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-01-06 18:06:42 +00:00
Guilhem 2eeb63e033 fix(frontend): improve workspace picker menu UI (#7491)
* Improve sidebar workspace picker

* nit

* nit
2026-01-05 22:23:51 +00:00
3699ce7a8f feat: new live onboarding for flows (#7194)
* Start workspace onboarding

* Add pictures to tutorial steps

* Remove unecessary step

* Continue tutorial by creating a flow together

* Add image into the Create Flow tutorial pop up

* Generate flow from frontend

* Set pause between each node

* Add automatic scripts overview

* Simplify tutorial, and add step to show the code

* Add input step

* Autoremove last step after 5 seconds

* Add flow typing when opening code editor

* Remove lock field from json file

* Add Guides tab on left menu

* Add /guides page

* Add tutorial card in Guides tab

* Add step to show data connector

* Add second text input to show 2 types of inputs and fill them dynamically

* Improve tutorial chronology

* Add flow input connexion with first sctript

* Improve overlay

* Improve wording

* Add new tutorial step to show node b

* Add test step

* Add cursor to pick typescript

* Improve end of tutorial

* Refactor

* Highlight bottom right corner for 5 and 6

* Fix last step overlay

* change home tutorial button

* guidelines nits

* Automate onNext() trigger on step 3

* Improve fakr cursor for Test this step button

* Improve overlay transitions

* Merge data connectors and test step steps

* Improve live code writing in step 3

* Add a step to complete the flow

* Improve the step where we generate remaining scripts

* Refactor

* Add blocking behavior on step 3

* nit about delay

* Prevent clicking on Next while code not generated

* Sharpen wordings

* Remove Svelte 4 and migrate to Svelte 5

* Remove unecesary helper function

* Add toast if the user clicks on Next button before code finished generating

* Add toasts to each step

* Improve tutorial trigger timing

* Improve delays

* Add cursor movement to Test Flow button

* Block previous on certain steps to prevent bug

* Fix for github npm check

* Fix for github npm check

* Unlike workspace onboarding and flow tutorial

* Rename flow tutorial with better name

* Remove the automatic trigger for flow previous and broken tutorial

* Push tutorials to Help sectionof the sidebar

* Fix redirection t /tutorials page

* Add tutorials page and update workspace onboarding flow

- Rename guides to tutorials page (/tutorials)
- Add workspace onboarding tutorial to tutorials page
- Remove Tutorial button from homepage
- Add welcome cards for empty workspace with 3 tutorial options
- Update workspace onboarding to redirect to homepage before starting
- Clean up URL parameter after tutorial completion
- Move Tutorials to Help menu in sidebar
- Remove automatic "action" tutorial trigger for new flows
- Add flow-live-tutorial (renamed from workspace-onboarding-continue)
- Add Previous button blocking with toast notifications in flow tutorial

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

* Add tutorials to workspace homepage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

* Start tutorials for Run/logs section

* Fix data connector

* Add flow execution graph from Run drawer

* Add tabs highlighting in drawer

* Improve tutorial on run drawer

* Add mouse cursor moving from graph tab

* Add cursor click on script in Drawer Graph tabs

* Add troubleshooting flow in tutorial

* Add step to show logs of failed step

* add step 7 to invite the user to fix by himself and se the new results

* Improve wording

* Nit improvements

* Nits

* Refactor

* Refactor

* Rename the tutorial

* Remove deleted file

* Improve wording

* Improve first step of troubleshooting flow tutorial

* Add tutorials to /tutorials page and create component

* Remove previous Flow tutorials

* Fixes, and improve tutorial button design

* Improve status in Tutorial button

* Align tutorial button to brand guidelines

* Add skip all to onboarding workspace tutorial

* Add skipped_all to tutorial_progress

* Connect backend and frontend for tutorial progress

* Add store and helper to display or not Tutorials from left menu

* Add reminder at the end of each tutorial

* Add tutorial banner

* Remove tutorials from elpty workspace

* Improve Tutorials page

* Align banner to guidelines

* Add reset tutorials buttons

* Refactor

* Refactor to make it easy to add new tutorials and tabs

* Improve tutorial config to make it easy to add new tutorials

* Refactor and remove hardcoded indexes

* Add getTutorialIndex in tutorial config file

* Nit

* Add Mark all as complete button in tutorial page

* Add skip tutorial button in banner toast

* Replace if else in tutorials router by map to make it easier to maintain and scale

* Delete broken simple app tutorial

* Add Guide flow guide buttons inside the Create Flow page

* Add flow editor tutorials into flow builder page

* Update existing app tutorials with new tutorial system

* Create a dedicated tutorial category for app editor

* Add global progress bar

* Add Reset & Skip at tutorial category level

* Add progress to tab title

* Nits on design

* Make progress bar a props and design nits

* Add active props for Tutorial Category

* Display tutorials according to the user role

* Adapt progress bar to the user role

* Add roles array for each tutorial

* Add Tutorials tab in Operator menu

* Edge case if no Category and no Tutorial available for my role

* Allow the user to reset a single tutorial

* Allow a user to mark as completed a single tutorial

* Nit on hoovering tutorial status

* Allow admins to see which tutorials are available per role

* Create utils that allow admins to see which tutorials can access other roles of their organization

* Refactor resetSingleTutorial and completeSingleTutorial into one function

* Improve role system

* Remove hardcoded MAX_TUTORIAL_ID

* Fix type assertion

* Remove console log

* Reduce recalculations when unrelated state changes

* Add console.error

* Remove unused function

* Add tutorial wrapper and better router

* Nits to pass npm checks

* Fix typescripts and lint errors

* Add SQLx query cache for tutorial_progress queries

* Improve wording for workspace tutorial

---------

Co-authored-by: Diego Imbert <diego@windmill.dev>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-12-08 16:42:35 +00:00
Diego Imbert 9bbab3321e feat: Data tables (#7226)
* data tables settings ui

* install runed

* zod 4 fixes

* use new toJSONSchema

* Migrate ducklake catalogs to more generic custom instance databases

* fix compilation

* Safety conversion for old duckdb ffi

* data tables settings

* ts client basis

* inline run works

* datatables work

* Revert "datatables work"

This reverts commit 6e1588d59e.

* datatables work (without leaking pg credentials)

* println

* separate sqlUtils.ts

* nit

* Separate custom instance db Select and Wizard components

* nit

* nit wording

* add tags to custom instance dbs

* error when trying to use ducklake as datatable or opposite

* show status in dropdown

* data table instance setup works

* sqk function for ducklake

* factorize logic

* fix temp reactivity

* Data table assetexplore

* Migrate S3 permissions to modal

* Revert "Migrate S3 permissions to modal"

This reverts commit 0631d03cb0.

* nit query -> fetch

* Custom instance setup new look

* run_language_executor separate fn

* run_inline param

* nit wording

* Better typed client

* Data tables display as assets in frontend

* asset db icon

* nit

* cleaner errors

* nit

* Fix sed calls in mac

* run_inline_script_preview in python client

* basic python datatable client

* datatable and datalake parser in python

* ducklake client python

* nit fix

* Fix migration producing NULL instead of {} when no custom databases

* merge conflict fail

* python ducklake client arg fix

* parse or infer sql types in ts client

* ts asset parser, detect datatable & ducklake R/W

* fix sql repl for other read ops than select

* export type SqlTemplateFunction

* rename list_custom_instance_pg_databases

* typecheck datatable and ducklake name in Typescript

* Fix typecheck datatable and ducklake in TS

* declare module overriding instead of extending

* infer_sql_type in python client

* SqlQuery object in python

* fix merge conflicts

* update const_format

* CI fix

* factor out to var_identifiers

* sqlx prepare

* unnecessary security (admin is required)

* clearer comment

* ee repo ref

* nit snake case

* claude step 1: detect var declarations

* move detect_sql_access_type to common mod

* claude step 2: detect when saved vars are queried

* Revert "claude step 2: detect when saved vars are queried"

This reverts commit 1e1f930568.

* Revert "claude step 1: detect var declarations"

This reverts commit f866f4819d.

* remove ducklake/datatable and default

* detect data table assigns in var_identifiers

* Python parser successfully infers R/W/RW from ducklake / datatable

* still register ducklake/datatable if not used as unknown R/W

* Go to settings button in Assets Dropdown on not found

* nit

* sqlx prepare fail

* manual fix, somehow sqlx prepare won't do it

* fix frontend ci

* ee repo ref

* ducklake_user doesnt exist in unit tests

* nit fix

* ui nit

* nit

* nit missing clone

* fork ducklakes and datatables

* fix surface hover bug

* stupid mistake

* better deeply reactive mutable derived

* Ducklake picker

* Editor bar data tables

* DuckDB supports datatables

* datatable in duckdb asset parser

* duckdb asset parser var_identifiers

* Revert "duckdb asset parser var_identifiers"

This reverts commit 88068b1a77.

* sqlx prepare

* Box pin in test_workflow_as_code to fix stack overflow

* go to settings button

* ee repo ref

* fix compilation

* wording nit
2025-12-05 23:08:58 +00:00
Guilhem 0594257a25 fix(frontend): update workers page ui (#7264)
* Improve workers page

* Update group config drawer

* improve dirty workergroup config

* Make layout reactive

* fix section animation

* prevent opening dropdown while clicking New group config

* migrate workers page to svelte 5

* Open drawer upon adding a worker group

* nit critical alert table

* improve queue metrics drawer

* improve agent worker drawer

* harmonize copy icon

* improve agent worker doc

* improve layout

* Improve autoscaling event list

* Improve tags managment

* Remove default tags

* fix npm check

* Add info for agent workers

* improve agent worker jwt token creation

* Improve token display

* nit

* improve tag display

* create EE component

* nit

* harmonize tag overflow

* handle permission better

* improve env var presets

* handle permission for config

* nit alerts

* nit

* Improve custom tag creation in tag select

* optimistic tag addition

* nit

* nit

* fix typo

* improve workers table

* Group config tags

* show mismatch

* fix typo

* optimistic update when adding tag

* do not allow to create tag when picking a tag to watch in alerts
2025-12-04 22:09:25 +00:00
windmill-internal-app[bot]andwindmill-internal-app[bot] cabff41acf docs(changelog): add new entries from changelog (#7178)
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2025-11-27 14:09:23 +00:00
Diego Imbert f51991c25c fix workspace not removed from sessionStorage on log out (#7010) 2025-10-30 15:02:52 +00:00
Guilhem 94e5c21e25 glm/fix-prop-picker-position (#6991)
* fix button fill container

* fix popover overflow

* Update script picker to new guidelines

* fix expand sublow button

* fix scroll

* fix popover dark mode

* fix refresh button

* nit

* fix popover oferflow

* nit
2025-10-29 21:03:12 +00:00
wendrulandRuben Fiszel 663bc3b19a feat: is_admin non propagation + change endpoint for forks (#6958)
* fix is_admin non propagation + change endpoint

* Add deprecated endpoint to tell users to update cli

* Fix endpoint definitions

* fix

* fix

* fix

* fix

* fix

* fix

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2025-10-27 21:51:19 +00:00
Diego Imbert 1e095c6e3a Fix switch theme looking disabled (#6957)
* log out too bold

* nit wrap

* nit factor out popoverClasses
2025-10-27 17:17:47 +00:00