@salesforce/afv-skills 1.58.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 (242) 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-bot-upgrade/SKILL.md +3 -1
  17. package/skills/agentforce-d360-analyze/SKILL.md +28 -10
  18. package/skills/agentforce-d360-analyze/scripts/dc.py +200 -50
  19. package/skills/agentforce-d360-analyze/scripts/discover_sessions.py +10 -6
  20. package/skills/agentforce-d360-analyze/scripts/fetch_dc.py +22 -6
  21. package/skills/agentforce-d360-analyze/scripts/resolve_session.py +21 -13
  22. package/skills/agentforce-d360-analyze/scripts/tests/test_dc_and_resolve_session.py +298 -76
  23. package/skills/agentforce-d360-analyze/scripts/tests/test_default_target_org.py +432 -0
  24. package/skills/agentforce-d360-analyze/scripts/tests/test_discover_sessions.py +1 -1
  25. package/skills/agentforce-d360-analyze/scripts/tests/test_resolve_session_main.py +9 -3
  26. package/skills/agentforce-d360-analyze/scripts/tests/test_skill_md_prefix_block.py +176 -0
  27. package/skills/agentforce-generate/SKILL.md +21 -1
  28. package/skills/agentforce-generate/references/known-issues.md +51 -1
  29. package/skills/agentforce-generate/references/legacy-nga-regression-eval.md +547 -0
  30. package/skills/agentforce-generate/references/upgrade-legacy-agent-to-agentscript.md +577 -0
  31. package/skills/agentforce-generate/scripts/upgrade-legacy-nga-analyze.py +404 -0
  32. package/skills/agentforce-generate/scripts/upgrade-legacy-nga-run-trials.sh +58 -0
  33. package/skills/agentforce-persona-generate/SKILL.md +505 -0
  34. package/skills/agentforce-persona-generate/assets/checklist-template.md +100 -0
  35. package/skills/agentforce-persona-generate/assets/persona-encoding-template.md +252 -0
  36. package/skills/agentforce-persona-generate/assets/persona-template.md +229 -0
  37. package/skills/agentforce-persona-generate/assets/sample-dialog-template.md +50 -0
  38. package/skills/agentforce-persona-generate/assets/workshop-outline-template.md +97 -0
  39. package/skills/agentforce-persona-generate/examples/concierge-persona.md +189 -0
  40. package/skills/agentforce-persona-generate/examples/concierge-sample-dialog.md +70 -0
  41. package/skills/agentforce-persona-generate/examples/drover-persona.md +194 -0
  42. package/skills/agentforce-persona-generate/examples/drover-sample-dialog.md +62 -0
  43. package/skills/agentforce-persona-generate/examples/steady-hand-persona.md +188 -0
  44. package/skills/agentforce-persona-generate/examples/steady-hand-sample-dialog.md +68 -0
  45. package/skills/agentforce-persona-generate/examples/yt-persona.md +195 -0
  46. package/skills/agentforce-persona-generate/examples/yt-sample-dialog.md +66 -0
  47. package/skills/agentforce-persona-generate/references/attributions.md +36 -0
  48. package/skills/agentforce-persona-generate/references/interaction-design-notes.md +30 -0
  49. package/skills/agentforce-persona-generate/references/persona-encoding-guide.md +566 -0
  50. package/skills/agentforce-persona-generate/references/persona-framework.md +796 -0
  51. package/skills/agentforce-persona-generate/references/refinement-mappings.md +31 -0
  52. package/skills/agentforce-persona-generate/references/scoring-rubric.md +58 -0
  53. package/skills/agentforce-persona-generate/references/voice-catalog.json +2327 -0
  54. package/skills/agentforce-persona-generate/references/workshop-question-policy.md +90 -0
  55. package/skills/agentforce-persona-generate/references/workshop-scenario-seeds.md +119 -0
  56. package/skills/agentforce-persona-generate/scripts/parse_agent_yaml.py +223 -0
  57. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +17 -27
  58. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +7 -17
  59. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +47 -63
  60. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +10 -13
  61. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -46
  62. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +4 -21
  63. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +58 -82
  64. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +16 -315
  65. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +9 -24
  66. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -44
  67. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +11 -63
  68. package/skills/consumer-goods-rtr-datacloud-export-configure/SKILL.md +2 -1
  69. package/skills/consumer-goods-tpe-dashboard-configure/SKILL.md +2 -1
  70. package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/SKILL.md +2 -1
  71. package/skills/consumer-goods-tpe-datakit-deploy/SKILL.md +2 -1
  72. package/skills/data360-schema-get/SKILL.md +3 -3
  73. package/skills/design-systems-slds-validate/references/report-format.md +1 -1
  74. package/skills/dx-app-analytics-query/SKILL.md +8 -8
  75. package/skills/dx-org-manage/SKILL.md +1 -0
  76. package/skills/dx-org-shape-manage/SKILL.md +1 -0
  77. package/skills/dx-org-snapshot-manage/SKILL.md +172 -0
  78. package/skills/dx-org-snapshot-manage/examples/create_error_output.json +16 -0
  79. package/skills/dx-org-snapshot-manage/examples/create_success_output.json +19 -0
  80. package/skills/dx-org-snapshot-manage/examples/delete_output.json +9 -0
  81. package/skills/dx-org-snapshot-manage/examples/get_output.json +19 -0
  82. package/skills/dx-org-snapshot-manage/examples/list_output.json +36 -0
  83. package/skills/dx-org-snapshot-manage/references/cli_flags.md +179 -0
  84. package/skills/dx-org-snapshot-manage/references/snapshot_usage.md +55 -0
  85. package/skills/dx-org-trial-expiration-check/SKILL.md +1 -1
  86. package/skills/dx-org-trial-expiration-check/scripts/check_expiration.sh +2 -2
  87. package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +7 -5
  88. package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +11 -11
  89. package/skills/experience-content-media-image-generate/SKILL.md +83 -0
  90. package/skills/experience-content-media-image-generate/scripts/download-image.sh +84 -0
  91. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +12 -12
  92. package/skills/experience-ui-bundle-app-coordinate/references/phase-completion-validation.md +3 -3
  93. package/skills/experience-ui-bundle-app-coordinate/references/phase-execution-pattern.md +9 -7
  94. package/skills/experience-ui-bundle-app-coordinate/scripts/check-dangling-refs.mjs +132 -0
  95. package/skills/experience-ui-bundle-app-coordinate/scripts/check-hosting-target.mjs +62 -0
  96. package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-1-complete.mjs +71 -0
  97. package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-4-complete.mjs +95 -0
  98. package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-6-ready.mjs +78 -0
  99. package/skills/experience-ui-bundle-app-coordinate/scripts/check-prerequisites.mjs +21 -0
  100. package/skills/experience-ui-bundle-app-coordinate/scripts/check-sfdx-project.mjs +28 -0
  101. package/skills/experience-ui-bundle-custom-app-generate/SKILL.md +3 -1
  102. package/skills/experience-ui-bundle-custom-app-generate/scripts/resolve-uibundle-path.mjs +40 -0
  103. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +7 -5
  104. package/skills/experience-ui-bundle-frontend-generate/scripts/check-preconditions.mjs +70 -0
  105. package/skills/experience-ui-bundle-frontend-generate/scripts/resolve-ui-bundle.mjs +76 -0
  106. package/skills/experience-ui-bundle-frontend-generate/scripts/verify-rules.mjs +115 -0
  107. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +7 -3
  108. package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +1 -1
  109. package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +1 -1
  110. package/skills/experience-ui-bundle-metadata-generate/scripts/check-api-version.mjs +43 -0
  111. package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.mjs +131 -0
  112. package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.mjs +152 -0
  113. package/skills/experience-ui-bundle-mfa-configure/references/branding.md +10 -6
  114. package/skills/external-diagram-mermaid-generate/SKILL.md +12 -12
  115. package/skills/field-service-data-capture-migrate/references/component-mapping.md +1 -1
  116. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +1 -2
  117. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +1 -0
  118. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +1 -1
  119. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +12 -1
  120. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +4 -1
  121. package/skills/field-service-setup-orchestrator-get/SKILL.md +0 -1
  122. package/skills/insurance-brokerage-agency-billing-configure/SKILL.md +233 -0
  123. package/skills/insurance-brokerage-agency-billing-configure/references/accounting-setup.md +117 -0
  124. package/skills/insurance-brokerage-agency-billing-configure/references/billing-rules-config.md +237 -0
  125. package/skills/insurance-brokerage-agency-billing-configure/references/billing-settings.md +271 -0
  126. package/skills/insurance-brokerage-agency-billing-configure/references/demo-policy-config.md +138 -0
  127. package/skills/insurance-brokerage-agency-billing-configure/references/org-defaults.md +64 -0
  128. package/skills/insurance-brokerage-agency-billing-configure/references/permissions-config.md +226 -0
  129. package/skills/insurance-brokerage-agency-billing-configure/references/prerequisites.md +142 -0
  130. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +3 -2
  131. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +3 -3
  132. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +68 -45
  133. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +17 -18
  134. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +16 -3
  135. package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +16 -13
  136. package/skills/marketing-message-test/SKILL.md +194 -0
  137. package/skills/marketing-message-test/references/api-contract.md +333 -0
  138. package/skills/omnistudio-callable-apex-generate/SKILL.md +3 -3
  139. package/skills/omnistudio-datamapper-generate/SKILL.md +6 -6
  140. package/skills/omnistudio-datapacks-deploy/SKILL.md +11 -11
  141. package/skills/omnistudio-dependencies-analyze/SKILL.md +5 -5
  142. package/skills/omnistudio-epc-catalog-generate/SKILL.md +6 -6
  143. package/skills/omnistudio-flexcard-generate/SKILL.md +6 -6
  144. package/skills/omnistudio-integration-procedure-generate/SKILL.md +6 -6
  145. package/skills/omnistudio-omniscript-generate/SKILL.md +6 -5
  146. package/skills/platform-apex-logs-debug/SKILL.md +1 -0
  147. package/skills/platform-metadata-deploy/SKILL.md +1 -0
  148. package/skills/platform-models-api-configure/SKILL.md +5 -5
  149. package/skills/platform-soql-query/SKILL.md +1 -0
  150. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +17 -6
  151. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +107 -192
  152. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone-action.json +6 -6
  153. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +49 -20
  154. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +4 -4
  155. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +224 -290
  156. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/workflow.md +295 -0
  157. package/skills/service-itsm-incident-priority-configure/SKILL.md +1 -1
  158. package/skills/service-itsm-slack-itservice-configure/SKILL.md +2 -1
  159. package/skills/service-itsm-slack-itservice-configure/references/manage-slack-connection.md +2 -2
  160. package/skills/service-omni-channel-setup-coordinate/SKILL.md +20 -13
  161. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +5 -55
  162. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +0 -38
  163. package/skills/service-omni-command-center-analyze/SKILL.md +2 -3
  164. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  165. package/skills/tableau-next-asset-create/README.md +65 -0
  166. package/skills/tableau-next-asset-create/SKILL.md +409 -0
  167. package/skills/tableau-next-asset-create/references/admin-users.md +149 -0
  168. package/skills/tableau-next-asset-create/references/ai-readiness.md +355 -0
  169. package/skills/tableau-next-asset-create/references/alerts.md +111 -0
  170. package/skills/tableau-next-asset-create/references/build-workflow.md +819 -0
  171. package/skills/tableau-next-asset-create/references/dashboard-authoring.md +302 -0
  172. package/skills/tableau-next-asset-create/references/dashboard-design-principles.md +352 -0
  173. package/skills/tableau-next-asset-create/references/data-transform-gotchas.md +210 -0
  174. package/skills/tableau-next-asset-create/references/data-understanding.md +387 -0
  175. package/skills/tableau-next-asset-create/references/deep-reference-index.md +74 -0
  176. package/skills/tableau-next-asset-create/references/destructive-operations.md +62 -0
  177. package/skills/tableau-next-asset-create/references/edit-dashboard.md +273 -0
  178. package/skills/tableau-next-asset-create/references/edit-visualization.md +529 -0
  179. package/skills/tableau-next-asset-create/references/empty-source-handling.md +104 -0
  180. package/skills/tableau-next-asset-create/references/ingest-and-metric-gotchas.md +638 -0
  181. package/skills/tableau-next-asset-create/references/insight-bundle.md +69 -0
  182. package/skills/tableau-next-asset-create/references/large-flat-file-handling.md +331 -0
  183. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_envelope.json +54 -0
  184. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_shared-widget-fields.json +122 -0
  185. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/add_page.json +29 -0
  186. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/delete_page.json +27 -0
  187. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/discard_dashboard.json +11 -0
  188. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget.json +30 -0
  189. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget_to_page.json +30 -0
  190. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/place_widget_on_page.json +30 -0
  191. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_dashboard.json +16 -0
  192. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_page.json +26 -0
  193. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/rename_page.json +28 -0
  194. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/save_dashboard.json +18 -0
  195. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/undo.json +11 -0
  196. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_button_widget.json +69 -0
  197. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_container_widget.json +88 -0
  198. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_filter_widget.json +146 -0
  199. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_metric_widget.json +78 -0
  200. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_navigation_widget.json +80 -0
  201. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_parameter_widget.json +160 -0
  202. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_text_widget.json +103 -0
  203. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_visualization_widget.json +68 -0
  204. package/skills/tableau-next-asset-create/references/sdm-tool-reference.md +721 -0
  205. package/skills/tableau-next-asset-create/references/semantic-query-and-enrichment.md +355 -0
  206. package/skills/tableau-next-asset-create/references/shared-gates.md +322 -0
  207. package/skills/tableau-next-asset-create/references/sharing-and-promotion.md +306 -0
  208. package/skills/tableau-next-asset-create/references/tasks/ai-readiness-audit.md +51 -0
  209. package/skills/tableau-next-asset-create/references/tasks/analyze-data.md +48 -0
  210. package/skills/tableau-next-asset-create/references/tasks/build-dashboard.md +66 -0
  211. package/skills/tableau-next-asset-create/references/tasks/build-end-to-end.md +73 -0
  212. package/skills/tableau-next-asset-create/references/tasks/create-data-transform.md +51 -0
  213. package/skills/tableau-next-asset-create/references/tasks/create-viz.md +58 -0
  214. package/skills/tableau-next-asset-create/references/tasks/edit-dashboard.md +80 -0
  215. package/skills/tableau-next-asset-create/references/tasks/edit-sdm.md +106 -0
  216. package/skills/tableau-next-asset-create/references/tasks/edit-viz.md +40 -0
  217. package/skills/tableau-next-asset-create/references/tasks/enrich-model.md +68 -0
  218. package/skills/tableau-next-asset-create/references/tasks/ingest-flat-file.md +68 -0
  219. package/skills/tableau-next-asset-create/references/tasks/manage-alert.md +35 -0
  220. package/skills/tableau-next-asset-create/references/tasks/profile-dataset.md +47 -0
  221. package/skills/tableau-next-asset-create/references/tasks/promote-or-reuse.md +46 -0
  222. package/skills/tableau-next-asset-create/references/tasks/provision-user.md +39 -0
  223. package/skills/tableau-next-asset-create/references/tasks/query-model.md +92 -0
  224. package/skills/tableau-next-asset-create/references/tasks/review-verified-questions.md +42 -0
  225. package/skills/tableau-next-asset-create/references/tasks/share-asset.md +37 -0
  226. package/skills/tableau-next-asset-create/references/verified-questions.md +125 -0
  227. package/skills/tableau-next-asset-create/references/viz-authoring.md +992 -0
  228. package/skills/experience-ui-bundle-app-coordinate/scripts/check-dangling-refs.sh +0 -42
  229. package/skills/experience-ui-bundle-app-coordinate/scripts/check-hosting-target.sh +0 -26
  230. package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-1-complete.sh +0 -35
  231. package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-4-complete.sh +0 -47
  232. package/skills/experience-ui-bundle-app-coordinate/scripts/check-phase-6-ready.sh +0 -33
  233. package/skills/experience-ui-bundle-app-coordinate/scripts/check-prerequisites.sh +0 -20
  234. package/skills/experience-ui-bundle-app-coordinate/scripts/check-sfdx-project.sh +0 -15
  235. package/skills/experience-ui-bundle-custom-app-generate/scripts/resolve-uibundle-path.sh +0 -30
  236. package/skills/experience-ui-bundle-frontend-generate/scripts/check-preconditions.sh +0 -37
  237. package/skills/experience-ui-bundle-frontend-generate/scripts/resolve-ui-bundle.sh +0 -66
  238. package/skills/experience-ui-bundle-frontend-generate/scripts/verify-rules.sh +0 -58
  239. package/skills/experience-ui-bundle-metadata-generate/scripts/check-api-version.sh +0 -30
  240. package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +0 -73
  241. package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +0 -118
  242. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +0 -102
@@ -0,0 +1,819 @@
1
+ # SDM build workflow — Steps 0–11
2
+
3
+ The procedural spine for building a Tableau Next semantic model end to end. Load
4
+ this whenever the work requires creating or extending a model. SKILL.md holds the
5
+ priority rules and the one-line step map that routes here; this file holds the
6
+ full per-step detail and verified payloads. The deeper single-topic guides
7
+ (`data-understanding.md`, `sdm-tool-reference.md`, `semantic-query-and-enrichment.md`,
8
+ `viz-authoring.md`, `dashboard-authoring.md`, `ai-readiness.md`,
9
+ `large-flat-file-handling.md`, `empty-source-handling.md`) are referenced inline.
10
+
11
+ ## Contents
12
+
13
+ - [Workflow](#workflow)
14
+ - Step 0 — Ingest (CSV/Excel upload or existing database connection)
15
+ - Step 0.5 — Profile source schema before adding fields
16
+ - Step 1 — Discover candidate sources
17
+ - Step 2 — Verify data presence (HARD GATE — before building)
18
+ - Step 3 — Confirm join keys actually match
19
+ - Step 4 — Create the model and bind objects (one nested call)
20
+ - Step 5 — Confirm apiNames match expectations
21
+ - Step 6 — Create a relationship for every join
22
+ - Step 7 — Validate (BLOCKING — both checks must pass)
23
+ - Step 8 — Enrich: calculated fields and metrics
24
+ - Step 9 — Query the model (answer the question; also the Step 7 proof)
25
+ - Step 10 — Create visualizations (only when the user wants charts/dashboard)
26
+ - Step 11 — Build the dashboard
27
+
28
+ Copy this checklist into your working notes and check off each step as you complete it:
29
+
30
+ ```text
31
+ - [ ] Step 0 — Ingest (CSV/Excel upload or existing database connection)
32
+ - [ ] Step 0.5 — Profile source schema before adding fields
33
+ - [ ] Step 1 — Discover candidate sources
34
+ - [ ] Step 2 — Verify data presence (HARD GATE — before building)
35
+ - [ ] Step 3 — Confirm join keys actually match
36
+ - [ ] Step 4 — Create the model and bind objects (one nested call)
37
+ - [ ] Step 5 — Confirm apiNames match expectations
38
+ - [ ] Step 6 — Create a relationship for every join
39
+ - [ ] Step 7 — Validate (BLOCKING — both checks must pass)
40
+ - [ ] Step 8 — Enrich: calculated fields and metrics
41
+ - [ ] Step 9 — Query the model (answer the question; also the Step 7 proof)
42
+ - [ ] Step 10 — Create visualizations (only when the user wants charts/dashboard)
43
+ - [ ] Step 11 — Build the dashboard
44
+ ```
45
+
46
+ ## Workflow
47
+
48
+ ### Step 0 — Ingest (CSV/Excel upload or existing database connection)
49
+
50
+ When the user points at local files **or** a table on an existing Data Cloud
51
+ connection instead of already-modeled data assets, ingest them into DLOs and
52
+ DMOs first. The chain is fixed — **wait for status at each create**, do not
53
+ use row-count as a stand-in for ready. Payloads: `ingest-and-metric-gotchas.md`
54
+ §A (file) / §C (connection).
55
+
56
+ **File CSV / Excel — §A:**
57
+ `get_upload_connection` → `generate_presigned_credential` (per file) →
58
+ `infer_object_schema` (per file) → `create_data_stream` (per file, creates
59
+ the `*__dll` DLO) → **`run_data_stream` (per stream — materializes rows; do
60
+ this BEFORE the Step 2 row gate) + wait until the DLO is Active** →
61
+ **SCHEMA REALITY CHECK** (query the materialized DLO / `browse_data_assets`;
62
+ **not** `get_data_stream`) and copy the real Primary Key →
63
+ `create_data_model_object` (unsuffixed name — `Orders`, not `Orders__dlm`;
64
+ the platform adds `__dlm`) → **wait until the DMO is Ready** (`isEnabled:
65
+ false` is normal — do not gate on it) and copy the real Primary Key again →
66
+ `create_dlo_to_dmo_mapping` (per DMO) → inspect mapping via
67
+ `get_dlo_to_dmo_mapping_status` (`ERROR` / `INACTIVE`) → **poll `run_query`
68
+ until `<dmo>__dlm` exists** (`ACTIVE` on the status tool is metadata only;
69
+ `COUNT = 0` is READY).
70
+
71
+ **Existing database / lakehouse connection — §C:**
72
+ `list_connections` (exact `connectorType` casing) → optional
73
+ `test_existing_connection` (once; `9cg` id) → `list_data_connection_objects`
74
+ → `infer_object_schema` (table `resourceName`; uppercase `SCHEMA`/`DATABASE`)
75
+ → `create_data_stream` with `dataAccessMode: "Direct_Access"` and the
76
+ connection **developer name** → `run_data_stream` `interactive: false` →
77
+ then the same SCHEMA REALITY CHECK → DMO → mapping tail as §A.
78
+
79
+ Three non-obvious rules that each cost a failed call:
80
+ - **`category: "Other"`** on both the DLO (`dataLakeObjectInfo.category`) and the
81
+ DMO (`create_data_model_object.category`). File uploads are category-neutral;
82
+ `"Engagement"` or other semantic categories are wrong here.
83
+ - **DLO→DMO mapping field names carry a literal `__c` suffix on BOTH sides**
84
+ (`Order_ID__c` → `Order_ID__c`), matched 1:1. This is a *different* suffix from
85
+ the numeric `shouldIncludeAllFields` one (Step 5) — do not strip it or
86
+ numerically suffix it. Confirm the actual names from the materialized table
87
+ first — do not assume the CSV column survived ingestion.
88
+ - **The mapping isn't query-ready when `create_dlo_to_dmo_mapping` returns.**
89
+ Capture the returned `developerName` and call `get_dlo_to_dmo_mapping_status`
90
+ to inspect `fieldMappings` and to detect `ERROR` / `INACTIVE` (`ERROR` means
91
+ the mapping failed — do not re-POST create). `ACTIVE` is metadata deployed,
92
+ not "the `__dlm` table exists." Poll `run_query`
93
+ `SELECT 1 FROM <dmo>__dlm LIMIT 1` (or `COUNT(*)`): `42P01` / "table does
94
+ not exist" = keep polling; any success including `COUNT = 0` = READY.
95
+ `create_dlo_to_dmo_mapping` is **non-idempotent** — a duplicate call is a
96
+ hard error — so an ambiguous failure must be checked via the status tool
97
+ before assuming it needs retrying, never blindly retried.
98
+
99
+ Then build the SDM (Step 4) over the DMOs with `dataObjectType: "Dmo"` and
100
+ `dataObjectName: "<Entity>__dlm"`. For a DMO you just created and mapped in
101
+ this flow, wait until `run_query` succeeds on `__dlm` (`COUNT = 0` is READY;
102
+ `42P01` keep polling) — G2. Keep Step 2 as-is for any *discovered* extra
103
+ source. See `references/ingest-and-metric-gotchas.md` §A (file) / §C
104
+ (connection) for the complete per-tool payload shapes, the SCHEMA REALITY
105
+ CHECK, and the mapping query-readiness poll.
106
+
107
+ ### Step 0.5 — Profile source schema before adding fields
108
+
109
+ Before adding any field to a model, profile the source DMO/DLO to classify
110
+ every field. This replaces the blind bulk import and is what lets you set clean
111
+ apiNames, correct types, and field-level descriptions at creation time.
112
+
113
+ **Fetch schema and sample data:**
114
+
115
+ ```sql
116
+ SELECT * FROM "<dmo_name>__dlm" LIMIT 20
117
+ ```
118
+
119
+ (use the `__dll` DLO name for a DLO). From the sample, derive per-field signals
120
+ and classify **every** field into one of three buckets:
121
+
122
+ **Bucket A — Business fields to include (with descriptions).** All non-system
123
+ fields. Subdivide by confidence:
124
+ - *High confidence* — clear field name + type matches role + sample values
125
+ available → the AI can draft the description.
126
+ - *Low confidence* — ambiguous name, or values carry internal meanings the AI
127
+ cannot infer (e.g. `status = 'NA'`) → ask the user **one targeted question**
128
+ per ambiguity (never a blank form).
129
+
130
+ **Bucket B — System fields to hide (`isVisible: false`).** Names matching
131
+ `DataSource*`, `KQ_*`, `InternalOrg*`, `*SourceVersion*`, `cdp_sys_*`. Added to
132
+ the model **silently** — no user interaction, and **no descriptions required**
133
+ (they are hidden from agents). Add them with `isVisible: false`. If a Bucket B
134
+ name is also a Step 3 join key (e.g. `PartKey`), still include it in the nested
135
+ create payload — hidden is fine; omitted is not.
136
+
137
+ **Bucket C — Type mismatch warnings.** Fields where `storageDataType` conflicts
138
+ with the intended semantic type:
139
+ - `storageDataType: Text` but values match a `YYYY-MM-DD` pattern → date stored
140
+ as Varchar.
141
+ - numeric type but values look like sequential IDs (numeric **identifiers**, not
142
+ measures).
143
+ Flag these with an explanation **before** proceeding; document the workaround in
144
+ `businessPreferences`. These are source-data problems the SDM layer cannot fix.
145
+
146
+ This classification feeds the nested `semanticDimensions[]` /
147
+ `semanticMeasurements[]` on `create_semantic_model` (Step 4). Present it to
148
+ the user as a markdown table **before** that create call:
149
+
150
+ ```text
151
+ Sales Transactions — 9 fields found
152
+
153
+ BUSINESS FIELDS (6)
154
+ ✓ Region [Text → Dimension] High confidence
155
+ ✓ Product [Text → Dimension] High confidence
156
+ ✓ Amount [Number → Measure] High confidence
157
+ ✓ Order Date [⚠️ Text, date values] Low confidence — date stored as Varchar
158
+ ✓ Order ID [Number → Dimension] High confidence (identifier, not measure)
159
+ ? Status [Text → Dimension] Low confidence — what do values mean?
160
+
161
+ SYSTEM FIELDS (3) — will be hidden from agents
162
+ DataSource__c, KQ_order_id__c, InternalOrganization__c
163
+
164
+ WARNINGS
165
+ ⚠️ Order Date: stored as Varchar. Agents cannot do date math on this field.
166
+ Recommended: fix at DMO level. Workaround: DATE() calc dimension.
167
+ ```
168
+
169
+ Use `AskUserQuestion` for the **low-confidence Bucket A fields only** — one
170
+ question per ambiguity, not a form (e.g. "For `Status`, what do these values
171
+ mean? I see: 'Active', 'NA', 'Pending'"). Include the user's answer verbatim in
172
+ the field description per the AI readiness doc.
173
+
174
+ ### Step 1 — Discover candidate sources
175
+
176
+ Call `browse_data_assets` with `searchTerm: ""` plus an `AssetType` filter per
177
+ type: `MktDataModelObject` (DMOs, e.g. `ssot__Account`),
178
+ `MktDataLakeObject` (DLOs, e.g. `accounts`), `MktCalculatedInsightObject`
179
+ (CIOs). Matching the user's words to object **labels** is how the wrong
180
+ objects get picked — treat the label match as a candidate list, not an answer.
181
+ Calling convention, the `AssetType` case-sensitivity trap, and the
182
+ empty-`searchTerm` matrix are in `sdm-tool-reference.md`.
183
+
184
+ ### Step 2 — Verify data presence (HARD GATE — before building)
185
+
186
+ **Self-created mapping:** if the only sources are DMOs you just created and
187
+ mapped in Step 0 of this same flow, poll `run_query` until `<dmo>__dlm`
188
+ exists (`COUNT = 0` is READY; `42P01` / "table does not exist" = keep
189
+ polling). `get_dlo_to_dmo_mapping_status` `ACTIVE` is not this gate. Do not
190
+ skip the poll and proceed on metadata. Keep this gate as-is for every
191
+ *externally discovered* candidate (G2; `empty-source-handling.md`).
192
+
193
+ For every **discovered** candidate, check it has rows with `run_query` using the
194
+ source table name and suffix (DLO `__dll`, DMO `__dlm`, CIO `__dlc`):
195
+
196
+ ```sql
197
+ SELECT COUNT(*) AS n FROM accounts__dll
198
+ ```
199
+
200
+ Interpret the result:
201
+
202
+ | Result | Meaning | Action |
203
+ |---|---|---|
204
+ | count > 0 | usable | eligible to build on |
205
+ | count = 0 | empty table (has fields, no data) | do NOT build on it — surface to user |
206
+ | query errors `table … does not exist` | unmaterialized (0 fields) — **an error, not a zero** | exclude; cannot be joined; tell user |
207
+
208
+ A `run_query` error like `table "ssot__Activity__dlm" does not exist` is the
209
+ **signal that the object is unmaterialized** — do not retry it as a transient
210
+ failure and do not abort the whole task; record it as unusable and move on.
211
+ (Pre-creation, field presence comes from `browse_data_assets` metadata or this
212
+ error — `list_semantic_model_dimensions` has nothing to read until Step 5.)
213
+
214
+ **Hard gate:** a measure or dimension may only go on a viz/dashboard from an
215
+ object with verified `count > 0`. If **no** candidate matching the request has
216
+ rows, STOP and tell the user the sources are empty — do not build a model that
217
+ will render blank (the empty-of-rows vs. empty-of-fields distinction is drawn
218
+ in the table above). See `references/empty-source-handling.md` for the
219
+ authoritative exact wording to use when reporting this to the user.
220
+
221
+ ### Step 3 — Confirm join keys actually match
222
+
223
+ Find how the chosen objects relate and confirm the keys match across tables
224
+ using two separate `run_query` calls:
225
+
226
+ ```sql
227
+ -- 1. total rows on the candidate left table
228
+ SELECT COUNT(*) AS total_left FROM opportunities__dll
229
+
230
+ -- 2. rows that survive the join
231
+ SELECT COUNT(*) AS matched FROM opportunities__dll o
232
+ JOIN accounts__dll a ON o.account_id = a.account_id
233
+ ```
234
+
235
+ A key that matches ~0 rows produces an empty join — as broken as no
236
+ relationship. If the best candidate key matches roughly zero rows, do NOT
237
+ create the relationship on it: find another key, or report that the objects do
238
+ not actually relate. Record the matching `(leftColumn = rightColumn)` pairs for
239
+ Step 6.
240
+
241
+ ### Step 4 — Create the model and bind objects (one nested call)
242
+
243
+ `create_semantic_model` (apiName `[A-Za-z][A-Za-z0-9_]{0,79}`, no spaces;
244
+ `dataspace: "default"` unless told otherwise). **Always set `description`** on
245
+ the model — it is a first-class AI readiness requirement. If it returns `Label of
246
+ SemanticModel is required and cannot be empty`, that error is misleading — it
247
+ means the **apiName is malformed** (spaces or invalid characters), not that a
248
+ label is missing; fix the apiName.
249
+
250
+ **Bind fields in this same call.** Nested `semanticDataObjects[]` accept
251
+ explicit `semanticDimensions[]` / `semanticMeasurements[]` with
252
+ `shouldIncludeAllFields: false`. Build those arrays from the Step 0.5
253
+ classification **and** the Step 3 join keys, then send **one**
254
+ `create_semantic_model`. Do **not** follow with N
255
+ `add_semantic_model_dimension` / `_measure` calls (that is the 27-call bind
256
+ path). Do **not** use `shouldIncludeAllFields: true` — the flag is deprecated
257
+ and suffixes every apiName.
258
+
259
+ **Register the model with a workspace immediately after create succeeds.**
260
+ Call `add_workspace_asset` with the semantic model's returned id as `assetId`,
261
+ `assetType: "SemanticModel"`, and `assetUsageType: "Created"`. If the user did
262
+ not explicitly name a target workspace, call `list_workspaces` and **STOP to
263
+ ask the user which workspace to use** — do not infer or guess one.
264
+
265
+ For objects beyond the first few (bulk-create timeout at ≥ ~5
266
+ `semanticDataObjects[]`), create with the **anchor object and its nested
267
+ fields**, then `add_semantic_model_data_object` with the same nested
268
+ `semanticDimensions[]` / `semanticMeasurements[]` shape for each additional
269
+ object. If a table has no FK to the anchor, add the intermediate first, then
270
+ bridge through it.
271
+
272
+ **AI readiness requirements for `create_semantic_model`:**
273
+ - `description`: Required for AI readiness. Write a 1–2 sentence description
274
+ that states the model's goals and primary use cases (e.g. `"Tracks open
275
+ pipeline opportunities and associated account data for sales forecasting and
276
+ rep performance analysis"`). Include what the model is used for, not just
277
+ what data it contains.
278
+ - `categories`: Set to the relevant product category (`["Sales"]`,
279
+ `["Marketing"]`, etc.) where determinable from context.
280
+ - `agentEnabled`: Set to `true` explicitly. This gates whether AI agents
281
+ (Concierge / Agentforce for Analytics) can use this model.
282
+ - `businessPreferences`: One `# `-prefixed line per statement (hash + space),
283
+ matching the dedicated tools and the Edit Business Preferences UI. At
284
+ minimum include what the core measure means and any non-obvious terminology,
285
+ e.g.
286
+
287
+ ```text
288
+ # Revenue = sum of Amount on closed-won opportunities only
289
+ # Stage names: 0-Prospecting through 6-Closed Won
290
+ ```
291
+
292
+ Prefer `get_semantic_model_business_preferences` (`includeModelContent=false`)
293
+ then `update_semantic_model_business_preferences` (full replacement of that
294
+ string; send only `businessPreferences`). Fallback: set at create or via
295
+ `update_semantic_model` using the **same** `# `-prefixed-line format. **When the user provides no
296
+ domain context** (e.g. they say only "make it AI-ready" without explaining
297
+ the business meaning of fields): infer at least one substantive statement
298
+ from the column names and object. For example, for a `sales_data` object
299
+ with columns `amount`, `region`, `order_date`, write:
300
+
301
+ ```text
302
+ # amount = the transaction sale value
303
+ # region = the sales territory on the record, verify with domain owner whether this is geographic region or sales org territory
304
+ ```
305
+
306
+ Do not use a generic placeholder — that gives agents nothing
307
+ to work with. If you cannot infer meaningful context from the schema, ask
308
+ the user one targeted question: "To set businessPreferences, what does
309
+ `amount` represent and are there any non-obvious field values I should
310
+ know about?"
311
+
312
+ **AI readiness requirements for nested data objects** (same fields on
313
+ `create_semantic_model.semanticDataObjects[]` or a later
314
+ `add_semantic_model_data_object`):
315
+ - `description`: Required for AI readiness. Describe what this entity
316
+ represents (e.g. `"Active sales opportunities with stage and amount"`).
317
+ - `primaryNameField`: The field the AI uses to identify records in insight
318
+ narratives. Use `"Name"` for standard CRM objects; for file-upload DMOs use
319
+ the column that identifies the record (e.g. `"Product_Name"`, `"Account_Name"`).
320
+ **Known server bug:** `primaryNameField` cannot be set at creation time — the
321
+ server validates it against the field registry, which is always empty when the
322
+ data object is first created. This fails with `"<field> does not exist in
323
+ <object> fields"` regardless of bind mode. Document as a known gap; omit it
324
+ from the create / add-data-object call.
325
+
326
+ **Nested field arrays (Bucket A, B, and join keys)**
327
+
328
+ Put every classified field into `semanticDimensions[]` / `semanticMeasurements[]`
329
+ on the data object — **including Step 3 join keys** (e.g. `PartKey`,
330
+ `account_id__c`) even when they look like system columns. Omitting a join key
331
+ makes `add_semantic_model_relationship` fail with "field could not be found".
332
+ You control each `apiName` (clean, no suffix). Do not call
333
+ `add_semantic_model_dimension` / `_measure` for this initial bind; those tools
334
+ are only for adding a *single extra field* to an already-bound object.
335
+
336
+ Worked one-call payload (anchor object): `sdm-tool-reference.md`
337
+ (`create_semantic_model`). Extra objects after the timeout floor use the same
338
+ nested arrays on `add_semantic_model_data_object`:
339
+
340
+ ```jsonc
341
+ {
342
+ "modelApiNameOrId": "Pipeline_Analysis",
343
+ "apiName": "Opportunities",
344
+ "dataObjectName": "opportunities__dlm", // __dll DLO, __dlm DMO, __dlc CIO
345
+ "dataObjectType": "Dmo",
346
+ "label": "Opportunities",
347
+ "description": "Active sales opportunities with stage, amount, and close date",
348
+ "tableType": "Standard",
349
+ "shouldIncludeAllFields": false,
350
+ "semanticDimensions": [ /* Bucket A dims + join keys + Bucket B */ ],
351
+ "semanticMeasurements": [ /* Bucket A measures */ ]
352
+ }
353
+ ```
354
+
355
+ **Bucket A dimensions:** set `apiName` clean with no suffix, `label`,
356
+ `dataObjectFieldName` (the original `__c` column name), `dataType` (validated
357
+ against `storageDataType`), `displayCategory`, `description`, and
358
+ `isVisible: true`.
359
+
360
+ ```jsonc
361
+ {
362
+ "apiName": "Region", // clean, no suffix
363
+ "label": "Region",
364
+ "dataObjectFieldName": "region__c",
365
+ "dataType": "Text", // validated against storageDataType
366
+ "displayCategory": "Discrete",
367
+ "description": "Sales territory for the transaction. Values: West, East, North, South.",
368
+ "isVisible": true
369
+ }
370
+ ```
371
+
372
+ **Bucket A measures:** set `aggregationType` explicitly (**never `Auto`** on a
373
+ measure), `description`, `sentiment` for directional measures, and
374
+ `isVisible: true`.
375
+
376
+ ```jsonc
377
+ {
378
+ "apiName": "Amount",
379
+ "label": "Amount",
380
+ "dataObjectFieldName": "amount__c",
381
+ "dataType": "Number",
382
+ "aggregationType": "Sum", // explicit, never Auto on a measure
383
+ "description": "Gross transaction sale value in USD. Primary revenue measure.",
384
+ "sentiment": "SentimentTypeUpIsGood",
385
+ "isVisible": true
386
+ }
387
+ ```
388
+
389
+ **Critical fix — numeric identifiers.** Fields like `order_id__c` that are
390
+ numeric but are **identifiers** (high cardinality, sequential) must be added as
391
+ **dimensions** with `displayCategory: "Discrete"` and a description noting
392
+ "Record identifier — do not aggregate." This is what prevents an agent from
393
+ summing order IDs and returning a meaningless number. It also makes the field
394
+ usable as a metric `identifyingDimension` (bulk import ingested it as a Sum
395
+ measure and broke this).
396
+
397
+ ```jsonc
398
+ {
399
+ "apiName": "Order_ID",
400
+ "label": "Order ID",
401
+ "dataObjectFieldName": "order_id__c",
402
+ "dataType": "Number",
403
+ "displayCategory": "Discrete", // signals: group by, don't aggregate
404
+ "description": "Unique numeric identifier for the sales order. Record identifier — do not aggregate.",
405
+ "isVisible": true
406
+ }
407
+ ```
408
+
409
+ **Bucket B system fields** (`isVisible: false`): no description needed — they
410
+ are hidden from agents. If a Bucket B name is also a Step 3 join key, still
411
+ include it (hidden is fine; omitted is not).
412
+
413
+ ```jsonc
414
+ {
415
+ "apiName": "DataSource",
416
+ "label": "Data Source",
417
+ "dataObjectFieldName": "DataSource__c",
418
+ "dataType": "Text",
419
+ "isVisible": false // hidden from agents; no description needed
420
+ }
421
+ ```
422
+
423
+ **Bucket C Varchar dates.** Add the raw field as `Text` with a description
424
+ noting it is not suitable for date math, then **immediately** add a calculated
425
+ dimension using `DATE([Object].[RawField])` to produce the canonical date field.
426
+ The raw field stays visible but clearly non-recommended; the calc dimension is
427
+ the field agents should use — no ambiguity. The calc itself is still a
428
+ post-create `add_semantic_model_calculated_dimension` (Step 8) — nest only the
429
+ raw `Text` field in the create payload.
430
+
431
+ ```jsonc
432
+ {
433
+ "apiName": "Order_Date_Raw",
434
+ "label": "Order Date (Raw Text)",
435
+ "dataObjectFieldName": "order_date__c",
436
+ "dataType": "Text",
437
+ "description": "Order date stored as text (YYYY-MM-DD format). NOT suitable for date arithmetic. Use Order_Date calculated dimension for time-based analysis.",
438
+ "isVisible": true
439
+ }
440
+ ```
441
+
442
+ ```jsonc
443
+ {
444
+ "apiName": "Order_Date",
445
+ "label": "Order Date",
446
+ "expression": "DATE([Sales_Transactions].[Order_Date_Raw])",
447
+ "description": "Order date, parsed from text into Date type for time-based filtering and grouping."
448
+ }
449
+ ```
450
+
451
+ ### Step 5 — Confirm apiNames match expectations
452
+
453
+ With nested explicit fields (Step 4), apiNames are the values you set — they
454
+ are clean and known. Step 5 is a **light confirmation**: confirm the first few
455
+ match expectations with `list_semantic_model_dimensions` before writing
456
+ relationships or calculated fields.
457
+
458
+ **If you inherited a bulk-bound object (`shouldIncludeAllFields: true`):**
459
+
460
+ `shouldIncludeAllFields: true` **numerically suffixes every field apiName**
461
+ (`account_id__c` → `account_id5`) unpredictably, and the suffix differs per
462
+ object — the same logical column can become `account_id5` on one object and
463
+ `account_id3` on another. For every object to be joined, call
464
+ `list_semantic_model_dimensions` (and `list_semantic_model_data_objects` for
465
+ object apiNames) and copy the exact stored apiName. Never construct or assume
466
+ the suffixed name, and never assume the two sides of a join share a suffix.
467
+ Do not choose this bind for new objects — see the decision table.
468
+
469
+ ### Decision Table: How to bind fields
470
+
471
+ | Scenario | Approach |
472
+ |---|---|
473
+ | New SDM | Nested explicit fields in `create_semantic_model` (`shouldIncludeAllFields: false`). One call. |
474
+ | ≥ ~5 objects (bulk-create timeout) | Create with the anchor object + its nested fields; then `add_semantic_model_data_object` with nested arrays per extra object |
475
+ | Add an object to an existing SDM | Nested explicit fields on `add_semantic_model_data_object` (`shouldIncludeAllFields: false`) |
476
+ | Add one field to an already-bound object | `add_semantic_model_dimension` / `_measure` |
477
+ | Wide DMO (60+ fields) | Nest the top 15–20 business fields + **all join keys** + hidden Bucket B; omit niche columns rather than bulk-importing them undescribed |
478
+ | HardJoin logical view | `shouldIncludeAllFields: true` still — apiNames are assigned at first save (read back before joining) |
479
+ | NEVER | `shouldIncludeAllFields: true` on model-level create/add (deprecated; suffixes every apiName) |
480
+
481
+ ### Step 6 — Create a relationship for every join
482
+
483
+ For each key pair from Step 3, call `add_semantic_model_relationship` using the
484
+ **semantic dimension apiName you set in Step 4** (light-confirmed in Step 5)
485
+ with `leftFieldType: "TableField"` — NOT the underlying `dataObjectFieldName`
486
+ (that returns "field could not be found"). Because you controlled the apiName
487
+ at bind time, this resolves first-try. The apiNames below are illustrative —
488
+ replace them with the exact values from Step 4 / Step 5:
489
+
490
+ ```jsonc
491
+ {
492
+ "modelApiNameOrId": "Pipeline_Analysis",
493
+ "apiName": "Opp_To_Account",
494
+ "label": "Opportunity to Account", // REQUIRED (schema says optional; server rejects without it)
495
+ "joinType": "Auto", // MUST be Auto at model level
496
+ "cardinality": "ManyToOne",
497
+ "leftSemanticDefinitionApiName": "Opportunities",
498
+ "rightSemanticDefinitionApiName": "Accounts",
499
+ "criteria": [{
500
+ "joinOperator": "Equals",
501
+ "leftFieldType": "TableField",
502
+ "leftSemanticFieldApiName": "<Opportunities account-FK apiName from Step 5>",
503
+ "rightFieldType": "TableField",
504
+ "rightSemanticFieldApiName": "<Accounts Primary Key apiName from Step 5>"
505
+ }]
506
+ }
507
+ ```
508
+
509
+ ### Step 7 — Validate (BLOCKING — both checks must pass)
510
+
511
+ 1. `list_semantic_model_relationships` → confirm **every** object from Step 4
512
+ appears on the left or right of at least one relationship. Any object in no
513
+ relationship is an island — return to Step 6.
514
+ 2. `run_semantic_query` for a real measure grouped by a dimension (traversing a
515
+ join when there is more than one object). In this query, reference tables by
516
+ the **SDM data-object apiName** (from `list_semantic_model_data_objects`),
517
+ NOT the `__dll`/`__dlm` SQL name used by `run_query` — the underlying name is
518
+ rejected with `Failed to resolve table`, which is a query-construction bug,
519
+ not an empty-source signal. **If the result is genuinely empty, the model is
520
+ not shippable: do NOT create dashboards or viz — STOP and report to the
521
+ user.** An empty result means an empty source (Step 2) or a non-matching key
522
+ (Step 3) got through. This check is blocking even for a single-object model.
523
+
524
+ **After enrichment (Step 8), before shipping:** If you have added calculated
525
+ fields or metrics, use the Semantic Model AI Optimization similarity scan (in
526
+ Tableau Next's Semantic Model Builder UI) to check for overlapping or ambiguous
527
+ components. A `modelHealth: Low` result indicates fields that agents will
528
+ struggle to differentiate — resolve by improving descriptions or removing
529
+ redundant components before exposing the model to AI users.
530
+
531
+ ### Step 8 — Enrich: calculated fields and metrics
532
+
533
+ Raw fields are often not the answer the user asked for — engagement,
534
+ margin, win-rate, "deal size" are **derived**. Add them so the model
535
+ answers the real question, not just exposes columns.
536
+
537
+ **Identify the analysis intent first** (trend / comparison / part-to-whole /
538
+ correlation / forecast — see the Step 10 map in `tasks/create-viz.md`) so
539
+ enrichment produces the fields that intent needs: a business-meaningful date
540
+ anchor for trend/forecast, a slicing dimension for part-to-whole, two measures
541
+ for correlation.
542
+
543
+ **Create a metric by default when the request implies a tracked business
544
+ number AND a meaningful time dimension exists to anchor it.** Analytical asks
545
+ ("analyze account engagement", "pipeline health", "revenue by region", "win
546
+ rate over time") almost always want a named, time-bound **KPI** — a metric —
547
+ not just a raw column. A common miss is building the model and stopping; if
548
+ the user's intent names or implies a headline number, create the metric for it.
549
+
550
+ The time anchor must be **business-meaningful** — a real event date like
551
+ created/close/order/activity date — NOT a plumbing column. Skip the metric (or
552
+ ask) when the only date fields are system/load timestamps (e.g.
553
+ `cdp_sys_PartitionDate`, `*_SourceVersion`, ingestion timestamps), or when the
554
+ request is purely structural ("just model these tables"). A metric anchored on
555
+ a junk date is worse than no metric.
556
+
557
+ **For AI-readiness requirements** on the three component types below —
558
+ descriptions, value-meaning pairs, sentiment enums, singularNoun/pluralNoun,
559
+ insightsDimensionsReferences, pre-creation `list_*` duplicate checks, and the
560
+ unique-purpose test — see [ai-readiness.md](ai-readiness.md). What follows
561
+ here is build-order and payload mechanics only.
562
+
563
+ - **Calculated dimension** (derived grouping, e.g. bucketing, `UPPER(...)`,
564
+ date-part): `add_semantic_model_calculated_dimension`. Use TuA bracketed
565
+ references `[DataObjectApiName].[FieldApiName]` with the **suffixed**
566
+ apiNames from Step 5 — bare names fail with "Missing reference". Row-level
567
+ expressions get `level: "Row"` automatically.
568
+ - **Calculated measure** (derived numeric, e.g. ratio, average):
569
+ `add_semantic_model_calculated_measure`. For an expression that already
570
+ aggregates (`AVG(...)`, `SUM(...)/COUNT(...)`, `COUNTD(...)`), set
571
+ `aggregationType: "UserAgg"` — a level-aware aggregation with a bare
572
+ `Average`/`Sum` is rejected with "AggregativeFunction-level calculated
573
+ fields require UserAgg". A row-level arithmetic expression
574
+ (`[O].[Profit] / [O].[Sales]`) can use `aggregationType: "Auto"`.
575
+ - **Metric** (named, time-bound KPI — create one by default per above):
576
+ `add_semantic_model_metric`. Set exactly one of
577
+ `measurementReference.calculatedFieldApiName` (a calc measure) or
578
+ `measurementReference.tableFieldReference {tableApiName, fieldApiName}`
579
+ (a raw measure, suffixed apiName); add a `timeDimensionReference` pointing
580
+ at a **business-meaningful Date/DateTime** dimension (find candidates via
581
+ `list_semantic_model_dimensions` — `dataType: "Date"` — and pick a real event
582
+ date like created/close/order date, not a `cdp_sys_*`/load timestamp) and
583
+ `timeGrains`. Skip the metric only if no meaningful date dimension resolves.
584
+ Three mechanical rules — each costs a rejected call or a broken metric:
585
+ 1. **`insightsSettings.identifyingDimension` is REQUIRED** (point it at the
586
+ grain's primary key). The server accepts a metric without it (create
587
+ returns 201) but it then crashes the Tableau Next metric UI on open — a
588
+ downstream failure a "did the call succeed?" check won't catch. Always
589
+ set it; see ai-readiness.md for the full explanation.
590
+ 2. **`aggregationType` may NEVER be `Auto` on a metric** ("The aggregation type
591
+ (Auto) is not allowed for metric aggregation"). Use a concrete enum: `Sum`,
592
+ `Count`, `Average`, `Min`, `Max`. A metric over a *row-level ratio* calc
593
+ measure (e.g. `Profit_Margin`) takes `Average`, not `Auto`.
594
+ 3. **To count records, point `measurementReference` at a measurable (numeric)
595
+ field with `aggregationType: "Count"` — NOT at a dimension/Primary Key.** Counting via
596
+ `Order_ID` is rejected ("field name: (Order_ID) was not found in the model"
597
+ — it's a dimension). Use a numeric column (e.g. `Sales`) + `Count`; it counts
598
+ one row per record. The `identifyingDimension` can still be the Primary Key.
599
+
600
+ Verified shape (AI-ready):
601
+
602
+ ```jsonc
603
+ {
604
+ "modelApiNameOrId": "Pipeline_Analysis",
605
+ "apiName": "Total_Pipeline",
606
+ "label": "Total Pipeline",
607
+ "description": "Total open pipeline amount summed across active opportunities",
608
+ "measurementReference": { "tableFieldReference": { "tableApiName": "Opportunities", "fieldApiName": "Amount7" } },
609
+ "timeDimensionReference": { "tableFieldReference": { "tableApiName": "Opportunities", "fieldApiName": "Created_Date14" } },
610
+ "aggregationType": "Sum",
611
+ "timeGrains": ["Day", "Week", "Month", "Quarter", "Year"],
612
+ "sentiment": "SentimentTypeUpIsGood",
613
+ "insightsSettings": {
614
+ "identifyingDimension": {
615
+ "identifierDimensionReference": {
616
+ "tableFieldReference": { "tableApiName": "Opportunities", "fieldApiName": "Opportunity_Id5" }
617
+ }
618
+ },
619
+ "sentiment": "SentimentTypeUpIsGood",
620
+ "singularNoun": "opportunity",
621
+ "pluralNoun": "opportunities",
622
+ "insightsDimensionsReferences": [
623
+ { "tableFieldReference": { "tableApiName": "Opportunities", "fieldApiName": "Stage8" } },
624
+ { "tableFieldReference": { "tableApiName": "Accounts", "fieldApiName": "Region4" } }
625
+ ]
626
+ },
627
+ "additionalDimensions": [
628
+ { "tableFieldReference": { "tableApiName": "Opportunities", "fieldApiName": "Opportunity_Id5" } },
629
+ { "tableFieldReference": { "tableApiName": "Opportunities", "fieldApiName": "Stage8" } },
630
+ { "tableFieldReference": { "tableApiName": "Accounts", "fieldApiName": "Region4" } }
631
+ ]
632
+ }
633
+ ```
634
+ `additionalDimensions` **must mirror every field in `insightsSettings.identifyingDimension`,
635
+ `insightsDimensionsReferences`, and `filters`** — create validates the insight
636
+ dims (`Insight dimension (...) is missing from the metric additional dimensions`);
637
+ omitting a filter field makes the metric unqueryable
638
+ (`Metric Definition Filter Field ... is not found`).
639
+ **Exception: the server rejects `Date`-type fields in `additionalDimensions`**
640
+ (`"Field should have one of these data types: Text, Number, Boolean, Email,
641
+ PhoneNumber, Url. Actual data type: Date"`). If a filter field is a Date, do
642
+ not add it to `additionalDimensions`.
643
+ `fieldApiName` on the measurement/time/identifier references is the
644
+ **suffixed** apiName from the owning object (Step 5) — a date field from a
645
+ *different* object is rejected with "Table field … was not found in the
646
+ model".
647
+
648
+ If a calc measure references a calc dimension, create the dimension first.
649
+ Build calc fields only on objects that passed the Step 2 row check — a
650
+ derived field over an empty source is still empty.
651
+
652
+ **Step 8 closing checklist — run before proceeding to Step 9:**
653
+
654
+ For every visible raw measure in the model, verify one of the following is
655
+ true — if neither is, create the missing metric:
656
+
657
+ | Check | Pass condition |
658
+ |---|---|
659
+ | A metric already covers this measure | A metric's `measurementReference` points at this field (or a calc measure derived from it) |
660
+ | No meaningful time anchor exists | The only date fields are system/load timestamps (`cdp_sys_*`, `*SourceVersion`, ingestion columns) |
661
+ | Request is purely structural | User explicitly asked to "just model the tables" with no analytical intent |
662
+
663
+ This sweep must cover **every** visible raw measure — not just the first
664
+ one. The failure mode is anchoring on the primary KPI and stopping,
665
+ leaving secondary measures (e.g. `Amount` alongside `Engagement_Score`)
666
+ without metrics. A missing metric means agents fall back to raw field
667
+ aggregation, losing time-scoping, period comparison, and insight
668
+ breakdown context.
669
+
670
+ ### Step 9 — Query the model (answer the question; also the Step 7 proof)
671
+
672
+ **Before constructing a raw `run_semantic_query`:** if the user's question
673
+ implies a derived or named business number (e.g. "average order value",
674
+ "win rate", "gross margin"), call `list_semantic_model_calculated_measures`
675
+ and `list_semantic_model_metrics` first. If a calculated measure or metric
676
+ already satisfies the request, reference it directly rather than
677
+ reconstructing the aggregation from raw fields:
678
+ - A calculated measure → `semantic_field: { name: "<calc_measure_apiName>" }`
679
+ with `semantic_aggregation_method: "SEMANTIC_AGGREGATION_METHOD_USER_AGG"`
680
+ (for UserAgg expressions).
681
+ - A metric → use `run_semantic_query` with `structuredMetricQuery` (see shape below).
682
+
683
+ Do NOT bypass an existing `Avg_Order_Value` calculated measure to write
684
+ `AVG(Amount)` from a raw table field — the calculated measure encodes the
685
+ authoritative business definition.
686
+
687
+ `run_semantic_query` both validates the model (Step 7) and answers the user's
688
+ data question from it. Our MCP uses a **proto-shaped snake_case** body — NOT
689
+ the REST gateway's camelCase.
690
+
691
+ **Querying a metric** (`structuredMetricQuery`) — verified working shape:
692
+
693
+ ```jsonc
694
+ {
695
+ "semanticModelApiName": "Pipeline_Analysis",
696
+ "source": "tableau-next-pilot-mcp",
697
+ "structuredMetricQuery": {
698
+ "submetric_definition": { "metric_api_name": "Total_Pipeline" },
699
+ "time_grain": "Month" // Day | Week | Month | Quarter | Year
700
+ }
701
+ }
702
+ ```
703
+
704
+ `metric_api_name` is the only accepted key inside `submetric_definition` — all
705
+ other variants (`metricApiName`, `metricName`, `api_name`, `name`, `metric`) are
706
+ rejected with `Cannot find field`. `time_grain` is required.
707
+
708
+ **Querying raw fields** (`structuredSemanticQuery`) — verified working shape:
709
+
710
+ ```jsonc
711
+ {
712
+ "semanticModelApiName": "Pipeline_Analysis",
713
+ "source": "tableau-next-pilot-mcp",
714
+ "structuredSemanticQuery": {
715
+ "fields": [
716
+ { "expression": { "table_field": { "name": "region18", "table_name": "Accounts" } },
717
+ "alias": "Accounts.region18", "grouping": "ROW_GROUPING" },
718
+ { "expression": { "table_field": { "name": "Amount7", "table_name": "Opportunities" } },
719
+ "alias": "Opportunities.Amount7",
720
+ "semantic_aggregation_method": "SEMANTIC_AGGREGATION_METHOD_SUM" }
721
+ ],
722
+ "options": { "limit_options": { "limit": 10 },
723
+ "sort_orders": [ { "simple_sort_order":
724
+ { "sort_by_field_alias": "Opportunities.Amount7", "sorting_order": "DESC" } } ] }
725
+ }
726
+ }
727
+ ```
728
+
729
+ Rules that differ from raw SQL and from the REST gateway:
730
+ - Reference tables by the **SDM data-object apiName** (`table_field.table_name`),
731
+ never the `__dll`/`__dlm` name. The latter → `Failed to resolve table`.
732
+ - Dimensions carry `grouping: "ROW_GROUPING"`; raw measures carry
733
+ `semantic_aggregation_method: "SEMANTIC_AGGREGATION_METHOD_<SUM|AVG|COUNT|MIN|MAX>"`.
734
+ - A **calculated** field is referenced by `semantic_field: { name }` (no
735
+ `table_name`); a UserAgg calc measure uses
736
+ `semantic_aggregation_method: "SEMANTIC_AGGREGATION_METHOD_USER_AGG"`.
737
+ - `grand_total`, `limit`, `sort_orders` all live **inside** `options` — at the
738
+ top level they fail with `Cannot find field`.
739
+
740
+ The shapes above cover the common cases. See
741
+ `references/semantic-query-and-enrichment.md` for the full field-shape matrix,
742
+ aggregation-method mapping, and worked enrichment payloads when a
743
+ `run_semantic_query` or `add_semantic_model_calculated_*` /
744
+ `add_semantic_model_metric` call falls outside them.
745
+
746
+ See `references/sdm-tool-reference.md` for the complete tool list and the full
747
+ suffix-rule / apiName-mutation reference (the specific mutation gotcha is
748
+ already called out inline at Step 5 above).
749
+
750
+ ### Step 10 — Create visualizations (only when the user wants charts/dashboard)
751
+
752
+ **MANDATORY — design the narrative before touching a chart tool.** Before
753
+ calling `create_visualization`, design the story: KPIs → trends → breakdowns →
754
+ correlations, use business-friendly labels (never raw/technical field names),
755
+ pick diverse chart types (not every widget as a bar chart), and plan KPIs
756
+ top/left in the eventual layout. This is a hard gate alongside the Step 2/9
757
+ data-presence check (row count > 0) — do not build on an empty source. Read
758
+ `references/dashboard-design-principles.md` (MANDATORY, not optional) for the
759
+ full narrative-design guidance before proceeding.
760
+
761
+ Do this only after Step 9 returns real data — never chart a model that queried
762
+ empty.
763
+
764
+ **First, identify the user's analysis intent — it picks the analysis type, which
765
+ picks the chart.** Don't jump straight to a bar chart. Full intent → type →
766
+ tool map (including Line + `visualSpecification.forecasts` for predictive asks)
767
+ lives in `tasks/create-viz.md` / `viz-authoring.md` §3; payloads are in
768
+ `viz-authoring.md` §11.
769
+
770
+ One tool handles all chart types: `create_visualization`. It uses a
771
+ **`fields` map + `visualSpecification`** shape — NOT the `fieldRef /
772
+ measureFunction` shape from older tools. `objectName` is the **SDM
773
+ data-object apiName** (`Orders`), `fieldName` is the field apiName on that
774
+ object — use the **suffixed** apiName (Step 5) for any renamed field (e.g. a
775
+ breakdown on `Category3`). Each call returns the viz `id` and a generated
776
+ `name` (e.g. `monthly_sales_trend_1782144120674`) — **capture both** for the
777
+ dashboard.
778
+
779
+ Control chart type via `marks.panes.type` and `visualSpecification.layout`:
780
+ - **Line / Time Series** — `type: "Line"`, `layout: "Vizql"`, date on columns
781
+ - **Bar (comparative, sorted)** — `type: "Bar"`, `layout: "Vizql"`, dim on
782
+ columns + measure on rows, `sortIntent: "MeasureDescending"`
783
+ - **Donut / Pie** — `type: "Donut"`, `layout: "Radial"`, measure on **`Angle`
784
+ encoding** (NOT rows), dimension on Color; use when dim has < 5 unique values
785
+ - **Scatter / Correlation** — `type: "Circle"`, `layout: "Vizql"`, X-measure on
786
+ columns, Y-measure on rows, point dimension on Color
787
+ - **Heatmap** — `type: "Square"`, `layout: "Vizql"`, two dims on rows/columns,
788
+ measure on Color encoding
789
+ - **Table** — `type: "Text"`, `layout: "Table"`, dims in `groups`, measures in `rows`
790
+
791
+ The shape and rules above cover the common charts. See
792
+ `references/viz-authoring.md` for the full chart-type decision matrix, field
793
+ binding rules, `marks.panes.type` values, and verified payloads for every
794
+ chart type, plus the fallback approach for unsupported chart types (Funnel,
795
+ Dot Matrix, Map, Flow/Sankey).
796
+
797
+ ### Step 11 — Build the dashboard
798
+
799
+ `create_dashboard` (empty `widgets: {}`; seed one layout with an empty
800
+ `page_1` — full `style` + `columnCount`, since widgets get placed next; see
801
+ `dashboard-authoring.md` §1 — and always pass `minorVersion: -1` explicitly),
802
+ then place widgets on a **48-column grid**
803
+ (`rowHeight 20`, `maxWidth 1200`; half-width = colspan 24, quarter KPI = 12).
804
+ Place KPI widgets top/left per the narrative designed in Step 10 — see
805
+ `references/dashboard-design-principles.md` (MANDATORY, not optional).
806
+
807
+ - **Every widget kind (metric, visualization, text, or any other)** → hand
808
+ off to `tasks/edit-dashboard.md` (`edit_dashboard`'s atomic `upsert_*_widget`
809
+ + `place_widget_on_page` batch — upsert → place, same batch or a later one).
810
+ Viz widgets need the `id`/`name` from Step 10's `create_visualization` call;
811
+ metric widgets need `parameters.metricOption.{sdmApiName, sdmId}` (`source`
812
+ optionally references the metric); text widgets
813
+ need a Quill delta (`parameters.content: [{ insert, attributes }]`). This
814
+ is a **staged draft by default** — send `minorVersion: -1`, then only
815
+ persist via `save_dashboard` after the user confirms.
816
+ - See `references/dashboard-authoring.md` for the complete verified
817
+ `create_dashboard` payload shapes, grid spec, and the `&`-encoding gotcha,
818
+ and `references/edit-dashboard.md` for the full `edit_dashboard` operation
819
+ table and widget-name-location rule.