* fix: never replace an in-flight indexeddb open, only a settled one
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: keep a version history for session artifacts
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: let the assistant browse an artifact's earlier versions
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: pick an older artifact version from the preview panel
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(ai_evals): cover the change note the assistant writes on each edit
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: bound every indexeddb open, not only one told it is blocked
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): attach the right job path to preview runs
* fix(cli): keep a deliberately-absent file in the directory it was named in
* fix(cli): resolve links for a path naming a file that is not there
* fix(frontend): call a dev workspace a dev workspace in the merge UI
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(frontend): drop the unreachable dev-workspace guard on the fork modals
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
`list_users` and `list_pending_invites` had no ORDER BY, so Postgres returned
rows in heap order. An UPDATE rewrites the row at the end of the heap, which
sent the member whose role was just toggled to the bottom of the list the
settings page refetches right after.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow-editor): measure the redesigned step panels
Instruments the flow editor's step, loop and branch panels on the existing
anonymous `feature_usage` channel, so the redesign can be judged on how the
panels are actually used rather than on nothing.
Eight event kinds under a new `flow_editor` feature: panel opens and their
dwell (bucketed, per placement), placement-preference overrides, which
settings get configured or cleared, settings that read as invalid, the
prop-picker connect lifecycle, AI input suggestions, and the step header
menu that "Save to workspace" now lives behind.
Settings changes are diffed off `describeStepSettings`, the same view the
graph badges render, so the telemetry vocabulary cannot drift from the one
on screen. Only `panel_open` and `setting` carry an entity id — one opaque
id per editor mount — since a per-entity row is only worth its cost where
the spread per editing session is the question.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow-editor): keep the panel telemetry honest
Review follow-ups on the instrumentation:
- The top dwell bucket was `120s+`, and `+` is outside the charset
`is_identifier_shaped` accepts, so `log_feature_usage` skipped those
events and still answered 204 — the longest visits vanished with no
error on either side. Renamed to `120s_plus` and pinned every emittable
key against the backend's charset in a test, since the producer is
TypeScript and the validator is Rust.
- Dropped the per-session entity id from `setting`: it would pay a row per
session per day across twenty-four keys, for a distribution its plain
counter already largely answers.
- An armed connect that went away with its component never reported, so
`open` did not balance against `insert` + `abandon`.
- Session preview tabs keep hidden editors mounted, which billed panel
time nobody spent. `FlowEditorView` now publishes the visibility it
already knows about.
- Re-picking the active placement row logged a move, which also made
`auto:from_docked` mean two different things.
- The last dwell of a session was lost on tab close, since Svelte tears
components down on navigation but not on `pagehide`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(flow-editor): narrow the telemetry to panel placement
The eight-kind instrumentation measured more than could be read. With nothing
recorded before the redesign there is no baseline to compare panel opens, dwell
times, settings usage or connect funnels against, so those counters answered
questions nobody could act on while costing a row per key per day in an
instance-wide table.
What remains are the three numbers the modal panel is actually judged on: how
often the 1280px breakpoint puts the panel in a modal, and how often people
override that in each direction.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow-editor): stop counting placement in session preview tabs
Preview tabs keep every flow editor mounted and laid out at panel width
whether or not it is the visible one, and that panel is narrower than the
breakpoint by construction. Each flow tab opened in a session therefore
emitted a `breakpoint_modal` on mount, and one drag of the session panel
across 1280px emitted one per mounted tab — with no host dimension in the
key to separate that from the crossings the counter exists to measure.
Also corrects the comment on the no-op placement guard, which justified
itself with a key vocabulary that no longer exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow-editor): make the three placement counters comparable
Sessions were excluded from the breakpoint counter but not from the two
override counters, so a pin made in a session landed in the same bucket
used to judge the breakpoint, with no crossing in the denominator to read
it against. All three are now gated together.
An override is also only counted when it moves the panel. Choosing
"Detached" on an editor the width had already put in a modal states a
preference without changing anything, and the aggregate carries no width
to separate that from the wide-screen override that is the actual signal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(flow-editor): describe the two override keys by what emits them
They documented themselves as overriding `auto`, which is no longer the
rule: pinning Attached on a wide editor overrides `auto` and emits
nothing, while going from an Attached pin to Detached below the
breakpoint emits `force_detach` even though `auto` would have produced a
modal there too. This file is what someone reads when interpreting the
numbers, and "override of auto" is the misreading the emission rule
exists to prevent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow-editor): count the panel moving, not the breakpoint being armed
The tracker held "the breakpoint is responsible for this modal" rather
than "the panel is modal", so on a narrow editor pinning Detached and
releasing it back to Auto emitted a second breakpoint_modal for a panel
that never moved. It also died with the editor, which FlowBuilder rebuilds
through a `{#key}` on every reload — each rebuild re-armed it and counted
the same narrow editor again.
Both inflate the denominator that the two override counters are read
against, and both bias it the same way: toward concluding that nobody
overrides the breakpoint.
The tracker now follows the panel's placement across preference changes,
and FlowBuilder owns it from above the `{#key}`, which also puts the
session exclusion in one place instead of at each call site. The
moves-only rule moves into `forcedPlacementEvent` so both halves of it sit
in the module the tests can reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow-editor): ignore placements measured before the editor is laid out
A reload rebuilds the editor through `{#key renderCount}`, and the panel
controller is rebuilt with it: its width restarts at zero, which resolves to
`docked` because that is what is safe to render rather than because the editor
is wide. The breakpoint tracker read that transient as the panel having docked
and counted the real width landing as a fresh crossing, inflating the
denominator both override ratios are read against.
`useFlowPanelMode` now exposes `measured`, and the tracker skips anything
unmeasured instead of recording it as a placement.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(flow-editor): state the placement invariants once each
The width-zero rule had accumulated at four sites, two of which forward it
without being able to break it. Keep it beside the guards that enforce it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: scope cloned app policy and custom path to the fork's creator
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: share the app custom-path scoping rule across its call sites
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: tighten the cloned-app-policy comments
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: correct the execution_mode and custom-path scoping rationale
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The consumer name field in the NATS trigger config read "Required is using
JetStream" instead of "Required if using JetStream", matching the wording
already used by the sibling stream name field.
Fixes WIN-2335
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): sync package-lock.json with package.json
`npm ci` fails in cli/ because the lockfile predates two manifest changes:
windmill-parser-wasm-yaml was bumped to 1.770.0 and windmill-yaml-validator
1.1.1 was added, neither of which reached the lockfile.
Regenerated with `npm install --package-lock-only`; the only entries touched
are those two packages and windmill-yaml-validator's transitive deps.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(cli): drop the unread npm lockfile, bun.lock is the CLI's
Every install path in cli/ runs `bun install`: cli-tests.yml, git-sync-test.yml,
backend-test.yml, build.sh, and install_dev.sh (its --node branch installs the
generated npm/ bundle, which carries its own manifest). build-npm.ts synthesises
the published package.json from scratch, and change-versions.sh regenerates the
frontend and yaml-validator lockfiles but not this one.
So package-lock.json was read by nothing and verified by nothing, and drifted out
of sync with package.json unnoticed until `npm ci` refused to install. Deleting it
removes the second source of truth rather than hand-repairing it again on the next
bun-driven dependency change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: allow custom dev workspace environment labels
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: reject dev labels that shadow a tracked branch's namespace
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: guard dev labels against a repo's assumed default branch
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: state the badge-cap rationale once and drop unenforceable openapi constraints
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: offer a fixed list of environment labels instead of free text
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: match the accepted label set to the openapi enum exactly
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: stop describing the label set as dev/staging only
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): hide the fork workspace banner from operators
* fix(frontend): scope the operator gate to the workspace its role was fetched for
* fix(frontend): drop superseded whoami responses instead of writing a stale role
* fix(frontend): guard the remaining workspace-switch userStore writers
* feat: open a session edit in the preview panel from the edits list
* refactor: move the deploy-kind preview mapping next to its siblings
* feat: make preview the primary action in the session edits list
Clicking a row in the Edits popover now opens the item in the session
preview panel; kinds the panel cannot host fall back to their diff. Each
row gains an explicit Diff button for that item, and a pinned footer row
opens Review & deploy for the whole set.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: fold the open-and-flash rule into SessionPreviewTabs
The rule for when a preview open should flash the tab was written out at
three call sites, one of them with a looser condition. Move it onto the
tab owner as openAndPulse so the three agree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: preview data-pipeline edits from the edits list
A pipeline bundle is stored at `f/<folder>/data_pipeline` while its editor
is the folder's pipeline view, so the deploy-kind mapping has to route on
the folder. Without it the one remaining previewable kind fell through to
the drawer. Also pin the open-and-flash rule with tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: state the pipeline bundle path layout once
`f/<folder>/data_pipeline` was parsed independently in the compare page,
the home list and the session preview mapping — two of them disagreeing on
whether the trailing segment is checked — and built by hand in the editor.
Route all four through $lib/pipelinePaths.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: route the pipeline page through the shared bundle path
The route built the bundle path by hand and passed the draft kind as a
literal, the two halves of the editor disagreeing on where the layout is
stated.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: name a pipeline edit by its folder in the edits list
The bundle path is an implementation detail and the row's click lands on
the folder's editor, so showing `f/<folder>/data_pipeline` named something
the click doesn't open. Also derive the bundle regex from the draft-kind
const it has to agree with.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore(cli): drop unrelated package-lock.json change
The lockfile diff was an incidental regeneration from an older manifest
(it downgraded the locked svelte range below what cli/package.json
requires) and had nothing to do with this PR. Reset to main's version.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* refactor(recordings): build recordings from the completed run instead of live event capture
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(recordings): budget and isolate replay synthesis against hostile recordings
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(recordings): capture full logs and the run-time flow, upgrade v1 files
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(recordings): pick an existing run for the hub recording
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(recordings): cap recorded logs under the replay loader's text budget
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(recordings): pin picked runs to their executed version, keep v1 streamed logs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(recordings): bound pipeline finalize fan-out, pin flow schema to run version
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(recordings): warn on mixed-version fallback, bound code fetches
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: add public sharing option for job pages
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: gate public run sharing and address review findings
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review nits on public run sharing
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: key public run view on workspace, job and token
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the mermaid fullscreen dialog inside its pane and its emoji vector
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: load only the fonts a diagram needs and size chrome per breakpoint
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: cover every emoji class in the font preload without restyling diagrams
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the dialog chrome allowance in rem so it scales with the root font
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: decode mermaid entity codes when sampling text for the font preload
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: match mermaid's decimal-only entity codes and decode the Inter sample too
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: drop emoji format characters from the font sample
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: state the emoji subset spread and modifier exclusions precisely
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: re-render once fonts settle instead of hand-picking emoji subsets
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: give Modal a fill-height mode instead of measuring its chrome
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: guard the post-fonts re-render against a newer render
The re-render after document.fonts.ready assigned svg without re-checking
renderSeq after its own await. renderedCode is set before that await, so a
stale re-render landing last leaves svg holding the previous diagram while
renderedCode names the current source — showSvg stays true and paints the
wrong diagram.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: compact ai chat context for models with unknown context windows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: correct stale comment on unknown-model context window handling
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: surface assumed context window in usage indicator for unlisted models
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* [ee] feat: improve duckdb isolation
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: update ee-repo-ref to c4e6cbc1a1efeca5b71920c7903db2347d0eeda0
This commit updates the EE repository reference after PR #713 was merged in windmill-ee-private.
Previous ee-repo-ref: f630f7e73cb863e312430738d81d802a3971f7cd
New ee-repo-ref: c4e6cbc1a1efeca5b71920c7903db2347d0eeda0
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* chore: track origin EE branch in worktree setup when absent locally
* chore: pass --track to worktree add so upstream is set regardless of git config
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: guard first worktree arm on local branch to avoid remote-only DWIM
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
#10560 changed the script editor's change handler to read `e.detail` instead of
`editorCode`, on the reasoning that the two only diverge while the editor is
being torn down. They also diverge in a live editor: `setCode` dispatches the
string it was handed, but `alignCodeWithEditor` applies that string to Monaco
first, and the resulting `onDidChangeModelContent` runs `updateCode`
re-entrantly — so if the model normalized the text (EOL is the reachable case;
`ScriptBuilder` builds template content with a `\r\n` join), `editorCode`
already holds the buffer's version and the payload is the pre-normalization
one. Taking the payload leaves `code` disagreeing with what the editor shows,
which the external-write effect then tries to reconcile on every change.
The language-switch fix in that PR is `alignCodeWithEditor` clearing the timer
its own write armed; that part stands and is unaffected. This restores the
handler to the buffer-true read.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Picking a new language seeds the new template into `script.content`, which
`ScriptEditor` writes straight into Monaco. That write goes through
`onDidChangeModelContent`, which arms the keystroke debounce as if the user had
typed — so when the `{#key effectiveLang}` block then tears the editor down, the
unmount flush sees a pending timer, reads a stale `code`, and dispatches a change
that puts the previous language's template back. The editor kept showing the old
content under the new language.
`alignCodeWithEditor` now clears the timer its own write armed, restoring the
premise the unmount flush is guarded on: a pending timer means Monaco holds
something newer than `code`.
`ScriptEditor` also takes the change payload instead of re-reading `editorCode`.
A destroyed component's `bind:` writes no longer reach the parent, so the
re-read returned the value from before the change — which is what actually
wrote the old template back, and would equally have made the unmount flush
save stale text after real typing.
Fixes WIN-2330
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow-editor): surface an agent's tool-call status without moving the graph
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: count only an agent's tool calls and key them in one place
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: report an agent's replies alongside its tool calls
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: break the agent summary down by action kind
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: key agent tool nodes by kind and keep the summary clear of the tool row
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: read agent action status from the run's success array
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: pin the tool joins a local run cannot reach
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: place the agent summary beside the step and match MCP paths bare
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: feed a single-step agent test's calls into the graph status
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* perf: keep run status out of flow graph node and edge data
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: key the ai tool node memo on the agent's actions
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: state the edge-data and memo-key constraints as invariants
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: restore selection clearing and pin the zoom bar's border colour
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: show an agent's tool calls as they arrive instead of at step end
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* perf: stop editor runs from rebuilding on agent tool calls
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the same_worker pin when a suspend ends without approval
A disapproved or timed-out approval gate hands the flow back through the
UpdateFlow channel with unrecoverable = true. That flag means "the previous
step's worker died", and it is read by six sites. Five of them happen to want
what it does here, but continue_on_same_worker and continue_with_runners do
not: the worker that ran the approval step is alive, so unpinning the error
handler and routing it by tag breaks the ./shared contract of a same_worker
flow and can land it on a worker group that cannot run it — the same defect
#10551 fixed for the three producers that hand back a live flow.
Replace the boolean with StepFailureKind so the suspend producer can say
"worker alive, but this failure is not the module's to handle" instead of
overstating a worker death. The failed module's error policy is deliberately
still bypassed: the failure is recorded against the step the gate was holding
back, which never ran, so its retry would re-open the gate and its
continue_on_error would skip it outright (verified: the gated step is marked
Failure with a nil job id and the flow jumps past it). suspend.
continue_on_disapprove_timeout remains the way to continue past a gate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow-editor): flag that continue on error does not cover the approval gate
A resolved approval is recorded against the step the gate holds back, not
the step carrying the suspend, so continue_on_error never sees it: the flow
still stops on a disapproval or timeout. Point users at
suspend.continue_on_disapprove_timeout, which is what actually continues past
a gate, whenever both settings are on and that one is not.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: point re-opened previews at the tab already showing them
* fix: judge composed preview mutations as one change
* fix: treat a fullscreen preview as displayed when deciding to flash
* fix(frontend): draw the tab strip's scroll bar instead of the native one
The strip sizes its scroll row to the tabs, but a native horizontal
scrollbar claims layout height on top of that: Firefox spends 11px on
`scrollbar-width: thin` — `--wm-scrollbar-size` is WebKit-only, so the
4px it asks for is ignored there — which clipped the tabs at the top of
the 32px sessions strip and left a wide gutter under them.
Hide the native bar and draw a 4px thumb from `scrollLeft`/`scrollWidth`
instead: it costs no layout height, is the same size in every engine, and
sits on the strip's bottom edge, flush under the tabs. Tabs drop to `h-6`
so they clear it, and the strip's default height matches the sessions
caller's `h-8` so every strip has the same geometry.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(frontend): clamp the tab strip thumb at both ends of its track
WebKit's elastic overscroll drives `scrollLeft` negative, which slid the
thumb out of the track's left edge and into the strip's padding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(debugger): install debug session deps from the instance registry settings
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(debugger): keep install-time registry credentials out of the session-visible tree
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: drop em dashes from the debugger registry docs and comments
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(debugger): stop installing for a session that went away during the settings fetch
Also serves nativets sessions the npm settings their installer reads.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
When a module completes without spawning a job — an empty branch, an
empty for-loop, or a module already marked Success — the flow hands
itself back through the UpdateFlow channel, and the result processor
resumed it with unrecoverable = true regardless of what sent it. That
flag means "the previous step's worker died", which holds for none of
the three producers except a suspend that ended without approval.
The stale argument was inert until continue_on_same_worker and
continue_with_runners started reading it, since when the step after such
a module is pushed as an ordinary queued job. It is then routed by tag
and can land on any worker in the pool, breaking both the ./shared
directory contract and the guarantee that a same_worker flow stays on a
worker able to run it — a step whose tag resolves to a worker group that
cannot execute its language fails instantly, taking the flow with it.
Carry the flag on the UpdateFlow message so each producer states its own
case, rather than having the shared receiver assume the worst. The three
that hand back a live flow forward whatever their caller reported, so a
genuinely unrecoverable failure still crosses the hop unchanged.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: register mounted CA certificates in windmill_extra at startup
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: only claim a CA update when update-ca-certificates can read the mount
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: detect mounted CA certificates the way update-ca-certificates finds them
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
`windmill prepare-deps` was spawned with Bun's raw `spawn` in both the Python and
the TypeScript session, bypassing the nsjail wrapping the debugged script itself
gets. With ENABLE_NSJAIL=true, `uv pip install` (source distributions run their
build backend) and `bun install` (postinstall scripts) therefore executed
package-supplied code unconfined, next to the LSP, multiplayer and gateway
services in the windmill-extra container.
Both installers now go through the same nsjail wrapper as the debuggee, which the
two files no longer build separately. The jail keeps the environment
(`keep_env`), which is what carries the registry credentials and CA settings into
the installer; the debugged script's environment is unchanged and still holds
neither.
Killing the installer also did not reap the `uv` or `bun` it had spawned: those
were reparented to init and kept downloading, so both the timeout and the
cancel-on-disconnect only half-worked. The installer now runs in its own process
group and is signalled as a group, reading the group id back from /proc rather
than assuming it, since a group kill aimed at the service's own group would take
down every service in the container.
Two things that cancellation exposed: a kill was reported to the client as an
install failure, since it ends the read with nothing to parse - blaming the user
for their own Stop; and the standalone Bun server's close handler only dropped
the session from its map, so nothing there was ever cleaned up. The teardown flag
is also scoped to a launch rather than the session, because cleanup() runs when a
program finishes normally too.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: allow a dev workspace to have its own dev workspace
Fixes WIN-2324
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep every dev workspace in a chain on a distinct deploy branch
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: count a dev workspace the caller has no seat in as holding its label
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: keep the attach form standing when a candidate takes the last label
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the label toggle visible when a candidate's dev workspace clashes
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: describe the cycle guard by what holds, not by what changed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: refuse to archive a fork-backed dev workspace that owns a nested dev
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: put the deploy target and item filters under the pairing they configure
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: refuse to archive any dev workspace that owns a nested dev
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: fix the fixture family count
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: put the deploy target with the pairing line it restates, above protections
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: name the same family head in the workspace menu and the scope picker
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: stop offering to delete a dev workspace from the sidebar settings menu
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: state the visibility boundary the lineage root actually resolves to
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: serialize dev-pairing creation against teardown of the same workspace
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: lock both sides of an attach so adjacent pairings cannot share a label
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: serialize dev pairings on one key, the invariant being chain-wide
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: scope the pairing lock to the chains an operation reads
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: hold the pairing lock across renames and re-check the cycle under it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: hide the fork-delete action until the workspace entry has loaded
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: lock archive before it reads the pairing state it acts on
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: describe the archive lock test by what it pins, and drop an unused fixture row
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: add a load all next to load more in the tree view folder pager
* fix: bound tree node rendering and make a long load resumable
* fix: resume a failed first load from its saved cursor
* fix: size the show-more step by what a node holds, not what it renders
* fix: keep the pager visible mid-run and spin only the clicked button