Skip to content

Conversation

@hugocasa
Copy link
Collaborator

@hugocasa hugocasa commented Jan 7, 2026

No description provided.

@claude
Copy link
Contributor

claude bot commented Jan 7, 2026

Claude finished @hugocasa's task in 2m 47s —— View job


Code Review Summary

This PR enables access to results (prior step results) within early stop expressions in Windmill flows. Previously, early stop conditions could only access result (current step result), flow_input, and all_iters. Now users can reference results.step_a, results["step_b"], etc. to access outputs from earlier steps.

Overall Assessment

👍 Approved with minor suggestions

The implementation is clean and well-thought-out:

  1. Performance-conscious: The new get_id_ctx_for_expr function uses string pattern matching to lazily load the ID context only when results is actually referenced in the expression, avoiding unnecessary DB queries.

  2. Good refactoring: Converting get_transform_context from async to sync removes unnecessary overhead since the function performs no async operations.

  3. Frontend integration: The PropPicker components are properly updated to display both the current step result and prior step results in the early stop editor.

Suggestions

I've left inline comments on specific lines. The main points are:

  1. String matching heuristics (worker_flow.rs:292): The pattern matching for results., results[, results?. could have false positives (e.g., comments or local variables named results). This is an acceptable tradeoff but worth documenting.

  2. Empty previous_id (worker_flow.rs:295): The hardcoded empty string for previous_id in get_id_ctx_for_expr might be intentional, but deserves a comment explaining why.

  3. UX naming (PropPicker.svelte): Consider whether "Current Step Result" vs "Prior Step Results" would be clearer than "Step Result" vs "Results".

Security

No security concerns identified. The feature extends existing expression evaluation capabilities without introducing new attack vectors.


Testing Instructions

To verify the changes, open the Flow Editor and create a multi-step flow with at least two steps (e.g., Step A that returns some data, and Step B). Navigate to Step B's "Early Stop" settings panel (found in the step's advanced settings). Enable the "Stop flow if condition met" toggle. In the stop condition expression editor, you should now see a property picker on the right side that displays both "Step Result" (showing Step B's result) and "Results" (showing prior step outputs like results.a). Try writing an expression like results.a.someField === 'value' to verify that autocomplete works and the expression can reference prior step results.

db: &DB,
status: &FlowStatus,
) -> error::Result<Option<IdContext>> {
if expr.contains("results.") || expr.contains("results[") || expr.contains("results?.") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consideration: The string pattern matching approach works but has some edge cases to be aware of:

  1. False positives: An expression like let results = []; results.length or a comment containing results. would trigger a DB query even if results isn't actually used as the global.

  2. Missed cases: Patterns like results . (with whitespace) or results /*comment*/. wouldn't be detected.

This is a reasonable tradeoff for performance optimization, but worth documenting in a comment that these are heuristics. The worst case is an unnecessary DB query, which is acceptable.

if expr.contains("results.") || expr.contains("results[") || expr.contains("results?.") {
let flow_job = get_mini_pulled_job(db, &flow).await?;
if let Some(flow_job) = flow_job {
Ok(Some(get_transform_context(&flow_job, "", &status)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: The previous_id is hardcoded to "" here. Is this intentional?

Looking at other call sites, previous_id is typically passed to allow expressions to reference the immediately preceding step via previous_result. In the context of early stop, users might expect previous_result to work as well.

If this is intentional (e.g., because result already contains the current step's result), it would be helpful to add a comment explaining why.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 7, 2026

Deploying windmill with  Cloudflare Pages  Cloudflare Pages

Latest commit: a9e08f4
Status: ✅  Deploy successful!
Preview URL: https://a670955a.windmill.pages.dev
Branch Preview URL: https://hc-accept-results-in-early-s.windmill.pages.dev

View logs

}
$: search, $inputMatches, $propPickerConfig, pickableProperties, updateState()
$: (search, $inputMatches, $propPickerConfig, pickableProperties, updateState())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: The parentheses around the reactive statement are unusual. While this works, the standard Svelte pattern would be:

$: search, $inputMatches, $propPickerConfig, pickableProperties, updateState()

The extra parentheses don't affect functionality but are inconsistent with other reactive statements in the codebase.

Comment on lines +258 to +266
{#if result != undefined}
<span class={categoryTitleClasses}>Step Result</span>
<div class={categoryContentClasses}>
<ObjectViewer
{allowCopy}
json={{ result, ...(extraResults ? extraResults : {}) }}
on:select
/>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good addition: This properly displays the step result at the top of the picker, making it easy for users to reference both result (current step) and results (prior steps) in their early stop expressions.

One consideration: when both result and pickableProperties.priorIds contain data, users see both "Step Result" and "Results" sections. The naming could potentially be confusing. Consider whether "Current Step Result" vs "Prior Step Results" would be clearer.

animationDuration="4s"
>
{#if result != undefined}
{#if result != undefined && !pickableProperties}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good change: The condition now correctly renders PropPickerResult only when there's a result but NO pickableProperties, and PropPicker when pickableProperties exist (which will now also show the result via the new props).

This ensures the early stop editor shows both the step result and the full property picker with prior step results.

Comment on lines 4946 to 4951
})
}

pub async fn get_transform_context(
pub fn get_transform_context(
flow_job: &MiniPulledJob,
previous_id: &str,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good refactor: Converting get_transform_context from async to sync is a nice cleanup. The function was just building a HashMap and returning immediately - no actual async operations were happening. This removes unnecessary overhead and makes the call sites simpler (no more .await? needed).

@rubenfiszel rubenfiszel merged commit 4763eda into main Jan 7, 2026
4 checks passed
@rubenfiszel rubenfiszel deleted the hc/accept-results-in-early-stop branch January 7, 2026 07:06
@github-actions github-actions bot locked and limited conversation to collaborators Jan 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants