dataface 0.2.1.dev691__py3-none-any.whl → 0.3.0__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 (110) hide show
  1. dataface/DATAFACE_SYNTAX.md +1 -1
  2. dataface/agent_api/dashboards.py +13 -4
  3. dataface/agent_api/describe.py +6 -12
  4. dataface/agent_api/docs/yaml-reference.md +129 -16
  5. dataface/agent_api/pack.py +1 -2
  6. dataface/agent_api/project_session.py +15 -20
  7. dataface/agent_api/search.py +7 -10
  8. dataface/agent_api/skill_install.py +8 -1
  9. dataface/agent_api/skills.py +121 -70
  10. dataface/agent_api/surface_aliases.yaml +18 -0
  11. dataface/agent_api/validate.py +15 -26
  12. dataface/ai/events.py +11 -1
  13. dataface/ai/llm.py +18 -14
  14. dataface/ai/prompts.py +8 -2
  15. dataface/ai/skills/analyst-runbook/SKILL.md +25 -8
  16. dataface/ai/skills/dashboard-build/SKILL.md +14 -6
  17. dataface/ai/skills/dashboard-pack-scaffolding/SKILL.md +2 -2
  18. dataface/ai/skills/dashboard-replicate/SKILL.md +6 -5
  19. dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +1 -2
  20. dataface/ai/tools/__init__.py +0 -1
  21. dataface/cli/commands/render.py +10 -6
  22. dataface/cli/filesystem_project.py +64 -3
  23. dataface/cli/main.py +1 -1
  24. dataface/core/compile/authoring_warnings.py +1 -1
  25. dataface/core/compile/compiler.py +100 -67
  26. dataface/core/compile/config.py +3 -3
  27. dataface/core/compile/face_warnings.py +210 -0
  28. dataface/core/compile/models/config.py +9 -0
  29. dataface/core/compile/models/dbt_credential_contract.py +130 -0
  30. dataface/core/compile/models/source.py +747 -61
  31. dataface/core/compile/models/style/resolved/_base.py +3 -3
  32. dataface/core/compile/models/style/resolved/_cascade.py +6 -1
  33. dataface/core/compile/models/style/resolved/callout.py +1 -0
  34. dataface/core/compile/models/style/theme/__init__.py +2 -0
  35. dataface/core/compile/models/style/theme/axis.py +12 -12
  36. dataface/core/compile/models/style/theme/board.py +23 -0
  37. dataface/core/compile/models/style/theme/page.py +30 -3
  38. dataface/core/compile/models/style/theme/style.py +1 -1
  39. dataface/core/compile/normalize_layout.py +1 -1
  40. dataface/core/compile/normalizer.py +4 -1
  41. dataface/core/compile/resolve/_resolve.py +93 -43
  42. dataface/core/connections.py +103 -3
  43. dataface/core/dashboard.py +8 -5
  44. dataface/core/defaults/default_config.yml +4 -1
  45. dataface/core/defaults/themes/_base.yaml +13 -4
  46. dataface/core/diagnostics/__init__.py +6 -0
  47. dataface/core/diagnostics/codes_compile.py +63 -1
  48. dataface/core/diagnostics/codes_render.py +48 -0
  49. dataface/core/execute/adapters/adapter_registry.py +27 -13
  50. dataface/core/execute/executor.py +3 -3
  51. dataface/core/inspect/templates/categorical_column.yml +0 -2
  52. dataface/core/inspect/templates/date_column.yml +0 -2
  53. dataface/core/inspect/templates/model.yml +0 -3
  54. dataface/core/inspect/templates/numeric_column.yml +0 -3
  55. dataface/core/inspect/templates/quality.yml +0 -1
  56. dataface/core/inspect/templates/string_column.yml +0 -4
  57. dataface/core/project.py +62 -85
  58. dataface/core/registered_views/expander.py +9 -19
  59. dataface/core/registered_views/loader.py +3 -1
  60. dataface/core/registered_views/templates/inspector/column.yaml +0 -1
  61. dataface/core/registered_views/templates/inspector/table.yaml +1 -1
  62. dataface/core/render/chart/callout.py +4 -0
  63. dataface/core/render/chart/data_table_attachment.py +44 -121
  64. dataface/core/render/chart/emitters/_cartesian.py +18 -4
  65. dataface/core/render/chart/emitters/_label_overlap.py +378 -251
  66. dataface/core/render/chart/emitters/_layers.py +235 -27
  67. dataface/core/render/chart/emitters/_measured_label_padding.py +1 -1
  68. dataface/core/render/chart/emitters/_overlay.py +88 -63
  69. dataface/core/render/chart/emitters/area.py +9 -2
  70. dataface/core/render/chart/emitters/bar.py +62 -24
  71. dataface/core/render/chart/emitters/heatmap.py +7 -2
  72. dataface/core/render/chart/emitters/line.py +8 -1
  73. dataface/core/render/chart/features/__init__.py +0 -3
  74. dataface/core/render/chart/rendering.py +2 -1
  75. dataface/core/render/chart/table.py +61 -21
  76. dataface/core/render/chart/table_support.py +144 -82
  77. dataface/core/render/chart/time_unit_detect.py +178 -227
  78. dataface/core/render/chart/type_inference.py +115 -108
  79. dataface/core/render/chart/validation.py +44 -1
  80. dataface/core/render/chart/vega_lite.py +9 -1
  81. dataface/core/render/chart/vl_field_maps.py +4 -1
  82. dataface/core/render/data_format.py +198 -0
  83. dataface/core/render/dir_context.py +5 -4
  84. dataface/core/render/face_to_dict.py +133 -8
  85. dataface/core/render/faces.py +8 -18
  86. dataface/core/render/json_format.py +10 -1
  87. dataface/core/render/layout_sizing.py +9 -3
  88. dataface/core/render/renderer.py +44 -42
  89. dataface/core/render/shared_y_datasets.py +33 -0
  90. dataface/core/render/sizing.py +2 -0
  91. dataface/core/render/stable_domain.py +9 -1
  92. dataface/core/render/templates/nav/nav.css +4 -1
  93. dataface/core/render/templates/nav/nav.html +10 -0
  94. dataface/core/render/templates/nav/nav.js +24 -0
  95. dataface/core/render/templates/page.css +24 -0
  96. dataface/core/render/warnings/value_labels_crowd_width.py +2 -2
  97. dataface/core/render/yaml_format.py +19 -98
  98. dataface/core/serve/alias_index.py +5 -8
  99. dataface/core/serve/server.py +1 -1
  100. dataface/integrations/markdown.py +39 -35
  101. {dataface-0.2.1.dev691.dist-info → dataface-0.3.0.dist-info}/METADATA +1 -1
  102. {dataface-0.2.1.dev691.dist-info → dataface-0.3.0.dist-info}/RECORD +109 -106
  103. mdsvg/__init__.py +1 -1
  104. mdsvg/playground.py +5 -18
  105. mdsvg/renderer.py +5 -6
  106. mdsvg/style.py +7 -0
  107. dataface/core/render/chart/features/grouped_bar.py +0 -67
  108. {dataface-0.2.1.dev691.dist-info → dataface-0.3.0.dist-info}/WHEEL +0 -0
  109. {dataface-0.2.1.dev691.dist-info → dataface-0.3.0.dist-info}/entry_points.txt +0 -0
  110. {dataface-0.2.1.dev691.dist-info → dataface-0.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -676,7 +676,7 @@ style:
676
676
  label: Amount
677
677
  format: currency_whole
678
678
  align: right # left | center | right
679
- header_overflow: wrap-two # clip | truncate | wrap-two | wrap
679
+ header_overflow: wrap-two # clip | truncate | wrap-two (default) | wrap
680
680
  header_link: "/columns/amount"
681
681
  link: "/orders?id={{ order_id }}"
682
682
  background: "#fafafa"
@@ -115,11 +115,20 @@ class RenderDashboardArgs(BaseModel):
115
115
  variables: dict[str, Any] | None = Field(
116
116
  None, description="Variable values to apply to the dashboard"
117
117
  )
118
- format: Literal["json", "text", "yaml", "svg", "terminal"] | None = Field(
118
+ format: Literal["json", "text", "yaml", "svg", "terminal", "data"] | None = Field(
119
119
  None,
120
120
  description=(
121
- "Output format. 'json' (default) returns resolved chart "
122
- "semantics and executed data as structured JSON. 'text' "
121
+ "Output format. 'json' (default) returns the full resolved chart "
122
+ "semantics and executed data as structured JSON, nested by "
123
+ "layout. 'data' returns a flatter, narrower view: 'queries' "
124
+ "keyed by query name (sql, plus one copy of the rows) and "
125
+ "'charts' keyed by slug carrying title/description/type and "
126
+ "encoding, each referencing its query by name. It drops the "
127
+ "layout nesting, repeats no rows across charts that share a "
128
+ "query, and reports the variable values the rows were produced "
129
+ "with — but emits only the authored chart fields, not every "
130
+ "resolved one, so prefer 'json' when you need the full chart. "
131
+ "'text' "
123
132
  "returns a compact markdown summary of charts and data "
124
133
  "(most token-efficient). 'yaml' returns resolved Dataface "
125
134
  "YAML with inline data — valid input for re-compilation, "
@@ -165,7 +174,7 @@ def list_dashboards(
165
174
  continue
166
175
  if not any(key in content for key in DASHBOARD_KEYS):
167
176
  continue
168
- stem = Path(pf.relpath).stem
177
+ stem = pf.stem
169
178
  dashboards.append(
170
179
  DashboardSummary(
171
180
  file=pf,
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from pathlib import Path, PurePosixPath
5
+ from pathlib import Path
6
6
  from typing import Any
7
7
 
8
8
  from pydantic import BaseModel, ConfigDict, Field
@@ -352,17 +352,11 @@ def _describe_one_path(
352
352
  return [_describe_resolved(resolved, project=project)]
353
353
 
354
354
  faces = sorted(
355
- (
356
- pf
357
- for pf in project.iter_faces(under=resolved.relpath, recursive=True)
358
- if pf.is_yaml
359
- and not pf.is_private
360
- and not (pf.parent / INSPECT_TEMPLATE_MANIFEST).exists()
361
- ),
362
- # Relpaths under `under` share a common prefix, so a PurePosixPath key
363
- # reproduces the old absolute-Path sort order exactly on 3.10 and
364
- # 3.13 — don't "simplify" this to a str/.parts key.
365
- key=lambda pf: PurePosixPath(pf.relpath),
355
+ pf
356
+ for pf in project.iter_faces(under=resolved.relpath, recursive=True)
357
+ if pf.is_yaml
358
+ and not pf.is_private
359
+ and not (pf.parent / INSPECT_TEMPLATE_MANIFEST).exists()
366
360
  )
367
361
  if not faces:
368
362
  return [
@@ -533,7 +533,7 @@ Authored overlay for Style — all fields optional. Adds CSS shorthand coercers.
533
533
  | `variables` | [VariablesStyle](#variablesstyle) | ✓ | Variable controls chrome style. |
534
534
  | `page` | [PageStyle](#pagestyle) | ✓ | Page-level canvas style (behind the board). |
535
535
  | `footer` | [FooterStyle](#footerstyle) | ✓ | Page footer chrome visibility. |
536
- | `timestamp` | [TimestampStyle](#timestampstyle) | ✓ | Render-timestamp chrome: visibility, placement, format, and font. |
536
+ | `timestamp` | [TimestampStyle](#timestampstyle) | ✓ | Data-freshness chrome: visibility, placement, format, and font. |
537
537
  | `formats` | dict[str, str] | ✓ | Format alias map; None means no aliases at this cascade level. |
538
538
  | `palettes` | dict[str, str] | ✓ | Theme palette role assignments: open dict mapping role name to palette file name. Default seed: chrome, info, negative, positive, warning, category, sequence, diverge. |
539
539
  | `roles` | dict[str, str] | ✓ | Optional top-level theme role aliases: bare name → role.alias. e.g. ink: chrome.heading |
@@ -1126,6 +1126,7 @@ Authored overlay for TextStyle. Markdown / plain text content.
1126
1126
  | `column` | [TextColumnStyle](#textcolumnstyle) | ✓ | Multi-column layout for the face body-text block. |
1127
1127
  | `code` | [TextCodeStyle](#textcodestyle) | ✓ | Inline + fenced code box styling (font, background, border). |
1128
1128
  | `blockquote` | [TextBlockquoteStyle](#textblockquotestyle) | ✓ | Blockquote box styling (font, background, border/left-rule). |
1129
+ | `bold` | [TextBoldStyle](#textboldstyle) | ✓ | Inline bold-run styling (weight), distinct from heading weight. |
1129
1130
 
1130
1131
  <a id="placeholderstyle"></a>
1131
1132
  ## PlaceholderStyle
@@ -1241,14 +1242,14 @@ Authored overlay for FooterStyle. Page footer chrome: visibility, attribution te
1241
1242
 
1242
1243
  <a id="timestampstyle"></a>
1243
1244
  ## TimestampStyle
1244
- Authored overlay for TimestampStyle. Authored timestamp chrome: visibility, placement, strftime format, font, and y-offset.
1245
+ Authored overlay for TimestampStyle. Authored data-freshness chrome: visibility, placement, strftime format, font, and y-offset.
1245
1246
 
1246
1247
  | Field | Type | Optional | Description |
1247
1248
  |-------|------|:--------:|-------------|
1248
- | `visible` | bool | ✓ | Show the render-timestamp line. |
1249
+ | `visible` | bool | ✓ | Show the data-freshness line. |
1249
1250
  | `position` | enum: "top", "footer" | ✓ | Timestamp row: top page chrome or footer baseline. |
1250
1251
  | `align` | enum: "left", "right" | ✓ | Timestamp horizontal alignment within its row. |
1251
- | `format` | str | ✓ | strftime format string for the render timestamp. |
1252
+ | `format` | str | ✓ | strftime format for the data-freshness line, including any literal label text (e.g. '%H:%M %Z on %-d %b %Y'). The value is always UTC; a format that prints a clock must disclose the zone (%Z or a literal 'UTC'), else compile rejects it — an unlabeled clock reads as local. |
1252
1253
  | `y` | float | ✓ | Y-coordinate for top-positioned timestamp in pixels. |
1253
1254
  | `font` | [FontStyle](#fontstyle) | ✓ | Timestamp font style overrides (size, color, weight, ...). |
1254
1255
 
@@ -1937,6 +1938,14 @@ Authored overlay for TextBlockquoteStyle. Blockquote prose. Box group; border is
1937
1938
  | `background` | str | ✓ | Box background fill. |
1938
1939
  | `border` | [BorderStyle](#borderstyle) | ✓ | Box border (width, color, radius). |
1939
1940
 
1941
+ <a id="textboldstyle"></a>
1942
+ ## TextBoldStyle
1943
+ Authored overlay for TextBoldStyle. Inline **bold** text runs in markdown prose, distinct from heading weight.
1944
+
1945
+ | Field | Type | Optional | Description |
1946
+ |-------|------|:--------:|-------------|
1947
+ | `weight` | str \| float | ✓ | CSS font-weight for bold text runs and bold table cells. |
1948
+
1940
1949
  <a id="placeholderoverlay"></a>
1941
1950
  ## PlaceholderOverlay
1942
1951
  Authored overlay for PlaceholderOverlay.
@@ -2237,7 +2246,7 @@ Authored overlay for AxisElementStyle. Axis label or title: font + padding + VL-
2237
2246
  | `angle` | float | ✓ | Label rotation angle in degrees; None uses Vega-Lite's default. |
2238
2247
  | `align` | enum: "left", "right", "center", "inward", "outward" | ✓ | Horizontal text alignment of labels. 'left'/'right'/'center' are absolute. 'inward' hugs the plot (own-side: left on a left axis, right on a right axis); 'outward' hugs away from the plot (Vega-Lite's default growth direction). inward/outward are a no-op on an axis with no left/right edge. None uses Vega-Lite's default. |
2239
2248
  | `baseline` | str | ✓ | Vertical text baseline of labels; None uses Vega-Lite's default. |
2240
- | `overlap` | [AxisLabelOverlapConfig](#axislabeloverlapconfig) | ✓ | Label overlap strategy enablement. None inherits from the theme cascade. Set individual bools to enable/disable; the resolver applies them in fixed order tilt→skip. Temporal-bucketed axes ignore this and step the label cadence instead. |
2249
+ | `overlap` | [AxisLabelOverlapConfig](#axislabeloverlapconfig) | ✓ | Label overlap strategy enablement. None inherits from the theme cascade. Set individual bools to enable/disable; the resolver applies them in fixed order skip→tilt. On bucketed temporal axes, skip thins label visibility to a coarser calendar period without changing the axis or label time unit. |
2241
2250
  | `separation` | float | ✓ | Minimum pixel separation between labels; None uses Vega-Lite's default (0px). |
2242
2251
  | `visible` | bool | ✓ | Show axis labels; None uses Vega-Lite's default (labels shown). |
2243
2252
  | `time_unit` | enum: "auto", "year", "yearquarter", "yearmonth", "yearweek", "yearmonthdate", "monthofyear", "dayofweek", "dayofmonth", "dayofyear", "hourofday", "none" | ✓ | Label cadence for temporal axes; None inherits from the parent axis time_unit. |
@@ -2654,12 +2663,12 @@ Authored overlay for AxisGridZeroStyle. Zero-baseline gridline color and width o
2654
2663
 
2655
2664
  <a id="axislabeloverlapconfig"></a>
2656
2665
  ## AxisLabelOverlapConfig
2657
- Authored overlay for AxisLabelOverlapConfig. Two-bool overlap strategy enablement for CATEGORICAL axis labels.
2666
+ Authored overlay for AxisLabelOverlapConfig. Two-bool overlap strategy enablement for x-axis labels.
2658
2667
 
2659
2668
  | Field | Type | Optional | Description |
2660
2669
  |-------|------|:--------:|-------------|
2661
2670
  | `tilt` | bool | ✓ | Enable label tilt strategy; None inherits from parent axis. |
2662
- | `skip` | bool | ✓ | Enable parity-skip strategy (drops every other label); None inherits from parent axis. |
2671
+ | `skip` | bool | ✓ | Enable temporal label thinning; ignored on categorical axes; None inherits from parent axis. |
2663
2672
 
2664
2673
  <a id="datatablerowpaddingstyle"></a>
2665
2674
  ## DataTableRowPaddingStyle
@@ -2804,15 +2813,25 @@ Postgres source configuration.
2804
2813
  |-------|------|:--------:|-------------|
2805
2814
  | `type` | enum: "postgres" | | Source type identifier. |
2806
2815
  | `host` | str | | Database host name or IP address. |
2807
- | `dbname` | str | | Database name (matches dbt 'dbname'). |
2816
+ | `dbname` | str | | Database name (dbt accepts database as an input alias). |
2808
2817
  | `user` | str | | Database user name. |
2809
- | `password` | str | | Database password. |
2818
+ | `password` | str | | Database password (dbt accepts pass as an input alias). |
2810
2819
  | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2820
+ | `max_query_duration_seconds` | int | ✓ | Maximum execution time for one query in seconds. Overrides execution.max_query_duration_seconds for this source. |
2811
2821
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2812
2822
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2813
2823
  | `port` | int | ✓ | Database port number. |
2814
2824
  | `schema` | str | ✓ | Default schema for queries. |
2825
+ | `connect_timeout` | int | ✓ | Connection timeout in seconds. |
2826
+ | `role` | str | ✓ | PostgreSQL role to assume after connecting. |
2827
+ | `search_path` | str | ✓ | PostgreSQL search_path for the connection. |
2828
+ | `keepalives_idle` | int | ✓ | Idle seconds before TCP keepalives begin. |
2815
2829
  | `sslmode` | enum: "disable", "allow", "prefer", "require", "verify-ca", "verify-full" | ✓ | libpq SSL mode forwarded to psycopg2. None lets libpq decide its default. |
2830
+ | `sslcert` | str | ✓ | Path to the client SSL certificate. |
2831
+ | `sslkey` | str | ✓ | Path to the client SSL private key. |
2832
+ | `sslrootcert` | str | ✓ | Path to the trusted SSL certificate authority file. |
2833
+ | `application_name` | str | ✓ | Application name reported to PostgreSQL. |
2834
+ | `retries` | int | ✓ | Number of connection retry attempts. |
2816
2835
 
2817
2836
  <a id="snowflakesourceconfig"></a>
2818
2837
  ## SnowflakeSourceConfig
@@ -2822,15 +2841,38 @@ Snowflake source configuration.
2822
2841
  |-------|------|:--------:|-------------|
2823
2842
  | `type` | enum: "snowflake" | | Source type identifier. |
2824
2843
  | `account` | str | | Snowflake account identifier (e.g. xy12345.us-east-1). |
2825
- | `user` | str | | Snowflake user name. |
2826
2844
  | `database` | str | | Snowflake database name. |
2827
- | `warehouse` | str | | Snowflake virtual warehouse name. |
2828
2845
  | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2846
+ | `max_query_duration_seconds` | int | ✓ | Maximum execution time for one query in seconds. Overrides execution.max_query_duration_seconds for this source. |
2829
2847
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2830
2848
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2849
+ | `user` | str | ✓ | Snowflake user name. Omit for token-based authentication when supported. |
2831
2850
  | `password` | str | ✓ | Snowflake password. Omit when using OAuth or key-pair auth. |
2851
+ | `warehouse` | str | ✓ | Snowflake virtual warehouse name. |
2832
2852
  | `schema` | str | ✓ | Default schema for queries. |
2833
2853
  | `role` | str | ✓ | Snowflake role to assume for the session. |
2854
+ | `authenticator` | str | ✓ | Snowflake authenticator name or external-browser mode. |
2855
+ | `private_key` | str | ✓ | PEM private key used for key-pair authentication. |
2856
+ | `private_key_path` | str | ✓ | Path to a PEM private key for key-pair authentication. |
2857
+ | `private_key_passphrase` | str | ✓ | Passphrase for the configured private key. |
2858
+ | `token` | str | ✓ | OAuth access token for token authentication. |
2859
+ | `oauth_client_id` | str | ✓ | OAuth client identifier. |
2860
+ | `oauth_client_secret` | str | ✓ | OAuth client secret. |
2861
+ | `query_tag` | str | ✓ | Snowflake query tag applied to statements. |
2862
+ | `client_session_keep_alive` | bool | ✓ | Whether Snowflake keeps the client session alive. |
2863
+ | `host` | str | ✓ | Snowflake host override. |
2864
+ | `port` | int | ✓ | Snowflake port override. |
2865
+ | `proxy_host` | str | ✓ | HTTP proxy host for Snowflake connections. |
2866
+ | `proxy_port` | int | ✓ | HTTP proxy port for Snowflake connections. |
2867
+ | `protocol` | str | ✓ | Network protocol for the Snowflake connection. |
2868
+ | `connect_retries` | int | ✓ | Number of retries while opening a connection. |
2869
+ | `connect_timeout` | int | ✓ | Connection timeout in seconds. |
2870
+ | `retry_on_database_errors` | bool | ✓ | Whether database errors are retried. |
2871
+ | `retry_all` | bool | ✓ | Whether all connection errors are retried. |
2872
+ | `insecure_mode` | bool | ✓ | Whether TLS certificate verification is disabled. |
2873
+ | `reuse_connections` | bool | ✓ | Whether dbt reuses Snowflake connections. |
2874
+ | `s3_stage_vpce_dns_name` | str | ✓ | Private VPC endpoint DNS name for S3 staging. |
2875
+ | `platform_detection_timeout_seconds` | float | ✓ | Timeout for Snowflake platform detection. |
2834
2876
 
2835
2877
  <a id="bigquerysourceconfig"></a>
2836
2878
  ## BigQuerySourceConfig
@@ -2842,12 +2884,37 @@ BigQuery source configuration.
2842
2884
  | `project` | str | | GCP project ID. |
2843
2885
  | `dataset` | str | | BigQuery dataset name (equivalent to schema). |
2844
2886
  | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2887
+ | `max_query_duration_seconds` | int | ✓ | Maximum execution time for one query in seconds. Overrides execution.max_query_duration_seconds for this source. |
2845
2888
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2846
2889
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2847
2890
  | `keyfile` | str | ✓ | Path to service account JSON key file. |
2848
- | `keyfile_json` | dict[str, Any] | ✓ | Inline service account JSON dict. |
2891
+ | `keyfile_json` | dict[str, str \| int \| float \| bool] | ✓ | Inline service account JSON dict. |
2849
2892
  | `location` | str | ✓ | Dataset location (e.g. US, EU). |
2850
- | `method` | enum: "oauth", "service-account", "service-account-json" | ✓ | dbt-bigquery authentication method. Inferred from keyfile/keyfile_json when omitted: 'service-account-json' if keyfile_json set, 'service-account' if keyfile set, 'oauth' (Application Default Credentials) otherwise. |
2893
+ | `method` | enum: "oauth", "oauth-secrets", "service-account", "service-account-json", "external-oauth-wif" | ✓ | dbt-bigquery authentication method. Inferred from keyfile/keyfile_json when omitted: 'service-account-json' if keyfile_json set, 'service-account' if keyfile set, 'oauth' (Application Default Credentials) otherwise. |
2894
+ | `execution_project` | str | ✓ | GCP project billed for BigQuery execution. |
2895
+ | `quota_project` | str | ✓ | GCP project used for quota attribution. |
2896
+ | `api_endpoint` | str | ✓ | BigQuery API endpoint override. |
2897
+ | `priority` | enum: "interactive", "batch" | ✓ | BigQuery job priority. |
2898
+ | `maximum_bytes_billed` | int | ✓ | Maximum bytes a BigQuery job may bill. |
2899
+ | `impersonate_service_account` | str | ✓ | Service account to impersonate for BigQuery jobs. |
2900
+ | `job_retry_deadline_seconds` | int | ✓ | Deadline in seconds for retrying a BigQuery job. |
2901
+ | `job_retries` | int | ✓ | Number of BigQuery job retry attempts (dbt accepts retries as an input alias). |
2902
+ | `job_creation_timeout_seconds` | int | ✓ | Timeout in seconds while creating a BigQuery job. |
2903
+ | `job_execution_timeout_seconds` | int | ✓ | Timeout in seconds while executing a BigQuery job. |
2904
+ | `token` | str | ✓ | OAuth access token. |
2905
+ | `refresh_token` | str | ✓ | OAuth refresh token. |
2906
+ | `client_id` | str | ✓ | OAuth client identifier. |
2907
+ | `client_secret` | str | ✓ | OAuth client secret. |
2908
+ | `token_uri` | str | ✓ | OAuth token endpoint URI. |
2909
+ | `workload_pool_provider_path` | str | ✓ | Workload identity pool provider resource path. |
2910
+ | `service_account_impersonation_url` | str | ✓ | Service-account impersonation endpoint URL. |
2911
+ | `token_endpoint` | dict[str, str] | ✓ | OAuth token endpoint configuration. |
2912
+ | `compute_region` | str | ✓ | Dataproc compute region. |
2913
+ | `dataproc_cluster_name` | str | ✓ | Dataproc cluster name. |
2914
+ | `gcs_bucket` | str | ✓ | Cloud Storage bucket for Dataproc submission. |
2915
+ | `submission_method` | str | ✓ | dbt-bigquery Dataproc submission method. |
2916
+ | `dataproc_batch` | dict[str, str \| int \| float \| bool] | ✓ | Dataproc batch configuration. |
2917
+ | `scopes` | list[str] | ✓ | OAuth scopes requested for BigQuery credentials. |
2851
2918
 
2852
2919
  <a id="redshiftsourceconfig"></a>
2853
2920
  ## RedshiftSourceConfig
@@ -2857,14 +2924,43 @@ Redshift source configuration.
2857
2924
  |-------|------|:--------:|-------------|
2858
2925
  | `type` | enum: "redshift" | | Source type identifier. |
2859
2926
  | `host` | str | | Redshift cluster host name. |
2860
- | `dbname` | str | | Redshift database name. |
2861
- | `user` | str | | Redshift user name. |
2862
- | `password` | str | | Redshift password. |
2927
+ | `dbname` | str | | Redshift database name (dbt accepts database as an input alias). |
2863
2928
  | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2929
+ | `max_query_duration_seconds` | int | ✓ | Maximum execution time for one query in seconds. Overrides execution.max_query_duration_seconds for this source. |
2864
2930
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2865
2931
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2866
2932
  | `port` | int | ✓ | Redshift port number. |
2867
2933
  | `schema` | str | ✓ | Default schema for queries. |
2934
+ | `user` | str | ✓ | Redshift user name. Omit for IAM-based authentication when supported. |
2935
+ | `password` | str | ✓ | Redshift password. Omit for IAM-based authentication when supported. |
2936
+ | `method` | str | ✓ | Redshift authentication method. |
2937
+ | `cluster_id` | str | ✓ | Redshift cluster identifier for IAM authentication. |
2938
+ | `iam_profile` | str | ✓ | IAM profile ARN used for Redshift authentication. |
2939
+ | `autocreate` | bool | ✓ | Whether dbt may create the database user. |
2940
+ | `db_groups` | list[str] | ✓ | Redshift database groups assigned to the user. |
2941
+ | `ra3_node` | bool | ✓ | Whether the target uses Redshift RA3 nodes. |
2942
+ | `connect_timeout` | int | ✓ | Connection timeout in seconds. |
2943
+ | `role` | str | ✓ | IAM role ARN used for Redshift operations. |
2944
+ | `sslmode` | str | ✓ | SSL verification mode for the Redshift connection. |
2945
+ | `retries` | int | ✓ | Number of Redshift connection retry attempts. |
2946
+ | `retry_all` | bool | ✓ | Whether all connection errors are retried. |
2947
+ | `region` | str | ✓ | AWS region containing the Redshift target. |
2948
+ | `autocommit` | bool | ✓ | Whether Redshift statements autocommit. |
2949
+ | `access_key_id` | str | ✓ | AWS access key identifier. |
2950
+ | `secret_access_key` | str | ✓ | AWS secret access key. |
2951
+ | `idc_region` | str | ✓ | AWS IAM Identity Center region. |
2952
+ | `issuer_url` | str | ✓ | Identity-provider issuer URL. |
2953
+ | `idp_listen_port` | int | ✓ | Local port used for identity-provider callbacks. |
2954
+ | `idc_client_display_name` | str | ✓ | IAM Identity Center client display name. |
2955
+ | `idp_response_timeout` | int | ✓ | Identity-provider response timeout in seconds. |
2956
+ | `token_endpoint` | dict[str, str] | ✓ | Identity-provider token endpoint configuration. |
2957
+ | `is_serverless` | bool | ✓ | Whether the target is Redshift Serverless. |
2958
+ | `serverless_work_group` | str | ✓ | Redshift Serverless workgroup name. |
2959
+ | `serverless_acct_id` | str | ✓ | AWS account identifier for Redshift Serverless. |
2960
+ | `tcp_keepalive` | bool | ✓ | Whether TCP keepalives are enabled. |
2961
+ | `tcp_keepalive_idle` | int | ✓ | Idle seconds before TCP keepalives begin. |
2962
+ | `tcp_keepalive_interval` | int | ✓ | Seconds between TCP keepalive probes. |
2963
+ | `tcp_keepalive_count` | int | ✓ | Number of TCP keepalive probes before failure. |
2868
2964
 
2869
2965
  <a id="mysqlsourceconfig"></a>
2870
2966
  ## MySQLSourceConfig
@@ -2878,6 +2974,7 @@ MySQL source configuration.
2878
2974
  | `user` | str | | MySQL user name. |
2879
2975
  | `password` | str | | MySQL password. |
2880
2976
  | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2977
+ | `max_query_duration_seconds` | int | ✓ | Maximum execution time for one query in seconds. Overrides execution.max_query_duration_seconds for this source. |
2881
2978
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2882
2979
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2883
2980
  | `port` | int | ✓ | MySQL port number. |
@@ -2891,10 +2988,26 @@ DuckDB source configuration.
2891
2988
  |-------|------|:--------:|-------------|
2892
2989
  | `type` | enum: "duckdb" | | Source type identifier. |
2893
2990
  | `cache` | [Cache](#cache) | ✓ | Cache policy default for every query against this source, e.g. cache: 1h — queries inherit it and may refine it; cache: false opts them out. |
2991
+ | `max_query_duration_seconds` | int | ✓ | Maximum execution time for one query in seconds. Overrides execution.max_query_duration_seconds for this source. |
2894
2992
  | `lookml_path` | str | ✓ | Relative path to the directory of .lkml files backing this source's models. |
2895
2993
  | `models` | dict[str, [LookMLModelConfig](#lookmlmodelconfig)] | ✓ | Named LookML model collections nested under this source. |
2896
2994
  | `path` | str | ✓ | DuckDB file path or ':memory:' for an in-memory database. |
2897
2995
  | `schema` | str | ✓ | Default schema for unqualified table names (sets search_path). |
2996
+ | `duckdb_config` | dict[str, str \| int \| float \| bool] | ✓ | DuckDB connection configuration values, such as enable_external_access. |
2997
+ | `extensions` | list[str \| dict[str, str]] | ✓ | DuckDB extensions to install and load. |
2998
+ | `settings` | dict[str, str \| int \| float \| bool] | ✓ | DuckDB settings and pragma values. |
2999
+ | `secrets` | list[dict[str, str \| int \| float \| bool]] | ✓ | DuckDB secret definitions for external services. |
3000
+ | `external_root` | str | ✓ | Root path for dbt-duckdb external materializations. |
3001
+ | `use_credential_provider` | str | ✓ | Credential-provider chain used for external services. |
3002
+ | `attach` | list[DuckDBAttachmentConfig] | ✓ | Databases to attach to the DuckDB connection. |
3003
+ | `filesystems` | list[dict[str, str \| int \| float \| bool]] | ✓ | fsspec filesystem configurations attached to DuckDB. |
3004
+ | `remote` | DuckDBRemoteConfig | ✓ | Remote DuckDB connection configuration. |
3005
+ | `plugins` | list[DuckDBPluginConfig] | ✓ | dbt-duckdb plugin configurations. |
3006
+ | `disable_transactions` | bool | ✓ | Whether dbt-duckdb disables statement transactions. |
3007
+ | `keep_open` | bool | ✓ | Whether dbt-duckdb keeps its connection open. |
3008
+ | `module_paths` | list[str] | ✓ | Python module paths dbt-duckdb loads. |
3009
+ | `retries` | DuckDBRetriesConfig | ✓ | dbt-duckdb connection and query retry configuration. |
3010
+ | `is_ducklake` | bool | ✓ | Whether this source uses DuckLake. |
2898
3011
 
2899
3012
  <a id="sqlitesourceconfig"></a>
2900
3013
  ## SQLiteSourceConfig
@@ -24,7 +24,6 @@ from dataface.core.inspect.resolver import LayeredSchemaResolver
24
24
  from dataface.core.pack.models import PackProposal, ProposedDashboard
25
25
  from dataface.core.pack.planner import SourceEntry, plan_pack
26
26
  from dataface.core.pack.proposal_store import dump_proposal
27
- from dataface.core.project import Project
28
27
 
29
28
  if TYPE_CHECKING:
30
29
  from dataface.cli.filesystem_project import FilesystemProject
@@ -413,7 +412,7 @@ def _validate_scaffold_targets(
413
412
 
414
413
  def apply_proposal(
415
414
  proposal: PackProposal,
416
- project: Project,
415
+ project: FilesystemProject,
417
416
  overwrite: bool = False,
418
417
  ) -> ScaffoldResult:
419
418
  """Apply an approved PackProposal, writing a sparse ``faces/`` folder tree.
@@ -64,8 +64,8 @@ class ProjectSession:
64
64
  Composition roots (CLI, MCP, dft serve, Cloud, A lIe, Playground) construct
65
65
  one ProjectSession at their documented boundary and pass it (or its component
66
66
  resources) into core. Verb methods on ProjectSession are thin forwarders to
67
- agent_api functions — they unpack self.project.root so callers don't
68
- re-thread it at every call site.
67
+ agent_api functions — they unpack self.project so callers don't re-thread
68
+ it at every call site.
69
69
  """
70
70
 
71
71
  project: Project
@@ -206,15 +206,13 @@ class ProjectSession:
206
206
  # from_project(adapter_registry=...)) never reach here. But
207
207
  # from_project() can also be called WITHOUT an injected registry
208
208
  # against a non-filesystem Project (e.g. rendering a query-less face
209
- # through an in-memory Project in tests) — that's a supported path,
210
- # because build_adapter_registry only reads `.root` / `.sources` off
211
- # the project, never `.data_path()`. The cast's promise only breaks
212
- # if an adapter actually materializes a data file via
213
- # `FilesystemProject.data_path()`, which no non-filesystem caller
214
- # reaches (a non-filesystem Project has no data files to open on
215
- # local disk in the first place).
209
+ # through an in-memory Project in tests) — that's a supported path:
210
+ # build_adapter_registry takes a base Project and only special-cases
211
+ # FilesystemProject internally (data_dir / dbt-sibling detection fall
212
+ # back to None for any other host), so no data file is ever
213
+ # materialized via a non-filesystem Project's disk.
216
214
  return build_adapter_registry(
217
- cast("FilesystemProject", self.project),
215
+ self.project,
218
216
  read_only=self._read_only,
219
217
  profile_type=self._dialect,
220
218
  duckdb_config=self._duckdb_config,
@@ -244,7 +242,7 @@ class ProjectSession:
244
242
  # Cloud-managed sources on every refresh-driven path. Popping the
245
243
  # cached keys preserves the instance (and its class) while forcing the
246
244
  # next access to re-run each property body.
247
- for key in ("config_file", "sources", "warnings_ignore"):
245
+ for key in ("sources", "warnings_ignore"):
248
246
  vars(self.project).pop(key, None)
249
247
 
250
248
  def close(self) -> None:
@@ -264,7 +262,11 @@ class ProjectSession:
264
262
 
265
263
  @property
266
264
  def faces_dir(self) -> Path:
267
- return self.project.faces_dir
265
+ # ProjectSession is FS-only-meaningful here: `.open()` always builds a
266
+ # FilesystemProject, and this property's only caller
267
+ # (ai/context.py's resolve_dashboard_path) is a CLI/MCP-only path.
268
+ # Same reasoning as the adapter_registry cast above.
269
+ return cast("FilesystemProject", self.project).faces_dir
268
270
 
269
271
  @cached_property
270
272
  def _relationship_context(self) -> RelationshipContext | None:
@@ -285,14 +287,7 @@ class ProjectSession:
285
287
 
286
288
  def validate_paths(self, paths: list[Path] | None) -> list[ValidateResult]:
287
289
  results = _validate.validate_paths(paths, project=self.project)
288
- return _validate.annotate_with_data_lint(results, project_session=self)
289
-
290
- def _source_names(self) -> frozenset[str]:
291
- """Return the configured source names for /data/ alias validation.
292
-
293
- Reads from the project config file — no adapter registry or DB connection needed.
294
- """
295
- return frozenset(self.sources.sources.keys())
290
+ return _validate.annotate_with_data_lint(results, project=self.project)
296
291
 
297
292
  @overload
298
293
  def validate_query(
@@ -240,19 +240,16 @@ def _build_index(project: Project, *, under: str) -> list[dict[str, Any]]:
240
240
  continue
241
241
  if not isinstance(content, dict):
242
242
  continue
243
- title = content.get("title", PurePosixPath(pf.relpath).stem)
244
- relpath = pf.relpath
245
- prefix = f"{FACES_SUBDIR}/"
246
- # iter_faces(under=FACES_SUBDIR) guarantees relpath starts with
247
- # "faces/", but the fallback keeps face_path sane if that ever changes.
248
- face_path = (
249
- Path(relpath.removeprefix(prefix))
250
- if relpath.startswith(prefix)
251
- else Path(relpath)
243
+ title = content.get("title", pf.stem)
244
+ rel = PurePosixPath(pf.relpath)
245
+ # iter_faces(under=FACES_SUBDIR) guarantees relpath is under "faces/",
246
+ # but the fallback keeps face_path sane if that ever changes.
247
+ face_path = Path(
248
+ rel.relative_to(FACES_SUBDIR) if rel.is_relative_to(FACES_SUBDIR) else rel
252
249
  )
253
250
  entry = _index_entry_from_content(
254
251
  face_path=face_path,
255
- file_path=Path(relpath),
252
+ file_path=Path(pf.relpath),
256
253
  title=title,
257
254
  description=content.get("description", ""),
258
255
  content=content,
@@ -49,7 +49,11 @@ class InstallSkillsResult(BaseModel):
49
49
 
50
50
 
51
51
  def skills_for_file_install() -> list[Skill]:
52
- """Workflow skills exposed on the CLI surface."""
52
+ """Workflow skills exposed on the CLI surface.
53
+
54
+ All file-backed by construction: no ``project``/``extra_skills`` is passed,
55
+ so only wheel built-ins are listed.
56
+ """
53
57
  return sorted(
54
58
  (s for s in list_skills(surface="cli").skills if s.kind == "workflow"),
55
59
  key=lambda s: s.name,
@@ -87,6 +91,9 @@ def detect_legacy_skill_dirs(
87
91
 
88
92
 
89
93
  def _rendered_skill_md(skill: Skill) -> str:
94
+ # Narrowing only — skills_for_file_install lists built-ins, which always
95
+ # have a directory; a file-less one would raise on the `/` below anyway.
96
+ assert skill.directory is not None
90
97
  raw = (skill.directory / "SKILL.md").read_text(encoding="utf-8")
91
98
  if not raw.startswith("---"):
92
99
  raise ValueError(f"{skill.directory / 'SKILL.md'}: missing frontmatter")