Skip to content

bundle: delete libs/dyn; typed configuration with generated walkers - #7098

Draft
denik wants to merge 64 commits into
mainfrom
denik/typed-config-walkers
Draft

denik wants to merge 64 commits into
mainfrom
denik/typed-config-walkers

Conversation

@denik

@denik denik commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Changes

Bundle configuration (databricks.yml and its includes) was held as a dynamic value tree (libs/dyn), and every mutator converted the typed config.Root to that tree and back. On large bundles the conversion dominated command time (about 90 ms around every mutator on a 1000-job bundle). This PR deletes libs/dyn (and the Terraform converter tfdyn) and keeps the configuration only as typed Go structs.

  • Typed configuration. config.Root is the typed struct plus two side structures: pure ${...} references in fields that cannot hold a string (e.g. max_retries: ${var.n}) and a tree of source locations. libs/structs/structvar provides what the tree used to: decoding YAML/JSON straight into the structs (same warnings), a read-only View, writes, merge/override, and variable resolution with the same semantics and error text as dynvar. Paths are structpath everywhere, including diag.Diagnostic.Paths. generate commands write YAML with the new libs/structs/structyaml (identical output).
  • Generated walkers instead of reflection. Mutators use plain field access; where the whole configuration has to be traversed the code is generated by go run ./bundle/internal/validation: the required and enum walkers (only the paths that have rules), resource defaults, a strings walker (strings with ${, non-empty strings, forced-empty fields), and resource iterators in config (IterateAllResources/Permissions/Grants/ResourceStates, GetResource, PermissionsOf, Select, CopyResource). No pattern-based traversal of the typed configuration is left (structvar.ForEach is gone); View is only used where the data is dynamic: decode, merge/override, Python input and output, configsync, resolving arbitrary ${...} paths, JSON output.
  • Presence comes from ForceSendFields, never from locations. A value is present if it is non-zero, listed in its struct's ForceSendFields, or a pure reference; decoding records a written zero value in ForceSendFields. What this loses:
    • a written zero in a struct without ForceSendFields is indistinguishable from unset, e.g. job_id: 0 in run_job_task now warns as not set;
    • an empty mapping (presets: {}, resources: {}) is absent;
    • an explicit source: "" on spark_python_task/sql_task.file gets the GIT default on git_source jobs;
    • the pipeline development deprecation warning is not shown for development: true under mode: development (indistinguishable from the preset).
  • Root.GetLocationOf/GetLocationsOf keep main's names and return the closest location; ...Exact variants return where a value is written. GetLocation(path string) stays as on main.

Why

Measured on a 16-core Mac, 1000-job bundle (./task bench-1k, test server, wall time and peak RSS), main (4244322a26) vs #6954 vs this PR:

main #6954 this PR
bundle validate 11.2 s, 691 MB 1.15 s, 188 MB 0.49 s, 166 MB
bundle plan 13.8 s, 722 MB 1.65 s, 186 MB 0.75 s, 171 MB
bundle deploy 15.7 s, 720 MB 2.25 s, 277 MB 1.55 s, 238 MB

(User CPU for validate: 20.9 s → 1.56 s → 0.70 s. deploy with DMS at 1000 jobs fails on main: the test server caps a deployment version at 800 operations.)

Per component, ms for one bundle.Apply on the same bundle (bundle/internal/bundletest/component_benchmark_test.go, which only uses APIs that also exist on main; main additionally pays ~89 ms of conversion around every mutator):

main #6954 this PR
Load (YAML) 177 190 173
TranslatePaths 387 25 8.7
NormalizePaths 401 46 28
ResolveVariableReferences 541 24 2.2
Required 135 132 10.7
Enum 117 91 0.4
DropEmptyStrings 143 19 2.2
ExpandGlobReferences 150 16 14.7
RewriteWorkspacePrefix 121 17 2.2
TFOnlyReferences 98 19 2.1
SingleNodeCluster 128 2.5 0.8

The full acceptance suite takes the same time on all three, so the gain shows in commands on large bundles, not in the test suite (16-core Mac, interleaved runs: main 206-237 s, #6954 199-217 s, this PR 216-221 s).

Tests

Unit and acceptance tests. #7085 (merged) recorded main's behaviour in 21 bundle validate edge cases (acceptance/bundle/validate/{yaml_anchors,yaml_duplicate_keys,variables,targets,includes,nulls,references}); this PR updates the expectations of 19 of them, and Badness marks wrong behaviour:

  • fixed (Badness removed): timestamps nested in a complex default or used as a shorthand work; an inline << mapping is accepted; resources: null is accepted and targets: false is a warning with a location instead of an internal error; diagnostics of includes after a failed merge are reported;
  • changed, not wrong: the location of a duplicated app resource follows the entry that is kept; a reference to an unset struct resolves to the fields that are set;
  • wrong (Badness added, to fix): scalar target-variable overrides inherited through a << merge are not applied; an alias of a complex default is rewritten in place; a duplicate << key and a scalar among merged maps are accepted; a duplicate default key in a target variable becomes the default value; a reference that resolves to the wrong type leaves "" instead of dropping the element; the location of an unknown field in a complex variable default is lost; an include cannot clear a preset with ""/0/false.

Difference from #6954

#6954 is the first version of this change and shares its commits: same typed model, same deleted packages. Its mutators still traverse the typed configuration through reflection (structvar.View lookups and pattern-based ForEach/Walk), so it is 7-10x faster than main on large bundles but not faster for every component: Enum and Required barely improve and Load is slower than main (table above). This PR replaces those traversals with generated walkers and plain field access, which makes every component faster than main except Load, which is at parity, and validate/plan another 2.2x and deploy 1.45x faster than #6954. It decides presence by ForceSendFields only, where #6954 also used locations. Size: #6954 changes 7.7k/9.3k lines (added/removed) of hand-written non-test Go; this PR 9.8k/10.9k, plus 27.5k lines of generated walkers.

This pull request and its description were written by Isaac.

@github-actions github-actions Bot added DABs DABs related issues PyDABs AIR Databricks AI Runtime CLI labels Oct 17, 2026
@denik
denik force-pushed the denik/typed-config-walkers branch from cb28c17 to b955c4d Compare October 18, 2026 06:43
denik added a commit that referenced this pull request Oct 18, 2026
Co-authored-by: Isaac <no-reply@databricks.com>
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: d95e7b1

Run: 39461543158

Env ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 281 41 4:32
✅​ aws-windows-latest-4core-16gb 283 39 5:35
✅​ azure linux-2core-8gb 280 41 4:15
✅​ azure-windows-latest-4core-16gb 282 39 3:50
✅​ gcp linux-2core-8gb 281 41 4:17
✅​ gcp-windows-latest-4core-16gb 283 39 3:47
Top 4 slowest tests (at least 2 minutes):
duration env testname
5:32 aws-windows-latest-4core-16gb TestAccept
3:44 azure-windows-latest-4core-16gb TestAccept
3:43 gcp-windows-latest-4core-16gb TestAccept
2:01 aws linux-2core-8gb TestAccept

arushiagarwal-db pushed a commit to arushiagarwal-db/cli that referenced this pull request Oct 19, 2026
…atabricks#7085)

## Changes
Twenty-one new `bundle validate` acceptance tests, each pinning current
behaviour for a configuration edge case. They are grouped by topic under
`acceptance/bundle/validate/` (`yaml_anchors/` for anchors and merge
keys, `yaml_duplicate_keys/`, `variables/`, `targets/`, `includes/`,
`nulls/`, `references/`); ten existing tests on the same topics moved
into the groups.
- merge keys and aliases in `targets.*.variables` (inherited through a
target or root `<<`, aliases of a complex default, duplicate and
malformed `<<`, inline mappings),
- duplicate keys (with the processed JSON), timestamps as shorthands or
nested in complex defaults,
- references that resolve to the wrong type inside lists and maps,
- includes overriding fields with `""` / `0` / `false`, diagnostics
after a failed include merge,
- `resources: null`, `targets: false`, `schedule: null`,
- diagnostic locations for unknown fields in complex variables and
duplicated app resources.

`Badness` marks the cases where current behaviour is itself wrong:
- a timestamp nested in a complex default fails with an internal error;
a timestamp shorthand is dropped;
- an inline mapping as the merge value is rejected although the YAML
spec allows it;
- duplicate keys are accepted silently (the later one wins);
- an empty `resources:` section is an error without a location (empty
`targets`, `variables`, `sync` are fine); `targets: false` fails with an
internal "failed to rewrite" message;
- an explicitly null `schedule` is reported as missing its required
fields;
- a failed include merge reports an error without a location, and hides
the diagnostics of includes loaded after it.

## Why
They guard behaviour ahead of databricks#7098, which replaces `libs/dyn`: where it
differs, the test shows it.

## Tests
Goldens recorded on main.

This pull request and its description were written by Isaac.

---------

Co-authored-by: Isaac <no-reply@databricks.com>
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Commit: d95e7b19c | Workflow run

Regression test report

Selected 20 changed tests (limit=20, 6 not selected)

40 tests: regression=34 unreleased=0 coverage=6 failing=0

test branch main (649e21e) latest (v1.21.0)
TestAccept/bundle/override/cluster_id_clears_new_cluster_reference/DMS= ✅ ✅ ✅
TestAccept/bundle/override/cluster_id_clears_new_cluster_reference/DMS=true ✅ ✅ ✅
TestAccept/bundle/override/preset_clears_forced_schedule_reference/DMS= ✅ ❌ ❌
TestAccept/bundle/override/preset_clears_forced_schedule_reference/DMS=true ✅ ❌ ❌
TestAccept/bundle/resource_deps/whole_map_reference_hides_entries/DMS= ✅ ✅ ✅
TestAccept/bundle/resource_deps/whole_map_reference_hides_entries/DMS=true ✅ ✅ ✅
TestAccept/bundle/validate/app_resource_duplicate_name_location/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/app_resource_duplicate_name_location/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/includes/merge_failure_short_circuits_diagnostics/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/includes/merge_failure_short_circuits_diagnostics/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/nulls/resources_container/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/nulls/resources_container/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/references/resolved_invalid_sequence_items/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/references/resolved_invalid_sequence_items/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/references/to_omitted_struct_defaults/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/references/to_omitted_struct_defaults/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/required/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/required/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/required_struct_reference/DMS= ✅ ✅ ✅
TestAccept/bundle/validate/required_struct_reference/DMS=true ✅ ✅ ✅
TestAccept/bundle/validate/targets/inherited_from_root_merge/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/targets/inherited_from_root_merge/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/targets/scalar_rewrite_diagnostic/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/targets/scalar_rewrite_diagnostic/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/variables/complex_default_nested_timestamp/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/variables/complex_default_nested_timestamp/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/variables/complex_unknown_field_location/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/variables/complex_unknown_field_location/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/variables/target_inherited_from_target_merge/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/variables/target_inherited_from_target_merge/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/variables/target_timestamp_shorthand/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/variables/target_timestamp_shorthand/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/yaml_anchors/alias_mutates_complex_default/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/yaml_anchors/alias_mutates_complex_default/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/yaml_anchors/duplicate_merge_keys/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/yaml_anchors/duplicate_merge_keys/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/yaml_anchors/inline_merge_mapping/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/yaml_anchors/inline_merge_mapping/DMS=true ✅ ❌ ❌
TestAccept/bundle/validate/yaml_anchors/invalid_merge_sequence_member/DMS= ✅ ❌ ❌
TestAccept/bundle/validate/yaml_anchors/invalid_merge_sequence_member/DMS=true ✅ ❌ ❌
TestAccept/bundle/override/preset_clears_forced_schedule_reference/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle deploy\nWarning: required field \"quartz_cron_expression\" is not set\n  at resources.quality_monitors.dst.schedule\n  in databricks.yml:39:21\n     databricks.yml:30:17\n\nWarning: required field \"timezone_id\" is not set\n  at resources.quality_monitors.dst.schedule\n  in databricks.yml:39:21\n     databricks.yml:30:17\n\nUploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...\nCreated quality_monitors.dst\nCreated quality_monitors.src\nFiles: 1 uploaded, 0 deleted\nResources: 2 created, 0 changed, 0 deleted, 0 unchanged\n\n>>> print_requests.py //tables/main.default.dst/monitor\n{\n  \"schedule\": null\n}\n"
        	            	actual  : "\n>>> [CLI] bundle deploy\nUploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...\nCreated quality_monitors.dst\nCreated quality_monitors.src\nFiles: 1 uploaded, 0 deleted\nResources: 2 created, 0 changed, 0 deleted, 0 unchanged\n\n>>> print_requests.py //tables/main.default.dst/monitor\n{\n  \"schedule\": null\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,12 +2,2 @@
        	            	 >>> [CLI] bundle deploy
        	            	-Warning: required field "quartz_cron_expression" is not set
        	            	-  at resources.quality_monitors.dst.schedule
        	            	-  in databricks.yml:39:21
        	            	-     databricks.yml:30:17
        	            	-
        	            	-Warning: required field "timezone_id" is not set
        	            	-  at resources.quality_monitors.dst.schedule
        	            	-  in databricks.yml:39:21
        	            	-     databricks.yml:30:17
        	            	-
        	            	 Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...
        	Test:       	TestAccept/bundle/override/preset_clears_forced_schedule_reference/DMS=
        	Messages:   	bundle/override/preset_clears_forced_schedule_reference/output.txt vs /tmp/TestAcceptbundleoverridepreset_clears_forced_schedule_referen1191465666/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/override/preset_clears_forced_schedule_reference/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle deploy\nWarning: required field \"quartz_cron_expression\" is not set\n  at resources.quality_monitors.dst.schedule\n  in databricks.yml:39:21\n     databricks.yml:30:17\n\nWarning: required field \"timezone_id\" is not set\n  at resources.quality_monitors.dst.schedule\n  in databricks.yml:39:21\n     databricks.yml:30:17\n\nUploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...\nCreated quality_monitors.dst\nCreated quality_monitors.src\nFiles: 1 uploaded, 0 deleted\nResources: 2 created, 0 changed, 0 deleted, 0 unchanged\n\n>>> print_requests.py //tables/main.default.dst/monitor\n{\n  \"schedule\": null\n}\n"
        	            	actual  : "\n>>> [CLI] bundle deploy\nUploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...\nCreated quality_monitors.dst\nCreated quality_monitors.src\nFiles: 1 uploaded, 0 deleted\nResources: 2 created, 0 changed, 0 deleted, 0 unchanged\n\n>>> print_requests.py //tables/main.default.dst/monitor\n{\n  \"schedule\": null\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,12 +2,2 @@
        	            	 >>> [CLI] bundle deploy
        	            	-Warning: required field "quartz_cron_expression" is not set
        	            	-  at resources.quality_monitors.dst.schedule
        	            	-  in databricks.yml:39:21
        	            	-     databricks.yml:30:17
        	            	-
        	            	-Warning: required field "timezone_id" is not set
        	            	-  at resources.quality_monitors.dst.schedule
        	            	-  in databricks.yml:39:21
        	            	-     databricks.yml:30:17
        	            	-
        	            	 Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...
        	Test:       	TestAccept/bundle/override/preset_clears_forced_schedule_reference/DMS=true
        	Messages:   	bundle/override/preset_clears_forced_schedule_reference/output.txt vs /tmp/TestAcceptbundleoverridepreset_clears_forced_schedule_referen3307032095/002/output.txt
FAIL

latest (v1.21.0):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle deploy\nWarning: required field \"quartz_cron_expression\" is not set\n  at resources.quality_monitors.dst.schedule\n  in databricks.yml:39:21\n     databricks.yml:30:17\n\nWarning: required field \"timezone_id\" is not set\n  at resources.quality_monitors.dst.schedule\n  in databricks.yml:39:21\n     databricks.yml:30:17\n\nUploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...\nCreated quality_monitors.dst\nCreated quality_monitors.src\nFiles: 1 uploaded, 0 deleted\nResources: 2 created, 0 changed, 0 deleted, 0 unchanged\n\n>>> print_requests.py //tables/main.default.dst/monitor\n{\n  \"schedule\": null\n}\n"
        	            	actual  : "\n>>> [CLI] bundle deploy\nUploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...\nCreated quality_monitors.dst\nCreated quality_monitors.src\nFiles: 1 uploaded, 0 deleted\nResources: 2 created, 0 changed, 0 deleted, 0 unchanged\n\n>>> print_requests.py //tables/main.default.dst/monitor\n{\n  \"schedule\": null\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,12 +2,2 @@
        	            	 >>> [CLI] bundle deploy
        	            	-Warning: required field "quartz_cron_expression" is not set
        	            	-  at resources.quality_monitors.dst.schedule
        	            	-  in databricks.yml:39:21
        	            	-     databricks.yml:30:17
        	            	-
        	            	-Warning: required field "timezone_id" is not set
        	            	-  at resources.quality_monitors.dst.schedule
        	            	-  in databricks.yml:39:21
        	            	-     databricks.yml:30:17
        	            	-
        	            	 Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/dev/files...
        	Test:       	TestAccept/bundle/override/preset_clears_forced_schedule_reference/DMS=true
        	Messages:   	bundle/override/preset_clears_forced_schedule_reference/output.txt vs /tmp/TestAcceptbundleoverridepreset_clears_forced_schedule_referen1119958990/002/output.txt
TestAccept/bundle/validate/app_resource_duplicate_name_location/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: invalid value \"INVALID\" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER]\n  at resources.apps.app.resources[0].job.permission\n  in databricks.yml:17:25\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 1 warning\n\n>>> [CLI] bundle validate -o json\nWarning: invalid value \"INVALID\" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER]\n  at resources.apps.app.resources[0].job.permission\n  in databricks.yml:17:25\n\n[\n  {\n    \"job\": {\n      \"id\": \"2\",\n      \"permission\": \"INVALID\"\n    },\n    \"name\": \"job\"\n  }\n]\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nWarning: invalid value \"INVALID\" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER]\n  at resources.apps.app.resources[0].job.permission\n  in databricks.yml:13:25\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 1 warning\n\n>>> [CLI] bundle validate -o json\nWarning: invalid value \"INVALID\" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER]\n  at resources.apps.app.resources[0].job.permission\n  in databricks.yml:13:25\n\n[\n  {\n    \"job\": {\n      \"id\": \"2\",\n      \"permission\": \"INVALID\"\n    },\n    \"name\": \"job\"\n  }\n]\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -4,3 +4,3 @@
        	            	   at resources.apps.app.resources[0].job.permission
        	            	-  in databricks.yml:17:25
        	            	+  in databricks.yml:13:25
        	            	 
        	            	@@ -17,3 +17,3 @@
        	            	   at resources.apps.app.resources[0].job.permission
        	            	-  in databricks.yml:17:25
        	            	+  in databricks.yml:13:25
        	            	 
        	Test:       	TestAccept/bundle/validate/app_resource_duplicate_name_location/DMS=
        	Messages:   	bundle/validate/app_resource_duplicate_name_location/output.txt vs /tmp/TestAcceptbundlevalidateapp_resource_duplicate_name_location1637030938/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/app_resource_duplicate_name_location/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: invalid value \"INVALID\" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER]\n  at resources.apps.app.resources[0].job.permission\n  in databricks.yml:17:25\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 1 warning\n\n>>> [CLI] bundle validate -o json\nWarning: invalid value \"INVALID\" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER]\n  at resources.apps.app.resources[0].job.permission\n  in databricks.yml:17:25\n\n[\n  {\n    \"job\": {\n      \"id\": \"2\",\n      \"permission\": \"INVALID\"\n    },\n    \"name\": \"job\"\n  }\n]\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nWarning: invalid value \"INVALID\" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER]\n  at resources.apps.app.resources[0].job.permission\n  in databricks.yml:13:25\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 1 warning\n\n>>> [CLI] bundle validate -o json\nWarning: invalid value \"INVALID\" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER]\n  at resources.apps.app.resources[0].job.permission\n  in databricks.yml:13:25\n\n[\n  {\n    \"job\": {\n      \"id\": \"2\",\n      \"permission\": \"INVALID\"\n    },\n    \"name\": \"job\"\n  }\n]\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -4,3 +4,3 @@
        	            	   at resources.apps.app.resources[0].job.permission
        	            	-  in databricks.yml:17:25
        	            	+  in databricks.yml:13:25
        	            	 
        	            	@@ -17,3 +17,3 @@
        	            	   at resources.apps.app.resources[0].job.permission
        	            	-  in databricks.yml:17:25
        	            	+  in databricks.yml:13:25
        	            	 
        	Test:       	TestAccept/bundle/validate/app_resource_duplicate_name_location/DMS=true
        	Messages:   	bundle/validate/app_resource_duplicate_name_location/output.txt vs /tmp/TestAcceptbundlevalidateapp_resource_duplicate_name_location347876656/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/includes/merge_failure_short_circuits_diagnostics/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: unknown field: unexpected_after_failure\n  at bundle\n  in b.yml:2:3\n\nError: cannot merge map with string\n\nName: test-bundle\n\nFound 1 error and 1 warning\n\nExit code: 1\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nError: cannot merge map with string\n\nName: test-bundle\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,6 +2,2 @@
        	            	 >>> [CLI] bundle validate
        	            	-Warning: unknown field: unexpected_after_failure
        	            	-  at bundle
        	            	-  in b.yml:2:3
        	            	-
        	            	 Error: cannot merge map with string
        	            	@@ -10,3 +6,3 @@
        	            	 
        	            	-Found 1 error and 1 warning
        	            	+Found 1 error
        	            	 
        	Test:       	TestAccept/bundle/validate/includes/merge_failure_short_circuits_diagnostics/DMS=
        	Messages:   	bundle/validate/includes/merge_failure_short_circuits_diagnostics/output.txt vs /tmp/TestAcceptbundlevalidateincludesmerge_failure_short_circuits347290271/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/includes/merge_failure_short_circuits_diagnostics/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: unknown field: unexpected_after_failure\n  at bundle\n  in b.yml:2:3\n\nError: cannot merge map with string\n\nName: test-bundle\n\nFound 1 error and 1 warning\n\nExit code: 1\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nError: cannot merge map with string\n\nName: test-bundle\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,6 +2,2 @@
        	            	 >>> [CLI] bundle validate
        	            	-Warning: unknown field: unexpected_after_failure
        	            	-  at bundle
        	            	-  in b.yml:2:3
        	            	-
        	            	 Error: cannot merge map with string
        	            	@@ -10,3 +6,3 @@
        	            	 
        	            	-Found 1 error and 1 warning
        	            	+Found 1 error
        	            	 
        	Test:       	TestAccept/bundle/validate/includes/merge_failure_short_circuits_diagnostics/DMS=true
        	Messages:   	bundle/validate/includes/merge_failure_short_circuits_diagnostics/output.txt vs /tmp/TestAcceptbundlevalidateincludesmerge_failure_short_circuits3328612494/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/nulls/resources_container/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nValidation OK!\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nError: expected a map at \"resources\", found nil\n\nName: test-bundle\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,9 +2,9 @@
        	            	 >>> [CLI] bundle validate
        	            	+Error: expected a map at "resources", found nil
        	            	+
        	            	 Name: test-bundle
        	            	-Target: default
        	            	-Workspace:
        	            	-  User: [USERNAME]
        	            	-  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
        	            	 
        	            	-Validation OK!
        	            	+Found 1 error
        	            	 
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/bundle/validate/nulls/resources_container/DMS=
        	Messages:   	bundle/validate/nulls/resources_container/output.txt vs /tmp/TestAcceptbundlevalidatenullsresources_containerDMS=195159381/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/nulls/resources_container/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nValidation OK!\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nError: expected a map at \"resources\", found nil\n\nName: test-bundle\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,9 +2,9 @@
        	            	 >>> [CLI] bundle validate
        	            	+Error: expected a map at "resources", found nil
        	            	+
        	            	 Name: test-bundle
        	            	-Target: default
        	            	-Workspace:
        	            	-  User: [USERNAME]
        	            	-  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
        	            	 
        	            	-Validation OK!
        	            	+Found 1 error
        	            	 
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/bundle/validate/nulls/resources_container/DMS=true
        	Messages:   	bundle/validate/nulls/resources_container/output.txt vs /tmp/TestAcceptbundlevalidatenullsresources_containerDMS=true1485228307/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/references/resolved_invalid_sequence_items/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate -o json\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[1]\n  in databricks.yml:15:40\n\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[3]\n  in databricks.yml:15:72\n\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[4]\n  in databricks.yml:15:86\n\n{\n  \"on_failure\": [\n    \"a@example.test\",\n    \"b@example.test\",\n    \"c@example.test\"\n  ]\n}\n"
        	            	actual  : "\n>>> [CLI] bundle validate -o json\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[1]\n  in databricks.yml:15:40\n\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[2]\n  in databricks.yml:15:72\n\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[2]\n  in databricks.yml:15:86\n\n{\n  \"on_failure\": [\n    \"a@example.test\",\n    \"b@example.test\",\n    \"c@example.test\"\n  ]\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -7,3 +7,3 @@
        	            	 Warning: expected string, found map
        	            	-  at resources.jobs.j.email_notifications.on_failure[3]
        	            	+  at resources.jobs.j.email_notifications.on_failure[2]
        	            	   in databricks.yml:15:72
        	            	@@ -11,3 +11,3 @@
        	            	 Warning: expected string, found map
        	            	-  at resources.jobs.j.email_notifications.on_failure[4]
        	            	+  at resources.jobs.j.email_notifications.on_failure[2]
        	            	   in databricks.yml:15:86
        	Test:       	TestAccept/bundle/validate/references/resolved_invalid_sequence_items/DMS=
        	Messages:   	bundle/validate/references/resolved_invalid_sequence_items/output.txt vs /tmp/TestAcceptbundlevalidatereferencesresolved_invalid_sequence_1073213516/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/references/resolved_invalid_sequence_items/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate -o json\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[1]\n  in databricks.yml:15:40\n\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[3]\n  in databricks.yml:15:72\n\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[4]\n  in databricks.yml:15:86\n\n{\n  \"on_failure\": [\n    \"a@example.test\",\n    \"b@example.test\",\n    \"c@example.test\"\n  ]\n}\n"
        	            	actual  : "\n>>> [CLI] bundle validate -o json\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[1]\n  in databricks.yml:15:40\n\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[2]\n  in databricks.yml:15:72\n\nWarning: expected string, found map\n  at resources.jobs.j.email_notifications.on_failure[2]\n  in databricks.yml:15:86\n\n{\n  \"on_failure\": [\n    \"a@example.test\",\n    \"b@example.test\",\n    \"c@example.test\"\n  ]\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -7,3 +7,3 @@
        	            	 Warning: expected string, found map
        	            	-  at resources.jobs.j.email_notifications.on_failure[3]
        	            	+  at resources.jobs.j.email_notifications.on_failure[2]
        	            	   in databricks.yml:15:72
        	            	@@ -11,3 +11,3 @@
        	            	 Warning: expected string, found map
        	            	-  at resources.jobs.j.email_notifications.on_failure[4]
        	            	+  at resources.jobs.j.email_notifications.on_failure[2]
        	            	   in databricks.yml:15:86
        	Test:       	TestAccept/bundle/validate/references/resolved_invalid_sequence_items/DMS=true
        	Messages:   	bundle/validate/references/resolved_invalid_sequence_items/output.txt vs /tmp/TestAcceptbundlevalidatereferencesresolved_invalid_sequence_4140955185/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/references/to_omitted_struct_defaults/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate -o json\n{\n  \"default\": {\n    \"bundle_root_path\": \".\"\n  },\n  \"type\": \"complex\",\n  \"value\": {\n    \"bundle_root_path\": \".\"\n  }\n}\n"
        	            	actual  : "\n>>> [CLI] bundle validate -o json\n{\n  \"default\": {\n    \"actual_branch\": \"\",\n    \"branch\": \"\",\n    \"bundle_root_path\": \".\",\n    \"commit\": \"\",\n    \"origin_url\": \"\"\n  },\n  \"type\": \"complex\",\n  \"value\": {\n    \"actual_branch\": \"\",\n    \"branch\": \"\",\n    \"bundle_root_path\": \".\",\n    \"commit\": \"\",\n    \"origin_url\": \"\"\n  }\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -4,3 +4,7 @@
        	            	   "default": {
        	            	-    "bundle_root_path": "."
        	            	+    "actual_branch": "",
        	            	+    "branch": "",
        	            	+    "bundle_root_path": ".",
        	            	+    "commit": "",
        	            	+    "origin_url": ""
        	            	   },
        	            	@@ -8,3 +12,7 @@
        	            	   "value": {
        	            	-    "bundle_root_path": "."
        	            	+    "actual_branch": "",
        	            	+    "branch": "",
        	            	+    "bundle_root_path": ".",
        	            	+    "commit": "",
        	            	+    "origin_url": ""
        	            	   }
        	Test:       	TestAccept/bundle/validate/references/to_omitted_struct_defaults/DMS=
        	Messages:   	bundle/validate/references/to_omitted_struct_defaults/output.txt vs /tmp/TestAcceptbundlevalidatereferencesto_omitted_struct_defaults348251130/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/references/to_omitted_struct_defaults/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate -o json\n{\n  \"default\": {\n    \"bundle_root_path\": \".\"\n  },\n  \"type\": \"complex\",\n  \"value\": {\n    \"bundle_root_path\": \".\"\n  }\n}\n"
        	            	actual  : "\n>>> [CLI] bundle validate -o json\n{\n  \"default\": {\n    \"actual_branch\": \"\",\n    \"branch\": \"\",\n    \"bundle_root_path\": \".\",\n    \"commit\": \"\",\n    \"origin_url\": \"\"\n  },\n  \"type\": \"complex\",\n  \"value\": {\n    \"actual_branch\": \"\",\n    \"branch\": \"\",\n    \"bundle_root_path\": \".\",\n    \"commit\": \"\",\n    \"origin_url\": \"\"\n  }\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -4,3 +4,7 @@
        	            	   "default": {
        	            	-    "bundle_root_path": "."
        	            	+    "actual_branch": "",
        	            	+    "branch": "",
        	            	+    "bundle_root_path": ".",
        	            	+    "commit": "",
        	            	+    "origin_url": ""
        	            	   },
        	            	@@ -8,3 +12,7 @@
        	            	   "value": {
        	            	-    "bundle_root_path": "."
        	            	+    "actual_branch": "",
        	            	+    "branch": "",
        	            	+    "bundle_root_path": ".",
        	            	+    "commit": "",
        	            	+    "origin_url": ""
        	            	   }
        	Test:       	TestAccept/bundle/validate/references/to_omitted_struct_defaults/DMS=true
        	Messages:   	bundle/validate/references/to_omitted_struct_defaults/output.txt vs /tmp/TestAcceptbundlevalidatereferencesto_omitted_struct_defaults1845207380/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/required/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: required field \"catalog_name\" is not set\n  at resources.volumes.my_volume\n  in databricks.yml:35:7\n\nWarning: required field \"job_id\" is not set\n  at resources.jobs.my_job_1.tasks[0].run_job_task\n  in databricks.yml:25:13\n\nWarning: required field \"job_id\" is not set\n  at resources.jobs.my_job_1.tasks[1].run_job_task\n  in databricks.yml:29:24\n\nWarning: required field \"name\" is not set\n  at resources.models.my_model_1\n  in databricks.yml:13:7\n\nWarning: required field \"schema_name\" is not set\n  at resources.volumes.my_volume\n  in databricks.yml:35:7\n\nWarning: required field \"source\" is not set\n  at artifacts.my_artifact.files[0]\n  in databricks.yml:7:9\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 6 warnings\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nWarning: required field \"catalog_name\" is not set\n  at resources.volumes.my_volume\n  in databricks.yml:35:7\n\nWarning: required field \"job_id\" is not set\n  at resources.jobs.my_job_1.tasks[1].run_job_task\n  in databricks.yml:29:24\n\nWarning: required field \"name\" is not set\n  at resources.models.my_model_1\n  in databricks.yml:13:7\n\nWarning: required field \"schema_name\" is not set\n  at resources.volumes.my_volume\n  in databricks.yml:35:7\n\nWarning: required field \"source\" is not set\n  at artifacts.my_artifact.files[0]\n  in databricks.yml:7:9\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 5 warnings\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -5,6 +5,2 @@
        	            	   in databricks.yml:35:7
        	            	-
        	            	-Warning: required field "job_id" is not set
        	            	-  at resources.jobs.my_job_1.tasks[0].run_job_task
        	            	-  in databricks.yml:25:13
        	            	 
        	            	@@ -32,3 +28,3 @@
        	            	 
        	            	-Found 6 warnings
        	            	+Found 5 warnings
        	            	 
        	Test:       	TestAccept/bundle/validate/required/DMS=
        	Messages:   	bundle/validate/required/output.txt vs /tmp/TestAcceptbundlevalidaterequiredDMS=3058353080/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/required/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: required field \"catalog_name\" is not set\n  at resources.volumes.my_volume\n  in databricks.yml:35:7\n\nWarning: required field \"job_id\" is not set\n  at resources.jobs.my_job_1.tasks[0].run_job_task\n  in databricks.yml:25:13\n\nWarning: required field \"job_id\" is not set\n  at resources.jobs.my_job_1.tasks[1].run_job_task\n  in databricks.yml:29:24\n\nWarning: required field \"name\" is not set\n  at resources.models.my_model_1\n  in databricks.yml:13:7\n\nWarning: required field \"schema_name\" is not set\n  at resources.volumes.my_volume\n  in databricks.yml:35:7\n\nWarning: required field \"source\" is not set\n  at artifacts.my_artifact.files[0]\n  in databricks.yml:7:9\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 6 warnings\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nWarning: required field \"catalog_name\" is not set\n  at resources.volumes.my_volume\n  in databricks.yml:35:7\n\nWarning: required field \"job_id\" is not set\n  at resources.jobs.my_job_1.tasks[1].run_job_task\n  in databricks.yml:29:24\n\nWarning: required field \"name\" is not set\n  at resources.models.my_model_1\n  in databricks.yml:13:7\n\nWarning: required field \"schema_name\" is not set\n  at resources.volumes.my_volume\n  in databricks.yml:35:7\n\nWarning: required field \"source\" is not set\n  at artifacts.my_artifact.files[0]\n  in databricks.yml:7:9\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 5 warnings\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -5,6 +5,2 @@
        	            	   in databricks.yml:35:7
        	            	-
        	            	-Warning: required field "job_id" is not set
        	            	-  at resources.jobs.my_job_1.tasks[0].run_job_task
        	            	-  in databricks.yml:25:13
        	            	 
        	            	@@ -32,3 +28,3 @@
        	            	 
        	            	-Found 6 warnings
        	            	+Found 5 warnings
        	            	 
        	Test:       	TestAccept/bundle/validate/required/DMS=true
        	Messages:   	bundle/validate/required/output.txt vs /tmp/TestAcceptbundlevalidaterequiredDMS=true883521652/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/targets/inherited_from_root_merge/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate -t dev -o json\nWarning: expected map, found string\n  at targets.dev.variables.x\n  in databricks.yml:13:14\n\n{\n  \"default\": \"base\",\n  \"value\": \"base\"\n}\n"
        	            	actual  : "\n>>> [CLI] bundle validate -t dev -o json\n{\n  \"default\": \"overridden\",\n  \"value\": \"overridden\"\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,9 +2,5 @@
        	            	 >>> [CLI] bundle validate -t dev -o json
        	            	-Warning: expected map, found string
        	            	-  at targets.dev.variables.x
        	            	-  in databricks.yml:13:14
        	            	-
        	            	 {
        	            	-  "default": "base",
        	            	-  "value": "base"
        	            	+  "default": "overridden",
        	            	+  "value": "overridden"
        	            	 }
        	Test:       	TestAccept/bundle/validate/targets/inherited_from_root_merge/DMS=
        	Messages:   	bundle/validate/targets/inherited_from_root_merge/output.txt vs /tmp/TestAcceptbundlevalidatetargetsinherited_from_root_mergeDMS1552774493/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/targets/inherited_from_root_merge/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate -t dev -o json\nWarning: expected map, found string\n  at targets.dev.variables.x\n  in databricks.yml:13:14\n\n{\n  \"default\": \"base\",\n  \"value\": \"base\"\n}\n"
        	            	actual  : "\n>>> [CLI] bundle validate -t dev -o json\n{\n  \"default\": \"overridden\",\n  \"value\": \"overridden\"\n}\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,9 +2,5 @@
        	            	 >>> [CLI] bundle validate -t dev -o json
        	            	-Warning: expected map, found string
        	            	-  at targets.dev.variables.x
        	            	-  in databricks.yml:13:14
        	            	-
        	            	 {
        	            	-  "default": "base",
        	            	-  "value": "base"
        	            	+  "default": "overridden",
        	            	+  "value": "overridden"
        	            	 }
        	Test:       	TestAccept/bundle/validate/targets/inherited_from_root_merge/DMS=true
        	Messages:   	bundle/validate/targets/inherited_from_root_merge/output.txt vs /tmp/TestAcceptbundlevalidatetargetsinherited_from_root_mergeDMS1887044192/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/targets/scalar_rewrite_diagnostic/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: expected map, found bool\n  at targets\n  in databricks.yml:4:10\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 1 warning\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nError: failed to rewrite [TEST_TMP_DIR]/databricks.yml: expected a map or sequence, found bool\n\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,13 +2,8 @@
        	            	 >>> [CLI] bundle validate
        	            	-Warning: expected map, found bool
        	            	-  at targets
        	            	-  in databricks.yml:4:10
        	            	+Error: failed to rewrite [TEST_TMP_DIR]/databricks.yml: expected a map or sequence, found bool
        	            	 
        	            	-Name: test-bundle
        	            	-Target: default
        	            	-Workspace:
        	            	-  User: [USERNAME]
        	            	-  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
        	            	 
        	            	-Found 1 warning
        	            	+Found 1 error
        	            	 
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/bundle/validate/targets/scalar_rewrite_diagnostic/DMS=
        	Messages:   	bundle/validate/targets/scalar_rewrite_diagnostic/output.txt vs /tmp/TestAcceptbundlevalidatetargetsscalar_rewrite_diagnosticDMS387438788/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/targets/scalar_rewrite_diagnostic/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: expected map, found bool\n  at targets\n  in databricks.yml:4:10\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 1 warning\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nError: failed to rewrite [TEST_TMP_DIR]/databricks.yml: expected a map or sequence, found bool\n\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,13 +2,8 @@
        	            	 >>> [CLI] bundle validate
        	            	-Warning: expected map, found bool
        	            	-  at targets
        	            	-  in databricks.yml:4:10
        	            	+Error: failed to rewrite [TEST_TMP_DIR]/databricks.yml: expected a map or sequence, found bool
        	            	 
        	            	-Name: test-bundle
        	            	-Target: default
        	            	-Workspace:
        	            	-  User: [USERNAME]
        	            	-  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
        	            	 
        	            	-Found 1 warning
        	            	+Found 1 error
        	            	 
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/bundle/validate/targets/scalar_rewrite_diagnostic/DMS=true
        	Messages:   	bundle/validate/targets/scalar_rewrite_diagnostic/output.txt vs /tmp/TestAcceptbundlevalidatetargetsscalar_rewrite_diagnosticDMS2122635447/002/output.txt

latest (v1.21.0):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nWarning: expected map, found bool\n  at targets\n  in databricks.yml:4:10\n\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nFound 1 warning\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nError: failed to rewrite [TEST_TMP_DIR]/databricks.yml: expected a map or sequence, found bool\n\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,13 +2,8 @@
        	            	 >>> [CLI] bundle validate
        	            	-Warning: expected map, found bool
        	            	-  at targets
        	            	-  in databricks.yml:4:10
        	            	+Error: failed to rewrite [TEST_TMP_DIR]/databricks.yml: expected a map or sequence, found bool
        	            	 
        	            	-Name: test-bundle
        	            	-Target: default
        	            	-Workspace:
        	            	-  User: [USERNAME]
        	            	-  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
        	            	 
        	            	-Found 1 warning
        	            	+Found 1 error
        	            	 
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/bundle/validate/targets/scalar_rewrite_diagnostic/DMS=true
        	Messages:   	bundle/validate/targets/scalar_rewrite_diagnostic/output.txt vs /tmp/TestAcceptbundlevalidatetargetsscalar_rewrite_diagnosticDMS75414454/002/output.txt
FAIL
TestAccept/bundle/validate/variables/complex_default_nested_timestamp/DMS=: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nValidation OK!\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nWarn: unable to convert typed configuration to dynamic configuration: cannot convert struct field to dynamic type \"time\": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC)\nError: exit error: cannot convert struct field to dynamic type \"time\": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC)\n\nName: test-bundle\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,9 +2,10 @@
        	            	 >>> [CLI] bundle validate
        	            	+Warn: unable to convert typed configuration to dynamic configuration: cannot convert struct field to dynamic type "time": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC)
        	            	+Error: exit error: cannot convert struct field to dynamic type "time": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC)
        	            	+
        	            	 Name: test-bundle
        	            	-Target: default
        	            	-Workspace:
        	            	-  User: [USERNAME]
        	            	-  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
        	            	 
        	            	-Validation OK!
        	            	+Found 1 error
        	            	 
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/bundle/validate/variables/complex_default_nested_timestamp/DMS=
        	Messages:   	bundle/validate/variables/complex_default_nested_timestamp/output.txt vs /tmp/TestAcceptbundlevalidatevariablescomplex_default_nested_time3124688163/002/output.txt

latest (v1.21.0): same output as main (649e21e)

TestAccept/bundle/validate/variables/complex_default_nested_timestamp/DMS=true: branch ✅ | main (649e21e) ❌ | latest (v1.21.0) ❌

main (649e21e):

acceptance_test.go:1307: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1307
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1077
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:655
        	Error:      	Not equal: 
        	            	expected: "\n>>> [CLI] bundle validate\nName: test-bundle\nTarget: default\nWorkspace:\n  User: [USERNAME]\n  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default\n\nValidation OK!\n"
        	            	actual  : "\n>>> [CLI] bundle validate\nWarn: unable to convert typed configuration to dynamic configuration: cannot convert struct field to dynamic type \"time\": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC)\nError: exit error: cannot convert struct field to dynamic type \"time\": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC)\n\nName: test-bundle\n\nFound 1 error\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -2,9 +2,10 @@
        	            	 >>> [CLI] bundle validate
        	            	+Warn: unable to convert typed configuration to dynamic configuration: cannot convert struct field to dynamic type "time": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC)
        	            	+Error: exit error: cannot convert struct field to dynamic type "time": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC)
        	            	+
        	            	 Name: test-bundle
        	            	-Target: default
        	            	-Workspace:
        	            	-  User: [USERNAME]
        	            	-  Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default
        	            	 
        	            	-Validation OK!
        	            	+Found 1 error
        	            	 
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/bundle/validate/variables/complex_default_nested_timestamp/DMS=true
        	Messages:   	bundle/validate/variables/complex_default_nested_timestamp/output.txt vs /tmp/TestAcceptbundlevalidatevariablescomplex_default_nested_time3010864864/002/output.txt

latest (v1.21.0): same output as main (649e21e)

14 more failure details omitted, see the full report in the run.

denik and others added 20 commits October 20, 2026 14:53
NewPattern builds a pattern from parts like NewPath does for paths, so
callers don't nest the single-step constructors or parse pattern strings.

Co-authored-by: Isaac <no-reply@databricks.com>
…r refs and locations

Bundle configuration is the typed struct plus structvar (pure ${...}
references in non-string fields and a tree of source locations). YAML and
JSON decode straight into the structs; mutators, validators, variable
resolution, configsync and generate work on structvar views and structpath
paths. libs/dyn is deleted.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
… test

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
- configsync: compare int and int64 values as equal when restoring variable
  references (variables decode as int, remote fields are int64).
- schema annotations: merge object/sequence defaults as documented; accept an
  empty annotations file.
- python mutator: use ResourceKeySet.AddPattern instead of a duplicate helper.
- structvar.Set: don't rewrite an unchanged leaf location node (avoids cloning
  every ancestor per write).
- ResolveConfigAgainstState: resolve all references in one call so lookups are
  shared.

Co-authored-by: Isaac <no-reply@databricks.com>
…ctpath

Co-authored-by: Isaac <no-reply@databricks.com>
- apply_default_task_source: an explicit source: "" is user-set; detect it by
  its definition location (not every task type has ForceSendFields).
- pipeline_development_deprecated: warn on any user-written development value,
  including a reference resolving to an unset field.
- rewriteShorthands: a later merged map takes precedence over an earlier one,
  and merge keys are expanded before detecting a full variable definition.
- required: look dashboards up by a path that round-trips keys with path syntax.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
…om parts

Earlier rebases resolved conflicts with #6980 in favor of string paths,
reverting call sites that main already builds with structpath.NewPath.
Restore main's versions where this PR has no reason to change them, and make
the app permission checks take a path node instead of parsing a string.

Co-authored-by: Isaac <no-reply@databricks.com>
… parts

Root's location lookups take a *structpath.PathNode and say which value they
return: NearestLocations/NearestLocation fall back to the closest ancestor
with a location (for diagnostics), ExactLocations/ExactLocation return where
the value is written (empty if a mutator set it). The string variants are
gone, so callers build paths from parts instead of formatting and parsing
them. Literal MustParsePattern/MustParsePath calls in production code are
replaced with NewPattern/NewPath, and the app permission checks read the
resource view directly.

Co-authored-by: Isaac <no-reply@databricks.com>
denik and others added 24 commits October 20, 2026 14:54
ConfigureClusterPolicyDefinition, ConfigureDashboardSerializedDashboard,
ConfigureGenieSpaceSerializedSpace, InitializeVolumePaths,
ExpandPipelineGlobPaths, ComputeIdToClusterId, PrependWorkspacePrefix and
RewriteSyncPaths read the typed resources and fields directly instead of
traversing patterns and views. Inline JSON documents are converted by one
helper (inlineJSON).

Co-authored-by: Isaac <no-reply@databricks.com>
…hecks

config.Resources gains standard iterators over the places that hold libraries
(task and for_each_task libraries, cluster libraries, job and pipeline
environment dependencies, AI Runtime code sources). ExpandGlobReferences,
the remote path collection, the duplicate library name check and the artifact
glob expansion use them (and the typed artifacts) instead of pattern traversal
and reflection on the sequences.

Co-authored-by: Isaac <no-reply@databricks.com>
- Resource defaults move to bundle/config/resourcedefaults; the generator
  emits a typed walker (resourcemutator/defaults_walk.go) that visits only the
  paths with defaults and sets the unset ones, instead of one pattern traversal
  per default. bundle.SetDefaults is gone. A nil parent block (schedule: null)
  is not materialized, as on main.
- SingleNodeCluster reads the cluster specs directly (clusters, job clusters,
  task and for_each_task clusters, pipeline clusters).
- walk.Forced and walk.IsZeroStruct are shared by the generated walkers;
  Root.LocationTree exposes the locations tree.

Co-authored-by: Isaac <no-reply@databricks.com>
Top level fields and resource groups come from the locations tree, resources
and job tasks from the typed configuration, instead of a pattern traversal of
views. SelectTarget deletes targets and environments (with their locations)
rather than only clearing them.

Co-authored-by: Isaac <no-reply@databricks.com>
ResourceKeySet.AddResources/AddResourcesOfType iterate the typed resources
instead of matching a pattern over a view; the backwards-compatibility copy
of 'python' to 'experimental.python' is a field assignment plus the locations
subtree.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
…ngs walker

The scope (whole configuration with or without resources, resources only,
variable lookups) selects a walk.Strings entry point instead of a pattern
matched over a view; the reflective string walk in hasReferencesToResolve
goes away. Resolution itself still looks up arbitrary references in the view.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Replace View lookups and reflective walks where the types are known:
- validate_engine, secret value, auth interpolation: plain field access with
  the presence rules (ForceSendFields, written, references);
- environments -> targets: field assignment plus Root.MoveMetadata;
- apps: Resources.PermissionsOf; plan prevent_destroy: Resources.GetResource
  (JobRun gets the GetLifecycle override it was missing);
- DropEmptyStrings, CollectEscapeTelemetry: walk.Strings modes EmptyFields
  and NonEmpty; AllResourcesHaveValues: IterateAllResources;
- selectResources: Resources.Select/CopyResource plus Root.CopyMetadata;
- direct extractReferences and ResolveConfigAgainstState: walk.ResourceStringsOf
  plus Root.ReferencesUnder;
- SetVariables iterates the typed variables and copies the default from a view
  of the field.

The resources iterator generator is now a text/template.

Co-authored-by: Isaac <no-reply@databricks.com>
- the python -> experimental.python copy carries references and copies the
  slices (Root.CopyMetadataAt; MoveMetadata uses it);
- MergeGrants uses IterateAllGrants;
- keyed merges and reference checks take built paths (IsReferenceAt);
  Root.IsReference(string) is gone;
- ReferencesUnder(nil) covers every reference;
- SingleNodeCluster no longer stores a context;
- resolveIn is a regular function;
- defaults_walk.go is registered as generated;
- remove structvar.ForEach and structpath.MustParsePattern, now unused.

Co-authored-by: Isaac <no-reply@databricks.com>
Locations are for diagnostics and path resolution. View, the generated
required/enum/defaults walkers and the mutators no longer treat a value
with a location as present; a value is present if it is not zero, is
forced, or holds a pure reference. Decoding records written zero values
in ForceSendFields, so they stay present where the struct has one.

Decisions:
- Required: an empty string never satisfies a required field, even if
  forced; a forced false/0 does (zero is meaningful for bools and numbers).
  So name: "" warns, and job_id: 0 in run_job_task warns (RunJobTask has
  no ForceSendFields; a job id 0 is invalid anyway).
- Defaults keep a forced "" (the user asked for an empty value).
- Lost: a written zero in a struct without ForceSendFields and an empty
  struct value (resources: {} -> summary .resources is null); an explicit
  source: "" on spark_python_task/sql_task.file (no ForceSendFields) gets
  the GIT default; development: true under mode: development no longer
  warns (indistinguishable from the preset).
- workspace.file_path with immutable folders is rejected in
  DefineDefaultWorkspacePaths, before the default is set, instead of by
  checking its location.
- Dashboard required checks report by paths, not by locations found.

Co-authored-by: Isaac <no-reply@databricks.com>
Revised decision for required fields: a string is set if it is non-empty
or forced, like every other scalar. An empty string is a valid value (the
built-in SQL template has a job parameter whose default resolves to "").
A written zero in a struct without ForceSendFields cannot be told apart
from unset, so it is reported missing (job_id: 0 in run_job_task warns);
JobParameterDefinition.default is exempt because an empty default is valid.

Also:
- a struct whose only set fields are references is present (Node.HasRefs),
  in required checks and in the required/defaults traversal;
- workspace.file_path with immutable folders is rejected in TranslatePaths
  again, after variable resolution, by value: any value other than the two
  defaults DefineDefaultWorkspacePaths sets is the user's;
- remote_path and StateToBundle no longer use locations for presence;
  Resources.IsZero is gone;
- resolve_config builds the state key from the path;
- SingleNodeCluster checks clusters as it visits them, ctx as an argument.

Co-authored-by: Isaac <no-reply@databricks.com>
… slices

Most mappings are small: search their keys linearly and index only from 16
keys. Keep the key location as a value and build the slice only for a
diagnostic. Load allocates 11% fewer bytes and 13% fewer objects.

Co-authored-by: Isaac <no-reply@databricks.com>
- Root.References/ReferencesUnder skip references whose path is no longer
  in the typed value, e.g. below a new_cluster that OverrideCompute cleared
  for bundle.cluster_id; deploying used to fail resolving them ("parent is
  nil pointer"). References is ReferencesUnder(nil).
- An explicit libraries: null on a pipeline (forced) errors again, as on main.
- Reference keys are rebased with path operations, not string concatenation.

Co-authored-by: Isaac <no-reply@databricks.com>
structaccess.Get treats a terminal nil pointer as reachable and skips
readonly/internal fields, so a new_cluster: ${var.cluster} that
OverrideCompute cleared came back, and bundle.terraform: ${var.x} was
dropped. A reference is now kept if the view finds its path: a nil
pointer (decoding allocates one for a reference) hides it.

Co-authored-by: Isaac <no-reply@databricks.com>
- A reference is kept only if the view shows it (a string): a forced nil
  pointer, e.g. a schedule: null that a target override referenced and a
  mutator then cleared, hides it too. The acceptance test covers the
  scenario (the test server does not reproduce the stale reference).
- Set no longer adds an empty location node to mark a value present:
  presence does not use locations, and it cloned every ancestor on each
  default write.

Co-authored-by: Isaac <no-reply@databricks.com>
- The generated strings walker does not descend into a field that holds a
  pure reference, as the view does: with tags: ${...} and preset tags added
  to the typed map, the hidden entries no longer become dependencies (a job
  referred to itself and planning failed with a cycle).
- The immutable-folder file_path check cleans the value: a root_path ending
  in / resolves the default to root//files.
- Scalars in dynamic values are not copied reflectively by the strings walk.

Co-authored-by: Isaac <no-reply@databricks.com>
- Every generated struct walker returns when its node holds a pure
  reference, so a referenced map entry, slice element or resource (also in
  the per-resource dispatch) hides what is below it, as in the view.
- Scoped walks (resources, one resource, variable lookups) build the
  reference tree from the references in their scope only.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Keeps the diff against main small: the string forms stay, as thin wrappers
over GetLocationOf/GetLocationsOf (the nearest-location lookups), and the
call sites that used them go back to main's text. The exact-location call
sites stay on GetLocationOfExact. Moving the remaining callers to built
paths is left for a follow-up.

Co-authored-by: Isaac <no-reply@databricks.com>
visitArgs.path is a built PathNode and the skip predicate is checked by the
dependency loops before building it. Candidates that are mostly empty (the
whl, jar and requirements of every library) check presence before building
the path. Same speed on TranslatePaths and NormalizePaths, 15% fewer
allocations, and no closure at the call sites.

Co-authored-by: Isaac <no-reply@databricks.com>
#7085 recorded main's behaviour. Where the typed configuration fixes a wrong
behaviour the Badness is gone (nested and shorthand timestamps, an inline
merge mapping, resources: null, a scalar targets section). Where it behaves
differently in a wrong way a Badness records it: scalar target variable
overrides inherited through a merge key, an aliased complex default rewritten
in place, duplicate and invalid merge keys, a duplicate default key, wrong
types turned into empty strings, the lost location of an unknown field in a
variable default, and presets that an include cannot clear.

Co-authored-by: Isaac <no-reply@databricks.com>
#7117 added ForceSendFields to Presets, so a written zero is recorded.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@denik
denik force-pushed the denik/typed-config-walkers branch from 857e2d4 to b9d8067 Compare October 20, 2026 12:56
…le and Deployment

Co-authored-by: Isaac <no-reply@databricks.com>
denik and others added 3 commits October 20, 2026 16:15
…ong type

Assign zeroed the slot, which left an empty string in lists and maps like main
did not. Delete now removes slice elements, and resolution applies the
updates from the last element to the first.

Co-authored-by: Isaac <no-reply@databricks.com>

This branch has not been deployed

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

Labels

AIR Databricks AI Runtime CLI DABs DABs related issues PyDABs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants