Skip to content

[BETA] feat(nodespec): the single unified dataflow spec (replaces standard/flow/materialized_view)#104

Open
haillew wants to merge 17 commits into
mainfrom
feature/uniform-dataflow-spec-poc
Open

[BETA] feat(nodespec): the single unified dataflow spec (replaces standard/flow/materialized_view)#104
haillew wants to merge 17 commits into
mainfrom
feature/uniform-dataflow-spec-poc

Conversation

@haillew

@haillew haillew commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

READ FIRST: nodespec is NOT a fourth spec type

nodespec is the single, unified dataflow spec that replaces standard, flow, and materialized_view going forward.

This is not a fourth option to pick from. It is the one and only spec every new pipeline should use. The three legacy formats are kept working only for backward compatibility during migration and are on a path to be retired. If you are writing a new pipeline: write nodespec.


What this PR adds (BETA)

A single, unified, node-based dataflow spec — nodespec — that supersedes the standard, flow, and materialized-view formats.

A nodespec is a graph of nodes that chain together:

source  ->  transformation  ->  target
  • source — where data comes from (table, files, stream)
  • transformation — how data is reshaped (SQL or Python)
  • target — where data lands, carrying its own table-level settings (CDC, data quality, quarantine, clustering, …)

Targets declare what feeds them via an explicit input list; sources and transformations reference their upstream within their own definition. The node graph lowers into the framework's existing flow-spec representation, so no engine changes are required and every current capability (CDC, snapshots, data quality, quarantine, sinks, table migration, materialized views) is preserved. A single nodespec can mix streaming-table and materialized-view targets, including chains where one feeds the other.

Legacy formats continue to work, and scripts/migrate_to_nodespec.py converts existing standard/flow/materialized_view specs.

Why

The three separate formats force users to learn three layouts, leak engine internals (flow groups, view registration, staging tables), nest settings deeply, hide pipeline topology, and force streaming tables and materialized views into separate files. nodespec collapses all of that into one readable, chainable model — easier for newcomers writing their first pipeline and for large teams reading and editing each other's pipelines.

Details

See the design decision record: docs/decisions/0008-unified-nodespec-dataflow-spec.md (full rationale, before/after examples, and the key decisions).

haillew added 3 commits June 21, 2026 19:20
Introduce the nodester dataflow spec: a single, node-graph format
(source -> transformation -> target) that replaces the separate standard,
flow, and materialized-view formats. The transformer lowers a node graph
into the framework's existing flow spec, so all current capabilities (CDC,
snapshots, data quality, quarantine, sinks, table migration, materialized
views) are preserved.

Highlights:
- Target nodes wire inputs via an `input` list; each item is a node name
  (auto flow name) or `{view, flow}` to define the SDP flow name explicitly
  and keep it stable across edits (renaming a flow forces a full refresh).
- A single spec may contain both streaming-table and materialized-view
  targets, including chains where one feeds the other.
- Python transformation nodes become their own view that applies
  apply_transform to their inferred upstream; inline python_transform on a
  source is still supported for backward compatibility.
- Inline SQL/Python sources (and append_sql) remain supported but warn, in
  both the nodester and legacy formats; the recommended alternative is a
  dedicated transformation node.
- Materialized view targets no longer accept an inline source_view (breaking);
  chain a source node via `input` instead.
- All 38 nodester samples updated and verified end to end on Databricks.
- Adds ADR-0007 and a rewritten nodester spec reference.

Co-authored-by: Isaac
The local .claude directory (Claude Code commands/settings) should not be
part of the repository. Add .claude/ to .gitignore and remove the previously
tracked command files from version control. The files remain on disk locally
(now ignored), so they persist across branch switches.

Co-authored-by: Isaac
@haillew haillew requested a review from rederik76 as a code owner June 26, 2026 07:24
haillew added 2 commits June 26, 2026 17:30
Move the feature-test GitHub workflow and the pattern-samples validation
notebooks (validate_run_1..4 + validation_utils) out of this branch. They are
kept on a local-only branch and intentionally not published upstream.

Co-authored-by: Isaac
Rename the spec type, schema (spec_nodespec.json + nodespecSpec), transformer
(NodespecSpecTransformer), migration script, sample bundle (nodespec_sample),
pipelines, docs, and the data_flow_type value from "nodester" to "nodespec".
No behavioural change — purely a rename.

Co-authored-by: Isaac
@haillew haillew changed the title [BETA] feat(nodester): Unified dataflow spec [BETA] feat(nodespec): the single unified dataflow spec (replaces standard/flow/materialized_view) Jun 26, 2026
haillew added 2 commits June 29, 2026 13:30
Rewrite the nodespec transformer around a single snake_case -> camelCase key
map plus flat/recursive converters, replacing the per-context allowlist maps
and per-source-type branches. Builders now copy every non-structural config key
as passthrough detail. Output is unchanged (verified identical across all 38
samples and end to end on Databricks); ~593 -> 440 lines.

Co-authored-by: Isaac
haillew added 10 commits July 8, 2026 12:33
nodespec is the framework's unified spec format, so specs may now omit
data_flow_type entirely.

- DataflowSpecBuilder defaults data_flow_type to "nodespec" at read time,
  so type selection, metadata validation, schema validation, and
  transformation all treat a type-less spec as nodespec.
- main.json routes any spec without an explicit legacy dataFlowType
  (standard/flow/materialized_view) to the nodespec schema.
- spec_nodespec.json no longer requires data_flow_type; the const remains
  so that, when present, it must be "nodespec".

Co-authored-by: Isaac
… renumber ADR to 0008

Field syntax (discriminated config for IntelliSense + consistent nesting):
- Targets wire inputs via `input_flows`.
- Data quality is a nested `data_quality` object; quarantine is a nested
  `quarantine` object (mode + optional target). Sink targets stay flat.
- Sources allow an optional inline `python_transform` (transformer already
  supports it).

Transformer reads the new field names. migrate_to_nodespec.py emits the new
shape, fixes historical-snapshot conversion (no spurious source node) and
table-migration source_details casing, and writes configs in a canonical field
order (idempotent normalise for existing nodespec specs).

Samples reordered to the canonical field order. Reference doc, feature-changes
doc updated.

Renumber ADR 0007 -> 0008 (0007 is taken by scripted-versioned-docs on main),
with an amendment mapping the original syntax to the shipped names.

Co-authored-by: Isaac
docs/nodespec_feature_samples_changes.md was an internal implementation/notes
doc that shouldn't live in the public repo. The authoritative user-facing
reference remains docs/source/dataflow_spec_ref_main_nodespec.rst and the
decision record docs/decisions/0008-unified-nodespec-dataflow-spec.md.

Co-authored-by: Isaac
The nodespec schema's $defs identifiers (sourceDeltaConfig, dataQuality,
inputFlows, …) were camelCase while everything a nodespec spec author writes is
snake_case. These names are schema-internal ref targets — they never appear in an
authored spec — but renaming them to snake_case makes the nodespec schema fully
self-consistent.

- Renamed all nodespec $defs keys and their internal #/$defs refs to snake_case.
- Updated main.json's external ref (#/$defs/nodespecSpec -> nodespec_spec).
- The external ref out to definitions_main.json#/definitions/cdcSettings is
  unchanged (that def's name is definitions_main's concern). Legacy standardSpec/
  flowsSpec/materializedViewsSpec refs are unaffected.

No behavioural change: authored specs and the transformer are untouched; 37/37
nodespec samples still validate.

Co-authored-by: Isaac
recursiveFileLookup was the last camelCase field an author writes in a nodespec
spec. Renamed to recursive_file_lookup in the schema and samples; the transformer
maps it back to the backend's `recursiveFileLookup` at build time (added to the
snake->camel key map), and the conversion script snakes it during migration.

Remaining camelCase in the schema is deliberately unchanged: JSON Schema keywords
(additionalProperties, allOf, ...), literal Spark/Auto Loader option keys inside
reader_options, framework-wide identifier values (source_type cloudFiles/
batchFiles/deltaJoin, disableOperationalMetadata), and the external cdcSettings
ref.

Co-authored-by: Isaac
Authors now write snake_case for nodespec's own enum values:
- source_type: cloudFiles -> cloud_files, batchFiles -> batch_files,
  deltaJoin -> delta_join (delta/kafka/python/sql unchanged).
- config_flags: disableOperationalMetadata -> disable_operational_metadata.

Nodespec-only: the transformer maps these back to the backend's expected values
(cloudFiles / disableOperationalMetadata) on output, so the backend and the
legacy standard/flow/materialized_view formats are unchanged. The conversion
script snakes them during migration.

Left as-is (not nodespec-owned): reader_options keys AND values are literal
Spark/Auto Loader tokens (e.g. binaryFile, addNewColumns, cloudFiles.format);
the engine reads them verbatim.

Co-authored-by: Isaac
Matches the schema/samples: source_type values are now cloud_files / batch_files
/ delta_join (delta/kafka/sql/python unchanged).

Co-authored-by: Isaac
…_type

Within target_delta_config, an intra-object if/then/else keyed on table_type now
enforces which fields are valid:
- table_type=mv: sql_path/sql_statement/refresh_policy/table_details allowed;
  cdc_settings/cdc_snapshot_settings/table_migration_details/once rejected.
- table_type=st (or omitted): the reverse.

table_type and the gated fields living in the same object is fine — if/then
conditions on one property and constrains its siblings. Editors now flag, e.g.,
sql_path on a streaming table or cdc_settings on a materialized view. 37/37
samples still validate.

Co-authored-by: Isaac
Extend the if/then pattern to other genuine field dependencies:
- source_delta_config: starting_version_from_dlt_setup=true requires
  cdf_enabled=true.
- target_delta_config: cluster_by_columns and partition_columns are mutually
  exclusive.
- snapshot_cdc_config: periodic snapshots forbid inline source_type/source
  (they read an upstream source node); historical snapshots require them; a
  file source requires path+format; a table source requires table.

Sinks are intentionally left flat/ungated for now (the sink redesign is
deferred, and target_type lives at the node level rather than in the sink
config). 37/37 samples still validate.

Co-authored-by: Isaac
…n exclusivity

Reflects the schema's conditional validation: streaming-table-only vs
materialized-view-only target fields are gated on table_type, and
partition_columns / cluster_by_columns are mutually exclusive.

Co-authored-by: Isaac
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.

[FEATURE]: nodespec — the single unified dataflow spec (replaces standard/flow/materialized_view)

1 participant