mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 00:03:08 +00:00
be475a79478c6282e5f22a5ccb601b951ca72f7d
13665 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
be475a7947 |
fix(debounce): claim whole batch in one UPDATE (no deadlock); assert test setup
Both Codex (P1) and Claude (P2) flagged a deadlock: the claim used two writable CTEs (claim_self then claim_rest), locking the self row before siblings, so two survivors of the same batch pulled concurrently acquired row locks in opposite order and PostgreSQL aborted one with deadlock_detected (a transient pull error on exactly the two-survivors race this path handles). Replace with a single `UPDATE ... WHERE debounce_batch = (...) AND consumed_at IS NULL RETURNING id` that claims the whole batch: both transactions lock rows in the same scan order, so one simply waits and re-evaluates under EvalPlanQual. A `claimed_self` flag (EXISTS id = self in the claimed set) plus the `mine` snapshot still distinguishes fresh-claim / consumed-by-other / own-re-pull. Also assert add_survivor_to_batch_of actually inserts a row (rows_affected == 1) so a mis-set-up test can't pass vacuously. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d961c870c3 | Merge branch 'main' into change-612b4c64 | ||
|
|
72f0b9bad7 |
chore: update ee-repo-ref to 6aabd7c5ce53b9153be05c3e7bc9a76eadb1a48a
This commit updates the EE repository reference after PR #631 was merged in windmill-ee-private. Previous ee-repo-ref: 30d740e619fad219108ec4b4c6a9d67c1ab42d46 New ee-repo-ref: 6aabd7c5ce53b9153be05c3e7bc9a76eadb1a48a Automated by sync-ee-ref workflow. |
||
|
|
78633937c0 |
fix(debounce): emit accumulation log after committing the claim transaction
append_logs opened a second pool connection while the claim transaction (and its batch row locks) were still held; under concurrent debounced pulls that risks pool-exhaustion stalls/timeouts. Defer the log line until after tx.commit(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4a8210d769 |
ci: check out windmill-ee-private for the Claude PR reviewer (#9796)
The Claude review workflow used a plain checkout, so the EE source (the *_ee.rs files that live in windmill-ee-private and are symlinked/gitignored in this repo) was absent — the reviewer could only see the CE surface and missed EE-only code like windmill-queue/src/jobs_ee.rs. Mirror the EE-checkout the Codex/Pi review workflows already do: read the PR head's backend/ee-repo-ref.txt via the API, check out windmill-ee-private at that ref, and substitute the EE files in (copy). Gated on WINDMILL_EE_PRIVATE_ACCESS being present. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8ffbe773fb |
fix(debounce): atomic claim+persist, GC only non-queued rows; reword comment
Address review findings: - [P1] Claim and accumulated-args persist are now in one transaction. Before, a crash between stamping batch rows consumed_by=self and the `UPDATE v2_job SET args` could let a zombie re-pull see its own prior claim and keep only its own args (dropping the siblings it had claimed). Wrapping claim + accumulate + persist in a tx makes them commit together or roll back together (re-pull then re-claims cleanly). - [P1] GC of consumed batch rows now also requires the job to no longer be in v2_job_queue. A consumed sibling can stay queued well past any time grace under a concurrency limit / backlog; reclaiming its marker by age alone let its eventual pull treat it as never-batched and re-run its item (a duplicate). Keeping the row until the job leaves the queue preserves the "already consumed" signal. Test extended with a still-queued consumed row that must survive GC. - [P2] Drop "Customer" attribution from a test doc comment (AGENTS.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b9711e5ace |
fix: re-pin stale-draft fork base when restoring an app deployment (#9792)
Restoring a version from an app's Deployment History sets the editor value directly (`onRestore`) without going through `loadApp`, so the fork base pinned for the stale-draft check is never refreshed. The restored value carries the `parent_version` that was baked in when that older version was deployed, so the deploy-time guard (`compareVersions`) compares an outdated base against the current head and falsely reports the editor is "not on latest", surfacing a spurious override/diff confirmation on deploy. Re-pin `parent_version` to the current head on restore, mirroring the existing seed (loadApp) and after-deploy re-pin sites. Follow-up to #9768. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ba107141f5 |
test(debounce): real-worker end-to-end accumulation test
Drives the full real path on --features enterprise,deno_core,private: push 3 same-key debounced flow jobs (real push() -> maybe_debounce collapses the batch), a real worker pulls the survivor (real pull() -> maybe_apply_debouncing claim+accumulate) and executes the deno flow, then asserts the executed result is the full accumulated set [1,2,3] and the two superseded messages are skipped. Complements the in-process unit tests with a genuine worker-execution run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e9cb80639b |
fix: restore libargon2-1 for PHP runtime in server image (#9795)
The apt-package trim in #9783 removed packages that transitively provided libargon2.so.1. The PHP CLI binary copied from php:8.3.30-cli-bookworm links against libargon2.so.1 (for argon2 password hashing), so PHP jobs fail at startup with: /usr/bin/php: error while loading shared libraries: libargon2.so.1: cannot open shared object file: No such file or directory Explicitly install libargon2-1 so the dependency no longer relies on an incidental transitive package. Fixes WIN-2101 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6664ce6dc0 |
fix(frontend): apply script editor timeout to preview/Test runs (#9794)
The custom timeout configured in the script editor settings was only
honored for deployed script runs: it is persisted on the script row and
passed as custom_timeout when running by hash/path. Preview ("Test")
runs derive their timeout solely from the `timeout` query param of
/jobs/run/preview, which the editor never sent, so Test silently fell
back to the instance default.
Forward the editor's timeout setting through ScriptBuilder ->
ScriptEditor -> JobLoader.runPreview as the preview run's timeout query
param. The backend already clamps custom_timeout against the instance
max in resolve_job_timeout, so previews get the same ceiling as deployed
runs.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
eb12a96267 |
test(debounce): scalar-arg accumulation + GC-then-repull no-loss
Close two accumulation edge gaps (both run on --features private,enterprise, exercising the real jobs_ee path): - accumulate bare-scalar values (the T | T[] union fallback): each scalar is wrapped and accumulated into the survivor's list. - GC reclaiming a survivor's consumed batch row before a re-pull must not lose data: the re-pull finds no row and keeps its already-persisted accumulated args (had_row=false fallback), rather than running empty. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
febef8e16a |
test(debounce): port the #9781 regression case, flow-node guard, full-path bench
- Port the regression from #9781 (test_post_preprocessing_debounce_into_running_survivor_loses_message): post-preprocessing survivor accumulates + runs, a later same-key message must start a new batch (survive) not be folded into the running survivor. Exercises the full EE path via jobs_ee::maybe_debounce_post_preprocessing. - Add the third EE entry point's guard: test_flow_node_debounce_running_survivor_not_superseded (maybe_debounce_flow_node). - Add an #[ignore] full-source throughput bench (bench_debounce_full_path) driving the real maybe_debounce + maybe_apply_debouncing end-to-end. All debounce tests exercise the real jobs_ee implementation (run with --features private,enterprise); none stub it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9d61e4e59e |
feat: self-host docs search for chat, mcp, cli; drop inkeep (#9772)
* feat: self-host docs search for chat, mcp and cli; remove inkeep
Embed a vendored docs snapshot (llms.txt/llms-full.txt) in the backend and
serve ranking + page rendering from GET /api/docs/{search,page}. The AI chat,
the MCP searchDocs/readDocsPage tools, and 'wmill docs' all consume it, so docs
search works with no runtime egress and is no longer EE-gated. Removes the
inkeep proxy. EE companion deletes inkeep_ee.rs (ee-repo-ref bumped).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor: name read_docs_page param `url` instead of `path`
search_docs returns each hit's `Source` URL, so the read tool now takes a
`url` argument to match — the AI/MCP loop reads "search gives a Source URL,
read takes that url" rather than copying a `Source:` URL into a `path` slot.
A bare `/docs/...` path is still accepted and canonicalized before lookup.
Regenerated openapi-deref, the MCP endpoint tools, and the frontend client.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* ci: add scheduled workflow to refresh the vendored docs snapshot
The backend embeds docs_snapshot/*.gz at build time, so the in-product docs
corpus is otherwise only as fresh as the last manual fetch.sh run. This adds a
weekly (and manually dispatchable) job that re-runs fetch.sh, sanity-checks the
result against truncation/garbage, and opens a PR via the internal app when the
snapshot changed — so a human reviews the docs diff before it rides into the
next release build.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor: make docs tool-result strings caller-neutral
The search/page endpoints back three differently-named consumers (the AI chat
`read_docs_page` tool, the MCP `readDocsPage` tool, and the `wmill docs` CLI),
so the shared rendered text shouldn't name one of them. Refer to "the docs
page-reading tool" and its `url` argument instead, and add tests pinning the
caller-neutral follow-up guidance.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: point ee-repo-ref at inkeep-removal companion rebased on EE main
The companion branch now carries only the inkeep_ee.rs deletion on top of EE
main (was based on the native-job-retry EE line, which polluted the EE PR diff).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(docs): expose docs:read in token catalog; precompute lowercased corpus
Addresses two review nits on the self-hosted docs PR:
- docs:read was enforced (ScopeDomain::Docs) but missing from the token scope
catalog (token.rs ALL_SCOPES), so it couldn't be selected when creating a
standard scoped token in the UI — leaving scope-restricted CLI/MCP docs use
effectively ungrantable. Add a read-only "Documentation" group (no write
surface) and a test asserting it is exposed.
- search ran page.body.to_lowercase() on the whole corpus per query. Lowercase
body/title/description once at parse time (into the OnceLock corpus) and scan
the precomputed copies instead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: update ee-repo-ref to 27a4f41b8e5603d6e444efcfc420bd1c44a07eed
This commit updates the EE repository reference after PR #630 was merged in windmill-ee-private.
Previous ee-repo-ref: c7ec3a0c2fa38d4cb5e50bf0265eef4710de4860
New ee-repo-ref: 27a4f41b8e5603d6e444efcfc420bd1c44a07eed
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
|
||
|
|
e192a4cf4d |
test(debounce): exhaustive edge cases + tighten consumed-batch GC grace
Tighten the consumed debounce-batch GC grace 1h -> 10min: per-op cost of the claim is unchanged (an indexed mark is as cheap as the old delete), so the only cost of retaining consumed rows is table growth, which a shorter grace bounds under high-throughput debounce (a survivor that could still reference a row is pulled long before 10min; GC is not correctness-critical since a re-pull whose row was swept falls back to its persisted args). Adds edge-case tests: never-batched keeps own args (CE fallback), concurrent claim partitions a batch disjointly (exactly-once under real concurrency), three survivors -> first takes all / rest run empty, non-accumulate debounce hard-deletes its batch rows (no leak), and the GC sweep deletes only past-grace consumed rows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
320984b92e |
feat(debounce): claim-based exactly-once batch consumption
Eliminates the rare duplicate/loss when two survivors land on one debounce batch (a narrow push/pull race), without locking the worker pull hot path. - migration: v2_job_debounce_batch gains consumed_at + consumed_by. - pull side (maybe_apply_debouncing): instead of deleting the batch on consume, a survivor atomically claims its own row + any unclaimed siblings (stamping consumed_by = itself) and accumulates exactly the rows it claimed. A second survivor of the same batch finds its row already consumed by another job and runs empty (no duplicate); a re-pulled survivor recognizes its own prior claim and keeps its accumulated args; a never-batched job (CE/legacy) keeps its own args. Non-accumulate debounce paths still hard-delete their batch rows. - complete_debounced_job (EE companion) never completes a running predecessor, so its in-flight run is not killed (no loss); the claim then prevents the duplicate the guard would otherwise allow. - monitor: GC sweep deletes consumed batch rows past a 1h grace. Together with the running-survivor guard this makes debounce accumulation exactly-once. Adds tests: batch_consumed_exactly_once, repull_keeps_accumulated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d865518934 |
feat: detect and guard against deploying stale drafts (#9768)
* feat: detect and guard against deploying stale drafts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: extend stale-draft warning to low-code app drafts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: discard stale draft on rebase instead of resetting to latest Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: animate AI chat thinking block open/close like tool calls Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: detect stale flow/app drafts by pinned version at load and deploy Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: reset version-staleness state on new drafts and after app deploy Addresses review: new-draft route reuse left stale version/draftBaseVersion (false stale-draft modal on a fresh flow/app); app deploy left parent_version pinned to the superseded base (false 'not latest' on a follow-up deploy). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
11d83ab1ec |
fix(python): serialize concurrent installs into shared wheel cache dir (#9787)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6c71c33470 |
fix(python): re-verify wheel RECORD on local cache reuse (once per worker) (#9775)
A corrupt local pip cache entry (e.g. wmill==1.739.0 missing s3_reader.py after out-of-band file loss on a persistent/shared cache volume) was trusted indefinitely: handle_python_reqs only checked the .valid.windmill marker on the reuse fast path. verify_wheel_record already guarded the install and S3-pull paths, but never ran again once the marker existed. Re-verify the wheel RECORD on the first reuse of each cache entry per worker process and repair (wipe + reinstall) on failure. A VERIFIED_VENVS in-memory set makes every subsequent reuse skip the scan, so the warm-cache hot path keeps paying only its original single stat. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
aa098c70c0 |
perf: drop v2_job side-table ON DELETE CASCADE FKs to speed retention deletes (#9786)
* perf: drop v2_job side-table ON DELETE CASCADE FKs to speed retention deletes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: document delete_jobs auth contract and workspace-scope jobs_export purge Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
962758c02d |
fix: pass SSL cert env vars to uv python install (#9790)
`install_python` cleared the subprocess environment via `env_clear()` and forwarded only a subset of variables, omitting `SSL_CERT_FILE` (from `PY_INDEX_CERT`/`PIP_INDEX_CERT`) and `UV_NATIVE_TLS` (from `PY_NATIVE_CERT`). This caused `invalid peer certificate: UnknownIssuer` errors when downloading managed Python runtimes in environments with corporate/private CAs. Forward both variables, mirroring the sibling `find_python` method and the pip install path in `python_executor.rs`. Fixes WIN-2100 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
754cae956a |
fix: use transaction for parallel_monitor_lock DELETE in last-iteration path (#9789)
In the last-iteration path of a parallel for-loop (nindex == len), the DELETE FROM parallel_monitor_lock ran on the pool (db) while the transaction tx (begun earlier) was still held. This dual-connection pattern requires 2 simultaneous connections from the per-worker pool (default max 5) and can trigger "pool timed out while waiting for an open connection" under concurrent load. Run the DELETE on the held transaction (&mut *tx) instead, matching the fix PR #7861 applied to other queries in this file. The transaction is committed shortly after, so including the DELETE in it is safe and consistent with the non-last-iteration path. Fixes WIN-2099 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
12f92e3ab7 |
[ee] feat(backend): native script retry without one-step-flow wrapping (#9688)
* feat(backend): native script retry without one-step-flow wrapping Schedules and data pipelines that retry a single script previously wrapped it in a one-step flow (JobKind::SingleStepFlow), creating extra job rows, a v2_job_status row, and UI projection complexity. This adds native retry on a plain JobKind::Script job. - RetrySettings: flatten Retry into a deduped retry_settings table, carried via the existing runnable_settings_handle (lazy, off the hot path). - push() materializes a bare-script-with-retry SingleStepFlow into a native Script job (gated on min-version + no handlers/retry_if). - add_completed_job re-pushes the next attempt on failure with backoff, tracking the attempt counter in v2_job_queue.extras and the chain via parent_job; schedule completion handlers fire only on the terminal attempt. - frontend: ScriptRetryChain shows the attempt chain on the run page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(backend): native retry_if eval + per-occurrence schedule handlers Extends native script retry to the two cases that previously stayed on the one-step-flow path: - retry_if: evaluated natively on the failure path via a feature-gated windmill-jseval dep (quickjs) over the failure result + flow_input; push materializes such policies natively only when quickjs is available. - on_failure_times / on_recovery: apply_schedule_handlers now resolves each past scheduled occurrence's terminal status across its native-retry chain (root OR any parent_job=root child succeeded) and excludes the current occurrence, so the counting is per-occurrence rather than per-attempt. All scheduled-script retries now go native (schedule.rs gate removed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(backend): always materialize retry_if natively; unsupported without quickjs retry_if is evaluated by the worker (which always has quickjs), not the pusher, so gating materialization on the pusher's feature was wrong. The flow path was never a real fallback either — the flow runtime needs quickjs to evaluate retry_if too. retry_if now always goes native; on a worker without quickjs it is unsupported and fails closed (no retry). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(backend): un-park asset-cascade (pipeline) retry Native retry resolves the blocker that parked pipeline retry: a retried subscriber is now a Script job (not a one-step flow / flow step), so it stays eligible for asset dispatch and can trigger its own downstream on recovery. - scripts.rs: persist // retry <count> [<delay>] to script_trigger on asset edges (was dropped with a TODO warning). - asset_dispatch.rs: is_eligible_kind keys off flow_step_id, not parent_job, so native-retry attempts dispatch on success while flow steps stay excluded. - tests: retry-bearing subscriber now dispatches as a native Script carrying the policy in runnable_settings_handle; native-retry attempt is eligible. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(backend): cap native retry interval, lazy result serialization, idempotent retry push Hardening from a self-review of the native retry path: - Cap the backoff at MAX_RETRY_INTERVAL to match the flow-runtime path (evaluate_retry); the exponential formula could otherwise schedule up to ~18h vs the flow path's 6h. - Serialize the failure result lazily (only when a retry_if policy needs it), so the common failure no longer pays the serialization on the failure path. - Push each retry with a deterministic id per (root, attempt). If a worker dies between enqueueing the retry and finalizing the current attempt, the reaper re-handles the attempt and lands here again — push rejects the duplicate id, so the retry is enqueued exactly once (no double-retry). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(backend): defer schedule handlers idempotently on retry-push replay (review P1) Address local-review findings: - P1: retry_pending was derived from the retry push *result*, so on a worker crash + reaper replay the duplicate-id push returned Err → retry_pending flipped to false → apply_schedule_handlers fired for the non-terminal attempt (and the terminal attempt later fired them again). Pre-check whether the deterministic retry id already exists and report it as pending without re-pushing, so the handler-deferral invariant is crash-idempotent too. - P2: refresh the stale 'wrap the script in a one-step flow' comment in the asset-cascade retry push — it now materializes a native Script. - Add RetrySettings <-> Retry round-trip unit tests (clamping edges). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(backend): native retry chain + per-occurrence status sqlx tests Close the two integration-test gaps flagged in local review: - chains_attempts_and_is_idempotent: drives maybe_enqueue_native_script_retry through attempt0 -> retry1 -> retry2 -> exhausted (counter, backoff, max-attempts) and asserts crash-replay idempotency (the P1 fix: a replayed completion reports pending without double-enqueueing). - per_occurrence_status_counts_recovered_as_success: pins the exact per-occurrence terminal-status query from jobs_ee::apply_schedule_handlers — a retried-but- recovered occurrence counts as success, retries (parent_job set) are excluded from occurrence counting, and the current occurrence is excluded. - canceled_job_does_not_retry: cancellation wins over a pending retry. Runtime sqlx API (no .sqlx cache entry needed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): exclude schedule handlers from the retry-attempt chain The retry chain listed all script children of the root by parent_job, but schedule completion handlers (on_failure/on_recovery/on_success) are also script children — when the occurrence has no retries, the handler's parent is the root itself, so a successful, never-retried job rendered a bogus 'Retries (1)' badge pointing at the handler. Filter children to re-runs of the same script (matching script_hash); real retries keep the root's hash, handlers run a different script. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): surface schedule handlers on the run page Extend the run-page chain component with schedule completion handlers: - A 'Handlers' row on a scheduled job links to the on_failure/on_recovery/ on_success runs that fired for that occurrence (found as children of the terminal attempt, identified by their synthetic created_by). - A handler's own run page now shows a 'Failure/Recovery/Success handler' label with a link back to the run it handled and its schedule. on_recovery and on_success share created_by, disambiguated by the recovery-only error_started_at arg. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(backend): restore folder_default_permissioned_as sqlx caches dropped by prepare An earlier `cargo sqlx prepare` on this branch ran before #8801's folder_default_permissioned_as test merged in, so it pruned the 3 query caches that test needs; cargo_test then failed under SQLX_OFFLINE. Restore them from main. * fix(backend): only cascade assets from native retry attempts, not handlers (review P1) is_eligible_kind keyed dispatch on flow_step_id alone, so every parented Script child became asset-eligible — including schedule/error/recovery handlers (Script jobs with parent_job set and no flow_step_id). A handler that declares assets would then trigger a cascade the old parent_job IS NULL guard prevented. Gate parented jobs on being a genuine retry attempt: a re-run of the SAME runnable as its chain parent (handlers run a different script). Runtime query, no sqlx cache. * fix(backend): cache the private-gated retry_setting asset-dispatch test query The same prepare-without-private that dropped the folder_default caches also pruned the cache for the retry_setting_dispatches_subscriber_as_native_script test query (asset_trigger_dispatch.rs:721). Regenerated with --features private. * fix(backend): exclude handler children from per-occurrence recovery (review) A scheduled occurrence's on_failure/on_success handler runs as a successful child (parent_job = occurrence), and the per-occurrence success EXISTS counted ANY successful child — so a failed occurrence whose error handler succeeded was marked 'recovered', breaking on_recovery (test_script/flow_schedule_handlers in the merge) and on_failure_times counting. EE query now scopes the EXISTS to same-runnable children (only native retry attempts); regenerate sqlx cache + bump ee-repo-ref. native_retry_test gains a handler-child regression case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(backend): scheduled-script retry is a native Script, not SingleStepFlow test_push_script_with_retry / test_try_schedule_with_retry (from main) asserted the old SingleStepFlow wrapping for scheduled-script retry; this PR makes it a native Script. Update both to assert kind='script' and that the retry policy is carried via runnable_settings_handle. * fix(backend): preserve dedicated_worker on native retry + saturate count casts (cubic) Address cubic CI review: - P1: the SingleStepFlow->native Script materialization dropped dedicated_worker, so a dedicated-worker scheduled script lost its dedicated pool on retry. Resolve it from the script row in push so the materialized Script keeps the dedicated tag. - P2: saturate the u32->i32 retry-attempt narrowings (RetrySettings::from) and the u32->i16 // retry count narrowing (scripts.rs) instead of wrapping. * fix(backend): use a retry-specific signal, not runnable equality (codex review) Address Codex CI review: - P1: is_native_retry_attempt treated any same-runnable parented Script child as a retry. WAC v2 inline children have that exact shape, so an inline child of an asset producer would cascade. Use a retry-specific signal instead: the job carries a retry_settings policy (always re-inserted by maybe_enqueue) and has no flow_innermost_root_job. Apply the same flow_innermost guard to the EE per-occurrence EXISTS (WAC inline children must not count as a recovery). - P1: the deterministic retry-id pre-check raced with push; a concurrent duplicate now resolves as 'retry pending' (re-check on the duplicate-id error) instead of flipping retry_pending to false and firing handlers early. - Tests: native_retry + asset_trigger_dispatch gain WAC-inline-child cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(backend): explicit native_retry_attempt marker, drop heuristics Replace the per-site "is this a retry?" inference (parent_job + runnable match + flow_innermost / retry_settings) with one explicit marker: a sparse native_retry_attempt(job_id, attempt) table, written in maybe_enqueue. The marker also carries the attempt counter (previously in v2_job_queue.extras), so it's the single source of truth. - asset_dispatch: is_native_retry_attempt is now one indexed EXISTS on the marker. - EE per-occurrence query: joins the marker instead of guessing by runnable/flow_innermost. - maybe_enqueue: reads/writes the marker (persistent) instead of queue extras. - Lifecycle: swept with the job in retention (log_cleanup), no FK to keep bulk delete cheap. - Eliminates handler / WAC-inline-child misclassification by construction. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(backend): sweep native_retry_attempt markers in the periodic retention path too (codex) The marker has no FK and relies on retention cleanup; log_cleanup.rs swept it but the periodic monitor.rs path deleted v2_job rows without it, orphaning markers. Add the same WHERE job_id = ANY(...) sweep there. * fix(backend): widen native_retry_attempt.attempt to integer (cubic) The smallint column was cast to/from u32 and could wrap a retry chain longer than i16::MAX into premature exhaustion. Use integer, matching the retry policy's i32 attempt count, so no narrowing occurs on the maybe_enqueue read/write path. * feat(frontend): mark retries via is_retry on listJobs; drop SAVEPOINT - Expose an is_retry flag on jobs (UnifiedJob/CompletedJob/QueuedJob + openapi), computed from the native_retry_attempt marker. The run-page chain now filters retry attempts by is_retry instead of the script_hash heuristic, so WAC v2 inline children (same script, parent_job) no longer render as retries (codex). - Revert the marker-cleanup SAVEPOINT (an unused pattern in this codebase): keep the plain catch-and-continue matching the other side-table deletes; the table is created by a startup migration so it always exists when cleanup runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(backend): mark is_retry sqlx(default) so non-list job queries can omit it The single-job GET query maps directly to CompletedJob/QueuedJob via FromRow but does not select is_retry, which errored with "no column found". Only the list endpoint populates the marker; #[sqlx(default)] lets every other query omit the column and default to None. * feat(backend): select is_retry in single-job GET too for consistency The list endpoint already exposes the marker; populate it on the single-job GET (both completed and queued variants) as well so a run loaded directly reflects its retry status. #[sqlx(default)] stays as a safety net for any other query. * feat(backend): reap orphaned native_retry_attempt markers via periodic sweep The marker has no FK to v2_job (to keep the hot bulk retention delete cheap), so direct job deletions (workspace/job delete, schedule clearing) would leave marker rows orphaned. Rather than add explicit cleanup to every v2_job delete site (which must then be remembered for every future path), reap orphans in the periodic delete_expired_items pass: DELETE FROM native_retry_attempt WHERE NOT EXISTS (the job). The table is sparse so the anti-join drives off it and probes v2_job by PK — cheap. Retention still sweeps markers inline (keeps the table small so this stays cheap); a transient orphan is harmless (nothing reads is_retry for a gone job). * fix(frontend): include flow handlers in retry chain handler row (codex) Schedule on_failure/on_recovery/on_success handlers can be flow paths (flow/...), whose handler job is a flow, not a script. The chain fetched children with jobKinds:'script', hiding flow handlers. Drop the kind filter — retry attempts are still selected by is_retry and handlers by created_by, so both kinds surface. * fix(backend): carry concurrency/debouncing settings into native retries maybe_enqueue re-pushed the next attempt with ConcurrencySettings/DebouncingSettings ::default(), dropping the script/pipeline concurrency settings the failed job carried in its runnable_settings_handle. A retry of a concurrency-limited script then inserted no concurrency_key and ran unbounded. Resolve both from the same handle (cached) and pass them in the payload, which push forwards to the materialized retry. Adds a regression test asserting the retry's handle resolves to the concurrency settings. * fix(backend): carry concurrency/debounce into scheduled-retry root + document retry-helper auth (codex) P1a (schedule.rs): the scheduled-retry materialization fetched the script's concurrency/debounce settings but passed ConcurrencySettings/DebouncingSettings ::default() into the SingleStepFlow payload, so the root attempt's handle held only the retry policy and the whole chain ran unbounded. Pass the fetched settings. Regression test asserts the root handle resolves to retry + concurrency. P1b (jobs.rs): document maybe_enqueue_native_script_retry's authorization contract — it is pub only for the integration test; the sole production caller is the worker completion path passing a DB-derived, already-authorized MiniCompletedJob. * docs(backend): attach native-retry auth contract to the function itself (codex) The doc block was merged with eval_retry_if's doc and bound to that function, leaving maybe_enqueue_native_script_retry undocumented. Split them: eval_retry_if keeps its own doc; the native-retry + authorization contract now sits directly above maybe_enqueue_native_script_retry. * docs(backend): regenerate served openapi-deref with is_retry + fix stale comments (codex) - Regenerate openapi-deref.{yaml,json} (served from lib.rs): they were stale since 1.734.0 and lacked is_retry on QueuedJob/CompletedJob, so clients reading the served spec couldn't see the field. Now current at 1.739.0. - schedule.rs: a retry_if gate is evaluated at failure time and fails closed without quickjs (no retry); it does not fall back to a flow path. - windmill-types jobs.rs: is_retry is selected by both the list and single-job GET endpoints (not list-only). * docs(backend): fix remaining stale retry_if/quickjs comments (codex) The retry_if block and the push materialization comments claimed push keeps retry_if on a flow path / the worker always has quickjs. The code always materializes native retry and the no-quickjs eval_retry_if path fails closed — correct the comments to that constraint. * docs(backend): fix stale quickjs-fallback + schedule-handler-restriction comments (codex) - Cargo.toml quickjs feature: without quickjs a retry_if gate cannot be evaluated and the job does not retry (no one-step-flow fallback). - jobs.rs handler-defer comment: apply_schedule_handlers resolves per-occurrence failure/recovery status across the retry chain, so the old 'restricted to schedules whose handlers don't need per-occurrence counting' claim is dropped. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f336674312 |
fix(debounce): atomic upsert robust to concurrent pull-time key deletion
Companion to windmill-ee-private: keep upsert_debounce_key a single atomic INSERT ... ON CONFLICT DO UPDATE so a chaining push cannot fail when the worker pull path concurrently deletes the holder's debounce_key (the prior read+UPDATE split could hit "no row updated"). Adds test_debounce_push_races_key_deletion_by_pull (races a chaining push against the key deletion 50x, asserts the push never errors) and refreshes the SQLx cache. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
248540ac4d |
feat: bounded-cascade selective execution for pipelines (UI + CLI) (#9695)
* feat: bounded-cascade selective execution for pipelines (UI + CLI) Run a prefix of a pipeline cascade: from a schedule/manual root, fan downstream but stop at chosen end node(s) — the path-between set over the asset-graph lineage DAG. Exposed as a canvas 'Run downstream up to…' pick mode and a 'wmill pipeline run <folder> --to' CLI command. No backend or parser changes; reads the existing graph, tags, and triggers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: surface bounded-run on the run caret, trigger-node kebab, and Test button Move 'Run downstream up to…' from the runnable kebab onto the play-button caret popover (Edit mode, next to Run / Run + trigger N downstream); add it to the trigger-node kebab so schedule/data_upload entrypoints expose it on the View page; and to the ScriptEditor Test split caret for the open script. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address CI review on bounded-cascade (cubic) - Port CLI engine test from Deno to bun:test under cli/test/ (won't run under bun test otherwise). - closure() now excludes the start node on a cycle back to it (descendants/ancestors contract); regression tests both engines. - CLI 'pipeline run --to' rejects unresolved/ambiguous end tokens instead of silently running a different subset. - Sort a copy in the runSelection order test so the launch-order assertions aren't invalidated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address standing review nits on bounded-cascade Resolves the four recurring P1/P2 findings from the codex/pi/claude reviews: - UI gate (P1): the canvas/trigger-node "Run downstream up to…" affordance was gated on the subscriber-only downstream map, so a valid start whose only downstream is a pure reader had a non-empty bounded set but no menu entry. Gate on the read-aware lineage downstream (buildLineageDownstreamMap), matching the bounded engine. - waitJob (CLI): a completed job without explicit success:true now counts as a failure, mirroring the frontend waitJobTerminal — the cascade only advances on a confirmed success. - Comment fix (CLI): the unbounded `run` path uses the read-aware lineage DAG (pure readers included); dropped the false "parity with the canvas cascade" (subscriber-only) claim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: expose bounded-run caret for pure-reader-only starts (codex P1) The canvas wiring from the prior commit passed `onStartBoundedRun` from the read-aware lineage map, but the leaf components still hid the popover that holds the "Run downstream up to…" action behind a subscriber-only gate: - RunnableNode rendered the Run-button caret only when `hasCascade = downstreamCount > 0` (subscriber-only). A valid start whose only downstream is a pure reader got `onStartBoundedRun` but no visible action. Now the caret opens when there's a cascade OR a bounded-run start (`hasCaret`), and the "Run + trigger N downstream" item is gated on `hasCascade` so it never reads "trigger 0". - ScriptEditor's Test split button activated only when `downstreamSubscribers > 0`, falling through to a plain Test button (no caret) otherwise. Now it also activates when `onBoundedRun` is set, with the "Test + trigger N" item gated on the count. For a manual root (no trigger-node kebab fallback) with a pure-reader downstream this was the only UI entry point, so it was previously unreachable. Verified in-browser: a manual-root script writing an asset read-only downstream now exposes "Run downstream up to…" on the ScriptEditor Test caret with the cascade item hidden. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: gate ScriptEditor bounded-run on read-aware downstream; fix CLI asset-end warning (codex P2) - Details-pane (ScriptEditor) bounded-run entry was gated only on `validStartPaths`, broader than the canvas which also requires read-aware downstream (`hasLineageDownstream`). An isolated start could thus expose "Run downstream up to…" and enter pick mode with no selectable end. Now gated on `lineageDownstreamPaths` (script paths with a downstream in `buildLineageDownstreamMap`), matching the canvas. - CLI dropped-end warning called `scriptPathOf(d)` unconditionally, which slices `script:`-length chars off an asset id too — `datatable:main/raw` printed as `le:main/raw`. Now prefix-checks like the JSON output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: correct --from error to exclude only row-backed event triggers (codex P2) The bounded-start validation message listed `kafka/webhook/…` as event triggers that can't start a bounded run, but webhook/data_upload are rowless and read as manual roots (valid starts). Only the row-backed native kinds (kafka/mqtt/nats/postgres/sqs/gcp/email — EVENT_TRIGGER_KINDS) are excluded; the message now names those. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: surface dropped ends in CLI JSON; disambiguate shared-trigger bounded start (codex P2) - CLI `run --json` silenced the dropped-end warning, and the JSON payload echoed the originally-resolved `--to` list with no reachable/dropped split — a resolved-but-unreachable end looked like a clean plan that silently runs only the start. JSON now includes `reachableEnds` and `droppedEnds` (shared `idLabel` helper, asset-id safe). - Trigger nodes dedupe per (kind, ref), so a schedule shared across scripts collapses to one node, but `recordSourceTrigger` kept only the first target path — the bounded-run action then rooted at an arbitrary script (or hid when only that first script lacked downstream). Now all target paths are tracked and the action is offered only when exactly one is a valid start with downstream; multi-eligible nodes suppress it rather than guess. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: don't run hidden drafts in View-mode bounded cascade (codex P1) launchCascadeScript unconditionally preferred drafts.get(path) over the deployed script. In View mode with drafts hidden (displayGraph is deployed-only), a bounded run started from a trigger-node kebab would execute preview jobs from hidden local draft content instead of the deployed scripts the user is looking at. Gate draft execution on `mode === 'edit' || includeDrafts` — the exact condition under which displayGraph includes drafts — so execution always matches the displayed graph. No-op for scripts without a draft; the edit-mode "Run + trigger N downstream" cascade is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d7939e514d |
chore: trim unused apt packages from server docker image (#9783)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1c3b2041dd |
fix(debounce): serialize upsert per key (simpler, race-free)
Companion to windmill-ee-private: the running-survivor guard and batch chaining are now protected by a per-key advisory lock instead of snapshot-sensitive single-statement SQL. This closes a concurrent-arrival data-loss race where a debounced late arrival's args could be dropped because the batch lookup couldn't see the predecessor's just-committed batch row. Extends test_debounce_concurrent_arrivals_after_running_survivor to pull the survivor and assert its accumulation includes BOTH racing late arrivals (shared batch), and refreshes the SQLx cache for the rewritten queries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cd42c6ca18 |
fix: decrypt secret variables via external backend in common resolvers (#9784)
`get_variable_or_self`, `get_variable_or_self_as`, `get_secret_value_as_admin` (and `transform_json_unchecked`'s `$var:` branch) in windmill-common always ran the raw `variable.value` through `decrypt()`. With an external secret backend (HashiCorp Vault / Azure Key Vault / AWS Secrets Manager) configured, that column holds a `$vault:`/`$azure_kv:`/`$aws_sm:` marker rather than base64 ciphertext, so base64 decoding failed with `Invalid byte 36, offset 0` (the `$`). This broke GitHub App git sync (git_sync_ee.rs) and any other consumer of these resolvers when an external backend is active. Move backend resolution (`get_secret_backend`, `get_secret_value`, `is_*_stored_value`, caching) into `windmill-common::secret_backend::resolver` so the low-level variable resolvers can route external markers through the configured backend's `get_secret()` instead of `decrypt()`. The windmill-store and windmill-api `secret_backend_ext` modules now re-export these from windmill-common (single source of truth / single backend cache) and keep only their write-side helpers. No `_ee.rs` files change. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
170cd79aaf |
fix: allow hyphens in postgresql database name validation (#9782)
* fix: allow hyphens in postgresql database name validation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: cover hyphen acceptance in validate_dbname Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
49e5c251d7 |
fix(debounce): harden running-survivor guard against concurrent arrivals
Companion to windmill-ee-private: switch the running-state check to a correlated EXISTS on the post-conflict-lock holder so two late arrivals racing after a survivor started running can't both spawn independent windows (the row lock serializes them; the second debounces into the first's fresh window). Adds a concurrent regression test (test_debounce_concurrent_arrivals_after_running_survivor) asserting exactly one late arrival survives and the other is debounced, and refreshes the SQLx cache for the updated upsert_debounce_key queries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e661e0f233 |
fix(debounce): add missing SQLx cache for test-only running-flag query
The cargo_test CI job compiles the test target with SQLX_OFFLINE=true; the new regression tests use `UPDATE v2_job_queue SET running = true ...` which was not in the offline cache (the library-only `cargo sqlx prepare` skipped test targets). check_oss/check_ee passed because they don't build tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f4eac04955 |
fix(debounce): never supersede a running debounce survivor
Companion to the windmill-ee-private change in upsert_debounce_key.
With debounce_args_to_accumulate + a concurrent_limit, a message arriving
while its debounce survivor is already running was marked completed/skipped
("Debounced Running by ...") and the running survivor deleted from the
queue, silently dropping accumulated elements. A slow step + concurrent
limit keeps the survivor running for a long window, so any arrival during
it was lost. The fix leaves a running survivor untouched and starts a fresh
debounce window for the late arrival.
Adds regression coverage in windmill-queue/tests/debounce_test.rs (push,
flow post-preprocessing, no-accumulation, committed-running, and
max-count-window cases) and refreshes the SQLx cache for the changed
upsert_debounce_key queries.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
74ebfc67f0 |
fix(frontend): nested-loop "Test this step" resolves iter to innermost loop (#9778)
* fix(frontend): nested-loop "Test this step" resolves iter to innermost loop In a loop-inside-a-loop, the inner step's "Test this step" tab prefilled its arguments using the outermost ancestor as the parent module, so flow_input.iter resolved to the parent loop's iteration value instead of the inner loop's. dfs(id, flow, true) returns [step, immediate parent, ..., root], so modules[modules.length - 1] is the outermost ancestor. The prop picker needs the immediate parent (modules[1]) so getFlowInput resolves iter at the innermost loop's level. A single loop was unaffected because both indices coincide; only depth >= 2 broke. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(frontend): nested-loop parent selection for test-step args Pins that modules[1] from dfs(stepId, flow, true) is the immediate parent for every step across all container types (for/while loops, branchone, branchall, aiagent tools) and nesting depths, and that getStepPropPicker then resolves flow_input.iter to the innermost enclosing loop. Covers >400 step positions across 107 generated flow shapes, plus explicit single/nested/while/branch iter-resolution cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(frontend): remove nested-loop parent selection test Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f6998ec54c |
feat: data tests for ducklake pipeline materialization (#9708)
* feat: data tests for ducklake pipeline materialization Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): data_test count badge on pipeline graph nodes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: surface annotation badges (incl. data_test) on deployed pipeline nodes Backend graph endpoint now parses each pipeline member's deployed body and returns partition/freshness/tag/retry/data_test, so badges render on deployed nodes, not only live drafts. Aligns the TS DataTest.relationships fields to snake_case to match the Rust serde wire shape (the type is now populated from both the parser and the backend JSON). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): keep materialize output edge when editing the producer in the pipeline graph The live-edit overlay re-derived a selected/edited script's lineage from // on inputs + body-inferred assets only, so the // materialize <asset> output (an annotation, not body SQL) was judged stale and its write-edge dropped on select — leaving the materialized asset unlinked (and the node's annotation badges hidden). Include the parsed materialize target in liveRefKeys and the draft writeOuts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: run all data tests in one pass with a structured per-test result Replace the raise-on-first-violation probes with a single materialize summary that embeds every test's violating-row count in a data_tests column (computed in a CTE, since DuckDB rejects subqueries inside struct literals). The worker reads the breakdown and decides pass/fail: a clean run returns the per-test summary in the result; a failing run errors with the FULL list (every test, ✓/✗ + counts), not just the first failure. Verified live (EE) for built-ins + custom, pass and multi-failure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): data-test pass/fail checklist in the job result DisplayResult renders a per-test checklist (✓/✗ + violation counts) above the raw result for managed materialize runs — from the structured data_tests on success, and parsed from the worker's breakdown message on failure. Shows in the script editor Test panel, the runs page, and the pipeline asset run pane. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): move data-test badge onto the producer→asset edge with run status The test badge now sits on the write-edge (the transformation link) rather than the producer node, since the tests assert on what the transformation produces. It's tinted by the producer's last-run status (green = passed, red = a test failed) and its hover title lists every declared test. Removes the now-redundant node badge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): render custom data-test scripts as their own clickable graph nodes A // data_test <script_path> custom test now appears as its own node below the asset it validates, joined by a dashed 'tests' edge. Clicking it opens the test script in the detail pane (dispatched like any runnable). Built-in tests stay folded into the edge badge; only script-backed tests become nodes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): type data-test edge field via AssetGraphResponse, not in-scope g BuiltEdge is declared at component scope, outside build(g), so referencing typeof g.runnables in its type failed CI's svelte-check (Cannot find name 'g'). Use the imported AssetGraphResponse type instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): anchor edge badge on routed path + a11y text on test icons Address review: the data-test edge badge anchored on the straight-line midpoint, floating off detoured edges — anchor it at detourX when the edge is routed through a gutter lane. Add sr-only pass/fail text so the checklist icons are distinguishable to screen readers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: close data-test enforcement bypass + gate badges to scripts + reject multi-stmt custom tests Address review (cubic) findings: - P1: managed materialize generates its own summary row carrying data_tests, and enforcement reads that column — but a // result_collection annotation (e.g. a scalar mode) could reshape the row and drop data_tests, silently bypassing a failing test. Force LastStatementAllRows for managed materialize runs so the summary row is always intact. - P2: asset-graph annotation badges were keyed by path only, so a flow sharing a path with a pipeline script inherited its badges. Gate the lookup on usage_kind == Script. - P2: a custom test body is embedded as a subquery, so a multi-statement body produced invalid SQL with an opaque DuckDB error. Validate single-statement up front with an actionable error; align docs/comments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: fail loud if fewer data-test outcomes recovered than declared Defense-in-depth from the fresh-context review: enforcement reads per-test outcomes off the materialize summary row, but if the data_tests column were ever dropped/reshaped at the FFI boundary, extract_data_tests would return fewer (or zero) outcomes and the run would silently pass unverified tests. Track the embedded test count on MaterializeExec and abort with a clear error when recovered < declared. Verified: normal run (4==4) unaffected; the scalar-result_collection bypass already fails. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: relationships data test same-lake reuse + schema-qualified target quoting Address Codex/Pi review (two P1s in the relationships codegen): - A relationship into the same ducklake as the materialize target minted a second ATTACH of that lake under _wm_ref_N while _wm_target already held it — DuckDB forbids attaching one database twice, so the test failed before it could run. Reuse _wm_target for same-lake references. - A schema-qualified target (ducklake://warehouse/main.dim_products.sku) emitted FROM _wm_ref_0."main.dim_products" — one quoted identifier with a literal dot — silently querying a nonexistent table. Quote each dotted segment so the dot stays a schema separator. Adds tests for both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): refresh data_test badge on deployed-script drafts + scope to materialize target Address Codex review nits (both P2): - resolveGraph: the existing-runnable draft-overlay branch kept the deployed data_tests, so adding/removing // data_test lines on an already-deployed script left the badge stale until redeploy. Refresh it from the live parse like the new-runnable branch. - AssetGraphCanvas: data tests were attached to every write-edge from a producer. They assert on the // materialize target (always a ducklake asset in v1), so only the ducklake write-edge now carries the badge and custom-test nodes — a producer's other (S3/datatable) outputs no longer show them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
88fca6a8c1 |
fix: enforce containment of python module dir for preview jobs (#9704)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d131d754e1 |
feat: ducklake time-travel UX (snapshot history + AT VERSION reads) (#9709)
* feat: ducklake time-travel UX (snapshot history + AT VERSION reads) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: catalog-qualify ducklake time-travel FROM hints (lake. prefix) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: render ducklake snapshot_time (microseconds since epoch) correctly Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor: merge ducklake History + Query into one master-detail tab Snapshot list (left) selects the version previewed in the read-only grid (right); newest auto-selected. Copy-clause moved to the preview's SQL line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: scope ducklake snapshot history to the table's versions Catalog-wide snapshots predate a table's creation; previewing AT a version before the table existed errored ("Table ... does not exist at version N"). The DUCKLAKE_SNAPSHOTS marker now takes the table and lists only snapshots from its first creation onward. Also: narrower snapshot-list pane on large screens (target a fixed width, not a fixed fraction). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: load ducklake preview columns at the pinned version + reset on asset switch Addresses CI review (codex/pi P1, cubic P2): - Historical previews loaded current-schema columns, so an AT(VERSION) read enumerating a column added in a later snapshot failed. Now DESCRIBE-loads the column set at the pinned version; the read is gated on columns matching the current version to avoid a stale-colDefs race on version switch. - selectedVersion no longer sticks across assets: the panel is keyed on path (remounts per asset) and effectiveVersion falls back to newest when the pick isn't in the current list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: match History tab UI (master-detail, full-FROM copy) after merge Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: handle catalog-only ducklake asset paths (no table segment) parseDbInputFromAssetSyntax threw on a catalog-only path like 'ducklake://main' (undefined.split('.')) — a real graph node (e.g. a consumer of the whole catalog). It now returns a table-less input instead of throwing, and DucklakeAssetPanel renders only the partition grid (no per-table history/time-travel) for table-less nodes. Adds parser unit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: escape ducklake catalog name in client-built time-travel DESCRIBE fetchDucklakeColumnsAtVersion interpolated the catalog name into an ATTACH string literal without escaping; double single-quotes (mirrors backend escape_sql_literal) so a quote-containing catalog name can't break out. Also fixed the v1.x docs checklist line to match the shipped full-FROM copy affordance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
920f5688ca |
chore(main): release 1.739.0 (#9746)
* chore(main): release 1.739.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>v1.739.0 |
||
|
|
3fafac275d |
feat(ai-chat): add /clear session command to start a fresh conversation (#9769)
* feat(ai-chat): add /clear session command to start a fresh conversation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): don't re-queue a built-in command flushed from the queue Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
89c46c803e |
docs: add ai-chat and ai-evals skills (#9770)
Add two agent skills under .agents/skills (symlinked into .claude/skills): - ai-chat: guidance for improving the Windmill AI chat / copilot, especially global mode — benchmark before/after with ai_evals, optimize finalContextTokens over cumulative, keep tool params and tool-result payloads minimal (no echoing content the model already has), treat prompts/tool-descriptions as benchmarkable surface. - ai-evals: author and run black-box benchmark cases for the AI generation modes, migrated from ai_evals/AGENTS.md and extended with run mechanics (workspace reuse, reading the summary). ai_evals/AGENTS.md becomes a pointer stub to the skill. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f99781ca5f |
fix: persist on-behalf-of user across app deploy paths (#9773)
* fix(frontend): persist on-behalf-of user when redeploying raw apps The raw-app deploy drawer reused AppEditorHeaderDeploy but never wired up the `preserveOnBehalfOf` bindable nor forwarded `preserve_on_behalf_of` in the createAppRaw/updateAppRaw request bodies. Without that flag, the shared backend handler (create_app_internal/update_app_internal) resets the policy's on_behalf_of to the deploying user on every deploy. So a publisher who set "App executed on behalf of <other user>" would silently lose it on the next deploy, unlike every other setting on the deploy page. Mirror the classic (low-code) app header: declare `preserveOnBehalfOf`, bind it to the deploy component, and send `preserve_on_behalf_of` on both create and update. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): preserve on-behalf-of in the draft-deploy path The draft-deploy path (deployDraft → AppService.createApp/updateApp for visual apps, deployRawAppDraft → createAppRaw/updateAppRaw for raw apps) carries the deployed app's policy forward but never sent preserve_on_behalf_of. So deploying a draft via the "Review & deploy drafts" UI silently reset the policy's on_behalf_of to the deploying user — the same backend reset behind the deploy-drawer bug, on a surface that has no on-behalf-of selector to re-set it. Send preserve_on_behalf_of whenever the carried policy has an on_behalf_of, for both app types. The backend still gates actual preservation on can_preserve_on_behalf_of, so a non-deployer cannot escalate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): preserve on-behalf-of in the AI-chat raw-app deploy The global AI-chat deploy path (`deploy_workspace_item` → createAppRaw/ updateAppRaw in copilot/chat/global/core.ts) carried the recomputed policy forward but omitted preserve_on_behalf_of, so deploying a raw app via chat reset the policy's on_behalf_of to the deploying user — the last of the deploy surfaces with this gap. Send the flag when the policy has an on_behalf_of, mirroring the editor and draft-deploy paths; the backend still gates preservation on can_preserve_on_behalf_of. Add a regression test asserting the flag is forwarded when the deployed policy carries an on_behalf_of. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a116715c41 |
feat(frontend): restore raw app 'open preview in separate window' (#9765)
* feat(frontend): restore raw app 'open preview in separate window' Re-adds the detached preview window dropped when preview hosting moved to the host (ui-builder f52d8e5b). Live-synced preview + dark mode, and wires the runnable bridge to the detached window so backend calls resolve there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): repaint detached raw app preview after refresh The detached preview tab is a blank app-preview.html shell fed by the editor over postMessage. A one-shot opener load listener can't survive the tab refreshing itself, so a manual reload left it blank. It now posts 'appPreviewReady' on every (re)load and the editor re-sends the build; the orphaned window is also closed when the editor unmounts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): keep load-based feed for detached preview initial open Relying solely on the appPreviewReady handshake left the detached window blank on first open against app-preview.html artifacts that predate the handshake (the pinned UI Builder tarball). Restore the one-shot load feed so initial open works regardless of the served shell; the handshake still covers manual refresh. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): re-sync dark mode when refocusing detached preview The focus-reuse path replayed the build but not the theme, so re-opening an existing detached window after a dark-mode toggle kept the stale theme until the next build. Extract a feedExternalPreview() helper (theme + build) used by the open, focus-reuse, load and handshake paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): bump ui_builder artifact to 062d11c (preview handshake) Pins the UI Builder artifact built from windmill-code-ui-builder#14, which adds the appPreviewReady handshake, detached-preview favicon and title. Activates refresh-repaint + favicon for the detached raw app preview. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): serve ui_builder static bundle in dev, proxy :4000 only as fallback The postinstall downloads the pinned UI Builder artifact into static/ui_builder, which SvelteKit already serves at /ui_builder. Skip the :4000 proxy when that bundle is present so dev uses it directly (matching prod / the backend's static-vs-:4000 fallback); no separate UI Builder dev server needed. Delete static/ui_builder to develop the builder against :4000. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): harden detached preview origin + scope window name Addresses cubic review on #9765: - P1: only honor appPreviewReady from a same-origin sender and post the build with targetOrigin=location.origin, so user app code that navigates the detached window cross-origin can't trigger/receive a build (app source). - P2: scope the detached window name per app path so two open editors don't collide over one OS-level preview window. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2e020b2ccc |
feat(ai-chat): context usage gauge + unified model settings menu (#9763)
* feat(ai-chat): show context usage as a gauge with hover tooltip Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): consolidate model, thinking & params into one dropdown Merge the model picker, reasoning-effort selector and prompt settings into a single dropdown with a model list, a thinking-effort slider and a hover-revealed Parameters submenu. The trigger shows the model and effort. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): polish model/thinking dropdown interactions Register the model rows and thinking slider as melt menu items (roving highlight + arrow-key navigation), keep the menu open on selection via a new DropdownV2 closeOnItemClick prop, use melt's createSubmenu for the Parameters flyout so it flips on screen edges, and use the brand accent for the context-usage gauge and slider. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): stop popover drift and keep Thinking section when unsupported Freeze the trigger width while the dropdown is open so the bottom-end popover doesn't shift as the effort label resizes (released on close, so no reserved padding). When a model has no reasoning support, show the Thinking section disabled with a message instead of removing it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): restore reasoning slider drag inside the menu The slider lives in a melt menu item, whose roving focus blurs the focused element on pointermove and aborted the native thumb drag. Stop the slider's pointer events from bubbling to the item so melt leaves it alone; focus-based highlighting still works. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): move Parameters to the top of the model settings menu Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ai-chat): hide the @ context picker in global mode Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ai-chat): only mark context gauge as a meter when the window is known A meter is a 0–100% reading; with an unknown context window there is no max to measure against, so role/aria-value* are dropped (previously valuenow fell back to the raw token count against an implicit valuemax of 100). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(frontend): note closeOnItemClick is read at mount-time Addresses a non-blocking review note on DropdownV2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(ai-chat): fix showContextPicker comment to match GLOBAL removal Addresses Pi review P2: GLOBAL no longer offers the @ context picker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(ai-chat): clarify showContextPicker hides only the manual @ button In GLOBAL, @-context is still invoked inline by typing @ in the input; only the redundant picker button is hidden. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4dbf873723 |
fix(frontend): stop flow step id generation from being poisoned by non-canonical keys (#9766)
* fix(frontend): stop flow step id generation from being poisoned by non-canonical keys
nextId computed the next step id from the max of charsToNumber over every
module id and flowState key. Only canonical auto-ids (a, b, ... aa, ab) have a
meaningful charsToNumber value, but flowState also holds copy ids ("z2"),
subflow result keys ("subflow:..."), reserved keys ("failure"/"preprocessor")
and user-renamed ids. The old `length >= 4` guard filtered long junk but let
short junk through, so e.g. duplicating step "z" (key "z2", charsToNumber 629)
made the next new step jump to "xg" and escalate from there.
nextId now only counts a key if it round-trips through numberToChars and is not
reserved, and the broken length cap is removed so large flows still get correct
ids.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(frontend): keep length cap in nextId to avoid regressing long renames
Address CI review: removing the length cap made all-lowercase renamed step
ids (e.g. "process", which round-trips through numberToChars) feed into the
max and poison id generation again — a regression versus the prior behavior,
since step ids can be renamed to ^[a-zA-Z][a-zA-Z0-9_]*$.
Restore the length>=4 skip and pair it with the round-trip canonical check,
so short non-canonical keys (copy ids "z2"/"c10", reserved/renamed short ids)
no longer poison the max while long renames stay out of the sequence. Update
the tests to reflect the actual coverage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
b5bd8245d8 |
fix: reject symlink traversal in job-dir path validation (#9713)
* fix: reject symlink traversal in job-dir path validation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: cover dangling symlink in job-dir path validation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: close symlink-traversal bypass via in-bounds `..` in path check Walk the normalized relative path instead of raw user components, so an in-bounds `..` (e.g. `foo/../evil/payload`) can no longer drift the walk past a planted symlink. Adds regression coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
de6192bec1 |
fix(frontend): highlight the runtime-chosen branch in flow graph viewer (#9755)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2a70ccc386 |
feat(frontend): show approval wait as a distinct segment in flow timeline (#9756)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
83cc5533ee |
feat: add /compact session chat command (#9764)
* feat: add session chat slash commands * feat: add /compact session chat command Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: dedupe built-in commands against same-named workspace skills Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
288318ac26 |
fix(apps): realign legacy raw-app drafts to raw_app draft kind (#9761)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3d48ba7738 |
feat(frontend): add filter submenu to collapsed AI sessions popover (#9757)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fada673bb4 |
chore: bump uv to 0.11.24 in images and CI (#9759)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |