Shared source AST standard (discovery + lineage types) - #84
Open
matthewmoorcroft wants to merge 2 commits into
Open
matthewmoorcroft wants to merge 2 commits into
matthewmoorcroft wants to merge 2 commits into
Conversation
PR A of a two-PR split: the DISCOVERY side only. Defines the shared, source-neutral standard that both ADF and Airflow map onto, with zero convert/IR-Activity coupling. The convert->package lineage plumbing is deferred to the stacked follow-up PR. - models/ir.py: the five standalone, source-neutral lineage TYPES only -- DataAsset, ControlEdge, DataEdge, MotifAnnotation, Lineage. The Activity-base fields (data_reads/data_writes/motif_id), the MotifActivity base-field change, and Pipeline.lineage are NOT here (deferred). MotifAnnotation's docstring no longer references the deferred Activity.motif_id. - lineage.py: only the neutral primitive cores control_edges_from_calls, _match_assets, data_edges_from_endpoints (task_key strings + DataAsset only, no Activity/Pipeline import). The IR-Activity-facing build_* layer is deferred. - ir_serde.py: only the neutral helpers data_asset_to_dict / data_asset_from_dict / lineage_to_dict. No Pipeline.lineage or Activity-base emission and no MotifActivity change (deferred). - models/discovery.py, discovery_lineage.py, discovery_serde.py: the full shared discovery AST -- SourceGraph/SourceNode/ContainerNode/GapNode + ScheduleSpec/ ParameterSpec/PolicySpec/SourceDependency, the neutrality fields (run_condition, default_policy, run_timeout_seconds, open best-effort data-asset vocabulary), source-neutral graph lineage derivation, and serde. - tests: test_discovery_model.py + test_discovery_lineage.py only. No changes to bundler/dab_writer.py. Rebased onto main so the airflow-loader split (#54/#55) is not spuriously reverted. Co-authored-by: Isaac <no-reply@databricks.com>
…ineage (#61) with_graph_lineage's docstring referenced :func:`flowx.lineage.with_lineage`, which lives only in the stacked conversion PR -- the discovery-only PR's reduced lineage.py has just the neutral cores. Reword to describe the behaviour in place (pure; attaches via dataclasses.replace) so this PR references nothing external to it. Docstring only; no import, call, or behaviour change. Co-authored-by: Isaac <no-reply@databricks.com>
This was referenced Sep 15, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
This PR establishes the source-neutral discovery contract that ADF and Airflow can both map onto before Databricks IR conversion. It adds the shared source AST, shared lineage vocabulary, JSON round-trip helpers, and source-neutral lineage derivation primitives. It is additive infrastructure only: this PR does not wire either source, change
convert, or change packaging.Shared source AST schema
SourceGraphOne source workflow (ADF pipeline or Airflow DAG):
name,source, optionaldescriptionparametersandvariables, each keyed toParameterSpec {type, default}schedule: ScheduleSpec {kind, expression, timezone, extensions}default_policy: PolicySpec {timeout_seconds, max_retries, retry_interval_seconds, extensions}run_timeout_secondsfor a whole workflow runtags, ordered top-leveltaskslineageproperties,extensions, and optional verbatimrawfor source-specific detailSourceNodeOne source task, kept source-faithful:
source_id,task_key,concept,source, optionalname, optional verbatimnative_typedependencies[]run_condition, optionalpolicydata_reads[],data_writes[]propertiesand optional verbatimrawThe JSON form also carries a
node_typediscriminator soSourceNode,ContainerNode, andGapNoderound-trip to the correct class.ContainerNodeExtends
SourceNodewithbranches: {label: SourceNode[]}. The branch labels remain source-owned: for example ADFIfConditionmaps totrue/false,ForEachandUntiltobody,Switchto case values plusdefault, and an Airflow task group can usegroup. Empty branches remain present instead of being flattened away.SourceDependencyEach upstream edge is
{upstream, conditions[], resolved}.conditionspreserves per-edge source outcomes such as ADFSucceeded/Skipped;resolved: falserecords a missing upstream rather than dropping it.GapNodeis the lossless fallback: it defaultsconcepttogap, adds an optionalreason, and retains the source payload inraw.Shared lineage schema
DataAsset {signature, identity?, asset_type?, properties}—signatureis the always-present neutral descriptor;identityis a resolved physical identity ornull, never guessed;asset_typeis an open vocabulary that includes physical assets (table,file,volume) and logical/value hand-offs such as a dataset or XCom.ControlEdge {source_workflow, target_workflow, via_task_key, wait_for_completion?, resolved}— a cross-workflow invocation with unresolved callees retained.DataEdge {source_task_key, target_task_key, match_kind, match_key, identity?, asset_type?}— a proven producer/consumer hand-off, recording whether matching used strongidentityor fallbacksignature.MotifAnnotation {motif_id, member_task_keys[], display_name?, databricks_replacement?, notes[]}— a neutral annotation shape for a detected multi-task motif.Lineage {control_edges[], data_edges[], motifs[]}— stable concrete lists, including when empty.Neutrality choices
The schema distinguishes execution semantics that look similar but belong at different levels:
SourceDependency.conditionsis per edge;SourceNode.run_conditionis a downstream-node aggregate rule such as an Airflow trigger rule.SourceNode.policyis task-specific;SourceGraph.default_policyis the cascading workflow default;SourceGraph.run_timeout_secondsis the timeout for the whole workflow run.DataAssetis deliberately best-effort and open. Discovery may record physical, logical, or value-level data, may leaveidentityunresolved, and must not invent a physical identifier.raw,properties, and graph-levelextensions.How the AST and lineage feed
inventory.jsonThe end-to-end discovery path built on this foundation is:
SourceGraphper workflow and one node per source task, preserving dependencies, branches, raw payloads, strategy metadata, reads/writes, and workflow-call markers.ContainerNodebranch. It derives control edges from workflow-call markers and data edges fromdata_writes->data_reads, then attaches oneLineageblock to the graph.activities[], preserves dependency conditions/raw data additively, and serializes the graph's lineage beside those activities as the per-pipelinelineageblock.source,source_dir,pipelines,summary) once their mapper targets this AST.This PR supplies the shared models, serde, walkers, and edge-building core used by that path. The source mappers and
inventory.jsonwiring land in the follow-on discovery PRs; #84 itself intentionally imports noflowx.sources.adforflowx.sources.airflowmodules.Non-breaking scope
Verification
The PR's unit coverage exercises model/serde round trips, container traversal, control-lineage derivation, two-tier data matching, unresolved edges, and source-neutral import guards.
make testandmake fmtwere the acceptance commands for the branch.