Repository navigation
feat(databricks): fluid genie apply, one Genie space over several data products - #870
Merged
Merged
Conversation
…t a metric view An update named every data source the space no longer holds as a metric view, since anything other than the space's own tables counted as one: taking an expose out of a product printed "updated: metric views". Each data source leaving the space is now read in Unity Catalog. One that is still a table is said as a table; a metric view, or one that is gone, as a metric view, as before. The read only names the change, so a failed read never fails the update.
… a marker of its own So a space kept by another command can reuse the converge. The messages that named `fluid apply`, the contract's block or "one space per product" now read them from a Voice, set for the converge's duration (a ContextVar, reset after); the product's wording is unchanged byte for byte. Space.token overrides the marker, Compiled.voice carries the voice, ACCESS_LINE and require_warehouse are shared, and a found space whose marker is not a product's is named by its own marker in the left-alone note. The summary counts joins, and an update names a change of instructions.join_specs as "joins".
…a products
`fluid genie apply <contracts|globs|folders...> --estate <id>` creates or updates one AI/BI
Genie space over the Databricks tables of every contract given (founder decisions 2026-11-12:
this flag shape, and CAN_RUN only for the principals that read every table of the estate, since
the space describes them all). Each table carries what a product space says of it. The space
adds a join_spec for each foreign key the contracts declare between its tables, resolved by
databricks_keys.foreign_keys among the estate's own tables, so each join is the Unity Catalog
FOREIGN KEY fluid apply declares: MANY_TO_ONE from the referencing table, ONE_TO_ONE when the
key is that table's primary key, one join for a composite key, a second alias for a key to its
own table, and every column qualified by its alias. A reference outside the estate, or to no
single primary key, is said. One text instruction with a section per product; no measure
snippets.
The space reuses the product space's identity rule and converge (databricks_genie.py): it is
found by [fluid-genie-estate:<id>] in the deploying identity's own folder, updated with its
etag only when it differs, and converges to exactly the contracts given. Each run checks that
every table exists before it writes, and names the product to apply first. `fluid genie remove
--estate <id>` trashes it. Options: --title, --env (overlays), --dry-run. Refusals and API
failures are typed CLI errors (genie_estate_refused, genie_estate_failed) with the fix.
Measured on Free Edition 2026-11-12 and modelled in the stand-in: join_specs are kept as sent;
a self-join under two aliases and ONE_TO_ONE are accepted; an unknown relationship tag is
refused ("Failed to parse export proto"), and so is a 51st table ("A space can have at most 50
tables": databricks-agent-skills' 30 is stale); 60 sample questions and an instruction of
60,000 characters are accepted.
Borrowed: the join_spec shape and its rules from databricks/databricks-agent-skills
(skills/databricks-genie-agents/references/create-genie-agent.md); the many-to-one orientation
of apache/ossie (OSI core-spec relationships[], whose `from` is the many side); contract-file
globbing as `fluid publish` does it.
…ENTS.md and CHANGELOG The command, what the space holds beyond a product's own (the joins from the declared foreign keys), who gets CAN RUN, how it is found again, and removal; the product space's update now names a leaving table as a table.
… test helpers Prompted by ripwire --quality-delta on the estate branch. differences names a change from two small tables of serialized_space parts instead of a ladder of comparisons (complexity 18 -> under the bar), and parses the live space once (_held). The stand-in's whole-space limits (one text instruction, a known relationship tag, 50 tables) move out of _genie_check into _genie_limit_problem. Estate.joins replaces the CLI's own count. The Genie tests read their writes through _fake_databricks.writes_since, and the estate tests write contracts through _databricks_cli.write_contract, which now takes a path below the root.
…ne overlay line per contract Found walking the command's journeys. `fluid --help` did not list `genie`; it now sits under Integrations next to publish and market, and the help-style lint covers it. A pack read with --env printed the loader's "overlay_applied" once per contract (19 lines for the tower pack): the header now names the overlay once, a missing overlay still warns, and --log-level DEBUG shows every line. The header no longer prints "0 table(s), 0 join(s)" before a refusal: the counts are shown only when the space compiled.
… note names its command Found by the diff review. The estate dropped the warnings databricks_keys.compile_keys gives (a primary key on a column without required: true, a labels.references that names no product and column), so a parent whose key column is nullable was reported as tagging no primary key, and a garbled reference made no join without a word. The estate now says both, as fluid apply says them of its Unity Catalog keys, and the no-key note names both conditions. The note on direct CAN_EDIT/CAN_MANAGE holders named `fluid apply` in an estate run; it reads the Voice.
…; remove names itself Found by the diff defect scan. - `fluid genie apply` skipped the gate `fluid apply` runs, so a schema-invalid contract died as "Unexpected error" (exit 2) and a pre-0.7 one was not refused. Each contract now passes plan.py's _gate_contract_for_plan_or_apply (genie_contract_invalid, contract_version_unsupported), and a file the loader cannot read is contract_load_failed; both name the file. - A glob followed symlinked folders: with two symlink loops in a pack, `**` never ended. Globs now expand as `fluid publish` expands them (one level per `**`); a folder argument is the recursive search, past no symlink. A glob whose match is an ignored folder itself (`*` on `target/`) is skipped as the docstring says. - A failed `fluid genie remove` said to re-run `fluid genie apply`; it names itself. - The product's "an apply in the next minute" note had become "another apply": the default Voice now keeps it word for word, and a test pins it.
… Genie adds its own
Measured on Free Edition 2026-11-13 over the 19 tower-management tables, whose Unity Catalog
foreign keys fluid apply declares. Whenever a table enters a space (a create, or an update
that adds one), Genie adds a join for each foreign key that touches it, with its own id and
the parent aliased <table>_2, beside the joins it was sent: 24 sent, 48 held. An update with
the same tables keeps exactly what it is sent. So the first run made the space, and the
second "updated: joins" it. Each write is now read back, and a space the service added to is
set back once and said ("Genie added N join(s) of its own ..."), so the next run finds no
change. The stand-in adds the same joins on a create and on an update that adds a table.
…s in the docs HONESTLY_TESTED gains the estate's unit (40), stand-in (1) and live Free Edition rows: the 19 tower-management products applied into fluid_iactest_estate, a cross-product question answered through the Genie Conversation API by the join the contracts declare, a second run with no change once Genie's own joins are taken out, removal, and the sweep (0 left). CHANGELOG and AGENTS.md say each contract passes fluid apply's gate and that a run takes out the joins Genie adds from Unity Catalog foreign keys.
…port in the estate tests
…contract paths ripwire --quality-delta flagged the estate's own new functions over the complexity bar: join_specs (27), contract_paths (18) and _refusals (16). Each now delegates one concern: _with_a_parent and _say_unjoined (which references can join, and what is said of the rest), _member_refusals (what one contract makes the estate refuse), and _matches and _folder_contracts (what one argument names). No behaviour change; the 40 estate tests pin it.
📄 Documentation ReminderThis PR appears to be missing a documentation reference. Our docs live in a separate repo. Please update the PR description with one of:
See the Contributing Guide for details. |
fas89
enabled auto-merge (squash)
November 12, 2026 23:42
Flagged by the code-quality bot on #870: remove() and Run(...)() ran inside assert statements, which python -O skips. Each call now runs on its own line and the assert checks its result (5 places; the bot named 3).
iac-tests.yml: the Databricks fake lane keeps both new tiers, the Genie estate's and the metric views'.
…ut keys After main's metric views (#865) the stand-in keeps a metric view among its tables with no constraints list, and _with_uc_joins read one for every data source of a new space: a KeyError on create (test_the_products_genie_space_holds_the_metric_view_the_apply_made). It reads constraints only where a table has them. The secrets baseline's line numbers move with the merge.
databricks_genie.py: #869's folder logic (_placed, _at_most_one, _check_folder, the move path, _found_again(space), _title_kept(space)) with the estate's Voice, Space.token and the read-back after every write (_keep_as_sent, on the move path's update too). #869's new messages read the Voice: one space per {scope}, the title setting, and the parentPath hint only where a space has that setting (Voice.folder_setting, empty for an estate). remove_estate takes #869's _placed(...)[0]. Docs and HONESTLY_TESTED keep both sides; the secrets baseline is main's with the line numbers refreshed.
fas89
added a commit
that referenced
this pull request
Nov 13, 2026
This was referenced Nov 13, 2026
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
fluid genie apply <contracts|globs|folders...> --estate <id>makes one Databricks AI/BI Geniespace over the Databricks tables of a set of data products, so one question can join them:
fluid genie apply tower-management/ --estate tower-management --title "Tower Management" --env databricks fluid genie remove --estate tower-managementIt follows the per-product space (#863) and reuses its compile helpers, its identity rule and
its converge:
allowed values and synonyms, the product's example questions as sample questions, and an
instruction section with its semantics, metrics and
agentPolicy.instructions.join_specs, one per foreign key the contractsdeclare between its tables. A foreign key is a column tagged
foreign-keywhoselabels.referencesis<product id>.<column>. feat(databricks): Unity Catalog primary and foreign keys from the contract #860'sdatabricks_keys.foreign_keysresolves it among the estate's own tables, so each join is the Unity Catalog FOREIGN KEY
fluid applydeclares.table's own primary key.
label) is reported.
CAN RUNgoes only to the principals that read everytable of the estate, because the space describes all of them. The others are named.
[fluid-genie-estate:<id>], only in thedeploying identity's own folder. It changes nothing on a second run and converges to
exactly the contracts given.
fluid genie removetrashes it.writes, and names the product to apply.
agentPolicyrules out question answering refuses the estate,and so does a set of more than 50 tables.
as a metric view.
Why
This is the follow-up #863 deferred: an estate space over several products, with joins from
the products' declared keys. The founder's design calls of 2026-11-12 were this flag shape
(not a manifest file or a pack reader), and CAN RUN only for the readers of every table.
Borrowed
skills/databricks-genie-agents/references/create-genie-agent.md:the
join_specsshape (a two-elementsqlwith a--rt=FROM_RELATIONSHIP_TYPE_*--tag),qualified columns, id and sort rules. Its "30 tables" is stale: 51 was refused live and
50 was taken.
core-specrelationships[]: the many-to-one orientation (fromis themany side).
databricks_genie.py(feat(databricks): a Genie space for a data product (extensions.databricksGenie) #863),databricks_keys.py(feat(databricks): Unity Catalog primary and foreign keys from the contract #860), andfluid publish'scontract globbing.
genie_spaces(the Go CLI's direct engine and its ownstate) and terraform-provider-databricks
databricks_genie_agent(v1.141.0, PrivatePreview, a typed shape that refuses
serialized_space).Validation
import-linter (6 contracts kept), product guardrail, and detect-secrets 1.5.0 on the changed
files are all clean.
passed. Two tests in
tests/cli/test_lazy_core_commands.pyfail identically on the basecommit; their
-Isubprocess imports the shared checkout'sfluid_build. The estate alone:40 tests.
tofu(offline provider mirror) and the real databricks-sdk 0.162.0against
_fake_databricks: on this tip the estate, product Genie and quality tiers passed (10). The full Databricks set (estate, Genie, Databricks, jobs, quality) passed 66 one rebase earlier, before feat(test): Databricks live checks, a Unity Catalog results table and an AI/BI dashboard #867 (fluid testlive checks) merged.databricks_only_forgetrack derives them, were applied with
fluid applyinto the scratch catalogfluid_iactest_estate(19 runs, rc 0, Unity Catalog keys declared).CAN RUNwent to the steward only;the analyst, who reads 16 of 19 tables, was named.
kWh?" was answered Basra Port, 1500, by a JOIN of
tenant_billingtosite_masteronsite_id.trashed the space, and a second remove found nothing to trash.
for each Unity Catalog foreign key touching it (24 sent, 48 held). The second run therefore
updated; each write is now read back and set back.
refused, and a self-join and ONE_TO_ONE are taken.
DROP CATALOG fluid_iactest_estate CASCADE: 0 catalogs and 0 spaces left.ripwire --quality-delta: exit 2 on 35 rows, judged and not gamed. They are test-helperidioms, the ContextVar idiom
iac/source.pyalready uses, parallel tests, andshort-horizon churn on the feat(databricks): a Genie space for a data product (extensions.databricksGenie) #863 code this follow-up had to extend. The new functions that
went over the complexity bar were split.
Docs
Docs PR linked
No docs needed
Docs TODO: forge_docs needs a "Genie space over several products" section next to the product space's on the Databricks page. It should cover:
fluid genie apply <contracts|globs|folders> --estate <id> [--title] [--env] [--dry-run]andfluid genie remove --estate <id>commands;foreign-keytag pluslabels.references);It is not written yet; the
needs-docslabel tracks it.Reviews
Both fix rounds were re-checked: no HIGH or MEDIUM.
garbled key was reported wrongly, and the share note named the wrong command. The CLI
journey walk found three UX defects (
fluid --helpdid not listgenie, oneoverlay_appliedline per contract, and zero counts before a refusal). All five are fixedand re-verified.
fluid genie applyskippedfluid apply's contract gate.folder that was walked,
removenamingapply, and one product message reworded.Follow-ups (not in this PR)
fluid genie(the docs live there).PR takes out the joins Genie adds, as it does for an estate.