Restarting a flow at the step right after an approval step copied the
approval step as completed, and the worker skips the gate at the restart
step, so a flow whose approval timed out or was disapproved could be
restarted past it without anyone approving. Restart resolution now
refuses a restart whose copied steps include an approval gate that never
opened, naming the step to restart from instead.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: stop handler, app and agent jobs resolving untrusted references
* perf: skip decoding untrusted args that hold no dollar or escape
* test: drop the transaction the recovery handler no longer takes
* fix: flag reserved names in object keys and tighten the jsonb parity test
* fix: keep schedule push, flow advance and wac approval on one pool connection
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: read flow args on the status-update tx instead of preloading them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: pin ee ref for the scim update_group fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to c465c042eeae3c5d9a0180f62a56e8f8d3e9e162
This commit updates the EE repository reference after PR #905 was merged in windmill-ee-private.
Previous ee-repo-ref: dc4d0327044ddc10ef53ad0b21ae37b96cf4aad2
New ee-repo-ref: c465c042eeae3c5d9a0180f62a56e8f8d3e9e162
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: stop holding a pool connection while waiting for another
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: count only earlier occurrences as schedule handler history
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to ac53164fd27fe962a020f3ee944949df1f760603
This commit updates the EE repository reference after PR #904 was merged in windmill-ee-private.
Previous ee-repo-ref: 047764f902f10fce0156138c4240ba5d1a2e969a
New ee-repo-ref: ac53164fd27fe962a020f3ee944949df1f760603
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* test: stop backend tests waiting out real time they do not need
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: delete the unused workspace_dependencies fixture
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: route the python SDK's S3 client settings through the S3 proxy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: drop the boto3 config key and expose the proxied bucket to polars
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: proxy S3 settings only for server-signed tokens and keep tls verify
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: isolate fork object storage under a per-fork prefix with read-through
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: guard fork storage isolation against traversal, shared lakes and orphaning
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: serialize fork purges with same-id creation and guard shared middle forks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: drop fork storage mappings on every replica and lock fork deletion against child creation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 455681e55eb7cdfcfdb7cd79cbae75b333e07943
This commit updates the EE repository reference after PR #893 was merged in windmill-ee-private.
Previous ee-repo-ref: 93f26e3cf816e4eb97b7d4dbbb5fc97fdf9821af
New ee-repo-ref: 455681e55eb7cdfcfdb7cd79cbae75b333e07943
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: add a toggle to leave forks of non-dev workspaces out of dev isolation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: track the non-dev forks toggle reactively and bound the forking rule checks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: point ee-repo-ref at the skip non-dev forks telemetry registration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep forks of archived dev workspaces isolated and warn about forkable non-dev forks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: reset tag lineage caches on every archive and unarchive
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: cache the token dev lineage lookup and trim isolation hot-path costs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: point ee-repo-ref at the merged skip non-dev forks telemetry registration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: route dev workspace and fork jobs to their own worker groups
* fix: harden dev workspace isolation against review findings
* fix: verify hashed dedicated tags and keep dev lineage across renames
* fix: let tag-scoped tokens reach jobs queued on dev workspace tags
* fix: expand tag scopes only in dev workspaces and forks
* fix: expand tag scopes only while dev workspace isolation is on
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: collapse the dev workspace isolation section by default
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: count dev workspace isolation toggles and dev-serving group flags
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: validate the dev workspace tag suffix on every instance config writer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 71e9c0de8317e839f955ebef6a819062f20ac418
This commit updates the EE repository reference after PR #892 was merged in windmill-ee-private.
Previous ee-repo-ref: b5ab84e1705f939c399f6315a03f88b55bce1804
New ee-repo-ref: 71e9c0de8317e839f955ebef6a819062f20ac418
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: relock apps in place so imported script changes reach them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: requeue an app relock whose version was rewritten meanwhile
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: relock every component of the version a superseded app relock requeues
Also share raw-app runnable dependency extraction between the worker and the dependency-map rebuild, and take no-key-update row locks, which still order against a deploy's UPDATE without blocking app_script inserts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: relock every component when an app relock runs on a newer version
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: resolve a requeued app relock's components when it runs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: regenerate every lock when an app relock relocks every component
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* perf: drive schedule already-exists check from v2_job_queue
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: index v2_job_queue by workspace and scheduled_for
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: allow early return on steps inside a branch to one
The "Early return for sync webhooks" flow setting could only point at a
top-level step. It can now also point at a step inside a branch to one,
in any branch including the default one, and through nested branches to
one.
When the branch is taken, the sync call returns that step's result as
soon as it completes while the rest of the flow keeps running. When it
is not taken, the call returns the flow's final result. The sync wait
loops already resolved nested steps and fell back to the final result,
so they are unchanged.
Worker: check_if_early_return_or_last_in_early_return_parent now
accepts a job whose step id matches early_return at any depth, as long
as every job between it and the root is a branch to one. A loop
iteration or a subflow reusing the same id does not take the stream.
The existing case, the last step of a branch to one that is itself the
early-return step, is unchanged.
Frontend: the native <select> is replaced by EarlyReturnPicker, a
searchable list in flow order where each nested step shows its path
(e.g. `a › Branch 1`). Branch to all and loops inside a branch to one,
and the steps inside them, are not offered: the results lookup cannot
resolve them when nested. Picking a nested step shows a hint that it
only runs if its branch is taken.
* fix: stream the last step when the early return branch is not taken
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep the stream off the last step once the early return step ran
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: list early return steps by id only
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: use the standard select for the early return step
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: match the early return step by its flow scope, not its path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep branch to all and loop steps off the early return stream
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: allow early return on steps inside a branch to all
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: resolve a nested branch to all or loop as the early return step
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: pin which step streams for an early return inside branches
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: return an empty nested loop or branch to all as its early return list
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* revert: read an empty container's early return status from the root
An empty nested loop or branch to all falls back to the flow's final result again,
so an empty top-level loop that failed keeps its real status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: stefan <stefan.radovanovic@ip-paris.fr>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: connect a data table as the job's own identity unless the runnable opts into its viewer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: key cached results by the data table mode and keep the flag across ai edits
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: hash the cache key's mode and viewer as fixed fields so no argument name can forge them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 2f74548ad1ec50c8e1faf17e42d5648160e833f1
This commit updates the EE repository reference after PR #878 was merged in windmill-ee-private.
Previous ee-repo-ref: 4ad768a777fff9f1ecc86a2803bc27d80f8242d6
New ee-repo-ref: 2f74548ad1ec50c8e1faf17e42d5648160e833f1
Automated by sync-ee-ref workflow.
* feat: rename the flag to viewer_data_access and apply it to object storage permission rules
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: describe viewer_data_access as covering object storage rules too
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 0ffb633473c5465513576afc6f54d18f93c7f1e1
This commit updates the EE repository reference after PR #879 was merged in windmill-ee-private.
Previous ee-repo-ref: e9e06a9eb94168f46b378276b01fa0117dc7d8ed
New ee-repo-ref: 0ffb633473c5465513576afc6f54d18f93c7f1e1
Automated by sync-ee-ref workflow.
* feat: show the viewer data access control as an icon button
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: export worker cpu, memory and occupancy metrics
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: refresh the exported memory limit with the cgroup readings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: per-script resource rollup and heaviest scripts table
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: pin the companion ee commit and drop job cpu entries on every exit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: count the cpu time of suspended workflow rounds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: count suspended workflow rounds' runtime and guard empty averages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: flush the runnable rollup hourly in key order and drop its workspace fk
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: order the rollup upsert in sql and sweep rows of deleted workspaces
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: keep the rollup orphan sweep on a primary key range
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to a1c00d73a05f227a61d2bf37cc86cd9f5bb1777a
This commit updates the EE repository reference after PR #866 was merged in windmill-ee-private.
Previous ee-repo-ref: c6bcb1bbb5a7e85e870f9de232a0ad1540acf2bd
New ee-repo-ref: a1c00d73a05f227a61d2bf37cc86cd9f5bb1777a
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: store a workflow-as-code parent's first start and wall duration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep a parked child's first start in its parent's timeline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: record a parked step with its whole span in the flow, cover inline checkpoints
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to ab8476e5feedc98b727f7d83acffa1807b3b9c5b
This commit updates the EE repository reference after PR #861 was merged in windmill-ee-private.
Previous ee-repo-ref: d5d95f51bb175944874e585e5931acb1eddd637a
New ee-repo-ref: ab8476e5feedc98b727f7d83acffa1807b3b9c5b
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: add retry to runScriptByPathAsync and runScriptByHashAsync
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: refuse retry with invisible_to_owner and keep the parent priority bump
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: refuse retry when a preprocessor would run and export the options type on jsr
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: keep the dispatching job as parent of a retried run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: cancel pending native retries with their caller and stop the chain on cancel
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: concurrency limit over-admits when the counter row is missing
* chore: bump ee ref
* fix: restore sqlx entry for the locked counter read and bump ee ref
* fix: strip NUL from ai agent output and job args before jsonb writes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: also strip NUL from tool call ids and citations
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A team plan canceled mid-month now keeps premium until the 1st, capped at the
executions its final invoice paid for. That reuses the past-due cap, so the job
refusal and the UI toast said "your last invoice is unpaid". Word both for a
canceled plan, and show it on the plan settings page.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: add an instance setting routing all dependency jobs to one tag
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep bunnative locks on bun and explain the default dependency routing
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: dispatch ai agent tools through the worker queue
* fix: respect worker tags when executing ai agent tools locally
* fix: cancel only direct-child tool jobs on agent failure
* fix: cancel queued descendant tool jobs on agent failure
* fix: route ai agent tools by the flow's tag rule
* fix: drop caller-supplied _MODULES at push for every job
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: never interpolate _MODULES into a tag and drop unused mut
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: opt-in move of perpetual runs to a newly deployed script version
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: keep the perpetual-run opt-in across relocks and check the new version's tag
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: pin the tag check on a perpetual version switch
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: count perpetual runs past the first queue page in the deploy prompt
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: restart perpetual runs on the version a deploy makes runnable
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: claim a perpetual run and queue its replacement in one transaction
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: honour a cancel that lands after the worker last read its queue row
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: leave the lost-cancel fix to its own PR and match the scale down to 0 wording
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: run a preprocessor the deployed version adds over the arguments carried over
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* style: shorten the wording of the modal's argument warning
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: never preprocess a restarted perpetual run, as every other restart does
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: preprocess for a replacement whose run had not been through one
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: push a perpetual replacement without the deployed debounce settings
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: name the runs the deploy button restarts
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* perf: skip the perpetual restart lookups on a deploy that is not perpetual
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: move perpetual runs before anything that can fail after the deploy commits
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: report cancellation on the queue listing so the deploy prompt can skip it
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: pass the tag workspace to the availability check after the merge
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* style: say which arguments are defined differently and which values are kept
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: resolve a dynamic tag before checking it for a restarted run
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: pin that a deployed dynamic tag is checked as it resolves
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix: run custom schedule handlers as the schedule
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: correct handler identity comment and pin preset path check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: pin preset handler paths instead of matching their name
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: warn on unpinned preset-named schedule handlers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 12a83524b81368043bc939caeb3764fd27a32523
This commit updates the EE repository reference after PR #833 was merged in windmill-ee-private.
Previous ee-repo-ref: bcfb0838f105d5bffbe49c6ed787116662ebe796
New ee-repo-ref: 12a83524b81368043bc939caeb3764fd27a32523
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: add pull_batch to claim jobs for many waiting workers at once
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep jobs admitted by earlier batch passes when a re-pull fails
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep suspended flows first on every batch re-pull pass
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs: plan for detecting skipped schedule occurrences
Design plan only, no implementation. Records the scheduler's re-anchoring
behaviour, the measurements behind it, and the three-piece design that came
out of reviewing the alternatives.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* docs: state the user-facing outcome in the schedule plan
The plan described the mechanism but never what a user would see, which made it
hard to judge what the work is worth.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* docs: state which cause the schedule plan catches, and correct its scope
Records which of the two causes each piece covers, and corrects the overrun
scope: a script schedule carrying retry or dynamic_skip is pushed as a
SingleStepFlow, so it re-arms at step 0 entry and its occurrences overlap like
a flow's. Resolves the no_flow_overlap question, splits the read-time work into
bounded detection and editor-only counting behind measured croner costs, and
fixes the delivery order.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* feat: count the occurrences a schedule skipped
A schedule that overruns its interval, or waits for a worker, silently loses
the occurrences in between: the scheduler keeps one queued occurrence and
re-anchors on the clock, so nothing records that a run was due and never
happened.
Recovers the sequence from rows that already exist rather than writing per
occurrence. `push_scheduled_job` anchors on `now_from_db` inside the
transaction that inserts the job, and `v2_job.created_at` defaults to that
same transaction timestamp, so `scheduled_for = find_next(created_at)` holds
exactly and the whole occurrence history is derivable.
The schedules list reports how many of the recent runs were followed by a lost
occurrence, and a new occurrences endpoint carries the per-run wait and
duration behind it. Detection is one `find_next` per gap, which stays bounded
on a full page; counting walks the gap and runs only for a single schedule.
The one write is `occurrence_baseline_at`, advanced at create, edit,
re-enable and re-arm. Gaps older than it span a pause, a cron change, a
re-enable or a reconciler re-arm, none of which mean runs were lost.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* feat: show the wait and run time behind a schedule's skipped occurrences
The list badge says a schedule is losing runs; this says which of the two
causes did it. A large wait means not enough workers, a long run means the job
outgrew its interval, and the pair is what tells them apart. Sits under the
existing upcoming-events panel, so due and overdue read together.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* feat: flag a schedule that is running late right now
Reconstruction is retrospective: a gap only appears once the next occurrence
has a row, which needs the current one to finish. A schedule wedged mid-run
shows nothing until it moves, which is the case an operator most wants to see.
An occurrence still in flight past the time its own successor was due will
cost that successor, so `now > find_next(scheduled_for)` is the signal, needing
no threshold and self-calibrating across a daily and a per-minute schedule. It
applies only where occurrences serialize; an overlapping schedule starts its
successor on time and would flag constantly while healthy.
The queue is read in one aggregating pass keyed on (trigger, runnable_path)
rather than a subquery per schedule, and an overlapping schedule holds more
than one root row, hence the aggregate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* feat: run the schedule overrun alert from the monitor pass
Wires `schedule_overrun_alerts` in next to `jobs_waiting_alerts`, every 30
iterations (~5 min). Its Enterprise implementation lives in
windmill-labs/windmill-ee-private#772; only the wiring and the OSS stub are
here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* chore: refresh the sqlx offline cache
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* feat: record and alert when a schedule skips occurrences
push_scheduled_job compares each chained occurrence with the slot after
the previous one. A gap is written to schedule.skipped_occurrences off the
push transaction, alerts once when a clean schedule starts skipping, and
recovers on the next clean chain. The schedules list shows a badge.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* feat: alert only on a streak of skipping runs, keep a recent skip visible
The skip state now describes the current streak and is written in the push
transaction, so it commits or rolls back with the push. The alert fires
once when 3 runs in a row skipped, and the list keeps a muted badge for 7
days after the latest skip. Editing or toggling a schedule resets it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* fix: name the missed-occurrence state after what it counts, alert only once committed
Renames the columns to late_run_streak, missed_occurrences and
last_missed_at, keeps the missed count after a streak ends so the muted
badge can show it, and rewords both badges. The alert task now reads the
streak FOR SHARE, which waits for the push transaction, so a push that
rolls back and retries alerts once. A failed slot count leaves the streak
untouched, and a schedule deleted mid-push no longer fails it. Adds an
integration test for the streak and its reset.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* fix: recover the late run alert, store the missed slot, name it missed throughout
The alert now recovers (and so acknowledges itself) when a streak that
alerted ends on a run on time, under the schedule:{path} resource used by
the other trigger alerts. last_missed_at records the last missed cron slot
rather than when the late run chained, and the counting helpers say
missed, since skipped already names occurrences queued and not run.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* fix: scope the late run alert to its workspace, acknowledge it on edit, toggle and delete
Recovery acknowledges alerts by resource alone, so the resource now
carries the workspace. Editing, toggling or deleting a schedule clears
its streak and a disabled or deleted one never chains a run on time, so
those handlers acknowledge its open alert after committing. Past the
1000-slot cap, last_missed_at falls back to the detection time.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
* refactor: raise the late run alert like the other critical alerts
Drops the recovery, the workspace-scoped resource and the acknowledgement
on edit, toggle and delete: the alert now fires once per streak with no
resource and is acknowledged from the alerts feed, as the trigger and job
failure alerts are. The FOR SHARE read stays, so a push that rolls back
across the flow path's retries still alerts once. Notes in openapi that
past 1000 misses in one late run the count is a floor and the time
approximate.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJ9wpjWp2YgLUSqt1Ai5d6
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat(telemetry): count trigger suspend, resume and discard
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor: shorten the telemetry disclosure to one line per category
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: correct the resume branch comments and note the pre-commit fire count
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: name feature adoption in the telemetry disclosure
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 9855e1b7a43a0a33e04f8accf1c497af3fd9b139
This commit updates the EE repository reference after PR #834 was merged in windmill-ee-private.
Previous ee-repo-ref: 1d5b128ec956c156fe549cf099ba0dbc1b6467bf
New ee-repo-ref: 9855e1b7a43a0a33e04f8accf1c497af3fd9b139
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: only restart a flow on a version of its own path and workspace
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: pin cross-workspace restart version rejection
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* perf: complete a job in one statement on the common path
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: take completion locks in one order on every path
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep a losing zombie completion from touching its wac parent
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: leave a flow's ping alone when a step completes during its cancel
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: probe only this test's completion for the lock wait
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: stamp a wac child's kept duration when its completed row exists
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJMJSJ2bDhYh7Shoh78Yyb
* perf: leave the parent ping out of completions with no flow to ping
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJMJSJ2bDhYh7Shoh78Yyb
* docs: note that the two completion statements must stay in step
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJMJSJ2bDhYh7Shoh78Yyb
* chore: update ee-repo-ref to 7a256cf353db7cf64a60a09fa0de7f3a8b27f626
This commit updates the EE repository reference after PR #830 was merged in windmill-ee-private.
Previous ee-repo-ref: 497137acb65e521568d46f3cbe1d66359f7f87ec
New ee-repo-ref: 7a256cf353db7cf64a60a09fa0de7f3a8b27f626
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: schedule hub scripts
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: pin hub script schedule push
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: retry scheduled hub scripts natively
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: address review nits on hub schedules
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat: open the hub picker from the script select
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: use a subtle button for the hub row
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* perf: skip parent status write when a parallel loop iteration starts
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep the loop viewer off the parent job while a parallel loop runs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs: state what a parallel module's job would hold
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* perf: test the parallel module with one containment check
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* perf: skip the flow_env ancestor walk for sub-flows with nothing to inherit
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep the flow_env mark out of sub-flow definitions replayed by restarts
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test: align the guest scopable-path test with app path validation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: keep a cancel that lands after a worker last read its queue row
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: take the cancellation from the queue row the completion deletes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: wait for the completion to block on the row instead of a timer
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: settle a missed cancel from the deleted queue row alone
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat: check dynamic worker tags on the tag they resolve to
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: pin the tag check after a flow preprocessor resolves the tag
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep tied tag placeholders exact and drop an unresolvable flow tag
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: describe tied tag placeholders in the custom tags editor
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: check workflow-as-code task tags on the tag they resolve to
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: judge a preprocessed flow's tag as written when it is run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: check a preprocessed flow's own tag, as written only where it reads args
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: explain dynamic tags in the worker tag picker
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: look up worker availability for typed static tags
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: poll worker availability with the tag list as it is at each run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: warn on custom tags with nothing fixed at their start or end
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: open the broad custom tag warning from the keyboard
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: resolve $flow_expr[...] dynamic tags on flow steps
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: run an unresolvable $flow_expr tag on the default tag and give it the step input scope
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: resolve $flow_expr tags by path lookup instead of expression evaluation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: say a $flow_expr tag fails to resolve, not to evaluate
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): word the $flow_expr help like the other dynamic tag lines
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reject a malformed $flow_expr placeholder instead of queueing its literal tag
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: render $flow_expr tag values through the $args path lookup
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: pin interpolate_args through the shared tag path rendering
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* refactor: make the app policy's principal the authority for its identity
* fix: align the app backfill with the sibling migration and audit the uncached address
* chore: refresh the sqlx cache after rebasing onto the merged base
* fix: resolve the app execution address uncached, it decides the job's authorization
* chore: cache the EE queries at the ref this branch pins
* chore: cache the EE queries at the ref this branch pins
* fix: derive the app draft's on-behalf-of address on read
* chore: cache the query the draft derivation test added
* fix: derive the app identity on the draft-table and version reads too
* docs: state the draft resolver's authorization contract
* fix: resolve a draft's principal against workspace membership only
* chore: cache the membership lookup the draft resolver added
* fix: drop an unresolvable draft's address instead of leaving it stale
* perf: evict the address cache on change so app dispatch can read it
* fix: evict on superadmin role changes, not only address changes
* refactor: make the app policy's address optional instead of derived on read
* fix: follow an external superadmin's rename into the apps that name them
* docs: state the removal gate once, and correctly
* refactor: drop the app-policy version constant that gated nothing
* docs: drop the last reference to the removed constant
* perf: read the address cache everywhere now that eviction reaches every replica
* fix: keep persisted addresses off the cache the poller evicts asynchronously
* docs: state where the cached address is accepted and where it is not
* docs: keep the cache rule in one place and drop the stale premise
* docs: sort the two lookups by how long a wrong answer lives
* fix: resolve the schedule address uncached where it is written to the row
* docs: name the release this actually ships in
* perf: evict a superadmin's key per workspace instead of the whole cache
* fix: evict every alias a superadmin principal can be spelled as
* docs: describe the trigger as it is
* docs: cover the round-tripped read in the cache rule
* docs: record why a stale dispatch address cannot escalate
* fix: validate a dispatch address against the principal's live binding
* fix: carry the validated address through to the job row and token
* fix: record the validated address on the job row, not the one handed in
* test: run the substep tag check as the non-superadmin it means to test
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: rewrite a stored app address that disagrees with its principal
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: record the accepted staleness window of the cached dispatch address
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: record the validated address on the job's audit row
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: record the accepted rename race of pre-transaction identity resolution
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: separate the app's stored address from the derived one in the resolver doc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: describe the job identity fast path the push comments skipped
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: backfill a legacy group-prefixed username as the group it names
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: resolve a schedule edit's identity before opening its transaction
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: never resolve a disabled member to a same-named superadmin
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: state what the email-change notify buys, and rewrap two comment lines
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: keep a group's runnables when offboarding a legacy group-prefixed member
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* fix: read the app author from the stored address, as execution does
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: record the rename race's full consequence as a known, accepted limitation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
* docs: record the keep-target group address case as a known, accepted limitation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY4bBCR1q2c5XB8s2r7Ysc
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: let `// materialize` declare a `dbt://` warehouse-relation write
`// materialize manual dbt://<warehouse>/<schema>/<name>` lets an ingestion
script in any language declare that it writes a warehouse relation, so it and
the dbt model reading that relation land on one asset node instead of two
disconnected pictures. `manual` is the only mode a warehouse target has —
nothing generates warehouse DDL — and the non-`manual` spelling is refused
rather than silently degraded. The `<warehouse>` segment is resolved against
the workspace's configured warehouses, like a descriptor's `profile.warehouse`.
The run records the same `materialized_partition` row a DuckLake target does,
from the generic job path rather than an executor: the DuckLake write engine is
DuckDB's, this declaration is anyone's.
With a non-dbt producer now possible, the blanket deploy-time refusal of
`# on dbt://<relation>` narrows to the shape that still cannot fire — every
writer of the relation being a dbt script, since a dbt run does not dispatch.
"Nothing produces it yet" stays accepted, as for every other asset kind, so
deploy order does not matter. A dbt script may not subscribe at all: its graph
ingest clears its own `dbt://` trigger rows. The one ordering the deploy cannot
catch — a subscription accepted before any producer, then claimed by a dbt
project — is named in that project's deploy log.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw1WrKeRRzyYHjfkuB83ek
* fix: address review — preview stamping, stale producer set, public doc
Three findings from the local review round:
- Record the warehouse write only for a DEPLOYED script job. The annotation is
a deploy-time contract (`manual`, three segments, a configured warehouse)
checked where write access to the path is also required; honouring it in a
preview, hub or inline-flow body let `jobs:run` alone restamp any relation's
last writer from a script that never touched it.
- Exclude the deploying script's own rows from the producer set. Read
committed, they describe the version being replaced, so a script dropping its
`// materialize` while adding a subscription counted itself as the producer
that would wake it and committed a dormant edge. It could not be that
producer anyway — the dispatcher skips self-loops.
- `AssetKind::Dbt`'s doc no longer claims dbt is the exclusive producer of a
warehouse relation, on both the types and the parser enum.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: review round 1 — dbt-script materialize, set-form rule, doc
- Refuse `// materialize` on a dbt script, the producer half of the rule the
trigger loop already applies to `// on`: the graph ingest republishes that
path's asset rows wholesale, so a declared write is wiped by the deploy that
accepted it while its runs keep stamping the relation.
- `dormant_dbt_subscriptions` now spells the same predicate its singular sibling
does: the producer set has to be non-empty (nothing produces it yet is deploy
order, not a dormant edge) and excludes the subscriber's own path (a script
never wakes itself). Both divergences are pinned by tests.
- The docs no longer claim the dbt deploy log covers a native producer that drops
its `// materialize`; it does not, and nothing else reports that case.
- An integration test over the deploy contract, since only a real deploy proves
the handler feeds `sole_dbt_producer` the canonical key `asset.path` holds —
the spelling that has to agree across the materialize target, the `// on` ref
and the refusal that joins them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: qualify the any-language claim, and pin the dbt-script refusal
`AssetKind::Dbt`'s contract (both enums), the two runtime guides and the deploy
comment said a script of any language may declare a `dbt://` write, which the
dbt-script refusal added last round contradicts. They now say "any language but
dbt's own", with the reason: a project's writes are read from its manifest.
The deploy-contract integration test covers that refusal for both annotations.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: teach the pipeline AI guidance the warehouse-relation target
The pipeline prompt (both sources, plus the regenerated bundle) told the model
`// materialize` is DuckDB-only and rejected on any other target, which now
steers users away from the very thing this PR adds. It distinguishes the managed
DuckLake write, still DuckDB-only, from the warehouse-relation declaration any
language but dbt's own may make.
`dbt_manifest.rs`'s module doc carried the same "the only thing that creates one"
overclaim the other four sites lost last commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: draw an explicit dbt:// subscription on the canvas
The editor suppressed every `// on dbt://…` overlay, which was right while the
deploy refused all of them. It now refuses only a relation dbt alone builds, so
the suppression hid the author's own annotation for exactly the case this PR
adds — a subscription woken by a native `// materialize manual dbt://…`
producer. The deploy stays the gate.
Also the two stale claims round 4 named: the live pipeline prompt dropped the
dbt-script exception the base prompt carries, and the doc's e2e requirements
still said every `dbt://` subscription is refused.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: refuse `// data_test` beside a `dbt://` materialize target
`// data_test` checks are verifier probes the DuckDB executor splices around a
managed write. A warehouse relation is written by the script itself, in any
language, so nothing would run them — and unlike the DuckLake `manual` case,
which at least fails loudly in that executor, a declarer in another language
deployed green with its data-quality assertions silently skipped.
Covered in the deploy-contract test and documented beside the annotation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: exclude a renamed producer from the sole-dbt producer set
The producer set already excluded the deploying script's own path, because its
committed rows describe the version being replaced. Under a rename the write
sits at the OLD path — still committed, and removed by the same uncommitted
transaction — so a producer renamed while it drops its `// materialize` and adds
`// on dbt://…` still counted as the producer that would wake it, and committed
a dormant edge.
The deploy-contract test covers it: without the exclusion the rename deploys
201 instead of being refused.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: take the rename test's parent hash from the create response
`format!("{:x}", …)` over the stored i64 drops leading zeros, while
`ScriptHash`'s deserializer hex-decodes and demands 8 bytes — so a hash below
2^60 would 422 the request instead of reaching the refusal it asserts on, on
roughly one in sixteen spellings of that script body. The create response
already carries the zero-padded form, as the rest of the suite uses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: state the concurrent-ingest interleaving honestly
`sole_dbt_producer`'s doc claimed the concurrent-deploy race only ever resolves
toward refusing. It does when the uncommitted producer is native; when it is the
dbt ingest, the check sees an empty producer set and accepts, and if that ingest
then commits and runs its warning query before the subscriber's trigger row
lands, neither side reports the dormant edge.
Not serialized: the two would have to share a per-relation lock, and the ingest
takes `script … FOR UPDATE` before its own advisory lock, so a deploy holding
relation locks first inverts that order into a cross-subsystem deadlock — a worse
failure than the cosmetic edge. Recorded beside the other orphaning the deploy
cannot catch, with the bound both share: the next deploy of that project warns.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: refuse a `dbt://` subscription that is not a whole relation
`# on dbt://main/analytics` deployed and persisted a trigger row. Every producer
spells `<warehouse>/<schema>/<name>` — the manifest ingest derives it from
`relation_name`, a `// materialize` target is checked against it — so a partial
one is an edge nothing can ever wake, which is what the dbt-only refusal exists
to prevent.
The shape now has one definition (`is_full_relation_path`) that both halves of
the deploy ask, rather than a segment count spelled twice: a subscription and a
write that disagreed would refuse and accept the same string.
Also rewrites the canvas test's comment as a current constraint per AGENTS.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: hold both halves of the deploy to one `dbt://` relation validator
A subscription checked the relation's shape but not its warehouse, so
`# on dbt://<unconfigured>/<schema>/<name>` deployed and persisted a trigger row
for something no producer can ever write: the write side refuses that exact
string, and a dbt project's `profile.warehouse` resolves against the same config,
so no later deploy fixes it and the dormant-edge warning cannot report it either.
The shape rule and the warehouse rule now live in one `validate_dbt_relation`
that both halves call, rather than being spelled per site — the previous two
rounds each closed one half of one rule, which is the drift that invites.
Also moves the parser test out from between a comment and the test it documents,
and names both refusals in the doc's list.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: drop the subscription-only clause from the shared refusal message
"so nothing can produce it" reads backwards on the `// materialize` side, which
is the producer. The remaining sentence says what is wrong on both.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: bound a `dbt://` relation by the asset-path column in the shared validator
`asset.path` is VARCHAR(255) and the manifest ingest drops a relation that
outgrows it rather than failing the whole graph, so past the column no producer
row can exist on either side. `script_trigger.trigger_ref` is unbounded text, so
an overlong subscription deployed and stayed dormant for good; an overlong write
reached Postgres and failed the deploy on a `value too long` instead of a message.
Both now refuse in the validator the two halves share, against the ingest's own
constant. The integration case computes the ref from that constant so it cannot
drift back under the bound.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: report a warehouse-lookup failure as the failure it is, and correct the boundary
`dbt_warehouse_exists` fails three ways — no such warehouse, the query itself,
and a setting with no `resource_path` — and all three became a 400 blaming the
user's warehouse name. A pool timeout mid-deploy told a retrying sync that a
transient server error was a permanent client one. Only `NotFound` is the
annotation's fault now.
The known-boundary paragraph claimed a flow-runner run still cascades. It does
not: it is routed by `flow_step_id`, which `is_eligible_kind` rejects, as
`asset_trigger_dispatch.rs` pins. Recording and cascading are decided separately,
so the paragraph now names all three routes rather than merging two of them — and
the row it omitted, an ordinary flow step, which records and never cascades.
E2E item 7 said "deployable" where the rule is "wakeable": with only the dbt
project reading the relation the producer set is empty, which deploys fine.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: correct two rationales the last commit got wrong
`Error::SqlErr` already maps to 400 in this codebase, so the query case's status
was never the thing at stake. What the `NotFound` match earns is that a query
failure and a malformed setting stop being described as an unconfigured warehouse
name, and that the malformed-setting `InternalErr` reaches its own 500 instead of
being flattened.
And a flow step is two shapes, not one: a step running a deployed script is a
`Script` job that records and never cascades, while a step with an inline body is
`FlowScript`, which the recording guard excludes along with previews.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: warn about dormant subscriptions from the run that publishes ownership too
A run whose static descriptor finds its profile moved re-ingests the version's
graph and republishes path ownership, exactly as a deploy does — so it can be
what leaves a subscription accepted while the relation had no producer with dbt
as its only one. That path discarded `persist_ingest`'s result and emitted no
warning, which also made the doc's enumeration of unreported orphanings wrong.
Both ownership-publishing points warn now. An agent worker still cannot: it
reaches these tables only through the API and its ingest publishes without
reading back, which the doc now says.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: an agent run publishes no ownership, and the warning has two callers
The agent-worker sentence called it an exception that publishes ownership without
warning. It publishes none: `Connection::Http` forces per-run models, and
`publishes_ownership()` is the negation of that, so an agent stores a job-pinned
snapshot and leaves workspace ownership with the deployed graph — it cannot orphan
a subscription at all.
`warn_dormant_subscribers`' own doc still named the deploy log as the only place
the warning shows, one commit after it gained its second caller.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: stop the managed-write rule from contradicting the dbt:// target
The sentence after the warehouse-relation paragraph says `// materialize` means
the runtime writes the table for you and the body is a bare SELECT. That is the
managed DuckLake rule, written before a `dbt://` target existed, and unqualified
it tells the model the opposite of what the paragraph above it just said — a
model following the more prominent one emits a SELECT for a warehouse relation,
which deploys and then writes nothing.
Both prompt sources now scope it, and both name the `// data_test` refusal beside
a `dbt://` target, which the badge list advertised without the caveat.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): keep nested template literals intact in template inputs
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: fail a flow step with an unresolvable $args tag instead of hanging
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): surface input expression errors when running a step test
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): treat an escaped \${ as literal text when escaping backticks
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: accept the string "null" as a tag component, reject only JSON null
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: leave a same_worker step's inert tag alone, log an unresolved flow tag
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): escape every backtick when the template walk desynchronizes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: leave a dedicated runnable's inert step tag alone
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reroute a step only when its own tag is what failed to resolve
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: name the inert-tag guard step_is_pulled_by_tag
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reject a tag only when it interpolates to nothing at all
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): validate the template walk instead of trusting a balanced stack
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: describe what an unresolvable tag actually interpolates to
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(frontend): decide template escaping with a real parser, not a hand-rolled scan
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: name is_flow_step on push now that it is load-bearing
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): heal an expression escaped before nested templates were handled
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reroute a step whose tag reads args that failed to evaluate
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: never hand a job that failed before running to a dedicated runner
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reroute only a step whose args failed, leave other tags untouched
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: drop the post-preprocessor tag fallback, leaving tag resolution untouched
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: leave interpolate_args exactly as it was
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: use a generic example in the template literal tests
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): show an expression escaped by the old rule as it was authored
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): surface input expression errors from every step-run entry point
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: state what is_dedicated_worker actually reads
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): heal only text whose backticks were all escaped by the old rule
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): match the old rule textually so an authored backslash still heals
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(frontend): heal only expressions the old rule broke, never ones that parse
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix: size job token to the premium cloud job timeout
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: give the job token setup headroom and drop dead MAX_TIMEOUT_DURATION
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: cap job token setup slack so self-hosted tokens stay at 7d
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>