Skip to content

[ui] Make "Scaffold all default config" work for backfill#33927

Open
HynekBlaha wants to merge 1 commit into
dagster-io:masterfrom
HynekBlaha:fix-backfill-config-scaffold
Open

[ui] Make "Scaffold all default config" work for backfill#33927
HynekBlaha wants to merge 1 commit into
dagster-io:masterfrom
HynekBlaha:fix-backfill-config-scaffold

Conversation

@HynekBlaha

@HynekBlaha HynekBlaha commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary & Motivation

In the Backfill Config Editor (opened via Materialize → Config → Add config on a partitioned asset), the "Scaffold all default config" button is always disabled, even though the asset has config fields with defaults.
The same button works correctly in the regular Launchpad.

Test Plan

class GreeterConfig(Config):
    greeting: str = "Hello"
    name: str = "World"
    suffix: str = "!"


@asset(partitions_def=DailyPartitionsDefinition(start_date="2024-01-01"))
def configurable_partitioned_asset(config: GreeterConfig) -> str:
    return f"{config.greeting}, {config.name}{config.suffix}"


defs = Definitions(assets=[configurable_partitioned_asset])

Run it

# Terminal 1 — backend
dagster-webserver -p 3333 -f test_backfill.py
# Terminal 2 — dev UI
cd js_modules
make dev_webapp
open http://localhost:3000

Steps

  1. Navigate to the asset configurable_partitioned_asset.
  2. Click Materialize → pick a range of at least 2 partitions (so backfill mode kicks in).
  3. In the launch dialog, expand the Config section → click Add config.
  4. In the Config Editor, observe the "Scaffold all default config" button.

Before:
Incorrectly shows that all defaults are expanded!
Screenshot 2026-06-14 at 23 03 53

After:
Screenshot 2026-06-14 at 23 02 58

@HynekBlaha

Copy link
Copy Markdown
Contributor Author

@OwenKephart, could you please take a look at this small fix?
You are the original author of backfill config. :)

@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug in BackfillLaunchpad where the "Scaffold all default config" button was always disabled because rootDefaultYaml was only ever set from savedConfig?.runConfigYaml, which is undefined when no config has been saved yet.

  • The fix adds a ?? fallback to result.data?.runConfigSchemaOrError.rootDefaultYaml when no saved config exists, mirroring what the regular launchpad (LaunchpadAllowedRoot) already does.
  • Unlike LaunchpadAllowedRoot, the fallback value is not passed through filterDefaultYamlForSubselection, which filters out op configs for ops not in the asset selection — a minor inconsistency worth noting.

Confidence Score: 4/5

Safe to merge. The change is small and targeted, adds a well-understood fallback consistent with the regular launchpad, and does not touch any data-write or launch paths.

The fix correctly mirrors what LaunchpadAllowedRoot already does for the non-backfill launchpad. The one open question is whether filterDefaultYamlForSubselection should also be applied to the fallback value — if the backend doesn't scope rootDefaultYaml to the asset selection, the scaffolded config could include op defaults for unselected assets. This is a cosmetic / UX concern rather than a correctness or data-integrity issue.

js_modules/ui-core/src/launchpad/LaunchpadRoot.tsx — verify whether the backend scopes rootDefaultYaml to assetSelection, and consider applying filterDefaultYamlForSubselection for consistency with LaunchpadAllowedRoot.

Important Files Changed

Filename Overview
js_modules/ui-core/src/launchpad/LaunchpadRoot.tsx Adds a fallback from runConfigSchemaOrError.rootDefaultYaml when no savedConfig exists, fixing the disabled "Scaffold all default config" button. The fallback skips the filterDefaultYamlForSubselection step that LaunchpadAllowedRoot applies, which may scaffold extra op configs when only a subset of assets in a job is selected.

Sequence Diagram

sequenceDiagram
    participant User
    participant BackfillLaunchpad
    participant GQL as GraphQL (runConfigSchemaOrError)
    participant LaunchpadTransientSessionContainer
    participant LaunchpadSession

    User->>BackfillLaunchpad: Open Config Editor (no saved config)
    BackfillLaunchpad->>GQL: PIPELINE_EXECUTION_ROOT_QUERY (with assetSelection)
    GQL-->>BackfillLaunchpad: "RunConfigSchema { rootDefaultYaml }"

    Note over BackfillLaunchpad: rootDefaultYaml =<br/>savedConfig?.runConfigYaml<br/>?? runConfigSchemaOrError.rootDefaultYaml
    BackfillLaunchpad->>LaunchpadTransientSessionContainer: rootDefaultYaml (schema defaults)
    LaunchpadTransientSessionContainer->>LaunchpadSession: rootDefaultYaml
    User->>LaunchpadSession: Click "Scaffold all default config"
    LaunchpadSession-->>User: Editor populated with defaults ✓
Loading

Reviews (1): Last reviewed commit: "[ui] Make "Scaffold all default config" ..." | Re-trigger Greptile

Comment on lines +121 to +125
const rootDefaultYaml =
savedConfig?.runConfigYaml ??
(result.data?.runConfigSchemaOrError.__typename === 'RunConfigSchema'
? result.data.runConfigSchemaOrError.rootDefaultYaml
: undefined);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing filterDefaultYamlForSubselection on the schema fallback

LaunchpadAllowedRoot always passes rootDefaultYaml through filterDefaultYamlForSubselection before handing it to LaunchpadTransientSessionContainer. That filter removes op-level config keys whose names are not in pipelineOrError.nodeNames, preventing config from unselected ops from appearing in the scaffolded output. The new fallback path here skips that step entirely.

In the common case where the backend's runConfigSchemaOrError query already scopes rootDefaultYaml to the assetSelection this is harmless, but if the backend returns defaults for the full asset job (all ops), "Scaffold all default config" will include configs for ops that are not part of the current backfill selection. Worth verifying which behaviour the backend exhibits, or applying the same filter here for consistency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Tested on extended

"""Minimal repro for testing Backfill Config "Scaffold all default config" button.

Usage:
    dagster-webserver -p 3333 -f /tmp/dagster_test_backfill_config/test_backfill.py

Then in another terminal:
    cd $DAGSTER_GIT_REPO_DIR/js_modules
    PATH="$HOME/.nvm/versions/node/v20.20.2/bin:$PATH" make dev_webapp

Test scenarios:
1. Backfill BOTH assets → "Scaffold all default config" includes configs for both
   `greeter_asset` and `farewell_asset`.
2. Backfill only ONE asset → scaffolded config contains ONLY that asset's
   config block (verifies filterDefaultYamlForSubselection).
"""

from dagster import (
    Config,
    DailyPartitionsDefinition,
    Definitions,
    asset,
)

PARTITIONS = DailyPartitionsDefinition(start_date="2024-01-01")


class GreeterConfig(Config):
    greeting: str = "Hello"
    name: str = "World"
    suffix: str = "!"


class FarewellConfig(Config):
    farewell: str = "Goodbye"
    closer: str = "cheers"


@asset(group_name="greetings", partitions_def=PARTITIONS)
def greeter_asset(config: GreeterConfig) -> str:
    return f"{config.greeting}, {config.name}{config.suffix}"


@asset(group_name="greetings", partitions_def=PARTITIONS)
def farewell_asset(config: FarewellConfig) -> str:
    return f"{config.farewell}{config.closer}"


defs = Definitions(assets=[greeter_asset, farewell_asset])

Works as expected.
[

Screen.Recording.2026-06-14.at.23.27.03.mov

](url)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants