dataface 0.3.1.dev78__py3-none-any.whl → 0.3.1.dev132__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. dataface/DATAFACE_SYNTAX.md +1 -1
  2. dataface/agent_api/__init__.py +11 -0
  3. dataface/agent_api/_paths.py +25 -50
  4. dataface/agent_api/board_artifact.py +1 -1
  5. dataface/agent_api/describe.py +4 -9
  6. dataface/agent_api/docs/yaml-reference.md +58 -26
  7. dataface/agent_api/migrate.py +101 -0
  8. dataface/agent_api/project_session.py +13 -1
  9. dataface/agent_api/query.py +1 -6
  10. dataface/agent_api/validate.py +5 -10
  11. dataface/cli/_error_format.py +13 -0
  12. dataface/cli/_project.py +77 -0
  13. dataface/cli/commands/board_artifact.py +5 -5
  14. dataface/cli/commands/describe.py +5 -5
  15. dataface/cli/commands/mcp.py +14 -12
  16. dataface/cli/commands/migrate.py +51 -0
  17. dataface/cli/commands/query.py +6 -6
  18. dataface/cli/commands/render.py +20 -27
  19. dataface/cli/commands/search.py +6 -5
  20. dataface/cli/commands/serve.py +15 -11
  21. dataface/cli/commands/validate.py +6 -5
  22. dataface/cli/main.py +39 -0
  23. dataface/core/compile/__init__.py +4 -0
  24. dataface/core/compile/compiler.py +78 -13
  25. dataface/core/compile/face_warnings.py +24 -8
  26. dataface/core/compile/inherit_resolver.py +126 -64
  27. dataface/core/compile/introspection.py +28 -30
  28. dataface/core/compile/merge.py +45 -48
  29. dataface/core/compile/migrations.py +504 -0
  30. dataface/core/compile/models/chart/authored/_base.py +2 -15
  31. dataface/core/compile/models/chart/authored/point_map.py +1 -3
  32. dataface/core/compile/models/chart/normalized/_base.py +3 -2
  33. dataface/core/compile/models/config.py +2 -2
  34. dataface/core/compile/models/primitives.py +7 -13
  35. dataface/core/compile/models/query/authored.py +107 -37
  36. dataface/core/compile/models/query/normalized.py +26 -0
  37. dataface/core/compile/models/refs.py +5 -1
  38. dataface/core/compile/models/style/resolved/_base.py +10 -13
  39. dataface/core/compile/models/style/resolved/_cascade.py +4 -1
  40. dataface/core/compile/models/style/resolved/area.py +2 -2
  41. dataface/core/compile/models/style/resolved/line.py +2 -2
  42. dataface/core/compile/models/style/resolved/pie.py +2 -3
  43. dataface/core/compile/normalize_charts.py +4 -7
  44. dataface/core/compile/normalize_queries.py +58 -3
  45. dataface/core/compile/parser.py +4 -1
  46. dataface/core/compile/resolve/_resolve.py +6 -10
  47. dataface/core/compile/schema_renderers/json_schema.py +12 -3
  48. dataface/core/compile/source_map.py +96 -59
  49. dataface/core/compile/sql_guard.py +80 -22
  50. dataface/core/compile/style_cascade.py +44 -9
  51. dataface/core/compile/vega_lite/__init__.py +2 -1
  52. dataface/core/dashboard.py +5 -9
  53. dataface/core/defaults/themes/editorial.yaml +2 -2
  54. dataface/core/defaults/themes/neon.yaml +1 -1
  55. dataface/core/defaults/themes/plain.yaml +2 -2
  56. dataface/core/defaults/themes/stark.yaml +1 -1
  57. dataface/core/defaults/themes/vivid.yaml +5 -5
  58. dataface/core/diagnostics/__init__.py +2 -0
  59. dataface/core/diagnostics/ansi.py +22 -0
  60. dataface/core/diagnostics/codes_compile.py +32 -1
  61. dataface/core/diagnostics/codes_execute.py +34 -8
  62. dataface/core/diagnostics/codes_query.py +7 -5
  63. dataface/core/diagnostics/execution.py +16 -2
  64. dataface/core/execute/adapters/adapter_registry.py +2 -1
  65. dataface/core/execute/adapters/dbt_adapter.py +6 -36
  66. dataface/core/execute/adapters/dbt_utils.py +83 -4
  67. dataface/core/execute/adapters/duckdb_adapter.py +17 -1
  68. dataface/core/execute/adapters/sql_adapter.py +7 -59
  69. dataface/core/execute/adapters/sqlite_adapter.py +15 -3
  70. dataface/core/execute/dbt_jinja.py +9 -0
  71. dataface/core/render/chart/data_table_attachment.py +13 -2
  72. dataface/core/render/chart/emitters/_cartesian.py +8 -0
  73. dataface/core/render/chart/emitters/_channels.py +1 -1
  74. dataface/core/render/chart/emitters/_label_overlap.py +4 -0
  75. dataface/core/render/chart/emitters/_overlay.py +1 -1
  76. dataface/core/render/chart/emitters/bar.py +7 -4
  77. dataface/core/render/chart/emitters/heatmap.py +1 -0
  78. dataface/core/render/chart/emitters/line.py +2 -2
  79. dataface/core/render/chart/emitters/pie.py +1 -1
  80. dataface/core/render/chart/time_unit_detect.py +63 -4
  81. dataface/core/render/chart/type_inference.py +79 -100
  82. dataface/core/render/terminal_text.py +1 -7
  83. dataface/data/schemas/board-resolved/board-resolved.schema.json +429 -133
  84. dataface/integrations/highlighting.py +2 -2
  85. {dataface-0.3.1.dev78.dist-info → dataface-0.3.1.dev132.dist-info}/METADATA +3 -2
  86. {dataface-0.3.1.dev78.dist-info → dataface-0.3.1.dev132.dist-info}/RECORD +93 -88
  87. mdsvg/parser.py +108 -51
  88. mdsvg/renderer.py +43 -7
  89. mdsvg/types.py +8 -1
  90. /dataface/data/highlighting/{face/v1.json → face.json} +0 -0
  91. {dataface-0.3.1.dev78.dist-info → dataface-0.3.1.dev132.dist-info}/WHEEL +0 -0
  92. {dataface-0.3.1.dev78.dist-info → dataface-0.3.1.dev132.dist-info}/entry_points.txt +0 -0
  93. {dataface-0.3.1.dev78.dist-info → dataface-0.3.1.dev132.dist-info}/licenses/LICENSE +0 -0
@@ -394,7 +394,7 @@ queries:
394
394
 
395
395
  # Inline values (no database)
396
396
  sample:
397
- type: values # Optional — also implied by `values:` presence
397
+ type: values # Optional — implied by `rows:`, `columns:`, or `values:`
398
398
  columns: [name, score]
399
399
  values:
400
400
  - [Alice, 92.4]
@@ -37,6 +37,9 @@ CLI/MCP files that contain logic beyond parse-and-dispatch.
37
37
  """
38
38
 
39
39
  from dataface.agent_api import skills as skills
40
+ from dataface.agent_api._paths import (
41
+ project_sources_for_face as project_sources_for_face,
42
+ )
40
43
  from dataface.agent_api.describe import (
41
44
  DescribeFaceArgs,
42
45
  DescribeFaceResult,
@@ -47,6 +50,11 @@ from dataface.agent_api.init import (
47
50
  InitResult as InitResult,
48
51
  init_project as init_project,
49
52
  )
53
+ from dataface.agent_api.migrate import (
54
+ MigrateError as MigrateError,
55
+ MigrateSummary as MigrateSummary,
56
+ migrate_paths as migrate_paths,
57
+ )
50
58
  from dataface.agent_api.project_session import ProjectSession as ProjectSession
51
59
  from dataface.agent_api.query import QueryFaceResult, query_face
52
60
  from dataface.agent_api.schema_hints import (
@@ -106,6 +114,8 @@ __all__ = [
106
114
  "InMemoryFace",
107
115
  "InitResult",
108
116
  "LinkContext",
117
+ "MigrateError",
118
+ "MigrateSummary",
109
119
  "Project",
110
120
  "ProjectSession",
111
121
  "ProjectSourcesConfig",
@@ -126,6 +136,7 @@ __all__ = [
126
136
  "describe_face",
127
137
  "get_inter_font_path",
128
138
  "init_project",
139
+ "migrate_paths",
129
140
  "query_face",
130
141
  "render_dashboard",
131
142
  "schema_hints",
@@ -6,8 +6,9 @@ import os
6
6
  from dataclasses import dataclass
7
7
  from pathlib import Path, PurePosixPath
8
8
 
9
- # tach-ignore(agent_api->cli: runtime host-type guard in no_project_hint; the local-filesystem check needs a non-cli signal on Project — deferred)
9
+ # tach-ignore(agent_api->cli: runtime host-type guard in resolve_face_or_error / _relpath_for_fs_location; the local-filesystem check needs a non-cli signal on Project — deferred)
10
10
  from dataface.cli.filesystem_project import FilesystemProject
11
+ from dataface.core.compile.config import ProjectSourcesConfig
11
12
  from dataface.core.diagnostics import (
12
13
  ERR_FILE_NOT_FOUND,
13
14
  ERR_INTERNAL,
@@ -27,54 +28,6 @@ from dataface.core.project_roots import (
27
28
  )
28
29
 
29
30
 
30
- def resolve_project_dir(project_dir: Path | None) -> Path:
31
- """Resolve the caller's `project_dir`, walking up from cwd when omitted.
32
-
33
- Boundary helper: every agent_api / CLI callsite that today forwards a
34
- possibly-None `project_dir` into a core helper routes through this so the
35
- cwd read stays at the agent_api boundary, not inside `dataface/core`.
36
- """
37
- if project_dir is not None:
38
- return project_dir.resolve()
39
- cwd = Path.cwd().resolve()
40
- return find_dft_root(cwd) or cwd
41
-
42
-
43
- def resolve_project_dir_from_paths(
44
- paths: list[Path] | None, project_dir: Path | None
45
- ) -> Path:
46
- """Resolve project_dir for a multi-path CLI verb (validate / describe).
47
-
48
- Explicit `--project-dir` wins. Otherwise, when all supplied paths are
49
- absolute, walk up from the first path's parent to anchor on the project
50
- that contains the file. Otherwise fall back to a cwd walk.
51
- """
52
- if project_dir is not None:
53
- return project_dir.resolve()
54
- if paths and all(p.is_absolute() for p in paths):
55
- anchor = paths[0].parent.resolve()
56
- return find_dft_root(anchor) or anchor
57
- return resolve_project_dir(None)
58
-
59
-
60
- def no_project_hint(project: Project) -> str:
61
- """Return a hint string when no project marker is found, else empty string.
62
-
63
- Only walks the local filesystem for a `FilesystemProject` — a non-filesystem
64
- host's `project.root` is not a real on-disk project directory (it's the
65
- server cwd), so there is no marker to walk up to and nothing to hint about.
66
- """
67
- if not isinstance(project, FilesystemProject):
68
- return ""
69
- project_root = project.root
70
- if find_dft_root(project_root) is not None:
71
- return ""
72
- return (
73
- f" No Dataface project marker found at or above {project_root}."
74
- f" Run from inside a Dataface project, or set project_dir."
75
- )
76
-
77
-
78
31
  def resolve_face_relpath(relpath: PurePosixPath, project: Project) -> ProjectPath:
79
32
  """Project-relative face identity -> ProjectPath, with the faces-first retry.
80
33
 
@@ -179,7 +132,6 @@ def resolve_face_or_error(path: Path, project: Project) -> FaceFile | Diagnostic
179
132
  return DatafaceError.from_code(
180
133
  ERR_FILE_NOT_FOUND,
181
134
  path=resolved.relpath,
182
- hint=no_project_hint(project),
183
135
  ).to_diagnostic(file=resolved.relpath)
184
136
  if (
185
137
  isinstance(project, FilesystemProject)
@@ -269,3 +221,26 @@ def build_yaml_render_context(
269
221
  project_root=project_root,
270
222
  output_dir=project_root,
271
223
  )
224
+
225
+
226
+ def project_sources_for_face(face_path: Path) -> ProjectSourcesConfig:
227
+ """The `sources:` registry of the project owning ``face_path``.
228
+
229
+ Editor surfaces compile buffer text rather than a file on disk, so they
230
+ have no `Project` to hand `compile()` — but without the source registry
231
+ compile cannot tell a query's SQL dialect, and dialect is what makes a SQL
232
+ parse finding trustworthy enough to report.
233
+
234
+ A face outside any project needs no special case: `find_project_root`
235
+ falls back to the starting directory, which has no `dataface.yml`, and the
236
+ registry comes back empty — compile then stays silent about SQL syntax
237
+ rather than guessing a dialect.
238
+
239
+ A `dataface.yml` that exists but does not load raises, deliberately. That
240
+ is a real fault at a real location, and swallowing it here would decide on
241
+ the author's behalf that a broken project config is invisible — while
242
+ handing the caller an empty registry indistinguishable from "no project".
243
+ Callers attribute the failure; they do not get to pretend it did not
244
+ happen.
245
+ """
246
+ return FilesystemProject(find_project_root(face_path.parent, None)).sources
@@ -70,7 +70,7 @@ class RenderBoardArtifactResult(BaseModel):
70
70
 
71
71
  def _read_or_error(path: Path) -> bytes:
72
72
  if not path.exists():
73
- raise DatafaceError.from_code(ERR_FILE_NOT_FOUND, path=str(path), hint="")
73
+ raise DatafaceError.from_code(ERR_FILE_NOT_FOUND, path=str(path))
74
74
  return path.read_bytes()
75
75
 
76
76
 
@@ -197,19 +197,15 @@ def describe_face(path: Path, *, project: Project) -> DescribeFaceResult:
197
197
  ],
198
198
  )
199
199
 
200
- return _describe_resolved(resolved, project=project)
200
+ return _describe_resolved(resolved)
201
201
 
202
202
 
203
- def _describe_resolved(
204
- resolved: ProjectPath, *, project: Project
205
- ) -> DescribeFaceResult:
203
+ def _describe_resolved(resolved: ProjectPath) -> DescribeFaceResult:
206
204
  """Describe an already-resolved face path — no re-resolution."""
207
- from dataface.agent_api._paths import no_project_hint
208
205
  from dataface.core.compile.compiler import compile_file
209
206
  from dataface.core.diagnostics.base import DatafaceError
210
207
 
211
208
  if not resolved.exists():
212
- hint = no_project_hint(project)
213
209
  return DescribeFaceResult(
214
210
  success=False,
215
211
  path=Path(resolved.relpath),
@@ -217,7 +213,6 @@ def _describe_resolved(
217
213
  DatafaceError.from_code(
218
214
  ERR_FILE_NOT_FOUND,
219
215
  path=resolved.relpath,
220
- hint=hint,
221
216
  ).to_diagnostic()
222
217
  ],
223
218
  )
@@ -349,7 +344,7 @@ def _describe_one_path(
349
344
  ]
350
345
 
351
346
  if resolved.is_yaml:
352
- return [_describe_resolved(resolved, project=project)]
347
+ return [_describe_resolved(resolved)]
353
348
 
354
349
  faces = sorted(
355
350
  pf
@@ -371,4 +366,4 @@ def _describe_one_path(
371
366
  ],
372
367
  )
373
368
  ]
374
- return [_describe_resolved(pf, project=project) for pf in faces]
369
+ return [_describe_resolved(pf) for pf in faces]
@@ -15,7 +15,7 @@ AuthoredFace (dataface) definition from YAML.
15
15
  | `source` | str | ✓ | Default source name for all queries in this face. Inheritable via meta.yaml cascade. |
16
16
  | `cache` | [Cache](#cache) | ✓ | Cache policy for every query in this dashboard, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts the whole dashboard out. Inheritable via the meta.yaml cascade. |
17
17
  | `variables` | dict[str, [Variable](#variable) \| VariableRef] | ✓ | Variable definitions for dynamic filtering and UI controls. |
18
- | `queries` | dict[str, [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef] | ✓ | Named query definitions (SQL, CSV, MetricFlow, HTTP, etc.). |
18
+ | `queries` | dict[str, [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef] | ✓ | Named query definitions (SQL, CSV, MetricFlow, HTTP, etc.). |
19
19
  | `charts` | dict[str, [BarChart](#barchart) \| [LineChart](#linechart) \| [AreaChart](#areachart) \| [ScatterChart](#scatterchart) \| [HeatmapChart](#heatmapchart) \| [PieChart](#piechart) \| [KpiChart](#kpichart) \| [TableChart](#tablechart) \| [PointMapChart](#pointmapchart) \| [GeoshapeChart](#geoshapechart) \| [CalloutChart](#calloutchart) \| [SparkBarChart](#sparkbarchart) \| ChartRef] | ✓ | Named chart definitions. When no explicit layout is present, charts render as an implicit row layout in authored order. |
20
20
  | `rows` | list[str \| [Face](#face) \| [BarChart](#barchart) \| [LineChart](#linechart) \| [AreaChart](#areachart) \| [ScatterChart](#scatterchart) \| [HeatmapChart](#heatmapchart) \| [PieChart](#piechart) \| [KpiChart](#kpichart) \| [TableChart](#tablechart) \| [PointMapChart](#pointmapchart) \| [GeoshapeChart](#geoshapechart) \| [CalloutChart](#calloutchart) \| [SparkBarChart](#sparkbarchart) \| [ForeachItem](#foreachitem) \| dict[str, [BarChart](#barchart) \| [LineChart](#linechart) \| [AreaChart](#areachart) \| [ScatterChart](#scatterchart) \| [HeatmapChart](#heatmapchart) \| [PieChart](#piechart) \| [KpiChart](#kpichart) \| [TableChart](#tablechart) \| [PointMapChart](#pointmapchart) \| [GeoshapeChart](#geoshapechart) \| [CalloutChart](#calloutchart) \| [SparkBarChart](#sparkbarchart)]] | ✓ | Vertical stack layout: list of chart names or inline chart/face definitions. |
21
21
  | `cols` | list[str \| [Face](#face) \| [BarChart](#barchart) \| [LineChart](#linechart) \| [AreaChart](#areachart) \| [ScatterChart](#scatterchart) \| [HeatmapChart](#heatmapchart) \| [PieChart](#piechart) \| [KpiChart](#kpichart) \| [TableChart](#tablechart) \| [PointMapChart](#pointmapchart) \| [GeoshapeChart](#geoshapechart) \| [CalloutChart](#calloutchart) \| [SparkBarChart](#sparkbarchart) \| [ForeachItem](#foreachitem) \| dict[str, [BarChart](#barchart) \| [LineChart](#linechart) \| [AreaChart](#areachart) \| [ScatterChart](#scatterchart) \| [HeatmapChart](#heatmapchart) \| [PieChart](#piechart) \| [KpiChart](#kpichart) \| [TableChart](#tablechart) \| [PointMapChart](#pointmapchart) \| [GeoshapeChart](#geoshapechart) \| [CalloutChart](#calloutchart) \| [SparkBarChart](#sparkbarchart)]] | ✓ | Horizontal layout: list of chart names or inline chart/face definitions. |
@@ -55,7 +55,7 @@ Variable definition from YAML.
55
55
  | `visible` | bool | ✓ | When False, the variable is not rendered in the UI but can still be set via URL params. |
56
56
  | `enabled` | bool \| str \| [SingleRowBoolProbe](#singlerowboolprobe) | ✓ | Enable this control. Accepts: static bool; a variable name or Jinja boolean expression string (no {{ }} required — bare names auto-wrap); or a {query, column} form that reads a single boolean cell from a named query. None = enabled. Absent variable in a string expression raises (use a default). |
57
57
  | `column` | str | ✓ | Column name in the query result to use as option values. |
58
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) | ✓ | Query name or inline query definition for populating options. |
58
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) | ✓ | Query name or inline query definition for populating options. |
59
59
  | `dimension` | str | ✓ | MetricFlow dimension name for populating options. |
60
60
  | `measure` | str | ✓ | MetricFlow measure name for populating options. |
61
61
  | `model` | str | ✓ | dbt model name for populating options. |
@@ -104,12 +104,12 @@ REST API query.
104
104
 
105
105
  | Field | Type | Optional | Description |
106
106
  |-------|------|:--------:|-------------|
107
+ | `url` | str | | HTTP endpoint URL for REST API queries. |
107
108
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
108
109
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
109
110
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
110
111
  | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
111
112
  | `type` | enum: "http" | ✓ | Query adapter type. |
112
- | `url` | str | ✓ | HTTP endpoint URL for REST API queries. |
113
113
  | `method` | enum: "GET", "POST", "PUT", "DELETE", "PATCH" | ✓ | HTTP method (GET, POST, PUT, DELETE, PATCH). |
114
114
  | `headers` | dict[str, str] | ✓ | HTTP request headers. |
115
115
  | `params` | dict[str, Any] | ✓ | HTTP query string parameters. |
@@ -119,18 +119,30 @@ REST API query.
119
119
 
120
120
  <a id="valuesquery"></a>
121
121
  ## ValuesQuery
122
- Inline rows embedded in YAML.
122
+ Inline data authored as a list of row mappings.
123
123
 
124
124
  | Field | Type | Optional | Description |
125
125
  |-------|------|:--------:|-------------|
126
+ | `rows` | list[dict[str, Any]] | | Inline data rows (list of row dicts). Use the compact columns-and-values form instead when row values are positional. |
127
+ | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
128
+ | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
129
+ | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
130
+ | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
131
+ | `type` | enum: "values" | ✓ | Query adapter type. |
132
+
133
+ <a id="compactvaluesquery"></a>
134
+ ## CompactValuesQuery
135
+ Inline data authored as column names plus positional row values.
136
+
137
+ | Field | Type | Optional | Description |
138
+ |-------|------|:--------:|-------------|
139
+ | `columns` | list[str] | | Column names for the compact syntax. Every compact values query also requires values. |
140
+ | `values` | list[list[Any]] | | Inline row-oriented data (list of lists) for the compact syntax. Every compact values query also requires columns. |
126
141
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
127
142
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
128
143
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
129
144
  | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
130
145
  | `type` | enum: "values" | ✓ | Query adapter type. |
131
- | `rows` | list[dict[str, Any]] | ✓ | Inline data rows (list of row dicts). |
132
- | `values` | list[list[Any]] | ✓ | Inline row-oriented data (list of lists) for the compact 'columns + values' syntax. |
133
- | `columns` | list[str] | ✓ | Column names for the compact 'columns + values' syntax. |
134
146
 
135
147
  <a id="schemaquery"></a>
136
148
  ## SchemaQuery
@@ -149,17 +161,40 @@ dbt source schema query.
149
161
 
150
162
  <a id="lookmlquery"></a>
151
163
  ## LookmlQuery
152
- LookML semantic query — compiled to SQL through likeml at compile time.
164
+ LookML query rooted at one explore.
165
+
166
+ | Field | Type | Optional | Description |
167
+ |-------|------|:--------:|-------------|
168
+ | `explore` | str | | LookML explore name. Presence of this key infers type: lookml. Use a merge query instead to combine multiple sources. |
169
+ | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
170
+ | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
171
+ | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
172
+ | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
173
+ | `type` | enum: "lookml" | ✓ | Query adapter type. |
174
+ | `fields` | list[str] | ✓ | Qualified 'view.field' refs to select. Optional at authoring time: collected from chart channels when absent. |
175
+ | `sorts` | list[str] | ✓ | Looker sort clauses, e.g. ['view.field', 'view.other desc']. Lowered to ORDER BY via likeml. |
176
+ | `pivots` | list[str] | ✓ | Qualified 'view.field' refs to pivot on. |
177
+ | `pivots_unwrapped` | bool | ✓ | Which of Looker's two pivot SQL shapes to emit: False/None = wrapped (5-layer DENSE_RANK), True = plain GROUP BY. |
178
+ | `total` | bool | ✓ | Looker grand-totals flag. |
179
+ | `subtotals` | list[str] | ✓ | Dimension refs to subtotal via GROUP BY ROLLUP. |
180
+ | `filter_expression` | str | ✓ | Looker query-level free-form boolean filter over ${view.field} refs. |
181
+ | `dynamic_fields` | list[dict[str, Any]] | ✓ | Looker dynamic fields: custom measures / custom dimensions synthesized into SQL. Table calculations compose as a derived wrapper query, not here. |
182
+ | `limit` | int | ✓ | Maximum number of rows returned. |
183
+ | `column_limit` | int | ✓ | Looker column_limit — caps wrapped-pivot columns. |
184
+ | `filters` | dict[str, Any] | ✓ | RAW Looker filter expressions keyed by 'view.field' (e.g. "13 months", "gold") — translated to SQL predicates by likeml. |
185
+
186
+ <a id="lookmlmergequery"></a>
187
+ ## LookmlMergeQuery
188
+ LookML query that merges multiple sources.
153
189
 
154
190
  | Field | Type | Optional | Description |
155
191
  |-------|------|:--------:|-------------|
192
+ | `merge` | list[dict[str, Any]] | | LookML merge sources — a list of source specs. Each is either an explore spec ({explore, fields, filters?, merge_on?}) or a query reference ({query, columns, merge_on?}) for a derived source such as a table-calc wrapper. First is the primary; each secondary's `merge_on` maps its key to the primary's. Presence of this key infers type: lookml. Compiled to a FULL OUTER JOIN via likeml. |
156
193
  | `source` | str \| dict[str, Any] | ✓ | Source name reference, or an inline file path (e.g. `./data/sales.csv`). File paths are detected by `/` or data file extension. An inline connection-bearing dict (`{type: postgres, ...}`) is rejected at compile time — reference a named source instead. |
157
194
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
158
195
  | `ignore` | list[str] | ✓ | Diagnostic codes to suppress for this query (e.g., ['WARN-FANOUT-RISK', 'WARN-REAGGREGATION']). |
159
196
  | `cache` | [Cache](#cache) | ✓ | Cache policy override for this query, e.g. cache: 5m — refines the policy inherited from the source and project scopes; cache: false opts out of result caching entirely. Queries with cache: false cannot be used as {{ queries.X.cache }} upstream references. |
160
197
  | `type` | enum: "lookml" | ✓ | Query adapter type. |
161
- | `explore` | str | ✓ | LookML explore name. Presence of this key infers type: lookml. |
162
- | `merge` | list[dict[str, Any]] | ✓ | LookML merge sources — a list of source specs. Each is either an explore spec ({explore, fields, filters?, merge_on?}) or a query reference ({query, columns, merge_on?}) for a derived source such as a table-calc wrapper. First is the primary; each secondary's `merge_on` maps its key to the primary's. Presence of this key infers type: lookml. Compiled to a FULL OUTER JOIN via likeml. |
163
198
  | `fields` | list[str] | ✓ | Qualified 'view.field' refs to select. Optional at authoring time: collected from chart channels when absent. |
164
199
  | `sorts` | list[str] | ✓ | Looker sort clauses, e.g. ['view.field', 'view.other desc']. Lowered to ORDER BY via likeml. |
165
200
  | `pivots` | list[str] | ✓ | Qualified 'view.field' refs to pivot on. |
@@ -182,7 +217,7 @@ Authored patch for bar and histogram charts.
182
217
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
183
218
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
184
219
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
185
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
220
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
186
221
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
187
222
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
188
223
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -211,7 +246,7 @@ Authored patch for line charts.
211
246
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
212
247
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
213
248
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
214
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
249
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
215
250
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
216
251
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
217
252
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -240,7 +275,7 @@ Authored patch for area charts.
240
275
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
241
276
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
242
277
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
243
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
278
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
244
279
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
245
280
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
246
281
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -269,7 +304,7 @@ Authored patch for scatter charts.
269
304
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
270
305
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
271
306
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
272
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
307
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
273
308
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
274
309
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
275
310
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -299,7 +334,7 @@ Authored patch for heatmap charts.
299
334
  | `type` | enum: "heatmap" | | Heatmap chart type. |
300
335
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
301
336
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
302
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
337
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
303
338
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
304
339
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
305
340
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -328,7 +363,7 @@ Authored patch for pie and donut charts.
328
363
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
329
364
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
330
365
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
331
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
366
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
332
367
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
333
368
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
334
369
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -351,7 +386,7 @@ Authored patch for KPI (key performance indicator) charts.
351
386
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
352
387
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
353
388
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
354
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
389
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
355
390
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
356
391
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
357
392
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -371,7 +406,7 @@ Authored patch for table charts.
371
406
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
372
407
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
373
408
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
374
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
409
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
375
410
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
376
411
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
377
412
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -392,7 +427,7 @@ Authored patch for point_map and bubble_map charts.
392
427
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
393
428
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
394
429
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
395
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
430
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
396
431
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
397
432
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
398
433
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -409,7 +444,7 @@ Authored patch for point_map and bubble_map charts.
409
444
  | `latitude` | str | ✓ | Column containing latitude values for point/bubble maps. |
410
445
  | `longitude` | str | ✓ | Column containing longitude values for point/bubble maps. |
411
446
  | `size` | str | ✓ | Data column used to scale bubble radius (quantitative). Only meaningful on bubble_map. |
412
- | `basemap` | [BasemapConfig](#basemapconfig) | ✓ | Tile-layer configuration for the map background. |
447
+ | `basemap` | [BasemapConfig](#basemapconfig) | ✓ | Styled geographic background layer. |
413
448
  | `style` | [PointMapChartStyle](#pointmapchartstyle) | ✓ | Chart-local style overrides. |
414
449
 
415
450
  <a id="geoshapechart"></a>
@@ -422,7 +457,7 @@ Authored patch for map and geoshape charts.
422
457
  | `conditional_formatting` | dict[str, [FieldConditionalFormatting](#fieldconditionalformatting)] | ✓ | Discrete rule-driven style overrides indexed by column name. |
423
458
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
424
459
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
425
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
460
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
426
461
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
427
462
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
428
463
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -459,7 +494,7 @@ Authored patch for spark_bar charts (compact horizontal bars).
459
494
  | `type` | enum: "spark_bar" | | SparkBar chart type. |
460
495
  | `id` | str | ✓ | Explicit chart ID (auto-generated from the chart's YAML key if omitted). |
461
496
  | `description` | str | ✓ | Human-readable description used by AI search and context tooltips. |
462
- | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
497
+ | `query` | str \| [SqlQuery](#sqlquery) \| [MetricflowQuery](#metricflowquery) \| [HttpQuery](#httpquery) \| [ValuesQuery](#valuesquery) \| [CompactValuesQuery](#compactvaluesquery) \| [SchemaQuery](#schemaquery) \| [LookmlQuery](#lookmlquery) \| [LookmlMergeQuery](#lookmlmergequery) \| QueryRef | ✓ | Named query reference, inline AuthoredQuery, or SQL string shorthand. |
463
498
  | `link` | str | ✓ | Click-through URL template for drill-down links. |
464
499
  | `model` | str | ✓ | Semantic-layer entry point, two dot-joined names: 'model.explore' for a LookML source, or 'source.semantic_model' for a dbt_profile (MetricFlow) source. Channel fields name the model's fields directly; roles are inferred from the LookML catalog / dbt semantic manifest. Mutually exclusive with `query:`. |
465
500
  | `warnings_ignore` | list[str] | ✓ | Codes of render warnings to suppress for this chart. |
@@ -929,14 +964,11 @@ Authored overlay for TableChartStyle. Table chart style overrides layered on top
929
964
 
930
965
  <a id="basemapconfig"></a>
931
966
  ## BasemapConfig
932
- Tile-layer configuration for point_map and bubble_map charts.
967
+ Styled geographic background layer for point_map and bubble_map charts.
933
968
 
934
969
  | Field | Type | Optional | Description |
935
970
  |-------|------|:--------:|-------------|
936
- | `type` | str | ✓ | Tile provider type (e.g. 'mapbox', 'raster'). |
937
- | `style` | str | ✓ | Tile style URL (e.g. Mapbox style URI). |
938
971
  | `source` | str | ✓ | Named geographic boundary source (e.g. 'us-states') for overlay rendering. |
939
- | `attribution` | str | ✓ | Attribution text for the tile provider. |
940
972
  | `fill` | str | ✓ | Fill color for geographic boundary overlay. |
941
973
  | `stroke` | str | ✓ | Stroke color for geographic boundary overlay. |
942
974
 
@@ -0,0 +1,101 @@
1
+ """Typed API for rewriting retained face YAML grammars."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import PurePosixPath
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field
8
+
9
+ from dataface.agent_api._paths import resolve_face_relpath
10
+ from dataface.core.project import FACES_SUBDIR, Project, ProjectPath
11
+
12
+
13
+ class MigrateError(BaseModel):
14
+ """One file that could not be migrated without manual action."""
15
+
16
+ model_config = ConfigDict(frozen=True, extra="forbid")
17
+
18
+ path: PurePosixPath = Field(
19
+ description="Project-relative path that requires manual action."
20
+ )
21
+ message: str = Field(description="User-ready explanation of the migration failure.")
22
+
23
+
24
+ class MigrateSummary(BaseModel):
25
+ """Results of a migration run, separated by outcome."""
26
+
27
+ model_config = ConfigDict(frozen=True, extra="forbid")
28
+
29
+ updated: list[PurePosixPath] = Field(
30
+ default_factory=list,
31
+ description="Files that were migrated, or would be migrated during a dry run.",
32
+ )
33
+ current: list[PurePosixPath] = Field(
34
+ default_factory=list,
35
+ description="Files that already satisfy the current YAML grammar.",
36
+ )
37
+ errors: list[MigrateError] = Field(
38
+ default_factory=list,
39
+ description="Files that require manual migration.",
40
+ )
41
+
42
+ @property
43
+ def success(self) -> bool:
44
+ return not self.errors
45
+
46
+
47
+ def migrate_paths(
48
+ paths: list[PurePosixPath] | None,
49
+ *,
50
+ project: Project,
51
+ dry_run: bool,
52
+ ) -> MigrateSummary:
53
+ """Migrate named face files/directories, or all public faces by default."""
54
+ updated: list[PurePosixPath] = []
55
+ current: list[PurePosixPath] = []
56
+ errors: list[MigrateError] = []
57
+ for face_path in _migration_paths(paths, project):
58
+ path = PurePosixPath(face_path.relpath)
59
+ try:
60
+ original = face_path.read_text()
61
+ from dataface.core.compile.migrations import migrate_face_yaml_text
62
+ from dataface.core.compile.parser import parse_yaml
63
+
64
+ migrated = migrate_face_yaml_text(original)
65
+ if migrated == original:
66
+ current.append(path)
67
+ continue
68
+ parse_yaml(migrated)
69
+ if not dry_run:
70
+ project.write_text(face_path.relpath, migrated)
71
+ updated.append(path)
72
+ except (OSError, ValueError) as exc:
73
+ errors.append(MigrateError(path=path, message=str(exc)))
74
+ return MigrateSummary(
75
+ updated=updated,
76
+ current=current,
77
+ errors=errors,
78
+ )
79
+
80
+
81
+ def _migration_paths(
82
+ paths: list[PurePosixPath] | None, project: Project
83
+ ) -> list[ProjectPath]:
84
+ if not paths:
85
+ return [
86
+ path
87
+ for path in project.iter_faces(under=FACES_SUBDIR)
88
+ if path.is_yaml and not path.is_private and not path.is_meta
89
+ ]
90
+ resolved: list[ProjectPath] = []
91
+ for path in paths:
92
+ selected = resolve_face_relpath(path, project)
93
+ if selected.is_yaml:
94
+ resolved.append(selected)
95
+ continue
96
+ resolved.extend(
97
+ child
98
+ for child in project.iter_faces(under=selected.relpath)
99
+ if child.is_yaml and not child.is_private and not child.is_meta
100
+ )
101
+ return resolved
@@ -6,7 +6,7 @@ import importlib.util
6
6
  import sys
7
7
  from dataclasses import dataclass
8
8
  from functools import cached_property
9
- from pathlib import Path
9
+ from pathlib import Path, PurePosixPath
10
10
  from typing import TYPE_CHECKING, Any, Literal, cast, overload
11
11
 
12
12
  if sys.version_info >= (3, 11):
@@ -20,11 +20,13 @@ from dataface.agent_api import (
20
20
  board_artifact as _board_artifact,
21
21
  describe as _describe,
22
22
  describe_query as _describe_query,
23
+ migrate as _migrate,
23
24
  query as _query,
24
25
  search as _search,
25
26
  validate as _validate,
26
27
  )
27
28
  from dataface.agent_api.board_artifact import EmitBoardArtifactResult
29
+ from dataface.agent_api.migrate import MigrateSummary
28
30
  from dataface.agent_api.validate import (
29
31
  ValidateResult,
30
32
  )
@@ -291,6 +293,16 @@ class ProjectSession:
291
293
  results = _validate.validate_paths(paths, project=self.project)
292
294
  return _validate.annotate_with_data_lint(results, project=self.project)
293
295
 
296
+ def migrate_paths(
297
+ self, paths: list[PurePosixPath] | None, *, dry_run: bool
298
+ ) -> MigrateSummary:
299
+ """Rewrite supported retired YAML syntax without touching current files."""
300
+ return _migrate.migrate_paths(
301
+ paths,
302
+ project=self.project,
303
+ dry_run=dry_run,
304
+ )
305
+
294
306
  @overload
295
307
  def validate_query(
296
308
  self,
@@ -280,12 +280,7 @@ def query_face(
280
280
 
281
281
  resolved_display = PurePosixPath(file_path.relpath)
282
282
  if not file_path.exists():
283
- from dataface.agent_api._paths import no_project_hint
284
-
285
- hint = no_project_hint(project)
286
- return _fail(
287
- name, resolved_display, [f"File not found: {resolved_display}{hint}"]
288
- )
283
+ return _fail(name, resolved_display, [f"File not found: {resolved_display}"])
289
284
 
290
285
  compile_result = compile_file(file_path.read_face())
291
286
  if not compile_result.success: