Commit Graph

14258 Commits

Author SHA1 Message Date
Ruben Fiszel 9af25a0783 chore: cache the query the derivation guard added 2026-08-04 09:54:43 +00:00
Ruben Fiszel 85e49b3c48 test: desynchronise the stored address so the guard pins the derivation 2026-08-04 09:54:43 +00:00
Ruben Fiszel 780a161873 refactor: stop reading the schedule's email column, deriving it from permissioned_as 2026-08-04 09:54:43 +00:00
Ruben Fiszel b7c3f66610 fix: record the validated address on the job row, not the one handed in 2026-08-04 09:54:41 +00:00
Ruben Fiszel 18fa197f6d fix: carry the validated address through to the job row and token 2026-08-04 09:09:47 +00:00
Ruben Fiszel a3ffd0b41e fix: validate a dispatch address against the principal's live binding 2026-08-04 08:52:13 +00:00
Ruben Fiszel 0718d4f50a docs: record why a stale dispatch address cannot escalate 2026-08-04 08:16:30 +00:00
Ruben Fiszel e6b5be11b3 docs: cover the round-tripped read in the cache rule 2026-08-04 08:03:19 +00:00
Ruben Fiszel 24e1525e4f docs: describe the trigger as it is 2026-08-04 07:43:39 +00:00
Ruben Fiszel 47633f817b fix: evict every alias a superadmin principal can be spelled as 2026-08-04 07:22:52 +00:00
Ruben Fiszel d2d5516e00 perf: evict a superadmin's key per workspace instead of the whole cache 2026-08-04 06:55:57 +00:00
Ruben Fiszel 47b756c0ab docs: name the release this actually ships in 2026-08-04 06:32:47 +00:00
Ruben Fiszel 70771bc915 fix: resolve the schedule address uncached where it is written to the row 2026-08-04 06:31:56 +00:00
Ruben Fiszel 91a093bde9 docs: sort the two lookups by how long a wrong answer lives 2026-08-04 06:31:56 +00:00
Ruben Fiszel 47655fc7c4 docs: keep the cache rule in one place and drop the stale premise 2026-08-04 06:31:56 +00:00
Ruben Fiszel 9f1418ec15 docs: state where the cached address is accepted and where it is not 2026-08-04 06:31:56 +00:00
Ruben Fiszel ff02b08ae0 fix: keep persisted addresses off the cache the poller evicts asynchronously 2026-08-04 06:31:56 +00:00
Ruben Fiszel a285c956f1 perf: read the address cache everywhere now that eviction reaches every replica 2026-08-04 06:31:56 +00:00
Ruben Fiszel cc0dc0a5c3 docs: drop the last reference to the removed constant 2026-08-04 06:31:56 +00:00
Ruben Fiszel 220f5f4972 refactor: drop the app-policy version constant that gated nothing 2026-08-04 06:31:56 +00:00
Ruben Fiszel 41aefcaf28 docs: state the removal gate once, and correctly 2026-08-04 06:31:56 +00:00
Ruben Fiszel 9e5f9ae340 fix: follow an external superadmin's rename into the apps that name them 2026-08-04 06:31:56 +00:00
Ruben Fiszel 9c310cef01 refactor: make the app policy's address optional instead of derived on read 2026-08-04 06:31:56 +00:00
Ruben Fiszel 3536740d06 fix: evict on superadmin role changes, not only address changes 2026-08-04 06:31:56 +00:00
Ruben Fiszel d24a2ed8c1 perf: evict the address cache on change so app dispatch can read it 2026-08-04 06:31:56 +00:00
Ruben Fiszel 492e4a0746 fix: drop an unresolvable draft's address instead of leaving it stale 2026-08-04 06:31:56 +00:00
Ruben Fiszel 972c988c70 chore: cache the membership lookup the draft resolver added 2026-08-04 06:31:56 +00:00
Ruben Fiszel 263901b34d fix: resolve a draft's principal against workspace membership only 2026-08-04 06:31:56 +00:00
Ruben Fiszel 33655970b9 docs: state the draft resolver's authorization contract 2026-08-04 06:31:56 +00:00
Ruben Fiszel 997db1d6d1 fix: derive the app identity on the draft-table and version reads too 2026-08-04 06:31:56 +00:00
Ruben Fiszel f602df7e3f chore: cache the query the draft derivation test added 2026-08-04 06:31:56 +00:00
Ruben Fiszel 578bbd52c7 fix: derive the app draft's on-behalf-of address on read 2026-08-04 06:31:56 +00:00
Ruben Fiszel 9755b47f25 chore: cache the EE queries at the ref this branch pins 2026-08-04 06:31:56 +00:00
Ruben Fiszel 45a76aef8f chore: cache the EE queries at the ref this branch pins 2026-08-04 06:31:56 +00:00
Ruben Fiszel fa08488088 fix: resolve the app execution address uncached, it decides the job's authorization 2026-08-04 06:31:56 +00:00
Ruben Fiszel 058b48b850 chore: refresh the sqlx cache after rebasing onto the merged base 2026-08-04 06:31:56 +00:00
Ruben Fiszel 50e4261479 fix: align the app backfill with the sibling migration and audit the uncached address 2026-08-04 06:31:56 +00:00
Ruben Fiszel e23c35898d refactor: make the app policy's principal the authority for its identity 2026-08-04 06:31:56 +00:00
Ruben Fiszel 4c4d6c98bf chore(main): release 1.778.0 (#10469)
* chore(main): release 1.778.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
v1.778.0
2026-08-04 01:49:00 +02:00
Diego Imbert 386e115daa feat: lazily expand s3 explorer folders one level at a time (#10420)
* feat: wire paged object storage listing module

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* feat: document list_stored_files_paged endpoint in openapi

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* feat: lazily expand s3 explorer folders one level at a time

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: pin ee-repo-ref to the paged listing branch

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: share object_store credential resolution and surface listing errors

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sqq2LhmWaGwP11Cf3UqWxe

* Chevron is cool

* page size 5000

* feat: make the load more row full-width, secondary and chevron-led

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sqq2LhmWaGwP11Cf3UqWxe

* fix: render newly loaded flat pages inside already-expanded folders

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: address review findings in the lazy s3 explorer

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: address review nits in the lazy s3 explorer

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: bump ee-repo-ref after merging main

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: document ambient credential contract and constrain max_keys schema

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: treat an exhausted page token as exhausted, not as a continuation

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: bump ee-repo-ref for canonical prefix validation

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: bump ee-repo-ref for prefix scoping and opaque cursors

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: invalidate a folder's in-flight load when deleting from it

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: discard a stale folder page after its level is invalidated

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: bump ee-repo-ref for bounded local listing

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: label folders whose final path segment is empty

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* feat: search files by any part of their path, not just folder prefix

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* feat: search files by path prefix instead of a full-bucket scan

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* fix: guard stale search responses and describe prefix search accurately

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: bump ee-repo-ref for the search prefix fallback fix

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: regenerate the served openapi specs

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: bump ee-repo-ref for the search cursor fallback fix

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqAc8mz6Gu698kBbJJVwcT

* chore: bump ee-repo-ref for the bounded search scan

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

* fix: surface a failed flat listing instead of spinning forever

The flat branch of loadFiles was awaited without a catch, and loadFlatFiles
clears its loading flags only on the success tail. Every caller reaches it
un-awaited, so a rejected listing left the drawer on "Loading content" with
nothing reported. Routing the filter box through this arm made it reachable
per keystroke rather than once per open.

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

* fix: give back the flat cursor when a page fails to load

"Load more" advanced `page` before requesting it, so a failed page left the
cursor pointing at a `listMarkers` slot that was never filled. The retry sent
no marker at all and silently replayed the first page, and the
`listMarkers.length == page` guard kept it there until the listing was reset.

Only reachable now that a failed page is retryable rather than a permanent
spinner.

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

* fix: scope the flat cursor rollback to its own listing

The rollback matched on the page number alone, so a page that failed after a
filter or storage change could roll back the *replacement* listing once it had
reached the same number, stranding its cursor. Tie it to the generation the
request was issued under.

The delete replay loop had the mirrored problem: it re-drove `page` by hand and
carried on past a failed page, leaving `page` ahead of `listMarkers` for good.

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

* fix: skip the delete replay when the fresh listing itself failed

clearAndLoadFiles dropped the result it already computes, so a failed
post-delete listing still ran the replay loop: each page advanced `page` with
an empty `listMarkers`, which never recovers because the marker-length guard
only pushes when the two agree. Every later "Load more" then replayed page one.

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

* fix: stop a superseded lazy load from writing into the search that replaced it

loadFolderPage resolves rather than throwing once its generation is stale, so a
filter change that switches the picker to the flat listing mid-flight left the
lazy branch free to expand a preselected file into the search's results and to
clear the search's loading flags. Guard both on the generation it started under,
as the flat branch already does.

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

* fix: check the listing generation throughout the reveal walk

Revealing a preselected key is a chain of round trips, so checking once at entry
left the rest of the walk free to keep loading after a filter change had already
switched the picker to the search — under the replacement generation, so the
per-level guards inside loadFolderPage saw nothing wrong.

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

* fix: let a late metadata failure clear only its own preview

The handler blanked fileMetadata and filePreview without checking that its
request still owned the pane, so selecting a second file while the first was
still loading meant the first's rejection wiped the second's preview.

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

* fix: key preview ownership on the request, not the selected key

Comparing the selected key let an older request speak for a newer one when both
targeted the same key, which a storage switch does, and made a request whose
selection had moved to something with no metadata return early with the spinner
still up — the case the handler exists to prevent.

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

* fix: clear the preview when the previewed file is deleted

The lazy branch refetches only the affected level and returns, so it never
reached the reset that the flat refresh gets from clearAndLoadFiles. The pane
renders from fileMetadata rather than from the selection, leaving the deleted
file previewed with working download, move and delete actions.

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

* fix: retire the in-flight preview load when its file is deleted

Clearing the pane was not enough: a metadata response computed before the DELETE
landed still repopulated it, restoring the deleted file's preview and its
download, move and delete actions. Deleting now retires the owning request, and
the success and preview writes honour that the same way the failure path does.

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

* fix: clear the preview loading flag when the delete retires its request

Retiring the in-flight metadata load left nobody to report its outcome, so in
lazy mode the pane sat on "Loading..." instead of falling back to the empty
state. The delete owns the flag once it has retired the request.

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

* chore: drop the regenerated openapi deref artifacts

They are generated files that CI only syntax-validates, never checks against
openapi.yaml, and the committed copies already differ from the spec they derive
from by ~9.7k lines. Regenerating here imported that pre-existing drift into a
feature diff, burying ~800 lines of actual change under ~17k lines of other
changes' staleness. Regenerating them is its own chore.

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

* docs: state the flat cursor invariant once, where the cursor lives

It was spelled out at four sites, which is what AGENTS.md asks not to do. The
rule now sits on the declaration it constrains and the guards reference it.

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

* nit ui

* fix: add the paged listing to the served openapi json

openapi_json() embeds openapi-deref.json via include_str!, and the Docker build
regenerates only the yaml artifact, so the json is served exactly as committed —
leaving the new operation out of the Scalar API reference.

Spliced in the operation and the two schemas it references rather than
regenerating, which would have re-imported ~7k lines of pre-existing drift
between the committed artifact and the spec it derives from.

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

* chore: bump ee-repo-ref for the filesystem symlink boundary

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

* chore: update ee-repo-ref to 0373b4bfdaf8dd51533552e2e4de63ceb3c18b4d

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

Previous ee-repo-ref: eb1a765bb9b29e0c94a6e4942c304934fa15406e

New ee-repo-ref: 0373b4bfdaf8dd51533552e2e4de63ceb3c18b4d

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-08-04 01:41:06 +02:00
Guilhem 5aeee564be fix: confirm step delete consequences in a single dialog (#10485) 2026-08-04 01:38:04 +02:00
Ruben Fiszel f5cf82f9aa handle a non-member superadmin on the dev workspace settings tab (#10492)
* fix: handle a non-member superadmin on the dev workspace settings tab

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

* fix: only seed the protections panel from a load this call produced

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

* fix: refetch rather than seed while a protection-rules fetch is in flight

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

* fix: always supersede the in-flight rules fetch instead of seeding by hand

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 01:37:41 +02:00
Ruben Fiszel fadbe079e4 fix: refresh the dev-workspace pairing after attach and detach (#10491)
* fix: refresh the dev-workspace pairing after attach and detach

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

* fix: report enforced protections, not only unconditional ones, on the paired view

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 00:55:42 +02:00
Ruben Fiszel db46d34727 fix: base a new fork on the dev workspace when forking from one (#10489)
* fix: base a new fork on the dev workspace when forking from one

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

* fix: carry dev-workspace fields on the superadmin-synthesized entry

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 00:51:33 +02:00
Ruben Fiszel f365929eaa feat(fork): merge a fork deletion on evidence, not on the counters (#10484)
* feat(fork): merge a fork deletion on evidence, not on the counters

`workspace_diff.ahead`/`.behind` record that a write happened on a side,
not what it was or who made it. That leaves one row shape undecidable: an
item the parent has and the fork does not can mean the parent added it,
the fork deleted it, or a git-sync pull reverted a deploy that had just
brought it in. #10467 kept every such row out of the merge direction,
which killed the phantom but also dropped the only way to propagate a
fork-side deletion and left a rename's old path behind in the parent.

Record the evidence instead:

- `workspace_diff` gains, per side, the last event's kind (`write` /
  `delete` / `rename_from`) and origin (`authored` / `sync`). Rows
  written before the migration have neither and keep #10467's behavior.
- The kind is probed from whether the path still holds an item once the
  write has committed; an item kind the probe doesn't map records no
  evidence rather than a deletion. Create and update are not split —
  nothing at that point tells them apart for every kind, and the
  comparison already recomputes existence per side.
- The origin comes from an `X-Windmill-Deploy-Origin` header the API
  scopes into a task-local for the request. It is the load-bearing half:
  recording `delete` alone would read a git-sync revert as a fork
  deletion and reproduce the original bug. Two clients set it — `wmill
  sync push` (which the git-sync auto-pull runs inside a job) and the
  compare page's parent→fork "Update fork". Merging the other way stays
  authored so a deletion keeps propagating up a fork chain.
- The merge direction admits a parent-only row only when the fork's last
  event was an authored delete or rename-away. Such a row stays opt-in,
  never bulk-selected, and reads "Removes in <parent>"; the update
  direction keeps offering it back as "New".

A fork deletion and a rename now merge into the parent, a rename leaves
no duplicate behind, and a fork the parent also edited surfaces in both
directions instead of the parent silently winning.

Fixes WIN-2289

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

* fix(fork): address review — detached tallies, enum wire values, doc duplication

Codex P1: a dependency job tallies its deploy whenever it happens to finish,
and the event kind is probed from the state at that moment. If anything
removed the path in between (a git-sync revert), the stale tally read that
deletion as its own and filed it as authored — handing the merge exactly the
removal this is meant to withhold. `tally_deployed_object_changes` now takes
`Option<DeployOrigin>`; `None` bumps the counter and leaves the evidence
columns as the last vouching tally left them, and the worker path passes it.
Covered by extending the removal-origin test: a detached tally after the sync
archive must not disturb `(delete, sync)`.

Also from review:
- `fork_removed_it` compares through `DeployOrigin::as_str()` /
  `DeployEventKind::as_str()` rather than repeating their wire values, so a
  renamed variant can't silently make the predicate always false.
- `deploy_origin`'s module doc no longer claims `sync` is inert: it cannot
  make the merge propose a removal, but it does drop a row out of both sides
  of the `all_ahead_items_visible` comparison.
- `WorkspaceDiffRow` says why only the fork half of the evidence is consumed.
- The delete-vs-revert rationale is stated once (the migration) instead of
  restated in eight files.
- `PATH_KEYED_TABLES` is swept by a test: its query is built at runtime, so a
  wrong table name is not a compile error and would only surface as a failed
  tally for that trigger kind in a fork.

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

* fix(fork): let only a request task vouch for a deploy event

Round 2 found the first fix incomplete. Detaching only the failed/cancelled
dependency path left the common route untouched: a dependency job that
succeeds calls `handle_deployment_metadata` from the worker, where
`deploy_origin::current()` read as `Authored`. A sync archiving the script
while its lock generation was pending then had its deletion probed on
completion and refiled as authored — the same fabricated removal, on the
path most deploys actually take.

`current()` now returns `Option`, `Some` only inside the request scope the
API always enters. Having no scope means "not the task that served this
write", which is true of every worker-side call and needs no marking at the
call site. The integration test drives the real `handle_deployment_metadata`
off a request task instead of the tally directly, and fails without this.

Two more from the same round:

- The script dependency handler passed no `renamed_from`, unlike the flow
  and app handlers next to it. A lock-generating create has no earlier
  tally, so that was the only chance for the path a rename vacated to be
  recorded at all — renames of Python/TS scripts left the old path in the
  parent, which the bash-only manual check missed.
- The tally now drops a `renamed_from` equal to the path itself. Callers
  pass the previous path whether or not the deploy moved the item, so an
  unfiltered one both counted the path twice and stamped it `rename_from`
  when nothing was renamed.

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

* fix(fork): carry a deploy's origin into the dependency job it queues

Round 3 caught the previous fix cutting too deep. Refusing a detached tally
any claim also refused its rename evidence, and a lock-generating deploy has
no other tally — so the `renamed_from` added alongside it was inert, and a
renamed flow, app or Python script still left its old path in the parent
with nothing to merge. Flows and apps always generate, so renames worked
essentially nowhere.

The two capabilities are now separate. `TallyEvidence` says whether the
tallying task served the write (`Served`, may probe what the path holds now)
or is reporting one that committed earlier (`Deferred`, may not), and each
column is written only from a source that answers for it. The origin itself
is a fact of the deploy either way, so the request stamps it into the
dependency job's args and the worker re-enters the scope with it — the last
place that knows it handing it to the only tally that will run.

Also from round 3: `WorkspaceDiffRow`'s event fields skip serializing `None`
rather than emitting `null`, matching what the schema declares (OpenAPI
3.0.3 ignores a `description` sibling of `$ref`, so those moved onto the
shared schemas).

Verified against a live worker: renaming a flow in a fork records
`(rename_from, authored)` on the vacated path and the merge offers its
removal, while the deployed path claims nothing.

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

* fix(fork): mark the CLI's parent-to-fork merge as sync

`wmill workspace merge --direction to-fork` is the CLI's "Update fork" and
deletes items in the fork, but without the marker the compare page sets. Its
deletions were recorded as authored fork decisions, so once the parent
recreated such a path the merge would offer deleting it there.

Also from review: an unrecognized deploy-origin arg now reads as no evidence
rather than as authored — strict where a request header is lenient, since an
unmarked request really is authored but an unreadable stored value is skew.
Reading the arg moved next to `stamp_origin_arg`, the half that writes it, so
the round trip a lock-generating deploy depends on is covered by one test.

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

* fix: drop the imports the shared arg reader made unused

CI compiles with `-D warnings`, so this was four red Backend jobs rather
than a lint.

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

* fix(fork): stop a stale deferred rename from restating a removed path

Nothing orders these events. A tally that served the write made its claim
inside its own commit, but a deferred one reports a write that landed at an
unknown remove. So a lock-generating rename whose dependency job finished
after a sync had removed the vacated path could overwrite `(delete, sync)`
with `(rename_from, authored)` — the path is gone either way, so the merge
would then offer removing it from the parent on the strength of the older
event.

A deferred claim now only writes where the side has none, which is the case
it exists for: a vacated path that nothing else has spoken for. The
regression asserts the ordering directly, and fails without the guard.

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

* fix(fork): record a rename's vacated path from the request that made it

The deferred mechanism could not be made correct, as round 7 showed: its
guard protected an existing row, but that row is deleted as soon as the two
workspaces agree on the path — so a rename job finishing after the
reconciliation inserted fresh, and the stale claim reappeared against
whatever the parent later recreated there. Ordering cannot be recovered
outside the row, because the row is disposable.

So the vacated path is now recorded by the request, which is inside its own
commit and whose row shares the counter's lifetime. A deploy that hands its
metadata to a dependency job — every flow and app, and any script needing a
lock — calls `tally_rename_vacated_path` once its transaction has committed;
scripts reach it through the post-commit hook they already had, which grew a
second variant rather than new plumbing.

That lets the whole deferred apparatus go: `TallyEvidence`, the origin job
arg and its round trip. `deploy_origin::current` is `Some` only inside a
request scope again, and `handle_deployment_metadata` hands `renamed_from`
to the tally only when it can answer for it — git-sync still gets it either
way, so the rename keeps naming itself in the commit message.

The vacated path's kind now reads `delete` rather than `rename_from` for
these deploys, since it is probed rather than declared. The merge treats the
two alike; only the row's tooltip is less specific.

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

* fix(fork): cover raw-app renames, and stop firing CI before the lock exists

Two things the vacated-path call broke or missed:

- `create_script` reads its third return value as "no lock generation
  needed" to decide whether the script is runnable now, and the new
  `VacatedPath` variant made that true for renames that do generate. Those
  fired dependent CI tests from the API against a version with no lockfile,
  and again from the dependency job. The variant now decides it explicitly.
- Raw apps rename through `update_app_raw`, a separate route into
  `update_app_internal`, which the new call had not been attached to. Both
  routes now go through one helper.

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

* test(fork): assert the kind only an inline rename can record

`rename_from` is what a deploy says when it knows it moved the item, which
only the path that reports both halves from its own request can. Nothing
pinned it, and that is the side the vacated-path change touched.

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

* chore: update ee-repo-ref to a45bec03922d305aad5893ed354dc029c7f97bb4

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

Previous ee-repo-ref: 62f494b2a51de0dfc0cfa0c3530ff19a1d32667c

New ee-repo-ref: a45bec03922d305aad5893ed354dc029c7f97bb4

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-08-04 00:50:27 +02:00
Ruben Fiszel a3c59c7320 perf: serve the rare schedule options on demand instead of inlining them (#10487)
* perf: serve the rare schedule options on demand instead of inlining them

* fix: let a real schedule argument win over a duplicate inside advanced

* fix: catch nested stripped schedule options and share the schema builder

* fix: check the whole schedule request for stripped options, not just advanced

* fix: do not point unknown schedule keys at the schema lookup
2026-08-04 00:15:20 +02:00
Diego Imbert 7e1c1fa3a4 feat(apps): use the windmill-client SDK from raw app frontend code (#10377)
* feat(apps): use the windmill-client SDK from raw app frontend code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): bound the raw app SDK token to deployed runnables

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): deny dependency jobs and survive a failed SDK mint

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): confine the SDK token's users scope to the viewer's identity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* docs: describe the full raw-app SDK sentinel narrowing

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): deny workflow-as-code replay for raw app SDK tokens

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): deny preview-flow restart replay for raw app SDK tokens

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): re-prompt when an app widens its SDK scopes mid-consent

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* feat(apps): support the frontend SDK in sandboxed raw apps

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): hand the sandboxed SDK token over only once per loaded document

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): bind the sandboxed SDK handoff to the document we loaded

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): use an unguessable nonce for the sandboxed SDK handoff

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): reply to the sandboxed SDK handshake over its own port

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): answer the raw app handshake only over a transferred port

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): set frontend_sdk_scopes in the S3-gated policy literals

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* docs: describe the sandboxed wrapper's credential as it now works

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* ui nit

* feat(apps): make the frontend SDK work in the raw app editor preview

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): guard the preview token mint and drop superseded responses

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): refuse job tokens on every raw app SDK mint path

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* docs: correct the mint caller list and the preview retry rationale

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): apply the consent response's render mode before rendering

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): restart the viewer when a redeploy changes the render mode

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): restart on every render-mode change, not just the first

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): clear the preview's SDK credential when scopes go away

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): remove window.process in the preview instead of blanking it

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* docs: cut the raw app SDK comments down to the invariant

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* refactor(apps): use randomUUID for the raw app handshake nonce

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): re-read the render mode before rendering without a token

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* fix(apps): make the raw app handshake nonce unguessable again

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* chore: pin the EE ref to a commit that builds against this OSS tree

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Gmsk9kAG7p9t2Qy6ADRJz

* refactor(apps): authenticate the raw app preview by session instead of a token

The editor preview is same-origin and unsandboxed, so app code there already
holds the editing user's session cookie. Minting a scoped bearer for it added
an endpoint and a portable 12h credential without containing anything.

Inject only BASE_URL and WM_WORKSPACE: `windmill-client` falls back to
credentialed same-origin requests when it finds no token, so the SDK runs as
the editing user. Drops POST /apps/preview_sdk_token and the mint/race
handling in the editor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* fix(sdk): send credentials only outside the browser

The API answers `Access-Control-Allow-Origin: *` and never sets
`allow_credentials`, so a credentialed cross-origin request fails before the
bearer is read — which is what a sandboxed raw app issues. Keying this on the
browser rather than on `WM_TOKEN` leaves non-browser callers byte-identical,
and browsers keep sending cookies same-origin through fetch's own default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* remove windmill-client from templates

* fix(sdk): drop credentials only for raw app bundles

A sandboxed raw app calls the API from an opaque origin, and the API answers
`Access-Control-Allow-Origin: *`, which a credentialed request can never pair
with. Gate on WM_RAW_APP, set by the two places that build a raw app's
`window.process.env`, so every other windmill-client consumer is untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* feat(apps): make frontend SDK access sandbox-only

An unsandboxed bundle runs same-origin with the viewer's full session, so a
consent prompt there implies a boundary that does not exist and the token adds
nothing it could not already do. Advertise scopes and mint only when isolation
is on; turning the toggle off clears the declared scopes with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* Revert "refactor(apps): authenticate the raw app preview by session instead of a token"

This reverts commit 81905e455b, restoring POST /apps/preview_sdk_token.

Session auth gave the preview the editing user's full permissions and worked
regardless of policy, so an app that would 403 for a viewer — or that declares
no scopes at all — ran fine in the preview and broke only once deployed. The
preview now takes the same credential as a deployed app, gated the same way:
sandbox off or no scopes means no env at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* docs(apps): state the sandbox-only SDK contract in the public schema

The Policy and EmbedTokenResponse descriptions still promised a token to any
raw app with non-empty scopes, and said raw apps skip tokens entirely. Point
authors at adding windmill-client themselves too, since the starter templates
no longer carry it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* fix(apps): drop the preview token before minting its replacement

A mint is asynchronous, so clearing the env only on the empty-scope path left
the running preview — and any build fed meanwhile — holding scopes the policy
had just removed, or a token for the workspace just left, for as long as the
request took.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* fix(apps): restart the preview realm when its credential changes

Re-feeding the build resets the preview's DOM but keeps its JavaScript realm,
so the previous bundle's timers, listeners and pending callbacks went on using
the client they imported — and the token it captured at module load — after the
policy dropped it. Reload both shells instead; each replays the build on its
way back, so only the new realm survives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* fix(apps): start the preview once per credential change

Restarting the realm made its shell replay the build immediately, so a delayed
mint ran the app once tokenless and again tokenful — mount-time side effects
twice per scope or workspace change. Hold the build back until the mint
settles: the shell comes back blank and whichever finishes last starts the app.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* fix(apps): wait for the detached preview shell before replaying

Its reload was only initiated, never awaited — unlike the inline iframe it had
no readiness flag — so a mint settling first posted the build to the retiring
document, which then ran alongside the replacement shell's own replay. Track
readiness from both paths that announce it: `load` for a freshly opened window,
`appPreviewReady` for a reload.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPCW1WB5QeYrgJmgwcywNA

* chore: update ee-repo-ref to 99e143fa1e2e6c33b3525366a5afe48f7a4f020e

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

Previous ee-repo-ref: 609e197fbc08f1ce83dd86f816748cc19d213f77

New ee-repo-ref: 99e143fa1e2e6c33b3525366a5afe48f7a4f020e

Automated by sync-ee-ref workflow.

* nit better description

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-08-03 19:31:57 +00:00
Guilhem ed9fb47f5a fix: stop the chat deleting drafts as deployed workspace items (#10476)
* fix: stop the chat deleting drafts as deployed workspace items

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

* fix: keep archived scripts deletable and defer malformed args to the schema

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

* fix: correct the delete/draft prompt claim and narrow the script probe catch to 404

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 19:17:53 +00:00
Ruben Fiszel 7103da6484 bind the fork banner's comparison to the workspace it describes (#10483)
* fix: bind the fork banner's comparison to the workspace it describes

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

* fix: invalidate in-flight comparisons when leaving a fork

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

* fix: drop a CI summary fetched for a superseded comparison

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 19:09:42 +00:00
Guilhem b718ea8495 fix: anchor overlays to their preview tab instead of the viewport (#10477)
* fix: anchor overlays to their host pane and mute them when hidden

* fix: portal hosted modals and menus into the pane they anchor to

* fix: keep non-listening dialogs off the overlay stack

* fix: drop the topmost gate from confirmation dialogs

* fix: silence overlays in a collapsed preview panel

* fix: keep overlays live in a full-screen preview of a collapsed session
2026-08-03 18:51:46 +00:00