Skip to content

feat(databricks): a build can be a Lakeflow pipeline whose expectations are the contract's DQ rules - #872

Merged
fas89 merged 24 commits into
mainfrom
feat/databricks-lakeflow-pipeline
Nov 13, 2026
Merged

fas89 merged 24 commits into
mainfrom
feat/databricks-lakeflow-pipeline

Conversation

@fas89

@fas89 fas89 commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

A Databricks product can now be built as a Lakeflow Declarative Pipeline (formerly Delta Live Tables), with the contract's data-quality rules as the pipeline's expectations. The pipeline graph's Data quality tab then shows each rule's passed and failed records, and fluid apply --mode amend-and-build prints the same counts.

What changed

  • Contract surface (founder, 2026-11-12): engine: lakeflow joins the 0.7.7 preview schema (stable 0.7.6 is unchanged; a 0.7.6 contract that uses it is told to move to 0.7.7). The build is pattern: embedded-logic with properties.sql, the SELECT of the dataset; upstreams are named by their Unity Catalog names.
  • The pipeline (iac/providers/databricks_lakeflow.py): one serverless databricks_pipeline per build, publishing into the expose's catalog and schema, with its source as a workspace file under the deploying identity's home (beside a dbt job's upload). forge-cli writes the source: CREATE OR REFRESH MATERIALIZED VIEW <table> with the expose's columns (types, NOT NULL, comments) and one CONSTRAINT <rule id> EXPECT (...) per row-level rule, then the contract's query, last in the file. A materialized view, not a streaming table: the query reads tables that a dbt CREATE OR REPLACE rewrites.
  • Rules → expectations, read once by the shared util/dq_checks.py::read_rules (feat(databricks): a dbt job runs its contract's DQ rules as dbt tests #871), as the dbt tests read them:
    • completeness → col IS NOT NULL; valid_values → the column's validationRules (IN, RLIKE, range comparisons), NULL passing;
    • uniqueness, freshness, and a valid_values rule whose column carries labels.references: <product id>.<column> need the whole table: one PRIVATE materialized view per check in the same pipeline (never published), whose one expectation is named after the rule. A reference resolves the referenced product's table through the workspace (fluid.workspace.yaml, FLUID_UPSTREAM_CONTRACTS), as an embedded-SQL build resolves consumes[], whether or not consumes[] lists that product;
    • schema → the declared column list itself (the update fails when the query does not give those columns and types);
    • refused: accuracy/anomaly/drift with no expression, a rule with no allowed values, a threshold an action cannot carry, two rules with one id (Lakeflow refuses two expectations of one name at the first update).
  • Severity (my call, as asked): info and warn → EXPECT (row kept, counted); error → ON VIOLATION DROP ROW (row never published, counted as dropped); critical → ON VIOLATION FAIL UPDATE (update fails, the table's write rolls back). No quarantine table: the rejected rows would sit outside the contract's access policy. A whole-table check has no row of the product to drop, so error only counts there; a critical one fails the update after the table was written (Lakeflow runs a check view after the table it reads). Uniqueness is checked by a private view: SELECT <key>, count(*) ... GROUP BY <key> with EXPECT (_fluid_rows = 1), NULL keys left out as fluid test and dbt's unique leave them out.
  • Coexistence (founder): the pipeline owns its table: no databricks_sql_table, its comments are the definition's, its readers get SELECT on the product's schema (exists before the first update; a precondition fails the plan if the schema holds any other table or view), and its table and column tags are set after each update forge-cli runs. A tag the contract no longer declares is removed only when the deploying identity set it (the tag's updated_by), so a steward's tag and an inherited one stay. Refused for now: masks, row filters, write grants, partitions, lifecycle.expire, another writer, a schema shared with another table. Not set up on a pipeline's table yet, each with a fluid validate warning: Unity Catalog keys (a materialized view refuses ALTER TABLE ADD CONSTRAINT), data quality monitoring (feat(databricks): data quality monitoring from a contract's freshness and completeness SLOs #864), its metric view (feat(databricks): Unity Catalog metric views from exposes[].semantics #865) and its place in the Genie space (feat(databricks): a Genie space for a data product (extensions.databricksGenie) #863, feat(databricks): one Genie space across deploying identities (parentPath); plan, diff and generate iac show it #869). The last three are set up at or right after tofu apply, and the pipeline creates its table at its first update, after that; a product whose tables are all a pipeline's gets no Genie space and no refusal.
  • Running (founder): fluid apply --mode amend-and-build starts an update of the pipeline this apply's OpenTofu state holds (by its fluid_product / fluid_build tags, as feat(databricks): amend-and-build runs the dbt job now and waits for it #861 finds a dbt job), waits, prints each rule's counts from the update's event log, and fails on a failed update with Lakeflow's error, less the row a failed expectation quotes (replace-and-build: a full refresh). amend and create-only only provision; a continuous pipeline is not started, but its tags are set once its table exists. A triggered pipeline sets pipelines.numUpdateRetryAttempts to 0 (found live: Lakeflow retried a failed update by itself and kept spending compute after the apply had failed).
  • fluid verify now compiles a Databricks expose from its contract file, as apply does (found live: verify expected 1 tag on a pipeline table that carries 10).
  • One pipeline per product build (founder): measured first on Free Edition, two pipelines exist side by side and a second update starts while the first runs.
  • Shared with feat(databricks): amend-and-build runs the dbt job now and waits for it #861's job runner: the state scan, the build selection, the Ctrl-C/SIGTERM guard and the two "nothing was started" error paths are one implementation in build_runners/_databricks_job_run.py, used by both runners (ripwire flagged the twins; the job runner prints what it printed before and its tests pass unchanged).

Industry pack

Under the industry-repo rule: Agenticstiger/industry-models#4 (merged, 8d82512b) made the pack's rules readable as written: the 57 enum constraints were A | B | C (forge-cli reads comma lists, as FLUID's own example writes them), and dp009's smart_meters_nonnegative_energy named five columns with no allowed values (now range >= 0 validationRules). The 24 *_ref rules stay as written: their columns carry labels.references. (A sibling session fixed the pack's generator to write the same form in industry-models#6.)

Borrow-before-build

  • Checked locally: engines/dbt/_test_mapping.py and providers/quality_engine.py (rule readers), databricks_jobs.py (workspace upload and run seam), build_runners/_embedded_sql_io.py (consumes resolution), build_runners/dlt/ (dlthub ingestion, not Lakeflow: nothing to reuse). The shared reader util/dq_checks.py comes from the dbt-tests session; this branch uses it.
  • Searched: dlt-meta/sdp-meta (metadata-driven DLT with DQE JSON and quarantine; license not Apache), databrickslabs/dqx v0.17.2 (ODCS → DQX checks: the mapping table followed here), datacontract-cli v1.2.9 (DQX exporter; no DLT exporter), Lakeflow's own expectation patterns (validation views for keys and references), the provider's databricks_pipeline docs, the event-log docs. No project emits Lakeflow expectations from a data contract.
  • Reuse: adapt the pattern (DQX mapping, Lakeflow validation views, the dbt job runner's polling and interrupt handling, now shared); no new dependency.

Validation

  • Gates, with CI's own commands on the branch head: ruff check fluid_build/ tests/, the dead-code gate, black==24.10.0 --check, the product guardrail (self-test, then the run), license headers, lint-imports (6 + 1 contracts kept), detect-secrets-hook 1.5.0 on the changed files (clean after a line-number refresh of the baseline), and scripts/mypy_strict.py.
  • Tests: the Databricks, runner, verify and tofu validate suites: 1651 passed, 24 skipped on the head (one stand-in-workspace test lost its connection to the stand-in server under load and passes alone). The fake-workspace e2e files pass 59/59 at -n 2; two of them flaked under -n 6 load and pass alone.
  • Full suite, CI's way (-n 6 --dist loadscope --ignore=tests/perf, tofu off PATH, pytest-randomly seed 20261112), then tests/perf serially: 35753 passed on the head (35b9490). The 9 failures and 2 collection errors are the shared venv's known baselines (lazy-core ×2, dlt dotenv ×4, the two tests/test_ci_* ImportErrors), two that fail the same way on pristine main (a test that needs dbt on PATH, and the v0.24.0 compat gate, which CI skips), and one load flake (a Snowflake MCP test whose local stand-in refused a connection; it passes alone 3/3). tests/perf: 41 passed.
  • ripwire --quality-delta (the branch diff applied to a fresh origin/main worktree). What stays:
    • run_builds_from_args complexity 110 → 114: the lakeflow branch shares the dbt job branch.
    • verify_databricks_expose 16 → 19: the pipeline's "created by its first update" wording.
    • Test-idiom clones with unrelated tests, and short-horizon churn from the parallel Databricks PRs.
    • The shape of run_job_build / run_pipeline_build, and rows inside feat(databricks): a dbt job runs its contract's DQ rules as dbt tests #871's dq_checks.py.
  • Live, Free Edition (manual; scratch catalog fluid_iactest_ldp, dropped after each run; nothing left). The 2026-11-12 row in HONESTLY_TESTED has the first run: counts per rule as promised, tags, verify, a critical failure, and the no-retry setting. 2026-11-13, rebased on feat(databricks): data quality monitoring from a contract's freshness and completeness SLOs #864 and feat(test): Databricks live checks, a Unity Catalog results table and an AI/BI dashboard #867:
    • The plan held no data quality monitor for a product with a completeness rule, and the views-and-tables precondition passed.
    • The update completed in 54 s with the same counts; verify passed all 10 tags.
    • After site_id's sensitivity was removed from the contract, the second apply (plan +0 ~0 -0) removed the column's tag, which the deploying identity had set, and kept the 8 table tags.
    • On a scratch schema, through the real entity-tag-assignments API, updated_by was the SCIM userName the build phase compares it with.

Reviews

One gate round; each review's findings were fixed and re-checked.

  • /security-review (on the rebased branch diff; an Opus reviewer, then an Opus false-positive filter per candidate): no HIGH or MEDIUM finding. One candidate: a failed critical expectation's error quotes the row that broke it ("Input data: '{...}'. Output record: '{...}'"), and fluid apply printed it to the terminal and CI log. Filtered to LOW, 5/10 (a contract author controls the query and its grants anyway, and masked tables are refused), below the report's cutoff; fixed anyway (ca082e7f). A follow-up pass over the commits after it found nothing. An earlier pass, before the rebase, found two issues below the cutoff, both fixed: a query ending in an unclosed /* swallowed forge-written text up to a */ in a rule description, so the rest ran as a second statement (the query now comes last in the file, and an open comment, string or backtick name is refused, lexed as Spark lexes it); readers' schema-level SELECT covered other teams' tables in an adopted schema (a precondition now fails the plan unless the schema holds only the pipeline's table).
  • /world-class-inspection (Diff mode, delivery review): no P0 or P1. Four P2, all fixed: a dbt build naming the expose beside a lakeflow build crashed the compile with a KeyError (now refused as another writer); the tag sync read a column the table no longer had and failed a completed update; fluid verify told a pipeline's table to re-run plain fluid apply for its tags; the data-loss gate's count of a pipeline's removal had no test.
  • /defect-scan (Diff mode): two Medium, fixed: two rules with one id gave the pipeline two expectations of one name, which Lakeflow refuses at the first update; the tag keys forge-cli managed were recorded in TBLPROPERTIES, and a refresh of a changed definition dropped that record, so a label the contract stopped declaring stayed on the table forever (the record is gone; removal goes by the tag's updated_by, live-checked). Seven Low, fixed (a continuous pipeline's tags never set; Ctrl-C during the start request left an update running; raw strings lexed with escapes; malformed event payloads raised; a rule's own severity ignored; failing warn rules marked as errors; the precondition missing views; verify's wording for a missing pipeline table; the USE CATALOG notice missing a pipeline schema's grant). The re-check (one auditor on the findings and the fix diff) found both Medium fixed and no new defect, plus one Low, fixed: the tag sync read only the first page of a tag list.
  • At the rebase onto feat(databricks): data quality monitoring from a contract's freshness and completeness SLOs #864: tofu validate failed for a lakeflow build with a completeness rule (the data quality monitor named a databricks_sql_table that a pipeline's table does not have); fixed with a test that fails without it.

Notes for the merge

Follow-ups (not in this PR)

  • Data quality monitoring, the metric view and the Genie space on a pipeline's table: set them up after the pipeline's first update (and check live whether the schema-level monitoring scan covers a materialized view).
  • Unity Catalog keys in the pipeline's definition (Lakeflow takes PRIMARY KEY / FOREIGN KEY constraints there), and masks and row filters in the definition.
  • A stand-in-workspace (_fake_databricks.py) tier for pipelines; a cron trigger; quarantine as an opt-in.
  • Spark errors other than expectation violations (a cast error, say) can still name one offending value when printed; the dbt job runner prints them the same way, so it is one change for both.
  • fluid genie apply (feat(databricks): fluid genie apply, one Genie space over several data products #870) puts a pipeline's table in an estate space (it runs after the products are built), but shares the space (CAN_RUN) only with principals holding a table grant, and a pipeline's readers hold SELECT on its schema: a reader of only a pipeline's table is not shared the estate space yet.
  • A Genie space left as it is (a product of pipeline tables only) keeps its last CAN_RUN shares until a later apply converges it: metadata only, since Genie runs questions on the asker's own grants. A converted table's old metric view is not dropped.

fas89 added 23 commits November 13, 2026 01:29
engine: lakeflow is a Databricks Lakeflow Declarative Pipeline: pattern
embedded-logic, properties.sql the SELECT of the dataset an expose binds.
Stable 0.7.6 is unchanged, so a contract opts in with fluidVersion 0.7.7
(founder decision 2026-11-12).

util/contract.py: is_lakeflow_build, and lakeflow joins
EXTERNALLY_RUN_ENGINES, so no local provider, planner or runner ever runs
its SQL; only the pipeline does.
The shared reading of contract dq rules (read_rules -> DqCheck) that the
Databricks dbt-tests work is landing. Copied unchanged so the Lakeflow
emitter can be built against it; this commit is dropped at the rebase onto
main once that branch has merged, and main's copy is kept.
…s expectations

A build with engine lakeflow is one serverless Lakeflow pipeline per build,
publishing the expose's table into the product's own catalog and schema.
forge-cli writes the pipeline's source around properties.sql: CREATE OR
REFRESH MATERIALIZED VIEW with the expose's columns (types, NOT NULL,
comments) and one expectation per row-level dq rule, so the pipeline
graph's Data quality tab shows each rule's passed and failed counts.

- Severity: info and warn are EXPECT (kept, counted), error is ON VIOLATION
  DROP ROW, critical is ON VIOLATION FAIL UPDATE. No quarantine table: the
  dropped rows would sit outside the contract's access policy.
- uniqueness, freshness and a valid_values rule whose column carries
  labels.references need the whole table: one PRIVATE materialized view
  per check in the same pipeline (never published), whose one expectation
  is named after the rule. A reference resolves to the referenced
  product's table through the workspace (fluid.workspace.yaml,
  FLUID_UPSTREAM_CONTRACTS), as embedded-SQL builds resolve consumes[].
  schema is the declared column list itself.
- The pipeline owns its table (founder decision 2026-11-12): no
  databricks_sql_table, its readers get SELECT on the product's schema
  (exists before the first update), and its table and column tags are set
  after each update fluid apply runs (they survive refreshes, measured on
  Free Edition). Masks, row filters, write grants, partitions, expire,
  another writer and a shared schema are refused for now.
- A materialized view, not a streaming table: the query reads tables a dbt
  CREATE OR REPLACE rewrites. Manual or continuous trigger; cron refused.

The rule-to-check mapping follows DQX (databrickslabs/dqx); the whole-table
checks follow Lakeflow's own expectation patterns (a validation view per
primary-key and referential check); the upload layout follows Databricks
Asset Bundles, like the dbt job's.
… reports its rules

Under --mode amend-and-build (replace-and-build: a full refresh) a
lakeflow build starts an update of the pipeline this apply's OpenTofu
state holds for it (by the fluid_product / fluid_build tags, as the dbt
job runner finds its job), waits for it, and prints each rule's passed,
failed and dropped records from that update's event log: what the
pipeline graph's Data quality tab shows. A failed update fails the build
with the event log's errors. After a completed update the table's and its
columns' tags are set as the contract declares them, and the keys the
previous run recorded (table properties fluid.tags, fluid.column_tags) but
the contract no longer has are removed; a tag that cannot be set fails
the build. amend and create-only only provision, and a continuous
pipeline is not started.

Found live on Free Edition: Lakeflow retried a failed triggered update by
itself (cause RETRY_ON_FAILURE), and the retries kept running after the
apply had failed. A triggered pipeline now sets
pipelines.numUpdateRetryAttempts to 0: the same rows fail the same rule.

The polling, interrupt handling and printing reuse the dbt job runner's
(_databricks_job_run.py, after databricks-sdk-py's waiters); the update
states and event-log fields are the Pipelines API's, read live.
fluid verify called the Databricks inspector outside the contract file's
contract_source block, so the emitter's pass it checks against could not
resolve a Lakeflow reference check's product from the workspace: the
pipeline dropped out, and verify expected 1 tag on a table that carries
10 (found live). It now compiles as fluid apply does.
The unreleased Added entry, the AGENTS.md paragraph beside the dbt job
runs, and the unit, tofu validate and manual live (Free Edition,
2026-11-12) rows.
…op flat

ripwire --quality-delta flagged what the Lakeflow commits grew:
_compile (complexity 16 to 19), managed_objects (14 to 33), the build
loop's second copy of the job branch, _merge_pipelines as a clone of
_merge_jobs, and _rules at 42. The table loop is _emit_tables, verify's
pipeline readers and tags are _pipeline_readers_and_tags, one _merge_part
takes jobs and pipelines, a lakeflow build and a dbt job build share one
branch of run_builds_from_args, and _rules is four small helpers. No
behaviour change: 1166 Databricks tests pass as before.
…rsion 0.7.7

A 0.7.6 contract with engine: lakeflow got jsonschema's anyOf message,
"'lakeflow' is not valid under any of the given schemas", which does not
say that the engine is in the 0.7.7 preview. format_schema_error now names
it and the fix.
Security review (both below the report's cutoff, fixed anyway):
- A query that left a /* comment open swallowed what forge-cli wrote
  after it, up to a */ inside a rule's description, so the text after
  that ran as a second statement (a dataset anywhere the deploying
  identity can write) and a check view vanished. The check views now come
  first and the contract's query last in the pipeline file, and a query
  that leaves a comment, string or backtick name open is refused
  (databricks-lakeflow-query), lexed as Spark SQL lexes it.
- Readers' SELECT on the product's schema also covered tables other
  teams keep in a schema the product adopted. Each such grant now has a
  precondition over data.databricks_tables: the plan fails, naming the
  other tables, unless the schema holds only the pipeline's table
  (databricks-lakeflow-schema-not-owned). Live: the pipeline's own
  materialized view passes it; a foreign table fails the plan with one
  clean line (bare names: the CLI's redactor masks a.b.c as a JWT).

Delivery review (P2):
- A dbt build naming the expose in its outputs beside a lakeflow build
  with none crashed _compile with a KeyError; now databricks-lakeflow-
  writers.
- The tag sync read a recorded column the table no longer has, failing
  a completed update; such a column took its tags with it and is skipped.
- fluid verify told a pipeline's table to re-run plain fluid apply for
  missing tags; it names --mode amend-and-build (ManagedTable.
  pipeline_written).
- The data-loss gate counting a pipeline's removal (and not its file and
  folder) is pinned by a test.

Also: a reference whose product consumes[] does not list resolves to the
product's one Databricks expose with that column (dq_checks now reports
no expose id for it), and the validate hint no longer writes a literal
fluidVersion (a repo guard forbids it).
…reconciling

Keys go on a table with ALTER TABLE ADD CONSTRAINT, which a Lakeflow
pipeline's materialized view refuses, so every apply would print a failed
key. Until the pipeline's definition carries them (Lakeflow takes PRIMARY
KEY and FOREIGN KEY constraints there), fluid validate says they are not
declared and post_apply leaves the table out.
A defect scan of the Lakeflow commits found two blocking defects and
seven small ones.

- Two dq rules with the same id gave the pipeline two constraints of one
  name, which Lakeflow refuses at the first update. The emitter refuses
  them (databricks-lakeflow-rule-id) and names the id.
- The tag keys forge-cli manages were recorded in TBLPROPERTIES, and a
  refresh of a changed definition dropped that record, so a label the
  contract stopped declaring stayed on the table. The record is gone: a
  tag is removed when the contract no longer declares it and the
  deploying identity (SCIM Me) set it. A steward's or an inherited tag
  stays; an unreadable identity removes nothing and says so.
- A continuous pipeline is not started by amend-and-build, so its tags
  were never set. They are now set once its table exists.
- Ctrl-C during the start request left a running update behind: the
  interrupt guard now covers the request as well as the wait.
- The lexer read r'...' as a string with backslash escapes; Spark reads
  a raw string without them. A malformed event-log payload no longer
  raises, a rule's own severity (rule_severity) wins over the check's,
  and a failing info/warn rule is marked as a warning, not an error.
- The schema precondition counts views as well as tables, the USE
  CATALOG notice counts a pipeline schema's SELECT grant, and verify
  says a missing pipeline table is created by the pipeline's first
  update.
The reader is a snapshot of feat/databricks-dbt-dq-tests, which has moved
on (fce21b0): a reference to a product consumes[] does not list is no
longer refused, its expose_id is None. The pipeline's reference resolver
already takes that case (the product's one Databricks table with the
column); a test now pins it. Whichever branch merges second finds the
same file.
Found at the rebase onto main: data quality monitoring (#864) turns on
for a schema whose table has a freshness or completeness SLO or rule,
and creates the monitor after the schema's databricks_sql_table. A
pipeline's table has none (the pipeline creates it at its first update),
so a lakeflow build with a completeness rule failed tofu validate with
"Reference to undeclared resource".

A pipeline's table is left out of monitoring. Its rules are the
pipeline's expectations, so they say nothing; a freshness or
completeness SLO on it is a fluid validate warning, which
contract.dq.monitoring.enabled: false silences. Whether the schema-level
scan covers a materialized view is not verified yet (Databricks' docs
name only views and foreign tables as unsupported). The CHANGELOG also
says now that a pipeline's keys are not declared.
…output

Security review (LOW after filtering, fixed anyway): a critical rule is
ON VIOLATION FAIL UPDATE, and Databricks' EXPECTATION_VIOLATION error
quotes the record that failed it ("Input data: '{...}'. Output record:
'{...}'"). fluid apply printed that error, so a row of the table, or of
what its query read, reached the terminal and the CI log, which more
people read than the table (and a pipeline's table takes no masks yet).

Each printed error is now cut where it starts quoting a record, and says
that the update's event log on the pipeline's page shows it. The flow
and the violated expectation's name stay, and the per-rule counts are
printed as before.
Found by the fix round's re-check: _tags_on read the first page of the
entity-tag-assignments list only, while fluid verify follows
next_page_token. A declared tag on a later page was set again, and a
stale one there was never removed. It now reads every page the same way
verify does, and a token seen twice (or more than 100 pages) fails the
build as an unreadable tag list. The test stand-in pages its list and no
longer serves the tag record the review round removed.
…mbing

ripwire --quality-delta flagged the Lakeflow run path as a twin of the
dbt job runner, which #861 has since landed on main: the state scan
(191 tokens), the build selection, the Ctrl-C/SIGTERM guard and the two
"no run was started" error paths. They are one implementation now, in
_databricks_job_run.py: selected_builds, tagged_in_state (with a ref
maker per resource type), single_ref, client_or_none and
interrupt_guard. Both runners print what they printed before; the job
runner's tests pass unchanged.

What stays alike is the shape of run_job_build and run_pipeline_build
(the signature and the first four lines): the flows after it differ (a
job run, or a pipeline compiled, updated or only tagged).
…enie place yet

Found at the rebase onto #865 and #869: fluid apply creates a metric view
(semantics) and sets the Genie space up right after tofu apply, and a
Lakeflow pipeline creates its table at its first update, in the build
phase after that. CREATE VIEW over the missing table would fail the first
apply of a lakeflow product with semantics.

What is set up at or right after tofu apply now reads the tables that
exist by then (_tables_before_build): the data quality monitors, the
metric views and the Genie space leave a pipeline's table out, and
fluid validate warns about its semantics and its place in the space. A
product whose tables are all a pipeline's gets no space and no refusal
(a space with no table is refused otherwise). Setting them up after the
pipeline's first update is a follow-up.
The dbt-tests PR (#871, head c724ed7) reads a valid_values rule through
quality_engine.value_domain, as fluid test does, drops parse_enum and
parse_range, and adds DqRefusal.needs_history. The pipeline emitter only
uses read_rules and DqCheck, whose shape is unchanged; its tests pass on
this copy. Whichever PR merges second finds the same file.
…es stays

Found by the re-check of the previous commit: for a product whose tables
are all a Lakeflow pipeline's, _genie returns no space, and
databricks_genie.converge reads a missing space as the block removed. It
then trashed the space an earlier apply had made (a product converted to
lakeflow) and said extensions.databricksGenie no longer asked for it,
which was not so.

converge_output_ports now leaves such a space as it is and says so;
converge is not called. The condition is one helper,
_genie_waits_for_pipelines, which _genie uses too.
…till checked

Found by the re-check of the previous commit (LOW, fixed in the same
pass): for a product whose tables are all a Lakeflow pipeline's, _genie
returned no space before compile_space ran, so a malformed
extensions.databricksGenie block (an unknown key, say) passed fluid
validate until a plain table joined the product. compile_space now runs
over all the tables to collect its refusals; the space itself is still
none.
@github-actions github-actions Bot added security Security-related changes or reports cli Changes to the CLI surface or implementation tests Test coverage or test infrastructure changes needs-docs Pull request needs a linked docs update or justification labels Nov 13, 2026
@github-actions

Copy link
Copy Markdown

📄 Documentation Reminder

This PR appears to be missing a documentation reference. Our docs live in a separate repo.

Please update the PR description with one of:

  • Link a docs PR — check the "Docs PR linked" box and paste the URL
  • Mark as no docs needed — check "No docs needed" with a justification
  • Acknowledge docs TODO — check "Docs TODO" and create the docs PR before merge

See the Contributing Guide for details.

@fas89
fas89 merged commit d2ddd7c into main Nov 13, 2026
52 checks passed
@fas89 fas89 mentioned this pull request Nov 13, 2026
1 task done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Changes to the CLI surface or implementation needs-docs Pull request needs a linked docs update or justification security Security-related changes or reports tests Test coverage or test infrastructure changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant