* Navigate with arrows
* Jumps to other side item + load 30 more
* No workspace selector
* Recommendations Claude check
* Navigation horizontal
* Same
---------
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* fix(cli): resolve cross-folder relative imports during lockgen on fresh DB
On a fresh workspace, lockfile generation for scripts that imported other
scripts via cross-folder relative imports (or barrel re-exporters) failed with
"Failed to find relative import" because the dep job's bun build hit the
server before any helper was deployed. Three independent bugs combined to
produce this:
1. wmill sync push --auto-metadata regenerated locks per script without
building a DoubleLinkedDependencyTree or calling uploadScripts, so
temp_script_refs was never sent to dependencies_async.
2. wmill script generate-metadata (the deprecated alias) had its own old
in-line implementation that bypassed the tree entirely.
3. The TypeScript WASM parser dropped re-exports (export * from, export { x }
from) when called with skip_type_only=false — the path used by
parse_relative_imports — so barrel files looked like leaves to the CLI's
dependency tree and their sibling helpers were missing from
temp_script_refs.
Fix:
- sync.ts: --auto-metadata mirrors generate-metadata's flow (dryRun pass to
populate tree → propagateStaleness → uploadScripts → real pass with tree).
- script.ts: deprecated wmill script generate-metadata now delegates to the
canonical generateMetadata, which already does the tree+upload dance.
- parser-ts: visit_export_all and visit_named_export had inverted skip_type_only
guards; aligned with visit_import_decl's pattern.
Includes 4 E2E tests reproducing each customer-hit failure path and a Rust
unit test for the re-export parser fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump windmill-parser-wasm-ts to 1.695.0
Pin the parser package to the version published with the re-export fix
(visit_export_all / visit_named_export skip_type_only=false) so the CLI
and frontend pick it up at the next release.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(cli): restore legacy stale-check in deprecated alias, add tree to gen pass
Delegating wmill script generate-metadata fully to the canonical handler
broke 4 workspace_deps_filter tests that rely on the legacy hash-with-deps
formula and the "No metadata to update" output string.
Restore the original in-line implementation (legacy stale-check preserved),
but add a DoubleLinkedDependencyTree + uploadScripts pass before the actual
generation step. The customer's bug only manifests on real lockgen, not on
the dry-run staleness check, so this preserves the existing test contract
while still fixing cross-folder relative imports for the deprecated alias.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(flows): inherit flow_env in sub-flow predicates
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(flows): align flow_env lookup with get_root_job_id and tighten gate
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(flows): drop recursive CTE, root_job propagation suffices
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(flows): walk via flow_innermost_root_job to respect imported-flow scope
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(flows): remove flow_env API endpoint, dead code from deno_core era
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The config module imports node:fs/promises (stat, mkdir), which breaks
non-Node bundlers like the Cloudflare Workers build of the hub. The
windmill SPA frontend got away with it via tree-shaking, but stricter
runtimes choke on the bare node: import even when unused.
Stop re-exporting ./config from the main entry and expose it via a
windmill-utils-internal/config subpath instead. CLI code already
deep-imports the source file, so it is unaffected. Bumps the package
to 1.4.0 and updates the frontend dependency to match.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: add ai chat resource action buttons
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: avoid proxied drawer state equality
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: show tool action cards
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Workspace rows in the sidebar picker and the Edit button on the run detail
page now render as `<a href>`, so middle-click and Ctrl/Cmd-click open
them in a new tab. Plain clicks keep their existing in-tab behavior
(workspace store switch / args prefill via `$initialArgsStore`).
The Edit href carries `?workspace=<current>` so the new tab loads the
editor in the same workspace as the run.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(cli): add --as-superadmin flag to workspace list-remote
Wires the existing /workspaces/list_as_superadmin endpoint into the
CLI so superadmins can enumerate every workspace on an instance from
CI tooling, not just the ones the calling user is a member of.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: regenerate CLI prompts and document the regen step
Adds an AGENTS.md note pointing to system_prompts/generate.py so future
CLI command edits keep the agent-facing CLI docs in sync, and
regenerates the auto-generated outputs for the new --as-superadmin flag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
REVIEW.md is shared across Claude/Codex/Pi reviewers; CLAUDE.md is
Claude-specific noise that doesn't belong in cross-CLI review policy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Policy is not GitHub-specific (also used by local-review skill); .github/
keeps only CI-tool output-format shims (codex/pi/claude prompt files).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: make local-review a single-source-of-truth cross-agent skill
The repo already had parallel skills directories (.agents/skills/ and
.claude/skills/) drifting between agents. Consolidate local-review
onto one canonical file in .agents/ and symlink the .claude/ entry to
it so Claude Code and Pi share the exact same SKILL.md (Anthropic's
Skills format is supported by both, only the discovery directory
differs).
The canonical SKILL.md now points reviewers at
.github/review-prompt-shared.md as the policy source — same shared
prompt the GitHub auto-review workflows already use — so local
reviews and CI reviews stay in lockstep.
Codex CLI doesn't support repo-level slash commands (its prompts live
in ~/.codex/prompts/). For Codex parity, ship scripts/local-review.sh
which pipes the SKILL + shared policy into 'codex exec' (or 'pi -p'
as a uniform entry point). Update AGENTS.md to document the three
invocation paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: make all skills cross-agent — single source in .agents/, symlink .claude/
Turn every skill into a single canonical file under .agents/skills/ and
a symlink under .claude/skills/. Editing any one SKILL.md now updates
all three CLIs (Claude Code reads .claude/, Codex and Pi auto-discover
.agents/).
Per-skill resolution:
- local-review: already symlinked (prior PR #9037)
- rust-backend, svelte-frontend: identical content → symlink, no edit
- refine: only differed in user_invocable frontmatter → add to canonical
- native-trigger: .claude/ had a newer Step 17 (sidebar visibility)
missing from .agents/ → use Claude content as canonical
- commit: .claude/ embedded a Claude-specific Co-Authored-By trailer
the harness already injects automatically → drop from canonical, use
agent-neutral .agents/ version
- pr: generalize "Run /local-review" to "Invoke the local-review skill
(/local-review in Claude Code, $local-review in Codex, pi --skill
local-review in Pi)" and drop the Claude-specific "Generated with
Claude Code" attribution from the PR body template — the harness
that invoked the skill can add its own trailer if desired
- adding-a-trigger: was only in .claude/ → move to .agents/ canonical
- update-sqlx: was only in .agents/ → add .claude/ symlink
Also drop scripts/local-review.sh — wrapper is redundant now that all
three CLIs natively discover the skill from their respective
directories. Update AGENTS.md accordingly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: share review policy across Claude/Codex/Pi via review-prompt-shared.md
All three reviewers now consume a single canonical policy document
(.github/review-prompt-shared.md) covering AGENTS.md compliance,
severity triage (P0/P1/P2), and a checklist for new public surfaces
(auth contract, module placement, half-finished pub fns, input
validation). Each tool's own prompt file shrinks to just its
output-format quirks, and each workflow concatenates shared +
tool-specific at runtime before invoking the model.
Drops the suppressive "Prefer at most 10 findings" / "Keep the review
high signal. If there is no clear issue, return no findings" wording
from Codex and Pi, which was clipping P1 and P2 findings (e.g.
half-finished pub fn, blocking I/O, wrong module placement).
Replaces it with severity triage so both reviewers report all P0/P1
and surface P2 when the diff invites it. Also makes AGENTS.md
authoritative for Codex (was CLAUDE.md, which is just @AGENTS.md in
this repo) and adds an explicit "new public function" checklist that
covers the missing-auth-check failure mode none of the three reviewers
flagged on the test PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: move test-coverage assessment to shared prompt, slim per-tool prompts
- Replace per-tool 'Reproduction instructions' with a single shared
'Test coverage assessment' section that asks each reviewer to
evaluate automated coverage (sufficient / thin / appropriate) and
describe what manual verification remains, if any.
- Slim per-tool prompts to the absolute minimum: just where to read
context, the comment header, severity tagging, and the Pi-only 'no
preamble' constraint. Everything else lives in the shared policy.
- Drop the model name from Pi's title ('Pi Review (DeepSeek V4)' →
'Pi Review') — the title's job is to let the bot find its own prior
comment when re-reviewing; the model is irrelevant to the reader.
The titles ('## Codex Review', '## Pi Review') stay because Codex and
Pi both post as github-actions[bot], so the heading is the only
discriminator the bot can use to find its own past comment in the
prior-discussion context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: scope test-coverage assessment to layers actually changed
Don't ask reviewers about integration tests on a frontend-only diff or
about playwright tests on a backend-only diff. The shared 'Test
coverage' section now lists categories (backend / frontend / CI-docs)
and tells the reviewer to skip the ones the PR does not touch — only
ask about Rust integration tests when backend handlers/workers/queues
were modified, only ask about frontend tests when components or state
machines were touched, and explicitly call out 'no automated tests
expected' for CI/docs/config diffs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: don't ask reviewers to flag missing frontend component tests
The Windmill frontend codebase doesn't generally test Svelte components
— existing tests cover pure-logic utilities only (flowDiff,
previousResults, copilot logic, dbtable queries, etc.). Asking
reviewers to flag every new component for lacking a test would produce
noise inconsistent with the established convention. Limit the
frontend test-coverage check to new pure-logic utilities (files that
would naturally have a sibling *.test.ts).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: point local-review skill at the shared review policy
Codex flagged (and Pi confirmed on its second pass) that slimming
.claude/review-prompt.md to output-only broke the local-review skill
contract — the skill still told Claude to read only that file for the
review criteria, so /local-review would no longer apply severity
triage, the public-surface checklist, or AGENTS.md compliance.
Update the skill to read .github/review-prompt-shared.md as the policy
source and .claude/review-prompt.md only for Claude output preferences.
Also align the local output format with the severity-tag convention
used by the workflow reviewers, and replace the lingering
'CLAUDE.md compliance' wording with 'AGENTS.md compliance'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(flows): don't bubble error when continue_on_error is on the last step
When the last step of a flow (or branch/forloop) failed with continue_on_error
or skip_failures enabled, should_continue_flow resolved to false (because the
flow was at its last step), and the flow was completed with success=false.
This made parent flows / subflows treat the run as a failure even though the
user explicitly asked to continue past errors.
Detect this case and set success=true so the failure is captured in the
result but not propagated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: explain why success is overridden post should_continue_flow
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: re-review on push, thread prior PR comments into reviewer context
- Add 'synchronize' to all three review workflow triggers so each push
to a PR branch re-runs Claude/Codex/Pi. Existing
cancel-in-progress concurrency groups ensure only the latest push's
review actually executes.
- Fetch the most recent up to 20 PR comments before each review and
inject them into the prompt context so the reviewer can recognize
its own previous review, focus on what changed, and avoid repeating
findings the human already addressed.
- Update the three review prompts (Claude, Codex, Pi) to instruct the
reviewer to honor the prior-discussion section when present.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: bump codex CLI to 0.128.0 for gpt-5.5 support
Codex 0.117.0 rejects the gpt-5.5 model with 'requires a newer version
of Codex'. 0.128.0 is the current stable release on npm.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: limit synchronize re-trigger to pi review only
Re-running Claude and Codex on every push gets expensive fast on busy
PRs. Pi (DeepSeek-V4) is cheap enough to re-run per push, while
Claude/Codex remain on opened/ready_for_review and re-trigger via
slash commands.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: install bubblewrap for codex sandbox; stream pi progress in logs
- Codex's vendored bwrap fails to set up loopback on some ubicloud
runners, leaving codex unable to read any local files. Install the
system bubblewrap package before running codex so its read-only
sandbox works reliably.
- Switch pi to --mode json and pipe events through jq to surface
agent/turn boundaries and tool calls live in the GitHub Actions log,
matching codex's progress visibility. Final assistant text is
extracted from the saved event log into pi-final-message.md for the
PR comment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: drop bubblewrap install, use codex -s danger-full-access
Codex's read-only sandbox uses bwrap which fails to set up loopback on
some ubicloud runners. Rather than apt-installing bubblewrap, switch to
the no-sandbox mode for parity with how Pi and Claude already operate
in the same workflow — runner is ephemeral and we trust the codex
prompt the same way.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: bump codex review model from gpt-5.4 to gpt-5.5
gpt-5.5 is positioned as the agentic successor to gpt-5.4 — same
per-token latency, fewer tokens to complete Codex tasks, and
explicitly stronger at holding context across large systems and
multi-tool reasoning, which matches the PR review workload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: add Pi+DeepSeek-V4 review and slash command dispatcher
Auto-reviews now fan out to Claude (Opus), Codex (gpt-5.4), and Pi
(DeepSeek-V4-Pro) on PR open/ready. PR comments support /review (all
three), /codex, /pi, /claude with optional extra context appended to
the prompt. All review workflows now substitute EE code before review
and gate the auto-trigger path on org membership of the PR author.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: fix command parser whitespace, align checkout v5, broaden PR perms
- Trim leading/trailing whitespace from comment first line so /review
with leading space parses correctly (caught by Pi review)
- Standardize EE checkout step on actions/checkout@v5 across all three
review workflows (caught by Pi review)
- Bump pull-requests permission to write to satisfy GitHub's PR
comment endpoint when issues=write alone is rejected
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [ee] fix(autoscaling): consider dedicated workers in scale decisions
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update ee-repo-ref.txt
* [ee] fix(autoscaling): mirror worker tag precedence (worker_tags wins)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 862d487032efe30d1e4a3cd0a1ed7169500c4cd9
This commit updates the EE repository reference after PR #556 was merged in windmill-ee-private.
Previous ee-repo-ref: cf87e9dcef2e95b1834b3f5c154209defc5a9ca2
New ee-repo-ref: 862d487032efe30d1e4a3cd0a1ed7169500c4cd9
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Adds three subpath entries to the windmill-components package's `exports`
and `typesVersions` so external consumers (e.g. windmillhub) can import
these components without resorting to private `node_modules` aliases.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: bind MySQL table listing to configured database name
* refactor: drop DATABASE() sentinel and cover single-table fallback
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: unify MySQL schema-resolution branches via explicit_db binding
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: replace SELECT * with explicit column lists
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: update sqlx offline query cache
* chore: update sqlx offline query cache
* chore: update sqlx offline query cache with EE support
* chore: update sqlx offline query cache, no deletions
* chore: update sqlx offline query cache after rebase
* fix: correct column names in explicit script query lists
- concurrency_limit → concurrent_limit (matches DB column name)
- runnable_settings → runnable_settings_handle (matches DB column name)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add missing delete_after_secs column to script queries
Also add integration test covering all explicit-column export queries.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add workspace export integration test covering all explicit-column queries
Covers tarball_workspace (folder, script, resource, resource_type, variable,
schedule, usr, group_) and the mcp_oauth_client SELECT query from windmill-mcp.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add tarball export integration test covering all explicit-column queries
Single test creates one of each entity type and exercises every runtime-checked
explicit-column query in tarball_workspace. Uses archive_type=tar to avoid
zip feature-gate in CI.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: fix stale verification step and CI contradiction in update-sqlx skill
- Regenerate current_files.txt after EE cache restoration so step 4 reports accurate diff
- Scope "Never use SQLX_OFFLINE=true" to local prepare (CI legitimately uses it)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: remove Co-Authored-By from commit skill template
* refactor: extract SCRIPT_COLUMNS const to single source of truth
Replaces 5 duplicated 44-column lists with a shared const in windmill-types.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add SAFETY comments to all dynamic SQL call sites
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: address review feedback on SAFETY comments
- Fix missed comment for obo_triggers loop in offboarding.rs
- Fix variable name in comment (table -> table_name) in offboarding.rs
- Fix api-settings comment to reference inline VALID_NAME regex, not validate_dbname()
- Add SAFETY comments to batch_execute calls in api-settings
- Fix db.rs comment: PG_SCHEMA is env var, not compile-time constant
- Add doc comments on RunnableSettingsTraitInternal constants
* docs: remove misleading SAFETY comment on static SQL
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix(forks): strip mode/enabled from merge-UI deploy payload
The CompareWorkspaces merge UI deploys a trigger by fetching the full
GET response from the source workspace and spreading it into an
updateXTrigger call on the target. That spread includes `mode` (and the
legacy `enabled`), so a fork→parent (or parent→fork) deploy would
overwrite the target's enabled/disabled state — silently disabling a
parent's trigger when its config is merged from a freshly-cloned fork
(clones are forced `mode='disabled'`). Affects azure/email/gcp/http
whose `update_trigger` SQL writes the `mode` column.
Strip `mode`/`enabled` in `getTriggersDeployData` so the backend's
existing `is_mode_unspecified()` preservation in `update_trigger` keeps
the target row's `mode` untouched. The same preservation already
protects the YAML/CLI round-trip (where the tarball export strips these
fields); this extends the same guarantee to the merge-UI path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(forks): wire Azure and Email triggers through merge UI deploy
CompareWorkspaces.svelte already lists Azure and Email triggers in its
diff (`triggerServices`), but `getTriggersDeployData` and `existsTrigger`
were missing the corresponding branches. Deploying either kind from the
merge UI threw "Unexpected trigger kind". Add the branches with the
same `stripOperationalState` pattern as the rest, and extend the
`triggersKind` whitelist in `checkItemExists`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: surface scope errors as 403 and show real message in CLI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address review feedback on scope error PR
- Backend: also patch handler-level check_scopes (lib.rs:223) — without
this, endpoints using check_scopes (scripts, flows, jobs, …) still
returned 401 for scope failures, which the CLI would render as the
misleading auth message.
- CLI: strip backend file refs and the duplicated "Permission denied:" /
"Not authorized:" prefix from the surfaced error body.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat(forks): strip operational state from triggers/schedules on git-sync export
When the source workspace is a fork (`wm-fork-*`), the tarball export now
omits `mode` from triggers and `enabled` from schedules. The trigger update
handler also preserves the existing DB `mode` when both fields are absent
from the request, instead of falling back to the BaseTriggerData default.
This prevents a fork's git-sync round-trip from flipping the parent
workspace's enabled/disabled state when a merge applies the fork's YAML
back to main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(forks): opt-in fork_triggers flag clones triggers/schedules disabled
Adds `workspace.fork_triggers` (default false) and a matching field on
CreateWorkspaceFork. When the user opts in, fork creation also runs
clone_triggers_and_schedules: every row in schedule and the ten
*_trigger tables is copied to the fork with mode='disabled' /
enabled=false. Listener identifiers (group_id, replication_slot_name,
subscription_name, …) are copied verbatim — the runtime suffix that
prevents the fork from competing with the parent ships in a follow-up
PR.
native_trigger is intentionally skipped: those triggers manage external
webhook state we don't want duplicated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(forks): warn before enabling triggers/schedules that conflict with parent
set_trigger_mode and schedule's set_enabled now check whether the parent
workspace has the same path actively enabled. If so, the call is rejected
with a `fork-conflict:<kind>:<parent_id>` error unless the request includes
`force=true`. The frontend interprets the prefix to surface a confirm-to-
proceed dialog.
This is the placeholder safety net until the Phase 3 listener-suffix work
removes the conflict for the namespaceable kinds (Kafka/MQTT/NATS/Postgres/
Azure/GCP-CreateNew). For SQS, GCP-Existing, and schedules — where there's
no namespacing fix — the warning is the durable solution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(forks): UI: opt-in clone-triggers checkbox + confirm-on-fork-conflict
Adds the user-facing surface for the fork-trigger work:
- CreateWorkspaceInner: new "Clone triggers and schedules" toggle in the
fork-creation dialog (default off). Sends fork_triggers in the request.
- forkConflict utility: detects the `fork-conflict:<kind>:<parent_id>`
error string from the backend, shows a confirm() dialog explaining
why the action is blocked, retries with `force: true` if accepted.
- Wires withForkConflictRetry into every trigger setMode and the
schedule setEnabled call, both in the per-kind editor components and
the +page.svelte list views (HTTP, websocket, kafka, NATS, SQS, MQTT,
GCP, Azure, Postgres, email, schedule).
OpenAPI spec gains the `force` field on each setmode/setenabled body.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(forks): CLI --fork-triggers flag, fork-trigger docs, skill update
- Adds --fork-triggers boolean to wmill workspace fork; passes
fork_triggers through to the create_fork API call.
- New docs/fork-triggers.md describing the model end-to-end (default,
opt-in clone, merge-direction filter, conflict warning, future
runtime-suffix work).
- Updates the adding-a-trigger SKILL.md to mention the fork-export
ignore-keys participation and the clone_triggers_and_schedules
block that new trigger kinds must extend.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: regenerate sqlx offline query cache for fork-trigger SQL
* fix(forks): replace browser confirm() with ConfirmationModal for fork conflict
The fork-conflict warning previously used the browser's native confirm()
which doesn't match Windmill's design system. Switches to a singleton
ConfirmationModal mounted at the (logged) layout root, driven by a new
forkConflictModal store. The withForkConflictRetry helper now sets the
store and awaits the user's choice via a Promise, instead of blocking
on window.confirm.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(forks): filter unchanged triggers in merge UI, add diff view, surface parent-only ones
The fork merge UI listed every trigger from the fork as a deployable item
regardless of whether it differed from the parent — so a fork created with
fork_triggers=true (which clones triggers in disabled state, otherwise
identical) showed every trigger as a "Fork-only" change. The 'Update
current' tab also missed triggers newly created in the parent that the
fork hadn't pulled yet.
This refactor:
- fetchAllTriggers now lists both fork and parent in parallel for each
trigger kind, then merges by path.
- Computes a per-trigger `changeKind` (new / modified / deleted-in-source)
using a JSON comparison that strips runtime + fork-local fields
(mode/enabled/server_id/last_server_ping/edited_at/edited_by/etc.) so
the disabled-on-clone difference doesn't show up as a change.
- Filters the trigger items in deployableItems by the current direction:
Deploy mode shows fork-side new/modified, Update mode shows parent-side
new/modified.
- Replaces the always-on "Fork-only" badge with proper New/Modified
badges and surfaces a Diff button (modal Drawer + Monaco DiffEditor)
for modified triggers — the diff strips the same ignored fields so
users see only the meaningful config differences.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(forks): always clone triggers/schedules disabled, drop opt-in flag
Disabled triggers and schedules are inert — no listener attaches, no cron
fires — so cloning them by default is safe by construction. Drops the
fork_triggers opt-in flag introduced earlier in this PR:
- Drops workspace.fork_triggers column (migration removed)
- Removes fork_triggers from CreateWorkspaceFork (API + OpenAPI)
- Removes the conditional in create_workspace_fork — clone always runs
- Removes the toggle from the fork-creation dialog
- Removes --fork-triggers from `wmill workspace fork`
- Updates docs/fork-triggers.md and adding-a-trigger SKILL.md
The merge UI continues to exclude triggers from the deploy/update default
selection, so a routine merge from a fork doesn't accidentally push
trigger config the user hasn't intentionally changed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(http-triggers): scope route exists check by workspace, skip non-workspaced clones in forks
The non-CLOUD branch of `route_path_key_exists` self-excluded by trigger
path alone, which silently masked cross-workspace collisions once forks
started cloning trigger rows verbatim. Tighten it to exclude only the
exact `(workspace_id, path)` row.
Fork creation also now skips non-workspaced HTTP triggers — their URL
has no workspace prefix, so a clone collides with the parent at the
matchit router (which silently drops one of two duplicates) and there is
no namespacing escape hatch. The clone copies all rows when CLOUD_HOSTED
or HTTP_ROUTE_WORKSPACED_ROUTE forces every route workspaced regardless.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(forks-ui): silent cancel on enable conflict, clean up trigger rows in compare view
forkConflict helper now returns undefined when the user dismisses the
modal instead of throwing, so the redundant 'Cannot enable: undefined'
toast no longer appears.
CompareWorkspaces trigger rows now mirror the script row layout: drop
the redundant Disabled badge and the Trash/Details buttons (both belong
on the dedicated trigger pages, not in the deploy/compare view); pass
triggerKind through so RowIcon picks the right kind-specific icon; move
extraLabel into the summary line; replace the yellow Modified badge
with the same green ↗ ahead / blue ↘ behind treatment scripts use.
Trigger diff drawer: switch JSON → YAML for parity with DiffDrawer, fix
zero-height monaco render with className=!h-full, drop the redundant
Original/Modified label banner above the diff.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(email-trigger): scope local_part exists check, skip non-workspaced clones in forks
Mirrors the HTTP route fix for the email-trigger non-CLOUD `email_exists`
check (in EE) which had the same path-only self-exclusion bug, and the
fork clone of `email_trigger` rows which copied non-workspaced
`local_part` verbatim. Skip non-workspaced rows in the clone unless the
instance is CLOUD_HOSTED (where lookup is workspace-scoped natively).
EE companion change in windmill-trigger-email/src/handler_ee.rs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 78512dd73b4a1c9f70574cff863374179e3a621b
This commit updates the EE repository reference after PR #554 was merged in windmill-ee-private.
Previous ee-repo-ref: 1ac77f50747b58e720a11162dfd309bc252a24ab
New ee-repo-ref: 78512dd73b4a1c9f70574cff863374179e3a621b
Automated by sync-ee-ref workflow.
* fix(forks): always-warn on parent row, kind-specific modal copy, cancel-aware toggles
- Conflict check now fires whenever the parent has the path (regardless of
parent's mode), since the cloned upstream identifier is shared by
construction; closes the Postgres slot-takeover gap when the parent is
disabled. Schedule's set_schedule_enabled gets the same treatment.
- Skip the warning entirely for HTTP and Email via a new
TriggerCrud::FORK_CONFLICT_ON_ENABLE const — both kinds are workspace-
scoped at runtime so cloned rows can't collide with the parent.
- Modal copy branches by failure family: split-events (Kafka/NATS/MQTT/SQS/
GCP/Azure), duplicate-firing (Websocket/Schedule), slot-takeover
(Postgres). Generic fallback for unknown kinds.
- withForkConflictRetry now returns boolean (true=committed, false=
cancelled). TriggerModeToggle reuses its existing innerTriggerMode local
state via a function binding for the regular Toggle, snapping back to
the prop when onToggleMode signals a cancel — needed because the native
bind:checked diverges from the parent's prop after a click and Svelte's
reactivity won't re-push a same-valued prop down. Schedule list page
uses {#key} on a reset version since it renders Toggle directly.
- Editor inners revert mode = previousMode on cancel; list pages skip the
re-fetch (loadTriggers/loadSchedules) on cancel to avoid pointless
network traffic and the schedule "Job stats loading..." flash.
- Drop withForkConflictRetry from HTTP and Email editors + list pages
since the backend never emits the conflict for those kinds.
* fix(forks-ui): widen onToggleMode types, scope schedule toggle reset by path
- TriggerEditorToolbar and TriggerSuspendedJobsModal forwarded
onToggleMode as `(mode) => void`, dropping the new boolean return so
any caller wired through them would silently no-op the cancel-revert.
Match the wider TriggerModeToggle signature.
- Schedule list page used a single resetVersion counter for every row's
{#key}, so cancelling on any one schedule remounted every <Toggle> on
the page. Switch to a per-path Record<string, number> bumped only for
the affected row.
* chore: bump ee-repo-ref to c3a4553 (email FORK_CONFLICT_ON_ENABLE override)
* fix(forks): include Suspended in conflict gate, use parent_workspace_id for fork detection
Three fixes from the Claude review on PR #8976:
- Suspended mode still attaches the listener (it just pauses auto-run of
queued jobs); two suspended fork+parent listeners would still split
Kafka events / share a PG slot. Gate set_trigger_mode on
`mode != Disabled` instead of `mode == Enabled` so Suspended also
surfaces the warning.
- workspaces_export.rs::fork_*_ignore_keys keyed off the wm-fork-* prefix
while set_trigger_mode and set_schedule_enabled key off
parent_workspace_id. Switch the export filter to query
parent_workspace_id once at the top of tarball_workspace and pass
is_fork through. The column is the contract; the prefix is a
creation-time naming convention that could in principle drift.
- TriggerModeToggle's suspend-dropdown action reassigned the non-bindable
`triggerMode` prop instead of the local `innerTriggerMode` mirror,
leaking inconsistent state if the dispatch was cancelled. Now writes
to innerTriggerMode like the Toggle's on:change handler does.
* fix(cli): skip setScheduleEnabled when local YAML lacks `enabled`
Tarball export from a fork strips `enabled` from schedules so the
fork→parent git-sync round-trip can't flip the parent's operational
state. The CLI's pushSchedule called setScheduleEnabled whenever
`localSchedule.enabled != schedule.enabled`, which evaluates truthy
when local is undefined (fork-pulled YAML) and remote is true/false —
sending `{ enabled: undefined }` that serializes to `{}` and gets
rejected by the backend (`SetEnabled.enabled` is required).
Skip the call when `localSchedule.enabled === undefined` so a sync push
of fork-pulled YAMLs preserves the target's existing enabled state
instead of erroring out. Trigger updates were already safe — the
backend's update_trigger preserves `mode` when the request omits it.
* Revert "fix(cli): skip setScheduleEnabled when local YAML lacks `enabled`"
This reverts commit 23ba7e72fc.
* feat(cli): --force flag and friendlier error on fork-conflict for schedule enable
`wmill schedule enable foo/bar` against a fork whose parent has the same
path used to surface the raw `fork-conflict:schedule:<parent>` error
body. The CLI now:
- accepts `--force` to bypass the warning (mirrors the API field and the
UI's "Enable anyway" confirmation),
- detects the `fork-conflict:` prefix on errors and prints a one-screen
explanation pointing at --force instead of the raw body.
Disable doesn't trigger the warning (the gate fires only on transitions
to listener-attaching modes), so no flag there. Trigger enable/disable
isn't exposed as a standalone CLI command — sync push goes through
updateTrigger which has its own backend mode-preservation, so no
fork-conflict surfaces from the CLI for those.
* chore: regenerate cli-commands docs after adding --force to schedule enable
* chore: update ee-repo-ref to 967f961f0a88b027d894aebd03977181129477a8
This commit updates the EE repository reference after PR #555 was merged in windmill-ee-private.
Previous ee-repo-ref: c3a4553296473932e15392a06415dd7fb9aa6591
New ee-repo-ref: 967f961f0a88b027d894aebd03977181129477a8
Automated by sync-ee-ref workflow.
* fix(forks): address CI dead-code, claude/cubic review feedback
- backend: cfg-gate `fork_trigger_ignore_keys` to match its already-gated
callsite. CI compiles with `-D warnings`, so the unused-fn under feature
combos that disable all trigger crates was breaking check_oss/check_ee/
cargo_test/test-linux/test-windows.
- cli: re-apply the `pushSchedule` undefined-skip (originally 23ba7e7,
reverted in 4d172a1). Tarball export from forks strips `enabled`, so
fork-pulled YAMLs that get sync-pushed back via `wmill schedule push`
would otherwise serialize `{ enabled: undefined }` → `{}` and the
backend's required `SetEnabled.enabled` rejects the body. Skipping
preserves the target's existing flag, which is the round-trip-safe
behavior. (`wmill workspace merge` extension to triggers/schedules is
tracked in #9001 — until then sync push is the only CLI path.)
- TriggerModeToggle suspend-dropdown action awaits onToggleMode and
resets `innerTriggerMode = triggerMode` on cancel, matching the Toggle
on:change handler. Without this, dismissing the fork-conflict modal on
a Suspend transition leaves the toggle stuck in 'suspended'.
- forkConflict: when a new modal opens with a previous resolver still
pending, resolve the older promise to false. Avoids a dangling promise
if the user clicks toggles on two rows in quick succession.
- schedules list: bump `toggleResetVersions[path]` on the
permission-denied branch so the Toggle re-mounts back to the prop's
`enabled` value. Without this, a user without write permission could
click the toggle and have it stick visually flipped.
- docs/fork-triggers.md: switch the merge-direction filter description
from `wm-fork-*` prefix to `parent_workspace_id IS NOT NULL` (matches
the code after 4dd38fe). Drop the misleading "merge-direction filter
strips identifier columns too" line in Future Work — the runtime
suffix is applied at listener attach, the stored column never carries
it, so no export filtering is needed there.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: distinguish job-already-completed from execution failure on OTLP span
handle_queued_job's bool return type conflated two distinct Ok(false)
cases: a real race with another worker (Error::AlreadyCompleted) and any
job execution that returned an error via process_result. The outer "job"
span recorded "job already completed by another worker" for both, so
every failed Python/bun/etc. script ended up with that misleading
otel.status_message even though the real error was correctly recorded
on the inner job_postprocessing span.
Replace the bool with a JobOutcome enum (Completed / Failed { description }
/ AlreadyCompleted). Failed carries the truncated error string, so the
outer span's Status.message now reflects the actual cause.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: extract user-facing error from error_value for span description
Cubic flagged that capturing e.to_string() before the match meant
ExitStatus failures (the most common failure mode for script jobs)
ended up with the generic "exit status: …" string rather than the
script error extracted from job logs by extract_error_value.
Move the description capture to after error_value is built and
deserialise it as ErrorMessage to pull out the structured message.
Falls back to "Job failed" if the value isn't shaped that way.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: use permissive description extraction for OTLP status message
Cubic flagged that strict ErrorMessage parsing downgraded real failures
to a generic "Job failed" whenever the result wasn't shaped as
{message, name} — e.g. agent-worker's "See logs for more details" raw
string, or runtime-written result.json files with a different shape.
Switch to parsing as serde_json::Value and pulling .message out if it's
a string, falling back to the whole value if it's a bare string. Only
fall back to "Job failed" when neither is available.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>