Files
windmill/frontend/src/lib/components/custom_ui.ts
T
Ruben FiszelandClaude Opus 4.8 248540ac4d feat: bounded-cascade selective execution for pipelines (UI + CLI) (#9695)
* feat: bounded-cascade selective execution for pipelines (UI + CLI)

Run a prefix of a pipeline cascade: from a schedule/manual root, fan
downstream but stop at chosen end node(s) — the path-between set over the
asset-graph lineage DAG. Exposed as a canvas 'Run downstream up to…' pick
mode and a 'wmill pipeline run <folder> --to' CLI command. No backend or
parser changes; reads the existing graph, tags, and triggers.

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

* feat: surface bounded-run on the run caret, trigger-node kebab, and Test button

Move 'Run downstream up to…' from the runnable kebab onto the play-button
caret popover (Edit mode, next to Run / Run + trigger N downstream); add it
to the trigger-node kebab so schedule/data_upload entrypoints expose it on
the View page; and to the ScriptEditor Test split caret for the open script.

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

* fix: address CI review on bounded-cascade (cubic)

- Port CLI engine test from Deno to bun:test under cli/test/ (won't run under bun test otherwise).
- closure() now excludes the start node on a cycle back to it (descendants/ancestors contract); regression tests both engines.
- CLI 'pipeline run --to' rejects unresolved/ambiguous end tokens instead of silently running a different subset.
- Sort a copy in the runSelection order test so the launch-order assertions aren't invalidated.

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

* fix: address standing review nits on bounded-cascade

Resolves the four recurring P1/P2 findings from the codex/pi/claude
reviews:

- UI gate (P1): the canvas/trigger-node "Run downstream up to…"
  affordance was gated on the subscriber-only downstream map, so a valid
  start whose only downstream is a pure reader had a non-empty bounded
  set but no menu entry. Gate on the read-aware lineage downstream
  (buildLineageDownstreamMap), matching the bounded engine.
- waitJob (CLI): a completed job without explicit success:true now
  counts as a failure, mirroring the frontend waitJobTerminal — the
  cascade only advances on a confirmed success.
- Comment fix (CLI): the unbounded `run` path uses the read-aware
  lineage DAG (pure readers included); dropped the false "parity with
  the canvas cascade" (subscriber-only) claim.

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

* fix: expose bounded-run caret for pure-reader-only starts (codex P1)

The canvas wiring from the prior commit passed `onStartBoundedRun` from
the read-aware lineage map, but the leaf components still hid the popover
that holds the "Run downstream up to…" action behind a subscriber-only
gate:

- RunnableNode rendered the Run-button caret only when
  `hasCascade = downstreamCount > 0` (subscriber-only). A valid start
  whose only downstream is a pure reader got `onStartBoundedRun` but no
  visible action. Now the caret opens when there's a cascade OR a
  bounded-run start (`hasCaret`), and the "Run + trigger N downstream"
  item is gated on `hasCascade` so it never reads "trigger 0".
- ScriptEditor's Test split button activated only when
  `downstreamSubscribers > 0`, falling through to a plain Test button
  (no caret) otherwise. Now it also activates when `onBoundedRun` is
  set, with the "Test + trigger N" item gated on the count.

For a manual root (no trigger-node kebab fallback) with a pure-reader
downstream this was the only UI entry point, so it was previously
unreachable. Verified in-browser: a manual-root script writing an asset
read-only downstream now exposes "Run downstream up to…" on the
ScriptEditor Test caret with the cascade item hidden.

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

* fix: gate ScriptEditor bounded-run on read-aware downstream; fix CLI asset-end warning (codex P2)

- Details-pane (ScriptEditor) bounded-run entry was gated only on
  `validStartPaths`, broader than the canvas which also requires
  read-aware downstream (`hasLineageDownstream`). An isolated start could
  thus expose "Run downstream up to…" and enter pick mode with no
  selectable end. Now gated on `lineageDownstreamPaths` (script paths with
  a downstream in `buildLineageDownstreamMap`), matching the canvas.
- CLI dropped-end warning called `scriptPathOf(d)` unconditionally, which
  slices `script:`-length chars off an asset id too — `datatable:main/raw`
  printed as `le:main/raw`. Now prefix-checks like the JSON output.

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

* fix: correct --from error to exclude only row-backed event triggers (codex P2)

The bounded-start validation message listed `kafka/webhook/…` as event
triggers that can't start a bounded run, but webhook/data_upload are
rowless and read as manual roots (valid starts). Only the row-backed
native kinds (kafka/mqtt/nats/postgres/sqs/gcp/email — EVENT_TRIGGER_KINDS)
are excluded; the message now names those.

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

* fix: surface dropped ends in CLI JSON; disambiguate shared-trigger bounded start (codex P2)

- CLI `run --json` silenced the dropped-end warning, and the JSON payload
  echoed the originally-resolved `--to` list with no reachable/dropped
  split — a resolved-but-unreachable end looked like a clean plan that
  silently runs only the start. JSON now includes `reachableEnds` and
  `droppedEnds` (shared `idLabel` helper, asset-id safe).
- Trigger nodes dedupe per (kind, ref), so a schedule shared across
  scripts collapses to one node, but `recordSourceTrigger` kept only the
  first target path — the bounded-run action then rooted at an arbitrary
  script (or hid when only that first script lacked downstream). Now all
  target paths are tracked and the action is offered only when exactly one
  is a valid start with downstream; multi-eligible nodes suppress it
  rather than guess.

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

* fix: don't run hidden drafts in View-mode bounded cascade (codex P1)

launchCascadeScript unconditionally preferred drafts.get(path) over the
deployed script. In View mode with drafts hidden (displayGraph is
deployed-only), a bounded run started from a trigger-node kebab would
execute preview jobs from hidden local draft content instead of the
deployed scripts the user is looking at.

Gate draft execution on `mode === 'edit' || includeDrafts` — the exact
condition under which displayGraph includes drafts — so execution always
matches the displayed graph. No-op for scripts without a draft; the
edit-mode "Run + trigger N downstream" cascade is unchanged.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:08:05 +02:00

176 lines
5.3 KiB
TypeScript

import type { SupportedLanguage } from '$lib/common'
export type FlowBuilderWhitelabelCustomUi = {
topBar?: {
path?: boolean
export?: boolean
history?: boolean
aiBuilder?: boolean
tutorials?: boolean
diff?: boolean
extraDeployOptions?: boolean
editableSummary?: boolean
settings?: boolean
draft?: boolean
}
settingsPanel?: boolean
settingsTabs?: {
schedule?: boolean
sharedDiretory?: boolean
earlyStop?: boolean
earlyReturn?: boolean
workerGroup?: boolean
concurrency?: boolean
debouncing?: boolean
cache?: boolean
}
triggers?: boolean
flowNode?: boolean
hub?: boolean
hubCode?: boolean
graph?: { aiBuilder?: boolean; dataflow?: boolean }
stepInputs?: { ai?: boolean }
stepAdvancedSettings?: boolean
languages?: (SupportedLanguage | 'docker' | 'bunnative')[]
scriptFork?: boolean
scriptEdit?: boolean
tagEdit?: boolean
editorBar?: EditorBarUi
downloadLogs?: boolean
tagSelectPlaceholder?: string
tagSelectNoLabel?: boolean
tagLabel?: string
aiAgent?: boolean
aiSandbox?: boolean
suggestIntegration?: boolean
suggestScript?: boolean
// Default timeout (in seconds) prefilled when enabling a custom step timeout.
// Defaults to 300 (5 minutes) when unset.
defaultTimeout?: number
}
export type DisplayResultUi = {
disableAiFix?: boolean
disableDownload?: boolean
}
export type PreviewPanelUi = {
disableHistory?: boolean
disableTracing?: boolean
disableTriggerCaptures?: boolean
disableTriggerButton?: boolean
disableJsonView?: boolean
displayResult?: DisplayResultUi
disableVariablePicker?: boolean
disableDownload?: boolean
tagLabel?: string
// Hide the args/SchemaForm pane entirely (use cases where the script
// is known to take no inputs — e.g. the pipeline editor's per-script
// preview). When set, the Test/Cancel button is rendered as a small
// floating affordance at the top-left of the preview area instead of
// inside the (now-absent) args column.
hideArgs?: boolean
// Render the LogPanel's logs/result as a left/right split instead of
// the default top/bottom. Pairs naturally with `hideArgs` when the
// preview area is the full bottom band.
logsResultSideBySide?: boolean
// Number of scripts that subscribe to assets this script writes (via
// `// on s3://…` etc.). When > 0, the Test button is rendered as a
// split: primary action runs just this step (cascade suppressed via
// `_wmill_skip_asset_dispatch`), and a caret exposes "Test + trigger N
// downstream" that lets the asset-dispatch hook fire downstream jobs.
// Undefined or 0 → plain Test button (no cascade UI).
downstreamSubscribers?: number
// Pairs with `hideArgs`: still hide the full args column, but when the
// script actually declares inputs render a compact SchemaForm between
// the floating Test button and the logs/result panel (e.g. a
// partitioned pipeline script that needs a `partition` arg to run).
// No-op when the script has no input properties.
argsAboveLogs?: boolean
// On mount, query the most recent top-level completed job for this
// script's path and load it into the preview pane so users immediately
// see the last run's logs/result instead of an empty panel. Used by the
// asset-graph details pane where selecting a script node should expose
// "what happened the last time this ran". No-op when a test is already
// in progress (we don't clobber a live run).
loadLastRunOnMount?: boolean
// Pipeline-only: when set, the Test split's caret popover gains a "Run
// downstream up to…" entry that calls this, letting the user bound the
// cascade from the script they're editing. Wired by the pipeline details
// pane only when the open script is a valid bounded-run start.
onBoundedRun?: () => void
}
export type EditorBarUi = {
contextVar?: boolean
variable?: boolean
resource?: boolean
reset?: boolean
type?: boolean
assistants?: boolean
multiplayer?: boolean
autoformatting?: boolean
editorSettings?: boolean
vimMode?: boolean
relativeLineNumbers?: boolean
aiGen?: boolean
aiCompletion?: boolean
library?: boolean
useVsCode?: boolean
diffMode?: boolean
s3object?: boolean
database?: boolean
ducklake?: boolean
dataTable?: boolean
debug?: boolean
history?: boolean
saveToWorkspace?: boolean
}
export type EditableSchemaFormUi = {
jsonOnly?: boolean
disableVariablePicker?: boolean
}
export type SettingsPanelMetadataUi = {
languages?: SupportedLanguage[]
disableScriptKind?: boolean
editableSchemaForm?: EditableSchemaFormUi
disableMute?: boolean
disableAiFilling?: boolean
}
export type SettingsPanelUi = {
metadata?: SettingsPanelMetadataUi
disableMetadata?: boolean
disableRuntime?: boolean
disableGeneratedUi?: boolean
disableTriggers?: boolean
}
export type ScriptEditorWhitelabelCustomUi = {
editorBar?: EditorBarUi
previewPanel?: PreviewPanelUi
disableTooltips?: boolean
}
export type ScriptBuilderWhitelabelCustomUi = {
topBar?: {
path?: boolean
editablePath?: boolean
settings?: boolean
extraDeployOptions?: boolean
editableSummary?: boolean
diff?: boolean
tagEdit?: boolean
}
settingsPanel?: SettingsPanelUi
disableTooltips?: boolean
editorBar?: EditorBarUi
previewPanel?: PreviewPanelUi
tagSelectPlaceholder?: string
// Default timeout (in seconds) prefilled when enabling a custom script timeout.
// Defaults to 300 (5 minutes) when unset.
defaultTimeout?: number
}