dataface 0.4.1.dev134__py3-none-any.whl → 0.4.1.dev304__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 (334) hide show
  1. dataface/DATAFACE_SYNTAX.md +11 -9
  2. dataface/__init__.py +7 -7
  3. dataface/agent_api/__init__.py +23 -20
  4. dataface/agent_api/_init_templates/README.md +3 -3
  5. dataface/agent_api/_init_templates/guide.yaml +1 -1
  6. dataface/agent_api/_init_templates/meta.yaml +4 -4
  7. dataface/agent_api/_link_scan.py +7 -7
  8. dataface/agent_api/_paths.py +14 -14
  9. dataface/agent_api/board_artifact.py +3 -3
  10. dataface/agent_api/{dashboards.py → boards.py} +45 -28
  11. dataface/agent_api/cache.py +1 -1
  12. dataface/agent_api/data_paths.py +2 -3
  13. dataface/agent_api/describe.py +25 -17
  14. dataface/agent_api/docs/yaml-reference.md +22 -23
  15. dataface/agent_api/file_refs.py +1 -1
  16. dataface/agent_api/files.py +21 -21
  17. dataface/agent_api/init.py +11 -9
  18. dataface/agent_api/migrate.py +7 -6
  19. dataface/agent_api/pack.py +19 -18
  20. dataface/agent_api/project_session.py +11 -11
  21. dataface/agent_api/query.py +65 -11
  22. dataface/agent_api/search.py +31 -29
  23. dataface/agent_api/serve.py +3 -5
  24. dataface/agent_api/skills.py +1 -1
  25. dataface/agent_api/surface_aliases.yaml +27 -27
  26. dataface/agent_api/validate.py +40 -32
  27. dataface/ai/__init__.py +4 -13
  28. dataface/ai/agent.py +6 -6
  29. dataface/ai/context.py +10 -5
  30. dataface/ai/llm.py +127 -15
  31. dataface/ai/mcp/__init__.py +8 -8
  32. dataface/ai/mcp/server.py +16 -16
  33. dataface/ai/prompts.py +9 -9
  34. dataface/ai/skills/analyst-runbook/SKILL.md +5 -5
  35. dataface/ai/skills/{dashboard-build → board-build}/SKILL.md +21 -27
  36. dataface/ai/skills/{dashboard-design → board-design}/SKILL.md +6 -12
  37. dataface/ai/skills/{dashboard-pack-scaffolding → board-pack-scaffolding}/SKILL.md +14 -14
  38. dataface/ai/skills/{dashboard-replicate → board-replicate}/SKILL.md +7 -15
  39. dataface/ai/skills/{dashboard-review → board-review}/SKILL.md +10 -14
  40. dataface/ai/skills/{dashboard-structural-review → board-structural-review}/SKILL.md +10 -10
  41. dataface/ai/skills/{dashboard-visual-review → board-visual-review}/SKILL.md +8 -8
  42. dataface/ai/skills/data-exploration/SKILL.md +1 -1
  43. dataface/ai/skills/dataface-mcp-setup/SKILL.md +10 -10
  44. dataface/ai/skills/dataface-troubleshooting/SKILL.md +7 -7
  45. dataface/ai/skills/drill-down-link/SKILL.md +1 -1
  46. dataface/ai/skills/report-design/SKILL.md +3 -3
  47. dataface/ai/tool_schemas.py +36 -43
  48. dataface/ai/tools/__init__.py +37 -71
  49. dataface/cli/_error_format.py +3 -4
  50. dataface/cli/_extras.py +23 -10
  51. dataface/cli/_project.py +11 -10
  52. dataface/cli/commands/board_artifact.py +1 -1
  53. dataface/cli/commands/extension.py +1 -0
  54. dataface/cli/commands/init.py +1 -1
  55. dataface/cli/commands/inspect.py +4 -3
  56. dataface/cli/commands/mcp_init.py +5 -5
  57. dataface/cli/commands/migrate.py +2 -2
  58. dataface/cli/commands/query.py +5 -5
  59. dataface/cli/commands/render.py +8 -8
  60. dataface/cli/commands/search.py +1 -1
  61. dataface/cli/commands/serve.py +8 -17
  62. dataface/cli/commands/skills_init.py +1 -2
  63. dataface/cli/filesystem_project.py +7 -7
  64. dataface/cli/main.py +70 -54
  65. dataface/core/__init__.py +8 -8
  66. dataface/core/{dashboard.py → board.py} +36 -36
  67. dataface/core/compile/__init__.py +16 -16
  68. dataface/core/compile/authoring_warnings.py +1 -1
  69. dataface/core/compile/board_artifact.py +11 -11
  70. dataface/core/compile/chart_focus.py +5 -5
  71. dataface/core/compile/compiler.py +143 -114
  72. dataface/core/compile/config.py +107 -42
  73. dataface/core/compile/data_table.py +31 -1
  74. dataface/core/compile/face_warnings.py +11 -11
  75. dataface/core/compile/introspection.py +9 -9
  76. dataface/core/compile/labels_env.py +14 -2
  77. dataface/core/compile/markdown.py +3 -3
  78. dataface/core/compile/merge.py +68 -35
  79. dataface/core/compile/meta.py +6 -6
  80. dataface/core/compile/migrations/__init__.py +2 -0
  81. dataface/core/compile/migrations/migrations.py +462 -51
  82. dataface/core/compile/migrations/versions/current.py +29 -0
  83. dataface/core/compile/migrations/versions/v0_4_0.py +2 -2
  84. dataface/core/compile/migrations/versions/v0_5_0.py +40 -0
  85. dataface/core/compile/models/{face → board}/authored.py +23 -24
  86. dataface/core/compile/models/{face → board}/normalized.py +47 -44
  87. dataface/core/compile/models/{face → board}/patch.py +8 -8
  88. dataface/core/compile/models/{face → board}/resolved.py +12 -12
  89. dataface/core/compile/models/cache.py +2 -2
  90. dataface/core/compile/models/chart/resolved/_base.py +2 -2
  91. dataface/core/compile/models/chart/resolved/_channel.py +1 -1
  92. dataface/core/compile/models/chart/resolved/pie.py +10 -0
  93. dataface/core/compile/models/config.py +26 -8
  94. dataface/core/compile/models/primitives.py +16 -9
  95. dataface/core/compile/models/query/authored.py +2 -2
  96. dataface/core/compile/models/refs.py +3 -3
  97. dataface/core/compile/models/source.py +3 -3
  98. dataface/core/compile/models/style/authored/table.py +1 -1
  99. dataface/core/compile/models/style/context.py +20 -16
  100. dataface/core/compile/models/style/resolved/__init__.py +9 -52
  101. dataface/core/compile/models/style/resolved/_base.py +69 -7
  102. dataface/core/compile/models/style/resolved/_marks.py +3 -2
  103. dataface/core/compile/models/style/theme/__init__.py +3 -3
  104. dataface/core/compile/models/style/theme/board.py +7 -7
  105. dataface/core/compile/models/style/theme/legend.py +0 -3
  106. dataface/core/compile/models/style/theme/style.py +2 -2
  107. dataface/core/compile/models/style/theme/table.py +7 -1
  108. dataface/core/compile/models/variable/authored.py +5 -1
  109. dataface/core/compile/normalize_charts.py +5 -5
  110. dataface/core/compile/normalize_layout.py +36 -36
  111. dataface/core/compile/normalize_queries.py +5 -7
  112. dataface/core/compile/normalize_variables.py +23 -16
  113. dataface/core/compile/normalizer.py +137 -65
  114. dataface/core/compile/parameterized.py +9 -10
  115. dataface/core/compile/parser.py +15 -15
  116. dataface/core/compile/resolve/__init__.py +9 -32
  117. dataface/core/compile/resolve/chart/__init__.py +56 -0
  118. dataface/core/compile/resolve/{_axes.py → chart/_axes.py} +183 -53
  119. dataface/core/compile/resolve/{_channels.py → chart/_channels.py} +8 -8
  120. dataface/core/compile/resolve/{_dispatch.py → chart/_dispatch.py} +11 -11
  121. dataface/core/compile/resolve/{_domain.py → chart/_domain.py} +70 -26
  122. dataface/core/compile/resolve/{_kwargs.py → chart/_kwargs.py} +3 -6
  123. dataface/core/compile/resolve/{_layers.py → chart/_layers.py} +3 -3
  124. dataface/core/compile/resolve/{_links.py → chart/_links.py} +1 -1
  125. dataface/core/compile/resolve/{_marks.py → chart/_marks.py} +1 -3
  126. dataface/core/compile/resolve/{_palette.py → chart/_palette.py} +7 -5
  127. dataface/core/compile/resolve/{_table.py → chart/_table.py} +7 -9
  128. dataface/core/compile/resolve/{adaptive_stroke.py → chart/adaptive_stroke.py} +8 -4
  129. dataface/core/compile/resolve/{area.py → chart/area.py} +75 -33
  130. dataface/core/compile/resolve/{bar.py → chart/bar.py} +235 -37
  131. dataface/core/compile/resolve/{geo.py → chart/geo.py} +5 -5
  132. dataface/core/compile/resolve/{heatmap.py → chart/heatmap.py} +30 -20
  133. dataface/core/compile/{label_data.py → resolve/chart/label_data.py} +3 -3
  134. dataface/core/compile/resolve/{line.py → chart/line.py} +135 -54
  135. dataface/core/compile/resolve/{pie.py → chart/pie.py} +32 -15
  136. dataface/core/compile/resolve/{pie_attachment.py → chart/pie_attachment.py} +32 -9
  137. dataface/core/compile/resolve/{scatter.py → chart/scatter.py} +30 -13
  138. dataface/core/compile/resolve/{simple.py → chart/simple.py} +7 -10
  139. dataface/core/compile/resolve/style/__init__.py +49 -0
  140. dataface/core/compile/resolve/style/axis_cascade.py +943 -0
  141. dataface/core/compile/resolve/style/board.py +524 -0
  142. dataface/core/compile/{style_cascade.py → resolve/style/chart_context.py} +10 -385
  143. dataface/core/compile/{inherit_graph.py → resolve/style/inherit_graph.py} +2 -2
  144. dataface/core/compile/{inherit_resolver.py → resolve/style/inherit_resolver.py} +4 -4
  145. dataface/core/compile/resolve/style/scale.py +183 -0
  146. dataface/core/compile/resolve/style/tokens.py +298 -0
  147. dataface/core/compile/{typography.py → resolve/style/typography.py} +13 -13
  148. dataface/core/compile/schema_renderers/board_resolved.py +11 -11
  149. dataface/core/compile/schema_renderers/highlight_manifest.py +2 -2
  150. dataface/core/compile/schema_renderers/prompt.py +3 -3
  151. dataface/core/compile/schema_renderers/textmate_grammar.py +1 -1
  152. dataface/core/compile/schema_renderers/vscode_schema.py +4 -2
  153. dataface/core/compile/schema_renderers/yaml_schema.py +3 -3
  154. dataface/core/compile/schema_renderers/yaml_schema_catalog.py +24 -4
  155. dataface/core/compile/single_series_allocation.py +8 -8
  156. dataface/core/compile/sizing.py +14 -14
  157. dataface/core/compile/source_map.py +57 -77
  158. dataface/core/compile/sources.py +2 -4
  159. dataface/core/compile/validate_formats.py +39 -7
  160. dataface/core/compile/validator.py +23 -23
  161. dataface/core/compile/variables.py +1 -1
  162. dataface/core/compile/vega_lite/mapping.py +6 -8
  163. dataface/core/compile/yaml_error_formatter.py +15 -6
  164. dataface/core/compile/yaml_patch.py +6 -6
  165. dataface/core/defaults/default_config.yml +47 -8
  166. dataface/core/defaults/themes/_base.yaml +1 -7
  167. dataface/core/defaults/themes/editorial.yaml +1 -1
  168. dataface/core/defaults/themes/stark.yaml +6 -4
  169. dataface/core/diagnostics/__init__.py +20 -0
  170. dataface/core/diagnostics/codes_compile.py +45 -11
  171. dataface/core/diagnostics/codes_execute.py +5 -5
  172. dataface/core/diagnostics/codes_render.py +231 -12
  173. dataface/core/diagnostics/execution.py +1 -1
  174. dataface/core/diagnostics/suppression.py +3 -3
  175. dataface/core/execute/__init__.py +2 -2
  176. dataface/core/execute/_duckdb_cache_base.py +27 -12
  177. dataface/core/execute/adapters/adapter_registry.py +5 -5
  178. dataface/core/execute/adapters/base.py +1 -1
  179. dataface/core/execute/adapters/dbt_adapter.py +1 -1
  180. dataface/core/execute/adapters/duckdb_adapter.py +2 -2
  181. dataface/core/execute/adapters/http_adapter.py +1 -1
  182. dataface/core/execute/adapters/schema_adapter.py +1 -1
  183. dataface/core/execute/adapters/sql_adapter.py +3 -5
  184. dataface/core/execute/adapters/sqlite_adapter.py +4 -3
  185. dataface/core/execute/adapters/values_adapter.py +1 -1
  186. dataface/core/execute/cache_backend.py +4 -4
  187. dataface/core/execute/cache_composition.py +1 -3
  188. dataface/core/execute/chart_data_provider.py +1 -1
  189. dataface/core/execute/chart_resolution.py +9 -2
  190. dataface/core/execute/collect.py +4 -4
  191. dataface/core/execute/duckdb_cache.py +3 -3
  192. dataface/core/execute/executor.py +9 -9
  193. dataface/core/execute/file_source_materializer.py +1 -1
  194. dataface/core/execute/parallel.py +1 -1
  195. dataface/core/execute/recording.py +4 -4
  196. dataface/core/execute/replay_provider.py +1 -1
  197. dataface/core/execute/source_resolver.py +2 -2
  198. dataface/core/execute/sql_literals.py +1 -2
  199. dataface/core/execute/trivial_local_cache.py +6 -4
  200. dataface/core/font_measure.py +155 -1
  201. dataface/core/fonts.py +146 -17
  202. dataface/core/inspect/renderer.py +1 -1
  203. dataface/core/inspect/resolver.py +5 -4
  204. dataface/core/numeric.py +15 -0
  205. dataface/core/pack/models.py +8 -8
  206. dataface/core/pack/planner.py +8 -7
  207. dataface/core/project.py +32 -16
  208. dataface/core/project_roots.py +10 -8
  209. dataface/core/registered_views/expander.py +103 -32
  210. dataface/core/registered_views/query_runner.py +1 -1
  211. dataface/core/registered_views/render_pipeline.py +49 -14
  212. dataface/core/registered_views/variable_planner.py +3 -3
  213. dataface/core/render/__init__.py +3 -3
  214. dataface/core/render/board_replay.py +8 -8
  215. dataface/core/render/chart/auto_link.py +5 -5
  216. dataface/core/render/chart/callout.py +44 -30
  217. dataface/core/render/chart/data_table_attachment.py +11 -13
  218. dataface/core/render/chart/emitters/_cartesian.py +108 -40
  219. dataface/core/render/chart/emitters/_channels.py +4 -4
  220. dataface/core/render/chart/emitters/_label_overlap.py +2 -2
  221. dataface/core/render/chart/emitters/_measured_label_padding.py +27 -12
  222. dataface/core/render/chart/emitters/_overlay.py +119 -36
  223. dataface/core/render/chart/emitters/_tooltip.py +6 -6
  224. dataface/core/render/chart/emitters/area.py +17 -5
  225. dataface/core/render/chart/emitters/bar.py +73 -16
  226. dataface/core/render/chart/emitters/line.py +18 -9
  227. dataface/core/render/chart/emitters/pie.py +4 -5
  228. dataface/core/render/chart/emitters/scatter.py +10 -5
  229. dataface/core/render/chart/features/__init__.py +0 -3
  230. dataface/core/render/chart/features/baseline.py +9 -9
  231. dataface/core/render/chart/features/endpoint_labels.py +305 -23
  232. dataface/core/render/chart/features/mirror_axis.py +40 -6
  233. dataface/core/render/chart/features/structured_tooltip.py +5 -3
  234. dataface/core/render/chart/features/value_labels.py +1 -1
  235. dataface/core/render/chart/kpi.py +49 -46
  236. dataface/core/render/chart/rendering.py +55 -21
  237. dataface/core/render/chart/series_label_truncation.py +81 -0
  238. dataface/core/render/chart/spark.py +1 -1
  239. dataface/core/render/chart/spark_bar.py +15 -10
  240. dataface/core/render/chart/spec.py +3 -7
  241. dataface/core/render/chart/table.py +396 -134
  242. dataface/core/render/chart/table_overflow.py +1 -1
  243. dataface/core/render/chart/table_static_pagination.py +79 -0
  244. dataface/core/render/chart/table_support.py +9 -6
  245. dataface/core/render/chart/text_truncation.py +96 -0
  246. dataface/core/render/chart/time_unit_detect.py +118 -36
  247. dataface/core/render/chart/title_overflow.py +39 -62
  248. dataface/core/render/chart/translate.py +5 -95
  249. dataface/core/render/chart/type_inference.py +1 -1
  250. dataface/core/render/chart/validation.py +6 -2
  251. dataface/core/render/chart/vega_lite.py +31 -8
  252. dataface/core/render/chart/vl_field_maps.py +189 -38
  253. dataface/core/render/column_packer.py +1 -1
  254. dataface/core/render/converters/chart.py +36 -18
  255. dataface/core/render/data_format.py +7 -7
  256. dataface/core/render/dir_context.py +12 -12
  257. dataface/core/render/face_resolve.py +179 -68
  258. dataface/core/render/face_to_dict.py +7 -7
  259. dataface/core/render/faces.py +61 -35
  260. dataface/core/render/font_support.py +71 -13
  261. dataface/core/render/fonts/DFTSansTabular-Medium.ttf +0 -0
  262. dataface/core/render/fonts/DFTSansTabular-SemiBold.ttf +0 -0
  263. dataface/core/render/fonts/InterVariable-Medium.ttf +0 -0
  264. dataface/core/render/fonts/InterVariable-SemiBold.ttf +0 -0
  265. dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
  266. dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
  267. dataface/core/render/fonts/SourceSerif4-Medium.ttf +0 -0
  268. dataface/core/render/fonts/SourceSerif4-SemiBold.ttf +0 -0
  269. dataface/core/render/fonts/_font_face.css +14 -16
  270. dataface/core/render/json_format.py +3 -3
  271. dataface/core/render/layout_sizing.py +73 -44
  272. dataface/core/render/layouts.py +12 -13
  273. dataface/core/render/prose.py +1 -1
  274. dataface/core/render/renderer.py +208 -107
  275. dataface/core/render/sizing.py +373 -209
  276. dataface/core/render/svg_utils.py +10 -13
  277. dataface/core/render/templates/scripts/table_pagination.js +42 -0
  278. dataface/core/render/templates/svg/styles.css +1 -1
  279. dataface/core/render/terminal.py +7 -7
  280. dataface/core/render/terminal_charts.py +1 -1
  281. dataface/core/render/terminal_layouts.py +1 -1
  282. dataface/core/render/text_format.py +4 -4
  283. dataface/core/render/variables_layout.py +5 -3
  284. dataface/core/render/variables_resolve.py +6 -9
  285. dataface/core/render/variables_strip.py +9 -2
  286. dataface/core/render/warnings/axis_title_truncated.py +43 -0
  287. dataface/core/render/warnings/base.py +30 -3
  288. dataface/core/render/warnings/callout_text_truncated.py +41 -0
  289. dataface/core/render/warnings/chart_title_truncated.py +36 -0
  290. dataface/core/render/warnings/facet_panel_width_below_minimum.py +71 -0
  291. dataface/core/render/warnings/kpi_label_truncated.py +34 -0
  292. dataface/core/render/warnings/palette_unsupported.py +2 -2
  293. dataface/core/render/warnings/registry.py +55 -7
  294. dataface/core/render/warnings/series_label_truncated.py +64 -0
  295. dataface/core/render/warnings/spark_label_truncated.py +37 -0
  296. dataface/core/render/warnings/static_pagination_capped.py +37 -0
  297. dataface/core/render/warnings/table_text_truncated.py +42 -0
  298. dataface/core/render/yaml_format.py +19 -4
  299. dataface/core/render_format.py +2 -2
  300. dataface/core/serve/alias_index.py +18 -18
  301. dataface/core/serve/port.py +9 -7
  302. dataface/core/serve/server.py +69 -70
  303. dataface/core/text/format_d3.py +126 -19
  304. dataface/core/text/numeral_scale.py +39 -0
  305. dataface/core/utils.py +45 -0
  306. dataface/core/validate.py +0 -1
  307. dataface/data/schemas/board-resolved/board-resolved.schema.json +448 -395
  308. dataface/data/schemas/yaml/0.5.0.json +1 -0
  309. dataface/data/schemas/yaml/manifest.json +7 -0
  310. dataface/integrations/markdown.py +16 -11
  311. dataface/schema_release.py +4 -2
  312. {dataface-0.4.1.dev134.dist-info → dataface-0.4.1.dev304.dist-info}/METADATA +11 -11
  313. {dataface-0.4.1.dev134.dist-info → dataface-0.4.1.dev304.dist-info}/RECORD +327 -306
  314. mdsvg/fonts.py +13 -6
  315. dataface/core/compile/chart_update.py +0 -205
  316. dataface/core/compile/models/style/resolved/_cascade.py +0 -1154
  317. dataface/core/compile/vega_config.py +0 -39
  318. dataface/core/render/chart/features/legend_toggle.py +0 -43
  319. dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +0 -93
  320. dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
  321. dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
  322. /dataface/agent_api/_init_templates/{dataface.yml → dbt_charts.yml} +0 -0
  323. /dataface/ai/skills/{dashboard-build → board-build}/examples/_smoke.yml +0 -0
  324. /dataface/core/compile/models/{face → board}/__init__.py +0 -0
  325. /dataface/core/compile/resolve/{_chart_rows.py → chart/_chart_rows.py} +0 -0
  326. /dataface/core/compile/{channel.py → resolve/chart/channel.py} +0 -0
  327. /dataface/core/compile/{enrich.py → resolve/chart/enrich.py} +0 -0
  328. /dataface/core/compile/{link_keys.py → resolve/chart/link_keys.py} +0 -0
  329. /dataface/core/compile/{tick_values.py → resolve/chart/tick_values.py} +0 -0
  330. /dataface/core/compile/{inherit_registry.yaml → resolve/style/inherit_registry.yaml} +0 -0
  331. /dataface/core/compile/{palette.py → resolve/style/palette.py} +0 -0
  332. {dataface-0.4.1.dev134.dist-info → dataface-0.4.1.dev304.dist-info}/WHEEL +0 -0
  333. {dataface-0.4.1.dev134.dist-info → dataface-0.4.1.dev304.dist-info}/entry_points.txt +0 -0
  334. {dataface-0.4.1.dev134.dist-info → dataface-0.4.1.dev304.dist-info}/licenses/LICENSE +0 -0
@@ -11,7 +11,7 @@ Dataface workflow — from a dbt project to a running dashboard.
11
11
  ### Step 1 — Validate your YAML
12
12
 
13
13
  ```bash
14
- dft validate faces/my_dashboard.yml
14
+ dft validate charts/my_dashboard.yml
15
15
  ```
16
16
 
17
17
  `dft validate` performs YAML schema + cross-reference validation. It checks that every field, chart reference, query name, and variable is correctly structured. **No database connection is required.**
@@ -62,8 +62,8 @@ rows:
62
62
  Then validate and render:
63
63
 
64
64
  ```bash
65
- dft validate faces/my_dashboard.yml
66
- dft render faces/my_dashboard.yml
65
+ dft validate charts/my_dashboard.yml
66
+ dft render charts/my_dashboard.yml
67
67
  ```
68
68
 
69
69
  Add each additional chart after the previous one validates and renders cleanly.
@@ -189,7 +189,7 @@ title: "Sales Overview"
189
189
  description: "Monthly KPIs and trend"
190
190
  tags: [sales, weekly]
191
191
 
192
- source: my_profile # Default source (a name from dataface.yml's sources: registry)
192
+ source: my_profile # Default source (a name from dbt_charts.yml's sources: registry)
193
193
 
194
194
  variables: # Optional — interactive controls
195
195
  queries: # Named queries
@@ -220,7 +220,7 @@ Top-level fields (22 total):
220
220
  | `description` | string | Description text |
221
221
  | `tags` | list[string] | Tags for categorization/search |
222
222
  | `text` | string | Markdown body for text-only faces |
223
- | `source` | string | Default source name for every query below (from `dataface.yml`'s `sources:` registry), or an inline file path for a single colocated CSV/JSON/Parquet file. Inheritable via the `meta.yaml` cascade. |
223
+ | `source` | string | Default source name for every query below (from `dbt_charts.yml`'s `sources:` registry), or an inline file path for a single colocated CSV/JSON/Parquet file. Inheritable via the `meta.yaml` cascade. |
224
224
  | `variables` | object | See [Variables](#variables) |
225
225
  | `queries` | object | See [Queries](#queries) |
226
226
  | `charts` | object | See [Charts](#charts) |
@@ -265,7 +265,7 @@ query param of the same name — which the face then reads as its variable. Thi
265
265
  gives a single detail face a clean per-entity URL:
266
266
 
267
267
  ```yaml
268
- # faces/milestone.yml (canonical URL: /milestone)
268
+ # charts/milestone.yml (canonical URL: /milestone)
269
269
  aliases:
270
270
  - /milestones/<name>
271
271
  variables:
@@ -284,7 +284,7 @@ and `milestone.yml` renders with `name` bound to `m3-public-launch`.
284
284
  treated as patterns.
285
285
 
286
286
  There are two ways to override a data/inspector route for a given path:
287
- - **Face file at that path** — create `faces/data/warehouse/schema/table.yml` and the server renders it directly (no redirect).
287
+ - **Face file at that path** — create `charts/data/warehouse/schema/table.yml` and the server renders it directly (no redirect).
288
288
  - **`aliases:` entry** — any face can declare `/data/…/` as an alias; the server issues a 302 to the declaring face's canonical URL.
289
289
 
290
290
  Both approaches work. A face file wins without a redirect round-trip; an alias lets a face live anywhere and still capture a system-view URL.
@@ -411,7 +411,7 @@ Common fields (all query types):
411
411
  | Field | Description |
412
412
  |-------|-------------|
413
413
  | `description` | Metadata sentence for AI search and tooltips |
414
- | `source` | Source name (from `dataface.yml`'s `sources:` registry), or an inline file path (`./data/x.csv`) for a single colocated CSV/JSON/Parquet. An inline connection dict (`{type: postgres, ...}`) is rejected — reference a named source instead. Not accepted on `http` queries. |
414
+ | `source` | Source name (from `dbt_charts.yml`'s `sources:` registry), or an inline file path (`./data/x.csv`) for a single colocated CSV/JSON/Parquet. An inline connection dict (`{type: postgres, ...}`) is rejected — reference a named source instead. Not accepted on `http` queries. |
415
415
  | `target` | dbt target name (defaults to `dev`) |
416
416
  | `filters` | Post-execution result filters |
417
417
  | `limit` | Maximum rows returned |
@@ -424,7 +424,7 @@ HTTP fields: `url`, `method`, `headers`, `params`, `body`, `json_path`.
424
424
  dbt-model fields: `model`, `columns`.
425
425
  Inline-values fields: `columns`, `values` (or `rows` for record-shape).
426
426
 
427
- Connection source types (named in the project root `dataface.yml`'s `sources:`
427
+ Connection source types (named in the project root `dbt_charts.yml`'s `sources:`
428
428
  registry — never inline in a face): `postgres`, `snowflake`, `bigquery`,
429
429
  `redshift`, `mysql`, `duckdb`, `sqlite`, `csv`, `json`, `parquet`, `http`, `dbt_profile`.
430
430
  CSV/JSON/Parquet may instead be referenced inline as a `source:` file path
@@ -928,6 +928,8 @@ variables:
928
928
 
929
929
  Top-level option-source binding (alternative to `options:`): `column`, `query`, `dimension` (MetricFlow), `measure` (MetricFlow), `model` (dbt).
930
930
 
931
+ Top-level `column` is `table.column`, and the table may be schema-qualified when it is not in the connection's default schema — `column: gis.fact_sales.property_type`. `filter()` accepts the same qualified form.
932
+
931
933
  Enabled/disabled forms (`enabled: false` means the control is inactive):
932
934
 
933
935
  ```yaml
dataface/__init__.py CHANGED
@@ -16,7 +16,7 @@ Quick Start:
16
16
  >>> from dataface import compile, Executor, render
17
17
  >>> from dataface.core.execute.adapters import build_adapter_registry
18
18
  >>>
19
- >>> # Compile YAML to Face
19
+ >>> # Compile YAML to Board
20
20
  >>> result = compile(yaml_content)
21
21
  >>> if result.success:
22
22
  ... face = result.face
@@ -53,10 +53,10 @@ if TYPE_CHECKING:
53
53
  from dataface.core import validate as validate
54
54
  from dataface.core.compile import (
55
55
  AnyQuery as AnyQuery,
56
- AuthoredFace as AuthoredFace,
56
+ AuthoredBoard as AuthoredBoard,
57
+ Board as Board,
57
58
  CompilationError as CompilationError,
58
59
  CompileResult as CompileResult,
59
- Face as Face,
60
60
  Layout as Layout,
61
61
  LayoutItem as LayoutItem,
62
62
  Variable as Variable,
@@ -80,11 +80,11 @@ __all__ = [
80
80
  "compile",
81
81
  "compile_file",
82
82
  "CompileResult",
83
- "Face",
83
+ "Board",
84
84
  "AnyQuery",
85
85
  "Layout",
86
86
  "LayoutItem",
87
- "AuthoredFace",
87
+ "AuthoredBoard",
88
88
  "Variable",
89
89
  "CompilationError",
90
90
  # Execute
@@ -105,10 +105,10 @@ __all__ = [
105
105
  _LAZY_ATTRS: dict[str, tuple[str, str]] = {
106
106
  "validate": ("dataface.core", "validate"),
107
107
  "AnyQuery": ("dataface.core.compile", "AnyQuery"),
108
- "AuthoredFace": ("dataface.core.compile", "AuthoredFace"),
108
+ "AuthoredBoard": ("dataface.core.compile", "AuthoredBoard"),
109
109
  "CompilationError": ("dataface.core.compile", "CompilationError"),
110
110
  "CompileResult": ("dataface.core.compile", "CompileResult"),
111
- "Face": ("dataface.core.compile", "Face"),
111
+ "Board": ("dataface.core.compile", "Board"),
112
112
  "Layout": ("dataface.core.compile", "Layout"),
113
113
  "LayoutItem": ("dataface.core.compile", "LayoutItem"),
114
114
  "Variable": ("dataface.core.compile", "Variable"),
@@ -50,9 +50,9 @@ if TYPE_CHECKING:
50
50
  project_sources_for_face as project_sources_for_face,
51
51
  )
52
52
  from dataface.agent_api.describe import (
53
- DescribeFaceArgs as DescribeFaceArgs,
53
+ DescribeBoardArgs as DescribeBoardArgs,
54
54
  DescribeFaceResult as DescribeFaceResult,
55
- describe_face as describe_face,
55
+ describe_board as describe_board,
56
56
  )
57
57
  from dataface.agent_api.docs import (
58
58
  DocsArgs as DocsArgs,
@@ -72,15 +72,20 @@ if TYPE_CHECKING:
72
72
  from dataface.agent_api.project_session import ProjectSession as ProjectSession
73
73
  from dataface.agent_api.query import (
74
74
  QueryFaceResult as QueryFaceResult,
75
- query_face as query_face,
75
+ query_board as query_board,
76
76
  )
77
77
  from dataface.agent_api.schema_hints import SchemaHints as SchemaHints
78
78
  from dataface.agent_api.validate import (
79
- ValidateDashboardArgs as ValidateDashboardArgs,
79
+ ValidateBoardArgs as ValidateBoardArgs,
80
80
  ValidateResult as ValidateResult,
81
81
  )
82
82
  from dataface.agent_api.validate_query import QueryDiagnostic as QueryDiagnostic
83
83
  from dataface.core.attribution import set_surface as set_surface
84
+ from dataface.core.board import (
85
+ BoardRenderResult as BoardRenderResult,
86
+ RenderFormat as RenderFormat,
87
+ render_dashboard as render_dashboard,
88
+ )
84
89
  from dataface.core.compile.compiler import (
85
90
  CompileResult as CompileResult,
86
91
  compile as compile,
@@ -88,11 +93,6 @@ if TYPE_CHECKING:
88
93
  from dataface.core.compile.config import (
89
94
  ProjectSourcesConfig as ProjectSourcesConfig,
90
95
  )
91
- from dataface.core.dashboard import (
92
- RenderedDashboard as RenderedDashboard,
93
- RenderFormat as RenderFormat,
94
- render_dashboard as render_dashboard,
95
- )
96
96
  from dataface.core.diagnostics import Diagnostic as Diagnostic
97
97
  from dataface.core.execute.cache_backend import (
98
98
  QueryResultCache as QueryResultCache,
@@ -110,6 +110,7 @@ if TYPE_CHECKING:
110
110
  FaceFile as FaceFile,
111
111
  InMemoryFace as InMemoryFace,
112
112
  Project as Project,
113
+ posix_relpath as posix_relpath,
113
114
  )
114
115
  from dataface.core.render.board_links import LinkContext as LinkContext
115
116
  from dataface.core.render.errors import RenderError as RenderError
@@ -117,7 +118,7 @@ if TYPE_CHECKING:
117
118
  __all__ = [
118
119
  "compile",
119
120
  "CompileResult",
120
- "DescribeFaceArgs",
121
+ "DescribeBoardArgs",
121
122
  "DescribeFaceResult",
122
123
  "Diagnostic",
123
124
  "DocsArgs",
@@ -132,15 +133,16 @@ __all__ = [
132
133
  "Project",
133
134
  "ProjectSession",
134
135
  "set_surface",
136
+ "posix_relpath",
135
137
  "ProjectSourcesConfig",
136
138
  "QueryDiagnostic",
137
139
  "QueryFaceResult",
138
- "RenderedDashboard",
140
+ "BoardRenderResult",
139
141
  "RenderError",
140
142
  "RenderFormat",
141
143
  "SchemaHints",
142
144
  "Topic",
143
- "ValidateDashboardArgs",
145
+ "ValidateBoardArgs",
144
146
  "ValidateResult",
145
147
  # Execution extension seams
146
148
  "AllowlistedSourceResolver",
@@ -148,11 +150,11 @@ __all__ = [
148
150
  "QueryResultCache",
149
151
  "SourceResolver",
150
152
  "WarehouseObserver",
151
- "describe_face",
153
+ "describe_board",
152
154
  "get_fonts_dir",
153
155
  "init_project",
154
156
  "migrate_paths",
155
- "query_face",
157
+ "query_board",
156
158
  "render_dashboard",
157
159
  ]
158
160
 
@@ -177,9 +179,9 @@ _LAZY_ATTRS: dict[str, tuple[str, str]] = {
177
179
  "dataface.agent_api._paths",
178
180
  "project_sources_for_face",
179
181
  ),
180
- "DescribeFaceArgs": ("dataface.agent_api.describe", "DescribeFaceArgs"),
182
+ "DescribeBoardArgs": ("dataface.agent_api.describe", "DescribeBoardArgs"),
181
183
  "DescribeFaceResult": ("dataface.agent_api.describe", "DescribeFaceResult"),
182
- "describe_face": ("dataface.agent_api.describe", "describe_face"),
184
+ "describe_board": ("dataface.agent_api.describe", "describe_board"),
183
185
  "DocsArgs": ("dataface.agent_api.docs", "DocsArgs"),
184
186
  "DocsResult": ("dataface.agent_api.docs", "DocsResult"),
185
187
  "DocsSearchHit": ("dataface.agent_api.docs", "DocsSearchHit"),
@@ -191,18 +193,18 @@ _LAZY_ATTRS: dict[str, tuple[str, str]] = {
191
193
  "migrate_paths": ("dataface.agent_api.migrate", "migrate_paths"),
192
194
  "ProjectSession": ("dataface.agent_api.project_session", "ProjectSession"),
193
195
  "QueryFaceResult": ("dataface.agent_api.query", "QueryFaceResult"),
194
- "query_face": ("dataface.agent_api.query", "query_face"),
196
+ "query_board": ("dataface.agent_api.query", "query_board"),
195
197
  "SchemaHints": ("dataface.agent_api.schema_hints", "SchemaHints"),
196
- "ValidateDashboardArgs": ("dataface.agent_api.validate", "ValidateDashboardArgs"),
198
+ "ValidateBoardArgs": ("dataface.agent_api.validate", "ValidateBoardArgs"),
197
199
  "ValidateResult": ("dataface.agent_api.validate", "ValidateResult"),
198
200
  "QueryDiagnostic": ("dataface.agent_api.validate_query", "QueryDiagnostic"),
199
201
  "set_surface": ("dataface.core.attribution", "set_surface"),
200
202
  "compile": ("dataface.core.compile.compiler", "compile"),
201
203
  "CompileResult": ("dataface.core.compile.compiler", "CompileResult"),
202
204
  "ProjectSourcesConfig": ("dataface.core.compile.config", "ProjectSourcesConfig"),
203
- "RenderedDashboard": ("dataface.core.dashboard", "RenderedDashboard"),
205
+ "BoardRenderResult": ("dataface.core.board", "BoardRenderResult"),
204
206
  "RenderFormat": ("dataface.core.render_format", "RenderFormat"),
205
- "render_dashboard": ("dataface.core.dashboard", "render_dashboard"),
207
+ "render_dashboard": ("dataface.core.board", "render_dashboard"),
206
208
  "Diagnostic": ("dataface.core.diagnostics", "Diagnostic"),
207
209
  "QueryResultCache": ("dataface.core.execute.cache_backend", "QueryResultCache"),
208
210
  "WarehouseObserver": (
@@ -222,6 +224,7 @@ _LAZY_ATTRS: dict[str, tuple[str, str]] = {
222
224
  "FaceFile": ("dataface.core.project", "FaceFile"),
223
225
  "InMemoryFace": ("dataface.core.project", "InMemoryFace"),
224
226
  "Project": ("dataface.core.project", "Project"),
227
+ "posix_relpath": ("dataface.core.project", "posix_relpath"),
225
228
  "LinkContext": ("dataface.core.render.board_links", "LinkContext"),
226
229
  "RenderError": ("dataface.core.render.errors", "RenderError"),
227
230
  }
@@ -4,7 +4,7 @@ title: Dataface
4
4
 
5
5
  # Welcome to Dataface
6
6
 
7
- This is your project's landing page. Everything under `faces/` becomes a
7
+ This is your project's landing page. Everything under `charts/` becomes a
8
8
  served page — edit the files here to build dashboards, reports, and docs
9
9
  for your data project.
10
10
 
@@ -18,7 +18,7 @@ queries and charts, then embed them inline with `{% raw %}{{ chart my_chart }}{%
18
18
 
19
19
  ## Next steps
20
20
 
21
- 1. Open `faces/guide.yaml` and tweak the content.
21
+ 1. Open `charts/guide.yaml` and tweak the content.
22
22
  2. Run `dft serve` and open the URL it prints.
23
- 3. Add new `.yaml` or `.md` files under `faces/` — they appear automatically.
23
+ 3. Add new `.yaml` or `.md` files under `charts/` — they appear automatically.
24
24
  4. Run `dft validate` to validate your face YAML for errors.
@@ -21,7 +21,7 @@ text: |
21
21
  Welcome to **Dataface** — dashboards as YAML files.
22
22
 
23
23
  This file is your starter guide. Edit it, add new `.yaml` files under
24
- `faces/`, and run `dft serve` to preview everything in your browser.
24
+ `charts/`, and run `dft serve` to preview everything in your browser.
25
25
 
26
26
  # ── Queries ───────────────────────────────────────────────────────────────────
27
27
  # `type: values` embeds data directly — no database needed.
@@ -1,4 +1,4 @@
1
- # Directory defaults for every face under faces/
1
+ # Directory defaults for every face under charts/
2
2
  #
3
3
  # meta.yaml is a *partial face* — it uses the exact same authoring surface as a
4
4
  # face file (source, theme, queries, charts, style, variables), but without a
@@ -6,8 +6,8 @@
6
6
  # directory and its subdirectories; each face overrides what it sets.
7
7
  #
8
8
  # A meta.yaml deeper in the tree layers on top of the ones above it, and a face
9
- # always wins over meta. Nested style keys merge (a face setting style.board.margin
10
- # keeps a meta-supplied style.board.width).
9
+ # always wins over meta. Nested style keys merge (a face setting style.frame.margin
10
+ # keeps a meta-supplied style.frame.width).
11
11
  #
12
12
  # Uncomment and edit to set project-wide defaults:
13
13
  #
@@ -15,5 +15,5 @@
15
15
  # source: my_warehouse # default data source for faces that don't name one
16
16
  #
17
17
  # style:
18
- # board:
18
+ # frame:
19
19
  # width: 1000
@@ -15,18 +15,18 @@ import re
15
15
 
16
16
  from pydantic import BaseModel, ConfigDict, Field
17
17
 
18
- from dataface.core.project import FACES_SUBDIR, Project
18
+ from dataface.core.project import CHARTS_SUBDIR, Project
19
19
 
20
- _FACES_GLOB = f"{FACES_SUBDIR}/**/*"
20
+ _FACES_GLOB = f"{CHARTS_SUBDIR}/**/*"
21
21
 
22
22
 
23
- def face_slug(relpath: str) -> str | None:
24
- """Return the dashboard slug for a ``faces/`` path (no prefix, no suffix).
23
+ def board_slug(relpath: str) -> str | None:
24
+ """Return the dashboard slug for a ``charts/`` path (no prefix, no suffix).
25
25
 
26
- ``faces/finance/rev.yml`` -> ``finance/rev``. Returns None for a path
27
- outside ``faces/`` -- link scanning only has meaning for dashboard slugs.
26
+ ``charts/finance/rev.yml`` -> ``finance/rev``. Returns None for a path
27
+ outside ``charts/`` -- link scanning only has meaning for dashboard slugs.
28
28
  """
29
- prefix = f"{FACES_SUBDIR}/"
29
+ prefix = f"{CHARTS_SUBDIR}/"
30
30
  if not relpath.startswith(prefix):
31
31
  return None
32
32
  tail = relpath[len(prefix) :]
@@ -16,7 +16,7 @@ from dataface.core.diagnostics import (
16
16
  )
17
17
  from dataface.core.diagnostics.base import DatafaceError
18
18
  from dataface.core.project import (
19
- FACES_SUBDIR,
19
+ CHARTS_SUBDIR as CHARTS_SUBDIR,
20
20
  FaceFile,
21
21
  Project,
22
22
  ProjectPath,
@@ -37,19 +37,19 @@ if TYPE_CHECKING:
37
37
  def resolve_face_relpath(relpath: PurePosixPath, project: Project) -> ProjectPath:
38
38
  """Project-relative face identity -> ProjectPath, with the faces-first retry.
39
39
 
40
- The relpath-native counterpart to ``resolve_face_path``: for callers that
40
+ The relpath-native counterpart to ``resolve_board_path``: for callers that
41
41
  already hold a project-relative face identity (Cloud's context strings)
42
42
  rather than a real filesystem path, this is the single owner of the
43
43
  face-relative vs root-relative reconciliation — a bare name like
44
- ``looker/x.yaml`` retries under ``FACES_SUBDIR`` when it doesn't exist at
45
- root, without producing a ``faces/faces/...`` double prefix when the
46
- caller already supplies the full ``faces/looker/x.yaml``.
44
+ ``looker/x.yaml`` retries under ``CHARTS_SUBDIR`` when it doesn't exist at
45
+ root, without producing a ``charts/charts/...`` double prefix when the
46
+ caller already supplies the full ``charts/looker/x.yaml``.
47
47
 
48
48
  Existence is checked via ``project.exists(...)`` so it works for all
49
49
  ``Project`` implementations (FilesystemProject, CloudManagedProject
50
50
  git-blob store, etc.) — not just the local filesystem. Paths that exist
51
51
  at root resolve from root — non-faces paths like ``models/schema.sql``
52
- are not rewritten under faces/. ``str`` is used only at the identity seam
52
+ are not rewritten under charts/. ``str`` is used only at the identity seam
53
53
  (``project.path`` / ``project.exists``); everything else is real
54
54
  ``PurePosixPath`` operations.
55
55
  """
@@ -57,8 +57,8 @@ def resolve_face_relpath(relpath: PurePosixPath, project: Project) -> ProjectPat
57
57
  assert_relpath(relpath_str)
58
58
  if project.exists(relpath_str):
59
59
  return project.path(relpath_str)
60
- if not relpath.parts or relpath.parts[0] != FACES_SUBDIR:
61
- candidate = PurePosixPath(FACES_SUBDIR) / relpath
60
+ if not relpath.parts or relpath.parts[0] != CHARTS_SUBDIR:
61
+ candidate = PurePosixPath(CHARTS_SUBDIR) / relpath
62
62
  candidate_str = str(candidate)
63
63
  if project.exists(candidate_str):
64
64
  return project.path(candidate_str)
@@ -78,7 +78,7 @@ def _relpath_for_fs_location(path: Path, project: Project) -> str:
78
78
  Two-try containment check (mirrors the pre-refactor behavior): ``resolve()``
79
79
  follows symlinks (handles e.g. ``/var`` -> ``/private/var`` on macOS); a
80
80
  symlink-blind ``normpath`` fallback accepts paths through intentional
81
- project-internal symlinks (e.g. ``faces/tasks/`` -> ``../../tasks/``).
81
+ project-internal symlinks (e.g. ``charts/tasks/`` -> ``../../tasks/``).
82
82
  Raises ``ValueError`` if neither check places *path* inside the root.
83
83
  """
84
84
  if not isinstance(project, FilesystemProject):
@@ -99,13 +99,13 @@ def _relpath_for_fs_location(path: Path, project: Project) -> str:
99
99
  raise ValueError(f"Path {path!r} is outside project root {root!r}") from None
100
100
 
101
101
 
102
- def resolve_face_path(path: Path, project: Project) -> ProjectPath:
102
+ def resolve_board_path(path: Path, project: Project) -> ProjectPath:
103
103
  """User/agent-supplied filesystem-shaped face path -> ProjectPath.
104
104
 
105
105
  Absolute and ``..``-leading paths are explicit locations: they relativize
106
106
  against a concrete ``FilesystemProject`` root (``_relpath_for_fs_location``,
107
107
  a leading ``..`` resolving against cwd for CLI shell-nav feel) and are
108
- returned as-is — no ``faces/`` retry, even when the exact path doesn't
108
+ returned as-is — no ``charts/`` retry, even when the exact path doesn't
109
109
  exist. A bare relative name (no explicit location) delegates to
110
110
  ``resolve_face_relpath`` for the faces-first retry, which works against
111
111
  any ``Project`` implementation.
@@ -131,7 +131,7 @@ def resolve_face_or_error(path: Path, project: Project) -> FaceFile | Diagnostic
131
131
  (``ERR-INTERNAL``) or a missing face (``ERR-FILE-NOT-FOUND``).
132
132
  """
133
133
  try:
134
- resolved = resolve_face_path(path, project)
134
+ resolved = resolve_board_path(path, project)
135
135
  except ValueError as exc:
136
136
  return DatafaceError.from_code(ERR_INTERNAL, message=str(exc)).to_diagnostic()
137
137
  if not resolved.exists():
@@ -238,11 +238,11 @@ def project_sources_for_face(face_path: Path) -> ProjectSourcesConfig:
238
238
  parse finding trustworthy enough to report.
239
239
 
240
240
  A face outside any project needs no special case: `find_project_root`
241
- falls back to the starting directory, which has no `dataface.yml`, and the
241
+ falls back to the starting directory, which has no `dbt_charts.yml`, and the
242
242
  registry comes back empty — compile then stays silent about SQL syntax
243
243
  rather than guessing a dialect.
244
244
 
245
- A `dataface.yml` that exists but does not load raises, deliberately. That
245
+ A `dbt_charts.yml` that exists but does not load raises, deliberately. That
246
246
  is a real fault at a real location, and swallowing it here would decide on
247
247
  the author's behalf that a broken project config is invisible — while
248
248
  handing the caller an empty registry indistinguishable from "no project".
@@ -1,7 +1,7 @@
1
1
  """Emit a resolved-board artifact + recording, and render one back.
2
2
 
3
3
  `emit_board_artifact` compiles, resolves, and executes a face, then writes the
4
- board's published `ResolvedFace` contract and a data sidecar (the rows it was
4
+ board's published `ResolvedBoard` contract and a data sidecar (the rows it was
5
5
  resolved against). `render_board_from_files` reads both back and renders
6
6
  without a compile step or a warehouse connection — the "skip the
7
7
  compilation" win the resolved-board artifact exists for.
@@ -22,7 +22,7 @@ from pydantic import BaseModel, ConfigDict
22
22
 
23
23
  from dataface.agent_api._paths import resolve_face_or_error
24
24
  from dataface.core.compile import compile_file
25
- from dataface.core.compile.models.face.resolved import ChartResolveFailure
25
+ from dataface.core.compile.models.board.resolved import ChartResolveFailure
26
26
  from dataface.core.compile.variables import normalize_multiselect_values
27
27
  from dataface.core.diagnostics import ERR_FILE_NOT_FOUND, Diagnostic
28
28
  from dataface.core.diagnostics.base import DatafaceError
@@ -89,7 +89,7 @@ def emit_board_artifact(
89
89
  ) -> EmitBoardArtifactResult:
90
90
  """Compile, resolve, and execute a face, then write its artifact + recording.
91
91
 
92
- Writes `artifact_path` (the published, data-free `ResolvedFace` contract)
92
+ Writes `artifact_path` (the published, data-free `ResolvedBoard` contract)
93
93
  and `recording_path` (the rows it was resolved against, the variable
94
94
  values, and a `recorded_at` stamp) — kept separate so the board contract
95
95
  carries no data payload.
@@ -7,18 +7,25 @@ values. No bare dict[str, Any] in any return position. Tool/CLI callers use
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import sys
10
11
  from pathlib import Path
11
- from typing import Any, Literal
12
+ from typing import Literal
12
13
 
13
14
  import yaml
14
- from pydantic import BaseModel, ConfigDict, Field, field_serializer
15
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer, model_validator
16
+
17
+ if sys.version_info >= (3, 11):
18
+ from typing import Self
19
+ else:
20
+ from typing_extensions import Self
15
21
 
16
22
  from dataface.agent_api._paths import resolve_face_or_error
17
- from dataface.core.compile import Face, compile_file
18
- from dataface.core.dashboard import RenderedDashboard as RenderedDashboard
23
+ from dataface.agent_api.query import VariableBinding, variables_to_dict as _vars_to_dict
24
+ from dataface.core.board import BoardRenderResult as BoardRenderResult
25
+ from dataface.core.compile import Board, compile_file
19
26
  from dataface.core.diagnostics import Diagnostic
20
27
  from dataface.core.project import (
21
- FACES_SUBDIR,
28
+ CHARTS_SUBDIR,
22
29
  Project,
23
30
  ProjectDirectory,
24
31
  ProjectPath,
@@ -32,7 +39,7 @@ DASHBOARD_KEYS = frozenset({"queries", "charts", "rows", "cols", "grid", "tabs"}
32
39
  # ---------------------------------------------------------------------------
33
40
 
34
41
 
35
- class DashboardSummary(BaseModel):
42
+ class BoardSummary(BaseModel):
36
43
  model_config = ConfigDict(
37
44
  arbitrary_types_allowed=True, populate_by_name=True, frozen=True
38
45
  )
@@ -62,12 +69,12 @@ class SkippedFile(BaseModel):
62
69
  return pf.relpath
63
70
 
64
71
 
65
- class ListDashboardsResult(BaseModel):
72
+ class ListBoardsResult(BaseModel):
66
73
  model_config = ConfigDict(arbitrary_types_allowed=True, frozen=True)
67
74
 
68
75
  success: bool
69
76
  errors: list[Diagnostic] = []
70
- dashboards: list[DashboardSummary] = []
77
+ boards: list[BoardSummary] = []
71
78
  count: int = 0
72
79
  directory: ProjectDirectory
73
80
  skipped_files: list[SkippedFile] = []
@@ -77,26 +84,26 @@ class ListDashboardsResult(BaseModel):
77
84
  return d.relpath
78
85
 
79
86
 
80
- class CompiledDashboard(BaseModel):
87
+ class CompiledBoard(BaseModel):
81
88
  model_config = ConfigDict(frozen=True)
82
89
 
83
90
  success: bool
84
- dashboard: Face | None = None
91
+ board: Board | None = None
85
92
  errors: list[Diagnostic] = []
86
93
  warnings: list[Diagnostic] = []
87
94
  raw_yaml: str | None = None
88
95
 
89
96
 
90
- class RenderDashboardArgs(BaseModel):
97
+ class RenderBoardArgs(BaseModel):
91
98
  """Validate, compile, and render a Dataface dashboard, returning resolved chart semantics + executed data for reasoning. Use this to verify YAML is valid, inspect query results, and iterate on a dashboard. When 'path' is provided, the response also includes a localhost URL the user can open in a browser to view the live dashboard (from the embedded HTTP server used by agent sessions). Pass `as_link=true` with a path to skip execution and return only the preview URL."""
92
99
 
93
100
  path: Path | None = Field(
94
101
  None,
95
102
  description=(
96
103
  "Path to dashboard YAML file (use this OR yaml_content). "
97
- "Face-relative (no faces/ prefix needed, e.g. 'looker/x.yaml') or "
98
- "root-relative (e.g. 'faces/looker/x.yaml') both resolve. If the "
99
- "path came from search_dashboards, use the hit's face_path."
104
+ "Face-relative (no charts/ prefix needed, e.g. 'looker/x.yaml') or "
105
+ "root-relative (e.g. 'charts/looker/x.yaml') both resolve. If the "
106
+ "path came from search_boards, use the hit's face_path."
100
107
  ),
101
108
  )
102
109
  yaml_content: str | None = Field(
@@ -112,8 +119,12 @@ class RenderDashboardArgs(BaseModel):
112
119
  "the list of available chart ids."
113
120
  ),
114
121
  )
115
- variables: dict[str, Any] | None = Field(
116
- None, description="Variable values to apply to the dashboard"
122
+ variables: list[VariableBinding] | None = Field(
123
+ None,
124
+ description=(
125
+ "Variable values to apply to the dashboard, as an array of {name, value} pairs. "
126
+ 'Example: [{"name": "region", "value": "US"}, {"name": "year", "value": 2024}]'
127
+ ),
117
128
  )
118
129
  format: Literal["json", "text", "yaml", "svg", "terminal", "data"] | None = Field(
119
130
  None,
@@ -148,20 +159,26 @@ class RenderDashboardArgs(BaseModel):
148
159
  ),
149
160
  )
150
161
 
162
+ @model_validator(mode="after")
163
+ def _no_duplicate_variable_names(self) -> Self:
164
+ if self.variables is not None:
165
+ _vars_to_dict(self.variables)
166
+ return self
167
+
151
168
 
152
169
  # ---------------------------------------------------------------------------
153
170
  # Public verbs
154
171
  # ---------------------------------------------------------------------------
155
172
 
156
173
 
157
- def list_dashboards(
174
+ def list_boards(
158
175
  project: Project,
159
176
  *,
160
- under: str = FACES_SUBDIR,
177
+ under: str = CHARTS_SUBDIR,
161
178
  recursive: bool = True,
162
- ) -> ListDashboardsResult:
179
+ ) -> ListBoardsResult:
163
180
  """List all available dashboards in a project."""
164
- dashboards: list[DashboardSummary] = []
181
+ dashboards: list[BoardSummary] = []
165
182
  skipped: list[SkippedFile] = []
166
183
 
167
184
  for pf in project.iter_faces(under=under, recursive=recursive):
@@ -176,7 +193,7 @@ def list_dashboards(
176
193
  continue
177
194
  stem = pf.stem
178
195
  dashboards.append(
179
- DashboardSummary(
196
+ BoardSummary(
180
197
  file=pf,
181
198
  title=content.get("title", stem),
182
199
  description=content.get("description", ""),
@@ -190,31 +207,31 @@ def list_dashboards(
190
207
  except OSError as e:
191
208
  skipped.append(SkippedFile(file=pf, reason=f"Read error: {e}"))
192
209
 
193
- return ListDashboardsResult(
210
+ return ListBoardsResult(
194
211
  success=True,
195
- dashboards=dashboards,
212
+ boards=dashboards,
196
213
  count=len(dashboards),
197
214
  directory=project.directory(under),
198
215
  skipped_files=skipped,
199
216
  )
200
217
 
201
218
 
202
- def get_dashboard(
219
+ def get_board(
203
220
  path: Path,
204
221
  *,
205
222
  project: Project,
206
223
  include_raw: bool = False,
207
- ) -> CompiledDashboard:
224
+ ) -> CompiledBoard:
208
225
  """Get the compiled structure of a dashboard."""
209
226
  # One faces-first resolve/exists/is-file seam, shared with the render path.
210
227
  face = resolve_face_or_error(path, project)
211
228
  if isinstance(face, Diagnostic):
212
- return CompiledDashboard(success=False, errors=[face])
229
+ return CompiledBoard(success=False, errors=[face])
213
230
  result = compile_file(face)
214
231
 
215
- return CompiledDashboard(
232
+ return CompiledBoard(
216
233
  success=result.success,
217
- dashboard=result.face if result.success else None,
234
+ board=result.face if result.success else None,
218
235
  errors=list(result.errors),
219
236
  warnings=result.warnings,
220
237
  raw_yaml=face.content if include_raw else None,
@@ -53,7 +53,7 @@ def project_cache_ctx(
53
53
  Precedence: ``no_cache=True`` (the CLI's ``--no-cache``) skips the cache
54
54
  entirely; ``cache_path`` (``--cache PATH``, or its ``DFT_CACHE_PATH`` env
55
55
  backing) opens that persistent file. Otherwise the project's
56
- ``dataface.yml`` ``cache:`` block picks the location — a configured ``path``
56
+ ``dbt_charts.yml`` ``cache:`` block picks the location — a configured ``path``
57
57
  opens that file (created if absent); no path opens the zero-config
58
58
  in-memory default. A project-level ``cache: false`` defaults the *cascade*
59
59
  to off without closing the door on a nearer scope's opt-in, so it still