@salesforce/afv-skills 1.59.0 → 1.60.0

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 (173) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-architecture-analyze/SKILL.md +66 -17
  3. package/skills/agentforce-architecture-analyze/assets/cli/show_access_token.yaml +4 -7
  4. package/skills/agentforce-architecture-analyze/references/contract.json +1 -1
  5. package/skills/agentforce-architecture-analyze/scripts/emit_env.py +2 -2
  6. package/skills/agentforce-architecture-analyze/scripts/main.py +40 -44
  7. package/skills/agentforce-architecture-analyze/scripts/rest_client.py +11 -3
  8. package/skills/agentforce-architecture-analyze/scripts/sf_cli.py +77 -10
  9. package/skills/agentforce-architecture-analyze/scripts/tests/test_end_to_end_fixture.py +1 -0
  10. package/skills/agentforce-architecture-analyze/scripts/tests/test_flow_children_inflation.py +1 -0
  11. package/skills/agentforce-architecture-analyze/scripts/tests/test_main_pipeline.py +53 -2
  12. package/skills/agentforce-architecture-analyze/scripts/tests/test_resolve_creds.py +184 -36
  13. package/skills/agentforce-architecture-analyze/scripts/tests/test_rest_client.py +17 -0
  14. package/skills/agentforce-architecture-analyze/scripts/tests/test_sf_cli.py +165 -11
  15. package/skills/agentforce-architecture-analyze/scripts/tests/test_skill_md_pipeline_block.py +319 -0
  16. package/skills/agentforce-d360-analyze/SKILL.md +28 -10
  17. package/skills/agentforce-d360-analyze/scripts/dc.py +200 -50
  18. package/skills/agentforce-d360-analyze/scripts/discover_sessions.py +10 -6
  19. package/skills/agentforce-d360-analyze/scripts/fetch_dc.py +22 -6
  20. package/skills/agentforce-d360-analyze/scripts/resolve_session.py +21 -13
  21. package/skills/agentforce-d360-analyze/scripts/tests/test_dc_and_resolve_session.py +298 -76
  22. package/skills/agentforce-d360-analyze/scripts/tests/test_default_target_org.py +432 -0
  23. package/skills/agentforce-d360-analyze/scripts/tests/test_discover_sessions.py +1 -1
  24. package/skills/agentforce-d360-analyze/scripts/tests/test_resolve_session_main.py +9 -3
  25. package/skills/agentforce-d360-analyze/scripts/tests/test_skill_md_prefix_block.py +176 -0
  26. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +17 -27
  27. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +7 -17
  28. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +47 -63
  29. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +10 -13
  30. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -46
  31. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +4 -21
  32. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +58 -82
  33. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +16 -315
  34. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +9 -24
  35. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -44
  36. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +11 -63
  37. package/skills/consumer-goods-rtr-datacloud-export-configure/SKILL.md +2 -1
  38. package/skills/consumer-goods-tpe-dashboard-configure/SKILL.md +2 -1
  39. package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/SKILL.md +2 -1
  40. package/skills/consumer-goods-tpe-datakit-deploy/SKILL.md +2 -1
  41. package/skills/data360-schema-get/SKILL.md +3 -3
  42. package/skills/design-systems-slds-validate/references/report-format.md +1 -1
  43. package/skills/dx-org-manage/SKILL.md +1 -0
  44. package/skills/dx-org-shape-manage/SKILL.md +1 -0
  45. package/skills/dx-org-snapshot-manage/SKILL.md +172 -0
  46. package/skills/dx-org-snapshot-manage/examples/create_error_output.json +16 -0
  47. package/skills/dx-org-snapshot-manage/examples/create_success_output.json +19 -0
  48. package/skills/dx-org-snapshot-manage/examples/delete_output.json +9 -0
  49. package/skills/dx-org-snapshot-manage/examples/get_output.json +19 -0
  50. package/skills/dx-org-snapshot-manage/examples/list_output.json +36 -0
  51. package/skills/dx-org-snapshot-manage/references/cli_flags.md +179 -0
  52. package/skills/dx-org-snapshot-manage/references/snapshot_usage.md +55 -0
  53. package/skills/dx-org-trial-expiration-check/SKILL.md +1 -1
  54. package/skills/dx-org-trial-expiration-check/scripts/check_expiration.sh +2 -2
  55. package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +7 -5
  56. package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +11 -11
  57. package/skills/experience-content-media-image-generate/SKILL.md +83 -0
  58. package/skills/experience-content-media-image-generate/scripts/download-image.sh +84 -0
  59. package/skills/external-diagram-mermaid-generate/SKILL.md +12 -12
  60. package/skills/field-service-data-capture-migrate/references/component-mapping.md +1 -1
  61. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +1 -2
  62. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +1 -0
  63. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +1 -1
  64. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +12 -1
  65. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +4 -1
  66. package/skills/field-service-setup-orchestrator-get/SKILL.md +0 -1
  67. package/skills/insurance-brokerage-agency-billing-configure/SKILL.md +233 -0
  68. package/skills/insurance-brokerage-agency-billing-configure/references/accounting-setup.md +117 -0
  69. package/skills/insurance-brokerage-agency-billing-configure/references/billing-rules-config.md +237 -0
  70. package/skills/insurance-brokerage-agency-billing-configure/references/billing-settings.md +271 -0
  71. package/skills/insurance-brokerage-agency-billing-configure/references/demo-policy-config.md +138 -0
  72. package/skills/insurance-brokerage-agency-billing-configure/references/org-defaults.md +64 -0
  73. package/skills/insurance-brokerage-agency-billing-configure/references/permissions-config.md +226 -0
  74. package/skills/insurance-brokerage-agency-billing-configure/references/prerequisites.md +142 -0
  75. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +3 -2
  76. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +3 -3
  77. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +68 -45
  78. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +17 -18
  79. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +16 -3
  80. package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +16 -13
  81. package/skills/marketing-message-test/SKILL.md +194 -0
  82. package/skills/marketing-message-test/references/api-contract.md +333 -0
  83. package/skills/omnistudio-callable-apex-generate/SKILL.md +3 -3
  84. package/skills/omnistudio-datamapper-generate/SKILL.md +6 -6
  85. package/skills/omnistudio-datapacks-deploy/SKILL.md +11 -11
  86. package/skills/omnistudio-dependencies-analyze/SKILL.md +5 -5
  87. package/skills/omnistudio-epc-catalog-generate/SKILL.md +6 -6
  88. package/skills/omnistudio-flexcard-generate/SKILL.md +6 -6
  89. package/skills/omnistudio-integration-procedure-generate/SKILL.md +6 -6
  90. package/skills/omnistudio-omniscript-generate/SKILL.md +6 -5
  91. package/skills/platform-apex-logs-debug/SKILL.md +1 -0
  92. package/skills/platform-metadata-deploy/SKILL.md +1 -0
  93. package/skills/platform-models-api-configure/SKILL.md +5 -5
  94. package/skills/platform-soql-query/SKILL.md +1 -0
  95. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +17 -6
  96. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +107 -192
  97. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone-action.json +6 -6
  98. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +49 -20
  99. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +4 -4
  100. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +224 -290
  101. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/workflow.md +295 -0
  102. package/skills/service-itsm-incident-priority-configure/SKILL.md +1 -1
  103. package/skills/service-itsm-slack-itservice-configure/SKILL.md +2 -1
  104. package/skills/service-itsm-slack-itservice-configure/references/manage-slack-connection.md +2 -2
  105. package/skills/service-omni-channel-setup-coordinate/SKILL.md +20 -13
  106. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +5 -55
  107. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +0 -38
  108. package/skills/service-omni-command-center-analyze/SKILL.md +2 -3
  109. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  110. package/skills/tableau-next-asset-create/README.md +65 -0
  111. package/skills/tableau-next-asset-create/SKILL.md +409 -0
  112. package/skills/tableau-next-asset-create/references/admin-users.md +149 -0
  113. package/skills/tableau-next-asset-create/references/ai-readiness.md +355 -0
  114. package/skills/tableau-next-asset-create/references/alerts.md +111 -0
  115. package/skills/tableau-next-asset-create/references/build-workflow.md +819 -0
  116. package/skills/tableau-next-asset-create/references/dashboard-authoring.md +302 -0
  117. package/skills/tableau-next-asset-create/references/dashboard-design-principles.md +352 -0
  118. package/skills/tableau-next-asset-create/references/data-transform-gotchas.md +210 -0
  119. package/skills/tableau-next-asset-create/references/data-understanding.md +387 -0
  120. package/skills/tableau-next-asset-create/references/deep-reference-index.md +74 -0
  121. package/skills/tableau-next-asset-create/references/destructive-operations.md +62 -0
  122. package/skills/tableau-next-asset-create/references/edit-dashboard.md +273 -0
  123. package/skills/tableau-next-asset-create/references/edit-visualization.md +529 -0
  124. package/skills/tableau-next-asset-create/references/empty-source-handling.md +104 -0
  125. package/skills/tableau-next-asset-create/references/ingest-and-metric-gotchas.md +638 -0
  126. package/skills/tableau-next-asset-create/references/insight-bundle.md +69 -0
  127. package/skills/tableau-next-asset-create/references/large-flat-file-handling.md +331 -0
  128. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_envelope.json +54 -0
  129. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_shared-widget-fields.json +122 -0
  130. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/add_page.json +29 -0
  131. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/delete_page.json +27 -0
  132. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/discard_dashboard.json +11 -0
  133. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget.json +30 -0
  134. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget_to_page.json +30 -0
  135. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/place_widget_on_page.json +30 -0
  136. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_dashboard.json +16 -0
  137. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_page.json +26 -0
  138. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/rename_page.json +28 -0
  139. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/save_dashboard.json +18 -0
  140. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/undo.json +11 -0
  141. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_button_widget.json +69 -0
  142. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_container_widget.json +88 -0
  143. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_filter_widget.json +146 -0
  144. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_metric_widget.json +78 -0
  145. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_navigation_widget.json +80 -0
  146. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_parameter_widget.json +160 -0
  147. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_text_widget.json +103 -0
  148. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_visualization_widget.json +68 -0
  149. package/skills/tableau-next-asset-create/references/sdm-tool-reference.md +721 -0
  150. package/skills/tableau-next-asset-create/references/semantic-query-and-enrichment.md +355 -0
  151. package/skills/tableau-next-asset-create/references/shared-gates.md +322 -0
  152. package/skills/tableau-next-asset-create/references/sharing-and-promotion.md +306 -0
  153. package/skills/tableau-next-asset-create/references/tasks/ai-readiness-audit.md +51 -0
  154. package/skills/tableau-next-asset-create/references/tasks/analyze-data.md +48 -0
  155. package/skills/tableau-next-asset-create/references/tasks/build-dashboard.md +66 -0
  156. package/skills/tableau-next-asset-create/references/tasks/build-end-to-end.md +73 -0
  157. package/skills/tableau-next-asset-create/references/tasks/create-data-transform.md +51 -0
  158. package/skills/tableau-next-asset-create/references/tasks/create-viz.md +58 -0
  159. package/skills/tableau-next-asset-create/references/tasks/edit-dashboard.md +80 -0
  160. package/skills/tableau-next-asset-create/references/tasks/edit-sdm.md +106 -0
  161. package/skills/tableau-next-asset-create/references/tasks/edit-viz.md +40 -0
  162. package/skills/tableau-next-asset-create/references/tasks/enrich-model.md +68 -0
  163. package/skills/tableau-next-asset-create/references/tasks/ingest-flat-file.md +68 -0
  164. package/skills/tableau-next-asset-create/references/tasks/manage-alert.md +35 -0
  165. package/skills/tableau-next-asset-create/references/tasks/profile-dataset.md +47 -0
  166. package/skills/tableau-next-asset-create/references/tasks/promote-or-reuse.md +46 -0
  167. package/skills/tableau-next-asset-create/references/tasks/provision-user.md +39 -0
  168. package/skills/tableau-next-asset-create/references/tasks/query-model.md +92 -0
  169. package/skills/tableau-next-asset-create/references/tasks/review-verified-questions.md +42 -0
  170. package/skills/tableau-next-asset-create/references/tasks/share-asset.md +37 -0
  171. package/skills/tableau-next-asset-create/references/verified-questions.md +125 -0
  172. package/skills/tableau-next-asset-create/references/viz-authoring.md +992 -0
  173. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +0 -102
@@ -0,0 +1,302 @@
1
+ # Dashboard Authoring — Tool Guide & Verified Payloads
2
+
3
+ Load this for **Step 11** (build the dashboard). Covers dashboard creation,
4
+ widget placement grid, metric/viz widgets, text/label widgets, and critical gotchas.
5
+
6
+ > **Before placing widgets on the 48-col grid:** design the narrative and
7
+ > visual hierarchy first — see `references/dashboard-design-principles.md`
8
+ > (KPIs top/left, F/Z eye-path, story order: KPIs → Trends → Breakdowns →
9
+ > Correlations). Grid placement below is the mechanics; the design doc is
10
+ > the intent.
11
+
12
+ ## Contents
13
+
14
+ - 1. Create the Empty Dashboard
15
+ - 2. Grid Specification
16
+ - 3. Editing Widgets — `edit_dashboard` (primary path)
17
+ - 4. Gotchas Summary
18
+ - 5. Global Filters — `add_global_filter_to_dashboard`
19
+ - 6. Deleting a Dashboard — `delete_dashboard`
20
+ - 7. Preflight before deleting a viz or dashboard — `list_asset_dependencies`
21
+ - 8. Show a dashboard vs metadata-only — `render_dashboard` / `get_dashboard`
22
+
23
+ ---
24
+
25
+ ## 1. Create the Empty Dashboard
26
+
27
+ Always start with an empty shell, then populate it with widgets.
28
+
29
+ ```jsonc
30
+ {
31
+ "name": "Brightleaf_Sales_Overview",
32
+ "label": "Brightleaf Sales Overview",
33
+ "workspaceIdOrApiName": "brightleaf_demo",
34
+ "description": "Overview of Brightleaf sales performance by region and product",
35
+ "widgets": {},
36
+ "layouts": [{
37
+ "columnCount": 48,
38
+ "rowHeight": 20,
39
+ "maxWidth": 1200,
40
+ "style": { "backgroundColor": "#ffffff", "gutterColor": "#f3f3f3", "cellSpacingX": 4, "cellSpacingY": 4 },
41
+ "pages": [{ "name": "page_1", "label": "Overview", "widgets": [] }]
42
+ }],
43
+ "minorVersion": -1
44
+ }
45
+ ```
46
+
47
+ This seeded-`page_1` shell is the **default** shape — see the decision rule
48
+ below for when `layouts: []` is the right call instead.
49
+
50
+ **`workspaceIdOrApiName` is an id like `1Dyxx...` or apiName from `list_workspaces`. NOT the workspace label with spaces — that 404s.**
51
+
52
+ **Silent name transforms — always read `name` back.** `create_dashboard` mutates the
53
+ `name` you send with no warning: a duplicate name in the org gets a numeric suffix,
54
+ non-ASCII/unicode characters are silently stripped, names past ~78-80 characters are
55
+ silently truncated, and spaces are silently converted to underscores. Never assume
56
+ the requested `name` was stored verbatim — read the response back before referencing
57
+ it (e.g. in a widget's `source` or a navigation target).
58
+
59
+ **Don't call `create_dashboard` with just `{name, label, workspaceIdOrApiName}`.**
60
+ That leaks a raw Java NPE (`Cannot invoke "java.util.Map.values()" ...getWidgets()`).
61
+ `widgets: {}` and a `layouts` value (`[]` or seeded, per the decision rule below)
62
+ are de-facto required even when empty — never omit either.
63
+
64
+ **Pick one create recipe based on what happens next — verified live
65
+ (2026-09-13):**
66
+
67
+ - **You will `edit_dashboard` immediately after — the common "create, then
68
+ place widgets" flow.** Seed one layout with an empty `page_1` (full `style`
69
+ block + `columnCount`, as in the shell above), then `edit_dashboard`.
70
+ `add_page`/`place_widget_on_page` operate *within* an existing layout — they
71
+ do not create the first one. A `create_dashboard` done with `layouts: []`
72
+ yields a dashboard with **no layout at all**, and the next `edit_dashboard`
73
+ `add_page` fails: `INVALID_INPUT: "Dashboard has no layout to add the page
74
+ to"`.
75
+ - **You will NOT edit/populate the dashboard next** (e.g. handing off a bare
76
+ shell). `layouts: []` is fine — there's nothing to place yet anyway.
77
+
78
+ If you do seed a page up front, give every layout a full `style` block and
79
+ `columnCount` — omitting `style` at create time renders an **unstyled
80
+ dashboard** (no crash), and omitting `columnCount` breaks grid math for every
81
+ widget placed later.
82
+
83
+ **Workspace cascade on delete.** `delete_workspace` cascade-deletes every
84
+ workspace-asset row whose `assetUsageType="Created"`, and that delete propagates
85
+ to the underlying asset — confirmed for `AnalyticsVisualization`, `SemanticModel`,
86
+ AND `AnalyticsDashboard` (not just dashboards). Assets added with
87
+ `assetUsageType="Referenced"` are NOT deleted — only their workspace association
88
+ is removed. `create_dashboard` and `create_*_viz` each auto-create a `"Created"`
89
+ workspace-asset row for the new asset, so any chart or dashboard authored in that
90
+ workspace will be swept up. ALWAYS call `list_workspace_assets` first to inventory
91
+ `Created`-typed assets before deleting a workspace — there's no undo.
92
+
93
+ **Scope and response shape.** `create_dashboard` creates in the current org only —
94
+ it does not copy a dashboard from another org. Verified live (2026-08-30): as
95
+ observed through this tool, `create_dashboard`'s response is a **flat JSON
96
+ object**, NOT double-wrapped. `edit_dashboard` and
97
+ `delete_dashboard`, by contrast, ARE double-wrapped as
98
+ `{"defaultExc": "<stringified JSON>", "responseCode": <number>}` (parse
99
+ `JSON.parse(response.defaultExc)`; see §6 for `delete_dashboard`'s success-path
100
+ exception). The wrapping is per-tool, don't assume it's family-wide.
101
+
102
+ **`minorVersion` must be sent explicitly.** The schema shows `minorVersion:
103
+ -1` as a default, but omitting it can surface a live client-visible error:
104
+ `ILLEGAL_QUERY_PARAMETER_VALUE: Illegal value for query parameter
105
+ 'minorVersion': ''`. Always pass `minorVersion: -1` on `create_dashboard` (and
106
+ on `edit_dashboard` — `edit-dashboard.md` §1).
107
+
108
+ ---
109
+
110
+ ## 2. Grid Specification
111
+
112
+ The grid is **48 columns** (`columnCount: 48`), `rowHeight 20`, `maxWidth 1200`.
113
+ Plan colspans in 48ths (a half-width chart = 24, a quarter KPI = 12).
114
+ `columnCount` is required on every layout at create time, alongside a full
115
+ `style` block (§1).
116
+
117
+ **Always name AND label every page.** If you seed a non-empty `layouts` block
118
+ on `create_dashboard`, each `layouts[].pages[]` entry needs both `name` (API
119
+ id, e.g. `page_1`) and `label` (display name) set explicitly — `create_dashboard`
120
+ does **not** auto-derive `page_N`. An omitted `name` is stored empty (any
121
+ navigation widget targeting it by `page_N` won't resolve) and an omitted
122
+ `label` renders a blank page tab. Set both at create time rather than fixing
123
+ page one up later via `edit_dashboard`'s `rename_page`.
124
+
125
+ ---
126
+
127
+ ## 3. Editing Widgets — `edit_dashboard` (primary path)
128
+
129
+ **Use `edit_dashboard` for all widget/page editing on an existing dashboard** —
130
+ add, replace, move, or remove a widget of any kind (metric, visualization, text,
131
+ button, container, filter, parameter, navigation), and add/rename/delete a
132
+ page. It is the only supported tool for this purpose — never hand-build a
133
+ dashboard-JSON PATCH.
134
+
135
+ Full operation-type table, the widget-identity rule (`widget.name`/`widget.id`
136
+ vs. `placement.name`), grid placement, draft-vs-persist mechanics
137
+ (`save_dashboard`/`undo`/`discard_dashboard`), and full widget deletion via
138
+ `remove_widget_from_dashboard` are all in **`edit-dashboard.md`**
139
+ (task guide: `tasks/edit-dashboard.md`).
140
+
141
+ ---
142
+
143
+ ## 4. Gotchas Summary
144
+
145
+ `create_dashboard` gotchas below. For `edit_dashboard` (§3, primary path)
146
+ gotchas, see `edit-dashboard.md` §7.
147
+
148
+ | Gotcha | Rule |
149
+ |---|---|
150
+ | Omitting `minorVersion` on `create_dashboard` | Client-visible `ILLEGAL_QUERY_PARAMETER_VALUE` on an empty value — always send `minorVersion: -1` explicitly |
151
+ | `create_dashboard` response wrapping | Flat JSON, NOT double-wrapped (verified live) — don't apply the `defaultExc`/`responseCode` parse to it |
152
+ | `&` in text labels | HTML-encodes to `&amp;` in round-trip — use "and" or "/" |
153
+ | `create_dashboard` `name` mutated silently | Dup → suffix; non-ASCII stripped; ~78-80 char truncation; spaces → underscores — always read `name` back |
154
+ | `create_dashboard` with no `widgets`/`layouts` | Leaks a raw NPE — always pass `widgets: {}` and a `layouts` value (`[]` or seeded) at minimum |
155
+ | `add_page`/`place_widget_on_page` right after `create_dashboard` with `layouts: []` | Fails `INVALID_INPUT: "Dashboard has no layout to add the page to"` (verified live 2026-09-13) — seed one layout with an empty `page_1` in `create_dashboard` instead when you'll edit immediately (§1) |
156
+ | Missing `layouts[].style`/`columnCount` on `create_dashboard` bulk-create | Renders an unstyled dashboard (no runtime crash at create time) — always include a full `style` block and `columnCount` |
157
+ | Bulk-create pages with no `name`/`label` | `create_dashboard` does NOT auto-derive `page_N` — set both explicitly on every page at create time |
158
+ | `delete_workspace` on a workspace with `Created`-typed assets | Cascades — deletes every viz/SDM/dashboard whose workspace-asset row is `assetUsageType="Created"` (`Referenced` rows only lose their association, asset itself survives); list workspace assets first |
159
+ | `delete_visualization` / `delete_dashboard` without `list_asset_dependencies` | Preflight first (§7) — returns **counts not ids**; if `analyticsdashboard` > 0, widgets are not cleaned (`source` silently nulled) — `remove_widget_from_dashboard` first |
160
+ | `workspaceIdOrApiName` is the workspace label with spaces | 404s — pass the id (`1Dyxx...`) or apiName from `list_workspaces`, NOT the label |
161
+
162
+ ---
163
+
164
+ ## 5. Global Filters — `add_global_filter_to_dashboard`
165
+
166
+ A global filter is **dashboard-wide**, not baked into one chart — it drives every
167
+ viz/metric widget on the dashboard that shares the filtered field's semantic model
168
+ and object. This is a different mechanism from `create_visualization`'s own
169
+ per-chart `filters` array (`viz-authoring.md`) or an `edit_visualization`
170
+ `addFilter` op (`edit-visualization.md`), which each scope to one visualization
171
+ only. Ask which the user means when it's ambiguous ("filter this chart" vs. "filter
172
+ the dashboard").
173
+
174
+ **Self-contained.** This tool builds the full filter-widget JSON and places it
175
+ on the grid internally — no separate read/replace round trip needed. Required
176
+ inputs: `dashboardIdOrApiName`, `semanticModelIdOrName`, `objectName`,
177
+ `fieldName`, and a grid `placement` (48-col grid, same conventions as §2; a
178
+ common size is full-width `colspan: 48, rowspan: 4` or half-width
179
+ `colspan: 24, rowspan: 4`).
180
+
181
+ **Value-matching rule of thumb:**
182
+
183
+ - **Relative-date presets** (e.g. "default to current quarter") — pass `operator`
184
+ alone (`CurrentQuarter`, `CurrentMonth`, `PreviousMonth`, `LastNDays`, …); omit
185
+ `initialValues`.
186
+ - **Exact/contains/range matching** — pass `operator` (`Equals`, `In`, `Contains`,
187
+ `GreaterThan`, …) **and** `initialValues` as a JSON array, even for one value
188
+ (`["West"]`, not `"West"`). Use a multi-element array for `In`.
189
+
190
+ One line each: `dataType` (`Text`/`Number`/`Date`/`Boolean`/`measure`) is
191
+ case-sensitive and defaults to `Text`; `selectionType` (`single`/`multiple`) defaults
192
+ to `multiple`; `filterLabel` (optional display label) defaults to `fieldName`;
193
+ `widgetName` (optional unique name) auto-generates as `filter_N` when omitted.
194
+ A global filter is just a widget under the hood — `edit_dashboard`'s
195
+ `remove_widget_from_page` only unplaces it; to fully remove one, use
196
+ `remove_widget_from_dashboard` with its `widgetName` (`edit-dashboard.md` §6).
197
+
198
+ ---
199
+
200
+ ## 6. Deleting a Dashboard — `delete_dashboard`
201
+
202
+ `delete_dashboard` permanently removes a dashboard. **Destructive, no undo** —
203
+ always get explicit user confirmation first (`SKILL.md` lifecycle bullets).
204
+
205
+ **Inputs:** `dashboardIdOrApiName` only (the id or apiName from
206
+ `list_dashboards`/`browse_data_assets`). Scope is the current org only.
207
+
208
+ **Response shape — verified live (2026-08-30):** double-wrapped as
209
+ `{"defaultExc": "<stringified JSON>", "responseCode": <number>}`. On the
210
+ success path (`responseCode: 204`), `defaultExc` is an **empty string** (`""`)
211
+ — NOT the literal string `"204"`, and not JSON-parseable either way. Branch on
212
+ `responseCode === 204` for success; don't try to parse `defaultExc` on that
213
+ path.
214
+
215
+ **Not idempotent.** A second `delete_dashboard` call against an
216
+ already-deleted (or never-existent) id returns a 404:
217
+ `RESOURCE_NOT_FOUND: "We couldn't find dashboard."` Treat that specific error
218
+ as success-equivalent if your flow might double-call delete.
219
+
220
+ **No cascade to referenced assets.** Deleting a dashboard does not delete the
221
+ visualizations/metrics it displayed — only the dashboard and its widget
222
+ placements. Compare `delete_workspace`'s cascade behavior (§1), which is a
223
+ different, broader operation.
224
+
225
+ ---
226
+
227
+ ## 7. Preflight before deleting a viz or dashboard — `list_asset_dependencies`
228
+
229
+ Call this before `delete_visualization` or `delete_dashboard`. Distinct from
230
+ `list_semantic_model_dependencies` (in-model calc/metric graph; see
231
+ `sdm-tool-reference.md`).
232
+
233
+ - **`assetId` is the 18-char id only** — an apiName returns a misleading
234
+ workspace error.
235
+ - Returns **COUNTS by type**, not dependent ids/names — you cannot name which
236
+ dashboards will break. `totalCount`/`nextOffset` page the queried-asset list,
237
+ not the dependents.
238
+ - Show the counts, then **end the turn and wait for a new unambiguous yes**.
239
+ A prior "delete" in the same request is a plan, not confirmation.
240
+ - `delete_visualization` does NOT clean dashboard widgets. If
241
+ `dependentAssets.analyticsdashboard` is nonzero, N dashboards will keep a
242
+ broken widget (`source` silently nulled). Prefer
243
+ `remove_widget_from_dashboard` first (`edit-dashboard.md` §6) for a clean
244
+ remove.
245
+
246
+ ---
247
+
248
+ ## 8. Show a dashboard vs metadata-only — `render_dashboard` / `get_dashboard`
249
+
250
+ **If the user wants to see, open, view, display, render, bring up, pull up,
251
+ load, or look at a dashboard** (its charts, filters, or layout) — including
252
+ when they name or id a dashboard and want to see it — call `render_dashboard`.
253
+ That is the dashboard analog of `get_visualization` for
254
+ charts. **`get_dashboard` does not render.**
255
+
256
+ Do **not** call `get_dashboard` just because the skill already uses it for
257
+ `edit_dashboard` prep. Metadata-only is for inspect / `edit_dashboard` prep.
258
+ Current org only.
259
+
260
+ ### `render_dashboard` — inline render
261
+
262
+ Retrieves the dashboard's full metadata **bundled with its referenced
263
+ assets** (semantic models and visualizations) so visualization / filter /
264
+ parameter widgets can render immediately — no extra per-asset fetches.
265
+
266
+ **Required inputs** (live sidecar — always send all four flags):
267
+
268
+ - `dashboardIdOrApiName` — id or apiName from `list_dashboards` or
269
+ `browse_data_assets`
270
+ - `addVisualization=true` — so visualization tiles can render
271
+ - `addSdm=true` — so filter / parameter widgets that need SDM metadata can
272
+ render
273
+ - `showDraft=true` — include an in-progress `edit_dashboard` draft;
274
+ `get_dashboard` will not show that draft
275
+ - `minorVersion=-1` — latest **Salesforce API** minor version, **not** a
276
+ dashboard revision
277
+
278
+ **Optional:** `pageName` (page GUID), `customViewId`, `height` (viewport
279
+ pixels — when passed, only assets visible within that height are included).
280
+
281
+ **Returns:** `{dashboard, bundledAssets}`. `dashboard` is the full metadata
282
+ (layout, widgets, owning workspace, `url`). `bundledAssets` is a map of
283
+ asset id → referenced semantic model / visualization. Read them at
284
+ `response.dashboard` and `response.bundledAssets`.
285
+
286
+ ### `get_dashboard` — metadata only (cheaper, no bundled assets)
287
+
288
+ Use when you need `label` / `widgets` / `layout` / owning workspace
289
+ programmatically, to diff two dashboards, or to prepare an `edit_dashboard`
290
+ batch. Do **not** use it when the intent is to *see* the dashboard — for that,
291
+ call `render_dashboard` above.
292
+
293
+ **Inputs:** `dashboardIdOrApiName`. Optional `minorVersion` is the Salesforce
294
+ API version (e.g. `67.42`), **not** a dashboard revision — invalid values
295
+ produce `Invalid version`.
296
+
297
+ **Returns:** fields **directly** on the response (`label`, `widgets`,
298
+ `layout`, `folder`, `url`) — flat, like `create_dashboard`; unlike
299
+ `edit_dashboard` / `delete_dashboard`, which double-wrap (§6, `edit-dashboard.md`
300
+ §1). `url` is a canonical **external**
301
+ Analytics-app link — it does not render the dashboard inline. For inline
302
+ rendering, call `render_dashboard`.
@@ -0,0 +1,352 @@
1
+ # Dashboard Design Principles — Supported Practices Mapped to MCP Tools
2
+
3
+ Load this alongside `viz-authoring.md` (Step 10) and `dashboard-authoring.md`
4
+ (Step 11). This file covers the **design layer** — narrative, visual
5
+ hierarchy, naming, field selection, color, filters, and the data-presence
6
+ gate — translated from universal dashboard design best practices to what the
7
+ `tableau-next-pilot` MCP tools **actually** expose.
8
+
9
+ > **No named templates/patterns exist.** The MCP surface is a small set of
10
+ > generic primitives: `create_visualization` (`marks.panes.type` ∈ `Bar`,
11
+ > `Line`, `Circle`, `Square`, `Text`, `Donut`; `layout` ∈ `Vizql`, `Radial`,
12
+ > `Table`), a raw 48-column dashboard grid (`create_dashboard` +
13
+ > `edit_dashboard`'s `upsert_*_widget`/`place_widget_on_page` batch),
14
+ > `add_global_filter_to_dashboard`,
15
+ > and the `list_/get_semantic_model_*` discovery tools. Every principle below
16
+ > is achieved by combining these primitives by hand — never invent a template,
17
+ > script, or param name that isn't in `viz-authoring.md` §11/§12 or
18
+ > `dashboard-authoring.md`.
19
+
20
+ ## Contents
21
+
22
+ - 1. Think Like a Business User (before any `create_*` call)
23
+ - 2. Design the Narrative Before Picking Charts (REQUIRED)
24
+ - 3. Visual Storytelling Flow — KPIs → Trends → Breakdowns → Correlations
25
+ - 4. Visual Hierarchy — F-Pattern, Z-Pattern, Progressive Disclosure, Color, White Space
26
+ - 5. Business-Question → Chart-Type Mapping
27
+ - 6. Naming Conventions
28
+ - 7. Field Selection Priority
29
+ - 8. Color Encoding — Real Mechanism, Not `color_dim`/`stack_dim`
30
+ - 9. Filters — Dimensions Not Measures
31
+ - 10. Data-Presence Gate — Hard Stop Before Any Viz
32
+ - 11. Dashboard Quality Checklist (MCP Reality)
33
+ - Not supported by the tableau-next-pilot MCP (do not attempt)
34
+
35
+ ---
36
+
37
+ ## 1. Think Like a Business User (before any `create_*` call)
38
+
39
+ Before calling `create_visualization` or `create_dashboard`, answer three
40
+ questions in your own reasoning — no tool enforces this:
41
+
42
+ 1. **What question does this chart answer?** If you can't state it in one
43
+ sentence, don't build it.
44
+ 2. **What action will the viewer take from this?** A chart with no
45
+ actionable follow-up is decoration.
46
+ 3. **Can a non-technical user read every label?** No raw field apiNames, no
47
+ `_Clc`/`_mtc` suffixes, no bare object names.
48
+
49
+ This discipline feeds directly into `create_visualization`'s `label`/`title`
50
+ and the dashboard's `label` — pull field labels from `list_semantic_model_dimensions`
51
+ / `list_semantic_model_measures` (their `label`, not `fieldName`), and cross-check
52
+ against the SDM's own `description` via `get_semantic_model`. This is pure
53
+ pre-authoring judgement; nothing downstream validates it for you.
54
+
55
+ ---
56
+
57
+ ## 2. Design the Narrative Before Picking Charts (REQUIRED)
58
+
59
+ Before the first `create_visualization` call, write out — even briefly:
60
+
61
+ - The business questions the dashboard must answer.
62
+ - The logical flow between them (what should the eye see first, second, third).
63
+ - Friendly names for every field you intend to use.
64
+ - Which dimensions are actually meaningful (not IDs, not descriptions).
65
+ - What action the viewer takes after seeing each section.
66
+
67
+ This is a hard **gate on Step 9** in SKILL.md — do not call
68
+ `create_visualization` until `run_semantic_query` (or `run_query`) has
69
+ returned real, non-empty rows for every source you plan to chart (§9 below).
70
+ The narrative is realized entirely through **intentional sequencing** of
71
+ `create_visualization` calls and widget placement — there is no
72
+ narrative/flow object to configure.
73
+
74
+ ---
75
+
76
+ ## 3. Visual Storytelling Flow — KPIs → Trends → Breakdowns → Correlations
77
+
78
+ There is no flow abstraction in the MCP. Realize the sequence by widget type
79
+ and grid row, top to bottom:
80
+
81
+ | Story beat | MCP realization | Placement guidance |
82
+ |---|---|---|
83
+ | **KPIs** | `edit_dashboard`'s `upsert_metric_widget` + `place_widget_on_page` (`parameters.metricOption.{sdmApiName, sdmId}`; `source` optionally references the metric) | Row 0, spanning the top band |
84
+ | **Trends** | `create_visualization` with `marks.panes.type: "Line"` | Immediately below the KPI band |
85
+ | **Breakdowns** | `create_visualization` with `marks.panes.type: "Bar"` (plain, stacked, or Donut for <5 categories) | Below trends |
86
+ | **Correlations** | `create_visualization` with `marks.panes.type: "Circle"` (scatter) | Bottom of the grid |
87
+
88
+ Sequence widgets top-to-bottom by **ascending `placement.row`** on the
89
+ 48-column grid — that ascending-row order *is* the story order. There is no
90
+ server-side flow validation; if you place the scatter chart at `row: 0` and
91
+ the KPIs at `row: 40`, the story reads backwards with no warning.
92
+
93
+ ---
94
+
95
+ ## 4. Visual Hierarchy — F-Pattern, Z-Pattern, Progressive Disclosure, Color, White Space
96
+
97
+ No `f_layout` / `z_layout` pattern objects exist. Build the eye-path by hand
98
+ with `placement.{row,column,rowspan,colspan}` on the 48-column grid
99
+ (`rowHeight: 20`, `maxWidth: 1200`).
100
+
101
+ **F-pattern (metrics-left/top-heavy scan):**
102
+
103
+ ```jsonc
104
+ // KPI band across the top
105
+ { "row": 0, "column": 0, "rowspan": 8, "colspan": 12 } // metric 1
106
+ { "row": 0, "column": 12, "rowspan": 8, "colspan": 12 } // metric 2
107
+ { "row": 0, "column": 24, "rowspan": 8, "colspan": 12 } // metric 3
108
+ { "row": 0, "column": 36, "rowspan": 8, "colspan": 12 } // metric 4
109
+ // Primary trend, full width, second scan line
110
+ { "row": 8, "column": 0, "rowspan": 18, "colspan": 48 }
111
+ // Secondary breakdowns, left-weighted third scan line
112
+ { "row": 26, "column": 0, "rowspan": 16, "colspan": 24 }
113
+ { "row": 26, "column": 24, "rowspan": 16, "colspan": 24 }
114
+ ```
115
+
116
+ **Z-pattern (left→right, then diagonal down to left→right again):**
117
+
118
+ ```jsonc
119
+ { "row": 0, "column": 0, "rowspan": 8, "colspan": 24 } // top-left
120
+ { "row": 0, "column": 24, "rowspan": 8, "colspan": 24 } // top-right
121
+ { "row": 8, "column": 0, "rowspan": 18, "colspan": 24 } // bottom-left
122
+ { "row": 8, "column": 24, "rowspan": 18, "colspan": 24 } // bottom-right
123
+ ```
124
+
125
+ **Progressive disclosure:** put the single most important KPI/chart at
126
+ `row: 0, column: 0` with the largest `colspan`/`rowspan`; secondary detail
127
+ (tables, drill-down breakdowns) go further down the grid. There is no
128
+ drill-down/expand mechanism — "progressive disclosure" here just means
129
+ "important things occupy the first screen's worth of rows."
130
+
131
+ **Color consistency:** the same category must map to the same color across
132
+ every chart on the dashboard. The only lever is the `Color` encoding
133
+ fieldKey in `marks.panes.encodings` — pick the *same underlying dimension*
134
+ (same `objectName`+`fieldName`) for the Color encoding on every chart that
135
+ breaks down by that category. There is no shared palette/legend registry;
136
+ consistency is achieved by field-binding discipline, not a setting.
137
+
138
+ **White space:** don't tile every grid cell. Leave gaps between widget
139
+ groups (skip a row or two between `placement.row` bands) and set
140
+ `layouts[].style.cellSpacingX` / `cellSpacingY` (verified default: `4`) on
141
+ the dashboard's `style` block — see `dashboard-authoring.md`. Overcrowding
142
+ the 48-column grid (i.e. every row fully packed edge-to-edge) is a real
143
+ readability failure with no tool-level guard; watch it yourself.
144
+
145
+ ---
146
+
147
+ ## 5. Business-Question → Chart-Type Mapping
148
+
149
+ Every row maps to an achievable `marks.panes.type` / `layout` combination —
150
+ no named templates. See `viz-authoring.md` §2–3 for the full decision matrix
151
+ and verified payloads; summarized here with the MCP-native mechanism:
152
+
153
+ | Business question | Data pattern | `marks.panes.type` / `layout` | Mechanism |
154
+ |---|---|---|---|
155
+ | Trending over time | Date + measure | `Line` / `Vizql` | Date on columns with `DateTruncMonth` (Continuous); measure on rows |
156
+ | Trend by category | Date + measure + dim | `Line` / `Vizql` | + `Color` encoding on the breakdown dimension |
157
+ | Best/worst performers | Dim + measure | `Bar` / `Vizql` | Server auto-sorts descending; override with `sortIntent` |
158
+ | Composition (<5 categories) | Dim (<5 vals) + measure | `Donut` / `Radial` | Measure on **`Angle`** encoding (not `rows`); dim on `Color` |
159
+ | Composition (≥5 categories) | Dim (≥5 vals) + measure | `Bar` / `Vizql` | Donut is unreadable beyond ~4 slices |
160
+ | Breakdown by two dimensions | 2 dims + measure | `Bar` / `Vizql` | + `Color` on 2nd dim + `stack: {isAutomatic:false, isStacked:true}` |
161
+ | Compare multiple measures | Dim + 2+ measures | `Bar` / `Vizql` | `MeasureNames`/`MeasureValues` synthetic fields, or both measures in `columns` |
162
+ | Performance hotspots | 2 dims + measure | `Square` / `Vizql` | Two dims on rows/columns; measure on `Color` encoding |
163
+ | Correlation | 2 measures | `Circle` / `Vizql` | X-measure on columns, Y-measure on rows; point dim on `Color`/`Detail` |
164
+ | Detailed rankings | Multiple dims + measures | `Text` / `Table` | Dims in `groups` (outer→inner), measures in `rows` |
165
+ | YoY comparison | Date + measure + year | `Line` / `Vizql` | `DatePartMonth` on axis, `DatePartYear` on `Color` — period overlay, not a continuous timeline |
166
+
167
+ **Map (point/geographic) and Flow (Sankey) are not on this table because
168
+ they are unsupported** — no matching mark type exists. See the closing
169
+ section for fallbacks.
170
+
171
+ ---
172
+
173
+ ## 6. Naming Conventions
174
+
175
+ **The mechanical rules — suffix stripping (`_Clc`/`_mtc`/`_MTC`/`_CLC`),
176
+ ≥3-word business-friendly titles, never exposing a raw suffixed apiName like
177
+ `Region4`/`Amount7` — live in `viz-authoring.md` §4. Follow them.** The
178
+ design-layer points on top of those mechanics:
179
+
180
+ - **Pull display labels from the SDM's own `label`** (via
181
+ `list_semantic_model_dimensions` / `list_semantic_model_measures`, or
182
+ `get_semantic_model`) — there is no `sdm_fields[field]['label']` accessor —
183
+ and cross-check the intent against the SDM `description`. The label should
184
+ answer the business question, not just rename the field.
185
+ - **Read the transformed name back.** `create_dashboard` and
186
+ `create_visualization` auto-transform the `name`/apiName you send
187
+ (spaces → `_`, truncation, unicode stripped) — after creation, read back
188
+ the actual stored `name` rather than assuming your input string survived
189
+ unchanged.
190
+
191
+ ---
192
+
193
+ ## 7. Field Selection Priority
194
+
195
+ **The priority ladder (CLC/calculated → meaningful categorical → geographic/org
196
+ → name fields → avoid generic text → never ID fields), the cardinality
197
+ thresholds (≤4 Donut / 5–50 Bar / 50+ Top-N), and the CLC + `UserAgg`
198
+ no-double-aggregation rule all live in `viz-authoring.md` §5 and §7.** The
199
+ design-layer framing on top of those mechanics:
200
+
201
+ - Field selection is a **judgement call, not an enforced check** — nothing
202
+ downstream stops you from charting an ID field or a raw description, and
203
+ there is no `recommend_diverse_chart_types()` scoring function. Apply the
204
+ ladder yourself when choosing among fields returned by
205
+ `list_semantic_model_*`.
206
+ - **Prefer CLC business-ready fields** precisely because they encode the
207
+ business definition — discover them via
208
+ `list_semantic_model_calculated_measures` /
209
+ `list_semantic_model_calculated_dimensions` before falling back to raw
210
+ fields.
211
+ - Binding note: a calculated measure takes `fieldName` only (**omit
212
+ `objectName`** — including it on a calculated measure throws
213
+ `UNKNOWN_EXCEPTION`) with `function: "UserAgg"`.
214
+
215
+ ---
216
+
217
+ ## 8. Color Encoding — Real Mechanism, Not `color_dim`/`stack_dim`
218
+
219
+ *(Encoding mechanics and the verified stacked-bar / `colorScale` payloads are
220
+ in `viz-authoring.md` §6 and §11 — this section covers the design intent and
221
+ the named-parameter myths.)*
222
+
223
+ **`color_dim` and `stack_dim` do not exist as parameters anywhere in the MCP
224
+ surface.** There is no automatic coloring for "templates when 2+ dimensions
225
+ are present" — nothing is automatic. Color is always an explicit encoding
226
+ entry:
227
+
228
+ ```jsonc
229
+ "marks": {
230
+ "panes": {
231
+ "type": "Bar",
232
+ "encodings": [{ "type": "Color", "fieldKey": "<dim_field_key>" }]
233
+ }
234
+ }
235
+ ```
236
+
237
+ - **Stacked bar** is NOT a `stack_dim` param — it is `type: "Bar"` +
238
+ a `Color` encoding on the second dimension + an explicit
239
+ `stack: { "isAutomatic": false, "isStacked": true }` block in `marks.panes`.
240
+ - **Any chart with a second dimension available** — add it as a `Color`
241
+ encoding by hand. When 2+ dimensions exist and you skip this, the chart
242
+ silently stays single-series; nothing warns you.
243
+ - **Diverging color scale** for signed / around-a-target measures (profit
244
+ variance, YoY change): set `"colorScale": "Diverging"` on the
245
+ Color-encoded measure's field binding. This is a real, verified param —
246
+ use it only when the measure is genuinely centered on zero or a target;
247
+ on all-positive data it misleads.
248
+
249
+ Cross-chart color coordination (same category → same color everywhere) is a
250
+ manual field-binding discipline (§4 above), not a dashboard-level setting.
251
+
252
+ ---
253
+
254
+ ## 9. Filters — Dimensions Not Measures
255
+
256
+ **Filter on dimensions, never on measures**, use business-friendly labels,
257
+ avoid ID fields, and prefer filter fields that also appear as encodings in
258
+ the vizzes on the same dashboard (so the filter visibly affects what the
259
+ user is looking at).
260
+
261
+ Two mechanisms, both real:
262
+
263
+ 1. **Per-viz filters** — `create_visualization`'s `filters[]` array:
264
+ `{ "fieldKey": ..., "type": "Dimension", "include": [...] }` (or
265
+ `exclude`) for categorical filters; `ValueRange`/`DateRange` for
266
+ continuous fields. A filter on a raw field is a WHERE (row filter); a
267
+ filter on an aggregated measure becomes a HAVING (group filter) — the
268
+ bucket is derived from the field, not chosen by you.
269
+ 2. **Global dashboard filters** — `add_global_filter_to_dashboard`, which
270
+ constructs the full filter-widget JSON and places it in one call
271
+ (self-contained — no separate read/replace round trip). Required
272
+ inputs: `dashboardIdOrApiName`, `semanticModelIdOrName`, `objectName`,
273
+ `fieldName`, `placement`. Set `filterLabel` explicitly for a
274
+ business-friendly display name (defaults to the raw `fieldName` if
275
+ omitted — exactly the kind of technical-name leak §6 warns against).
276
+
277
+ **Always look up `objectName` — never guess it.** There is no
278
+ `sdm_fields[field]['objectName']` accessor. `objectName` is the field's
279
+ owning SDM data-object apiName, discovered via
280
+ `list_semantic_model_data_objects`; it is a **required** param on
281
+ `add_global_filter_to_dashboard`. Calculated fields have no owning object —
282
+ omit `objectName` for them in `create_visualization` (same rule as §7).
283
+
284
+ ---
285
+
286
+ ## 10. Data-Presence Gate — Hard Stop Before Any Viz
287
+
288
+ **Confirm rows exist before building anything.** This is Rule 1 of the
289
+ parent skill (SKILL.md) and the primary defense against empty dashboards.
290
+
291
+ - `run_query` — `SELECT COUNT(*) AS n FROM <table>__dll` (or `__dlm`/`__dlc`)
292
+ for a raw-table presence check.
293
+ - `run_semantic_query` — for a model that traverses relationships, confirm
294
+ the query returns non-empty rows before charting from it (this doubles as
295
+ the Step 7 model-validation check in SKILL.md).
296
+
297
+ **If the result is zero rows or a "table does not exist" error, STOP.** Do
298
+ not call `create_visualization`. Report the empty/unmaterialized source to
299
+ the user and offer an alternative source or metric — see
300
+ `empty-source-handling.md` for exact wording.
301
+
302
+ **`query_data.py --count` and `lib.query.assert_has_rows` do not exist** —
303
+ those are constructs from an external scripting workflow, which has no
304
+ equivalent MCP tool. The gate is real; those helpers are not. Use
305
+ `run_query`/`run_semantic_query` directly.
306
+
307
+ ---
308
+
309
+ ## 11. Dashboard Quality Checklist (MCP Reality)
310
+
311
+ **Business clarity**
312
+ - [ ] Every chart title states the business question it answers (≥3 words, no jargon)
313
+ - [ ] Field labels come from SDM `label` (via `list_semantic_model_*`), not raw apiNames
314
+ - [ ] No `_Clc`/`_mtc`/`_MTC`/`_CLC` suffixes or raw apiNames visible to the end user
315
+ - [ ] KPIs → Trends → Breakdowns → Correlations story order reflected in ascending `placement.row`
316
+
317
+ **Visual design**
318
+ - [ ] Metrics placed top/left (row 0) per the F-pattern or Z-pattern grid coordinates in §4
319
+ - [ ] Same dimension used for `Color` encoding across every chart that breaks down by that category
320
+ - [ ] Grid not overcrowded — deliberate empty rows/`cellSpacingX/Y` between widget groups
321
+ - [ ] Donut used only for dimension cardinality < 5; Bar used at ≥ 5
322
+
323
+ **UX**
324
+ - [ ] Every `add_global_filter_to_dashboard` call has an explicit business-friendly `filterLabel`
325
+ - [ ] Filters target dimensions, never measures; avoid ID fields
326
+ - [ ] Filter fields also appear as an encoding in at least one on-dashboard viz where practical
327
+
328
+ **Technical**
329
+ - [ ] `run_query`/`run_semantic_query` confirmed non-empty rows before any `create_visualization` call (§10)
330
+ - [ ] CLC/calculated measures use `function: "UserAgg"`, never re-aggregated with `Sum`/`Avg`
331
+ - [ ] `objectName` omitted for calculated measures/dimensions; looked up (never guessed) for raw fields and global filters
332
+ - [ ] Bar charts left to auto-sort descending, or `sortIntent` set deliberately
333
+ - [ ] YoY comparisons use `DatePartMonth` + `DatePartYear` period overlay, not a continuous timeline
334
+ - [ ] Viz created before the dashboard widget references it by `source.{id,name}` (creation order matters)
335
+ - [ ] Text/label widgets added via `edit_dashboard`'s `upsert_text_widget` + `place_widget_on_page` — the only supported path
336
+ - [ ] A `401`/auth-failure on any `create_*`/`update_*`/`run_*` call is treated as an expired org auth token or MCP connection — refresh org auth and reconnect the MCP server, then retry the same call; do not misdiagnose it as an empty-source or malformed-payload error
337
+
338
+ ---
339
+
340
+ ## Not supported by the tableau-next-pilot MCP (do not attempt)
341
+
342
+ | Best-practices item | Why unsupported | Nearest achievable alternative |
343
+ |---|---|---|
344
+ | Map / geomap (point map, lat+lon+measure) | No map/geo mark type; lat/lon not routed | `Bar` chart by Region/Territory dimension |
345
+ | Flow / Sankey (`flow_sankey`, `flow_package_*`, `flow_simple_*`) | No flow/sankey mark type | Two `Bar` charts showing source→destination volumes separately |
346
+ | Conversion funnel | No funnel mark type | Sorted `Bar` by stage order; communicate each stage's count in the title/description |
347
+ | Dot matrix (2 dims + 2 measures) | No dot-matrix mark | `Circle` scatter (2 measures) or ranked `Bar` |
348
+ | Nightingale rose (measure-driven radius) | Radial layout supports `Donut` marks only — no measure-driven radius | Horizontal `Bar` |
349
+ | Radial spoke bars | Not available in current tools | `Bar` |
350
+ | Radial heatmap (2 ring dims + slice dim + measure) | Not available | `Square` heatmap (2D grid) |
351
+ | Map/Flow color variants (`geomap_*` encodings, `flow_uniform_fill`, node-size color rules) | No map or flow/sankey mark exists to carry them | N/A — see map/flow fallbacks above |
352
+ | Named layout patterns (`f_layout`, `z_layout`, `performance_overview`) with fixed filter/metric/viz counts | The patterns and their auto-select logic do not exist — only the raw 48-column grid | Hand-place widgets using the F-pattern/Z-pattern grid coordinates in §4; filter/metric/viz counts are an authoring choice, not a pattern constraint |