@salesforce/afv-skills 1.61.0 → 1.62.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 (44) hide show
  1. package/package.json +3 -3
  2. package/skills/agentforce-architecture-analyze/README.md +2 -1
  3. package/skills/agentforce-d360-analyze/README.md +1 -1
  4. package/skills/agentforce-generate/README.md +1 -0
  5. package/skills/crm-analytics-wave-generate/SKILL.md +212 -0
  6. package/skills/crm-analytics-wave-generate/assets/dashboard-template.json +66 -0
  7. package/skills/crm-analytics-wave-generate/assets/wapp-meta-template.xml +10 -0
  8. package/skills/crm-analytics-wave-generate/assets/wdash-meta-template.xml +6 -0
  9. package/skills/crm-analytics-wave-generate/assets/wdpr-meta-template.xml +11 -0
  10. package/skills/crm-analytics-wave-generate/examples/industry-binding-dashboard.wdash +67 -0
  11. package/skills/crm-analytics-wave-generate/examples/opportunity-revenue-recipe.wdpr +132 -0
  12. package/skills/crm-analytics-wave-generate/examples/pipeline-by-industry-quarter.wdpr +236 -0
  13. package/skills/crm-analytics-wave-generate/examples/wdpr-skeleton.wdpr +62 -0
  14. package/skills/crm-analytics-wave-generate/references/dashboard-authoring.md +157 -0
  15. package/skills/crm-analytics-wave-generate/references/dataset-hygiene.md +27 -0
  16. package/skills/crm-analytics-wave-generate/references/external-data-upload.md +61 -0
  17. package/skills/crm-analytics-wave-generate/references/method-decision-tree.md +12 -0
  18. package/skills/crm-analytics-wave-generate/references/r3-node-schema.md +194 -0
  19. package/skills/crm-analytics-wave-generate/references/scripts-reference.md +17 -0
  20. package/skills/crm-analytics-wave-generate/references/ui-section-template.md +68 -0
  21. package/skills/crm-analytics-wave-generate/scripts/check_connector_error.sh +53 -0
  22. package/skills/crm-analytics-wave-generate/scripts/create_app.js +59 -0
  23. package/skills/crm-analytics-wave-generate/scripts/deploy_and_run_recipe.js +122 -0
  24. package/skills/crm-analytics-wave-generate/scripts/fetch_dataset_xmd.js +59 -0
  25. package/skills/crm-analytics-wave-generate/scripts/find_unused_dataset_fields.js +96 -0
  26. package/skills/crm-analytics-wave-generate/scripts/gen_synthetic_dataset.js +137 -0
  27. package/skills/crm-analytics-wave-generate/scripts/patch_t_oppo_view.js +99 -0
  28. package/skills/crm-analytics-wave-generate/scripts/probe_recipe.js +47 -0
  29. package/skills/crm-analytics-wave-generate/scripts/upload_dataset.js +119 -0
  30. package/skills/crm-analytics-wave-generate/scripts/verify_dashboard_steps.js +99 -0
  31. package/skills/crm-analytics-wave-generate/scripts/verify_synthetic_dataset.js +86 -0
  32. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +59 -20
  33. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +5 -1
  34. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +2 -2
  35. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +2 -2
  36. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +5 -5
  37. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +5 -5
  38. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +67 -100
  39. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone-action.json +2 -2
  40. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
  41. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +23 -22
  42. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +3 -3
  43. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +182 -58
  44. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/workflow.md +157 -88
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@salesforce/afv-skills",
3
- "version": "1.61.0",
4
- "description": "Salesforce skills for Agentforce Vibes",
5
- "license": "CC-BY-NC-4.0",
3
+ "version": "1.62.0",
4
+ "description": "Salesforce's curated collection of agent skills for building applications. Optimized for Agentforce Vibes, compatible with all AI tools.",
5
+ "license": "Apache-2.0",
6
6
  "files": [
7
7
  "skills/"
8
8
  ],
@@ -22,7 +22,7 @@ Scaling note: large bots with many flows scale approximately linearly in flow co
22
22
 
23
23
  | Tool | Why |
24
24
  |---|---|
25
- | `sf` CLI (authenticated against the target org) | Shells `sf org display --target-org <alias> --json` for access token, and `sf sobject describe` for the 7-day channel probe |
25
+ | `sf` CLI (authenticated against the target org) | Must provide `sf org auth show-access-token` (preflighted at startup). Shells `sf org display --target-org <alias> --json` for org metadata, `sf org auth show-access-token --target-org <alias> --json --no-prompt` for the access token, and `sf sobject describe` for the 7-day channel probe |
26
26
  | Python 3.10+ | `pathlib`, dataclasses, `\|` union types, `concurrent.futures` |
27
27
 
28
28
  ---
@@ -143,6 +143,7 @@ Per-branch ancestor-path cycle detection is the primary termination primitive: t
143
143
 
144
144
  | Symptom | Fix |
145
145
  |---|---|
146
+ | `sf CLI is missing required command 'sf org auth show-access-token'` | Upgrade/reinstall Salesforce CLI, then rerun |
146
147
  | `sf org display failed` | Re-authenticate: `sf org login web --alias <alias>` |
147
148
  | `INVALID_FIELD` from a SOQL asset | Salesforce renamed / removed the field in a quarterly release. Run with `--reprobe` to refresh the 7-day channel cache and pick up the new schema |
148
149
  | `STATUS=PROBE_FAILED` on first run | Channel probe saw a mandatory field missing. Check `channels.json` under the probe cache dir for which sObject / field — may require org-side feature enablement |
@@ -25,7 +25,7 @@ Output: per-DMO JSON artifacts plus three derived files under `~/.vibe/data/agen
25
25
 
26
26
  | Tool | Why |
27
27
  |---|---|
28
- | `sf` CLI (authenticated against the target org) | Shells `sf org display --target-org <alias> --json` for the Data Cloud Query REST API access token |
28
+ | `sf` CLI (authenticated against the target org) | Must provide `sf org auth show-access-token` (preflighted at startup). Shells `sf org display --target-org <alias> --json` for the instance URL and `sf org auth show-access-token --target-org <alias> --json --no-prompt` for the Data Cloud Query REST API access token |
29
29
  | Data Cloud enabled on the target org | Required — the STDM + GenAI DMOs must have materialized for the session |
30
30
  | Python 3.10+ | `pathlib`, dataclasses, `\|` union types |
31
31
 
@@ -15,6 +15,7 @@ The skill routes agent work across the full lifecycle:
15
15
  | Diagnose Behavioral Issues | Trace-based debugging, routing/action analysis |
16
16
  | Deploy / Release | Draft iteration, deploy, explicit publish + activate |
17
17
  | Test an Agent | Coverage design, spec creation, run analysis |
18
+ | Migrate a Legacy Agent | `GenAiPlannerBundle` → `AiAuthoringBundle` (Agent Script) conversion, then optimize |
18
19
 
19
20
  ## Skill Structure
20
21
 
@@ -0,0 +1,212 @@
1
+ ---
2
+ name: crm-analytics-wave-generate
3
+ description: "Use to build a Wave Recipe, deploy a CRM Analytics dataset or dashboard, or author the full CRMA asset stack — apps, Wave Recipes (.wdpr), datasets, and dashboards — against a connected Salesforce org via REST and Metadata APIs, without CRM Analytics Studio or Dashboard Builder. TRIGGER when: user asks to build or create a Wave Recipe, generate a .wdpr or .wdpr-meta.xml, deploy a .wdash or .wapp, run a recipe or dataflowjob programmatically, upload CSV data via InsightsExternalData, validate a dataset schema with synthetic data, fetch dataset XMD, audit dashboard SAQL re-encoding, or find unused dataset fields. TRIGGER on keywords: Wave Recipe, CRMA, CRM Analytics, wdpr, wdash, wapp, WaveRecipe, Wave dataflow, dataflowjob, InsightsExternalData, SAQL, dataset XMD, Analytics Studio, R3 format, wave/recipes, wave/dashboards, or analytics on Salesforce objects (opportunities, leads, cases, accounts, sales). DO NOT TRIGGER for standard Salesforce reports or LWC components unrelated to CRM Analytics."
4
+ metadata:
5
+ version: "1.0"
6
+ minApiVersion: "59.0"
7
+ cliTools:
8
+ - tool: ["curl"]
9
+ semver: ">=7.0.0"
10
+ - tool: ["node"]
11
+ semver: ">=18.0.0"
12
+ - tool: ["sf"]
13
+ semver: ">=2.0.0"
14
+ ---
15
+
16
+ # Authoring CRM Analytics Assets
17
+
18
+ End-to-end workflow for authoring the full CRM Analytics asset stack — application, Wave Recipe, dataset, and Wave Dashboard — against a Salesforce org from a local SFDX project, without the UI.
19
+
20
+ ## Scope
21
+
22
+ - **In scope**: WaveApplication, WaveRecipe (R3 format), Wave datasets (recipe-driven + CSV upload via InsightsExternalData), Wave Dashboards (.wdash deploy + PATCH styling), SAQL step authoring, dataset XMD inspection, dashboard auditing.
23
+ - **Out of scope**: Standard Salesforce reports/dashboards, LWC analytics bridge components, Tableau CRM managed packages, org provisioning.
24
+
25
+ ---
26
+
27
+ ## Decision Tree — Pick the Right Method
28
+
29
+ Before acting, read `references/method-decision-tree.md` to select the correct API path for the user's goal. Wrong method choices (e.g. multipart upload instead of R3 JSON body) cause silent failures that are hard to diagnose.
30
+
31
+ ---
32
+
33
+ ## Required Inputs
34
+
35
+ - Authenticated org alias (verify with `sf org display -o <alias> --json`)
36
+ - Target CRMA app name (folder developer name)
37
+ - Recipe or dashboard name and output dataset alias
38
+ - Source sObjects to load (API names, e.g. `Opportunity`, `Account`)
39
+ - Field list per sObject
40
+ - Transformation intent: formula fields, aggregations, filter conditions
41
+
42
+ Defaults:
43
+ - Local Salesforce connector name: `SFDC_LOCAL` (verify via `GET /wave/dataConnectors`)
44
+ - API version: use `result.apiVersion` from `sf org display`
45
+ - Node.js binary: `/usr/local/lib/sf/bin/node` (bundled with sf CLI)
46
+
47
+ ---
48
+
49
+ ## Workflow
50
+
51
+ ### Phase Selection
52
+
53
+ Before starting, identify the user's goal and determine which phases to run:
54
+
55
+ | User goal | Phases to run |
56
+ |-----------|--------------|
57
+ | Build or update a recipe and dataset | 0 → 1 → 2 |
58
+ | Validate recipe output schema after run | 0 → 1 → 2 → 3 |
59
+ | Build or update a dashboard | 0 → 1 → 2 → 4 |
60
+ | Full end-to-end: recipe + validation + dashboard | 0 → 1 → 2 → 3 → 4 |
61
+ | Audit dataset for unused fields / stale SAQL | 0 → 1 → 5 |
62
+
63
+ Execute only the phases in the selected path. Do not revisit this table mid-workflow.
64
+
65
+ ### Phase 0 — Bootstrap Scripts
66
+
67
+ 1. **Check for helper scripts** — before running any script referenced in this workflow, verify the `scripts/` directory exists in the skill root. All 11 scripts listed in `references/scripts-reference.md` are bundled with this skill. If a script is somehow missing, check that the skill was installed from the full repo (not a partial clone). Do not regenerate scripts from the descriptions — the implementations are authoritative.
68
+
69
+ ### Phase 1 — Setup
70
+
71
+ 2. **Verify org connection** — run `sf org display -o <alias> --json`. Capture `accessToken` and `instanceUrl`. If EPERM error, shell is sandboxed — re-run with full permissions.
72
+
73
+ 3. **Look up or create the CRMA app** — run `scripts/create_app.js <alias> <appName>`. The script lists existing folders and only creates if missing. Do NOT include `assetIcon`.
74
+
75
+ ### Phase 2 — Recipe Authoring
76
+
77
+ 4. **Read the R3 node schema** — load `references/r3-node-schema.md` before writing any recipe JSON. Every node shape, critical field rule, and formula gotcha is documented there.
78
+
79
+ 5. **Author the recipe JSON** — the `.wdpr` file contains only the `recipeDefinition` object (the `nodes` map, `runMode`, and `ui` section). Do NOT wrap it in an API envelope (`fileFormat`, `label`, `name`, `recipe: {}`) — the deploy script reads this file directly as the `recipeDefinition` body; wrapping it causes the wrong JSON structure to be POSTed at `?format=R3`, leaving `targetDataflowId` null. Save to `force-app/main/default/wave/<RecipeName>.wdpr`. Key rules:
80
+ - `runMode` must be `"full"` for generated recipes — NOT `"R3"` (which is only used as a query param on the API endpoint). Use `"incremental"` instead of `"full"` for large objects where reprocessing the entire source on every run is too slow; use `"streaming"` for near-real-time pipelines. `"full"` is the safe default for new recipes.
81
+ - Use `sources` (array), never `source` (singular)
82
+ - One field per formula node — chain them in sequence
83
+ - Use `expressionType: "SQL"` and `type: "NUMBER"` or `type: "TEXT"` on formula fields
84
+ - For text date dimensions use `date_format(field, 'yyyy')` or `date_format(field, 'MM/yyyy')`
85
+ - Formula node action is `"formula"` — NOT `"computeExpression"`, `"augmentColumns"`, or `"transform"`. Fields array key is `"fields"` (NOT `"columns"`). Expression key is `"formulaExpression"` (NOT `"expression"`). Do NOT use backtick quotes around field names in expressions.
86
+ - Insert an `EXTRACT0` (`extractGrains`) node between the last formula node and aggregate
87
+ - Use the Designer-native `ui` section format — read `references/ui-section-template.md`
88
+
89
+ **CRITICAL — top-level `.wdpr` file structure.** `nodes` is a ROOT-LEVEL object with UPPERCASE string keys. Do NOT put nodes inside `ui`. Do NOT use an array for `ui.nodes`. The file has exactly three top-level keys: `runMode`, `nodes`, `ui`. See `examples/wdpr-skeleton.wdpr` for a minimal 5-node skeleton (load → formula → extractGrains → aggregate → save).
90
+
91
+ See the wrong-variant reference table in `references/r3-node-schema.md → "Common wrong variants"` for the full list of correct vs incorrect field names per node type.
92
+
93
+ 6. **Write SFDX metadata wrappers** — every `.wdpr` file MUST be accompanied by a `.wdpr-meta.xml` in the same directory. These are always created as a pair — outputting the recipe JSON without its metadata wrapper is incomplete. Use the template at `assets/wdpr-meta-template.xml`. Required fields: `<application>`, `<masterLabel>`, `<targetDatasetAlias>`, `<dataflow>` (set to the same value as `<targetDatasetAlias>`). Do NOT add `<description>`, `<label>`, `<accessType>`, `<shareType>`, or `<format>` — each triggers a deploy failure. Similarly, every `.wapp` requires a `.wapp-meta.xml`.
94
+
95
+ 7. **Deploy recipe via REST** — run `scripts/deploy_and_run_recipe.js <alias> <recipeFile.wdpr> <appName> <recipeName>`. This script: looks up folder id → POSTs or PATCHes recipe at `?format=R3` → asserts `targetDataflowId` returned → POSTs to `/wave/dataflowjobs` → polls until terminal.
96
+
97
+ 8. **Verify recipe ran** — terminal statuses: `Success`, `Failure`, `Warning`, `Cancelled`. On `Failure`, run `scripts/check_connector_error.sh <alias> <connectorId>` — exits 0 for `System` errors (safe to retry), exits 1 for `User`/`Limit` errors (do not retry; fix config first). If retries persist on a `System` error, run `POST /wave/dataConnectors/{id}/ingest` to force a connector refresh.
98
+
99
+ ### Phase 3 — Dataset Validation
100
+
101
+ 9. **Upload synthetic data** — run `scripts/gen_synthetic_dataset.js` (schema-driven, reads live XMD) then `scripts/upload_dataset.js`. Read `references/external-data-upload.md` for InsightsExternalData rules.
102
+
103
+ 10. **Verify with SAQL** — run `scripts/verify_synthetic_dataset.js`. A 200 on `Action=Process` only means ingestion was queued — wait for `Status=Completed`, then verify with a count query.
104
+
105
+ ### Phase 4 — Dashboard Authoring
106
+
107
+ 11. **Read dashboard rules** — load `references/dashboard-authoring.md` before writing any `.wdash`. Covers the two-step deploy+PATCH workflow, accepted widget parameters per type, and the SAQL re-encoding trap.
108
+
109
+ 12. **Build minimal `.wdash`** — use `assets/dashboard-template.json` as the starting structure. Keep widget parameters minimal at deploy time (see `references/dashboard-authoring.md → "Deploy-time rules"`). Deploy with `sf project deploy start`. When a listselector must drive another step's SAQL (not a global facet), put a `{{column(step.selection, ["Field"]).asEquality("TargetField")}}` binding in that step's query and set `broadcastFacet`/`useGlobal` to `false` on both steps — see `references/dashboard-authoring.md → "Query bindings"`.
110
+
111
+ 13. **Apply styling via PATCH** — after deploy, `PATCH /wave/dashboards/{id}` with enriched state. **Always `decodeAll` every `state.steps[*].query` immediately after GET, before any mutation + PATCH.** This is the #1 silent failure mode: the API HTML-encodes SAQL on write (`"` → `&quot;`). GET → PATCH without decoding encodes again (`&amp;quot;`, then `&amp;amp;quot;`) and every widget renders Error 119. The helper is in `references/dashboard-authoring.md`. Use `scripts/patch_t_oppo_view.js` as the canonical GET → decodeAll → merge → PATCH template.
112
+
113
+ 14. **Verify all steps render** — before running, edit the `DASH_ID` and `DS` constants at the top of `scripts/verify_dashboard_steps.js` to the deployed dashboard's 18-char ID and the dataset's current ID/versionId. Then run `scripts/verify_dashboard_steps.js <alias>`. For each step: GET stored SAQL → decode → swap dataset name for `id/currentVersionId` → POST to `/wave/query`. Confirm 200 + rows. **Do not mark done until user confirms widgets render in the browser.**
114
+
115
+ ### Phase 5 — Dataset Hygiene
116
+
117
+ 15. **Find unused fields** — run `scripts/find_unused_dataset_fields.js <alias> <DatasetName>`. Read `references/dataset-hygiene.md` for caveats (aggregateflex steps, multi-dataset SAQL, archived apps).
118
+
119
+ ---
120
+
121
+ ## Rules / Constraints
122
+
123
+ | Constraint | Rationale |
124
+ |-----------|-----------|
125
+ | Every `.wdpr` MUST be paired with a `.wdpr-meta.xml` in the same directory | Metadata wrapper is required for SFDX deploy; recipe JSON alone is an incomplete artifact |
126
+ | Always POST recipes at `?format=R3` with `recipeDefinition` in JSON body | Multipart upload creates recipe but never compiles it — `targetDataflowId` stays null |
127
+ | PATCH recipe with `?format=R3` JSON body only; never multipart PATCH | Multipart PATCH updates raw file but destroys compiled state → "tableModelInfo is null" |
128
+ | On `tableModelInfo is null`: DELETE then re-POST | Only re-POST triggers server-side compilation |
129
+ | Use `targetDataflowId` (02K...) on `/wave/dataflowjobs`, never recipe id (05v...) | `/wave/dataflowjobs` only accepts the dataflow id |
130
+ | One field per formula node | REST API accepts multiple but Recipe Designer breaks with "Can't Load the Recipe" |
131
+ | Use `column` not `col` in gridLayouts widget entries | `col` triggers `Unrecognized field "col"` on deploy |
132
+ | `widgetStyle.borderRadius` must be integer (e.g. `8`), not CSS string | String value rejected at PATCH |
133
+ | Use name-based dataset load in stored SAQL, not id/version | id/version load requires a `datasets` binding the PATCH input rep rejects |
134
+ | Always decode SAQL before PATCH — use `decodeAll` | GET returns HTML-encoded SAQL; re-encoding on every PATCH accumulates → `&amp;amp;quot;` |
135
+ | `valuestable` widget rejects all styling at PATCH — use `chart` with `visualizationType: "flatTable"` | chart widget accepts the full rich-styling param set |
136
+ | `POST /wave/dashboards` only creates retired classic dashboards | Use Metadata API deploy for modern grid dashboards |
137
+ | KPI total requires `group q by all` before `foreach` | Without it, `foreach` acts as a window function returning one row per source row |
138
+
139
+ ---
140
+
141
+ ## Gotchas
142
+
143
+ | Issue | Resolution |
144
+ |-------|------------|
145
+ | Recipe sits in `New` status, `targetDataflowId` null | Re-create via `POST /wave/recipes?format=R3` with `recipeDefinition` in JSON body |
146
+ | Formula type `"TEXT"` but expression returns number | Use `type: "NUMBER"` and `expressionType: "SQL"` — other type strings silently normalize to TEXT |
147
+ | Aggregate fails: `Can't find field _Year / _Month` | Auto-derived date helpers don't exist mid-pipeline; derive explicitly via `date_format` formula node |
148
+ | `400 errorCode 276` on POST recipe | Name already in use — PATCH the existing recipe instead |
149
+ | All widgets show Error after PATCH | SAQL re-encoding — run `decodeAll` on every step query before PATCH |
150
+ | `listselector` list text invisible on dark bg | Set `filterStyle.valueColor` — it defaults to dark; always set a contrasting color |
151
+ | `date_format(CloseDate, 'MMM yyyy')` produces numeric month | Use `'MM/yyyy'` for text dimensions — `MMM` silently returns integer in recipe formula context |
152
+
153
+ ---
154
+
155
+ ## Output Expectations
156
+
157
+ - `force-app/main/default/wave/<RecipeName>.wdpr` — Wave Recipe JSON (R3 format)
158
+ - `force-app/main/default/wave/<AppName>.wapp-meta.xml` — app metadata wrapper
159
+ - `force-app/main/default/wave/<RecipeName>.wdpr-meta.xml` — recipe metadata wrapper
160
+ - `force-app/main/default/wave/<DashboardName>.wdash` — dashboard state JSON
161
+ - `force-app/main/default/wave/<DashboardName>.wdash-meta.xml` — dashboard metadata wrapper
162
+
163
+ ---
164
+
165
+ ## Reusable Scripts
166
+
167
+ All scripts accept org alias as first positional arg. Read `references/scripts-reference.md` for full descriptions and usage.
168
+
169
+ | Script | Purpose |
170
+ |--------|---------|
171
+ | `scripts/create_app.js` | Idempotently create a CRMA app folder |
172
+ | `scripts/deploy_and_run_recipe.js` | Create + compile + run recipe end-to-end |
173
+ | `scripts/probe_recipe.js` | Fetch recipe compile status + `targetDataflowId` |
174
+ | `scripts/fetch_dataset_xmd.js` | Dump dataset XMD (dimensions/measures/dates) |
175
+ | `scripts/gen_synthetic_dataset.js` | Schema-driven synthetic data generator |
176
+ | `scripts/upload_dataset.js` | Upload CSV via InsightsExternalData API |
177
+ | `scripts/verify_synthetic_dataset.js` | Post-upload SAQL sanity checks |
178
+ | `scripts/find_unused_dataset_fields.js` | List XMD fields not referenced in any dashboard SAQL |
179
+ | `scripts/verify_dashboard_steps.js` | POST each step's SAQL to `/wave/query` and confirm rows |
180
+ | `scripts/patch_t_oppo_view.js` | Full-cycle PATCH template with dark theme and `decodeAll` |
181
+ | `scripts/check_connector_error.sh` | Check connector `errorCategory`; exits 0=System (retryable), 1=User/Limit (do not retry) |
182
+
183
+ ---
184
+
185
+ ## Cross-Skill Integration
186
+
187
+ | Need | Delegate to |
188
+ |------|-------------|
189
+ | Deploying Wave metadata via SFDX project deploy | Use standard `sf project deploy` — no skill needed |
190
+ | LWC components that consume Wave datasets | Use an appropriate LWC skill |
191
+
192
+ ---
193
+
194
+ ## Reference File Index
195
+
196
+ | File | When to read |
197
+ |------|-------------|
198
+ | `references/method-decision-tree.md` | Phase 1 — before choosing API path |
199
+ | `references/r3-node-schema.md` | Phase 2 — before writing any recipe node |
200
+ | `references/ui-section-template.md` | Phase 2 — when building the Designer-native `ui` section |
201
+ | `assets/wapp-meta-template.xml` | Phase 2 — when creating `.wapp-meta.xml` |
202
+ | `assets/wdpr-meta-template.xml` | Phase 2 — when creating `.wdpr-meta.xml` |
203
+ | `references/external-data-upload.md` | Phase 3 — InsightsExternalData rules and common errors |
204
+ | `references/dashboard-authoring.md` | Phase 4 — full dashboard deploy+PATCH rules and re-encoding trap |
205
+ | `assets/wdash-meta-template.xml` | Phase 4 — when creating `.wdash-meta.xml` |
206
+ | `assets/dashboard-template.json` | Phase 4 — minimal deploy-valid `.wdash` starting structure |
207
+ | `references/dataset-hygiene.md` | Phase 5 — unused field detection and SAQL re-encoding audit |
208
+ | `references/scripts-reference.md` | Any phase — full script descriptions and usage |
209
+ | `examples/wdpr-skeleton.wdpr` | Phase 2 — minimal 5-node skeleton (load → formula → extractGrains → aggregate → save) |
210
+ | `examples/opportunity-revenue-recipe.wdpr` | Phase 2 — fully annotated example recipe with all node types |
211
+ | `examples/pipeline-by-industry-quarter.wdpr` | Phase 2 — kitchen-sink join + formula chain + extractGrains + case/coalesce/quarter |
212
+ | `examples/industry-binding-dashboard.wdash` | Phase 4 — listselector driving a SAQL filter via `column().asEquality()` |
@@ -0,0 +1,66 @@
1
+ {
2
+ "dataSourceLinksInfo": {},
3
+ "gridLayouts": [{
4
+ "name": "default",
5
+ "numColumns": 24,
6
+ "pages": [{
7
+ "label": "Page 1",
8
+ "name": "Page_1",
9
+ "navigationHidden": false,
10
+ "widgets": [
11
+ { "name": "wgt_kpi", "row": 0, "column": 6, "colspan": 18, "rowspan": 4 },
12
+ { "name": "wgt_filter", "row": 0, "column": 0, "colspan": 6, "rowspan": 4 },
13
+ { "name": "wgt_chart", "row": 4, "column": 0, "colspan": 24, "rowspan": 9 },
14
+ { "name": "wgt_table", "row": 13, "column": 0, "colspan": 24, "rowspan": 9 }
15
+ ]
16
+ }]
17
+ }],
18
+ "widgets": {
19
+ "wgt_filter": { "type": "listselector", "parameters": { "step": "step_filter" } },
20
+ "wgt_kpi": { "type": "number", "parameters": { "step": "step_kpi", "measureField": "{MEASURE_FIELD}" } },
21
+ "wgt_chart": { "type": "chart", "parameters": { "step": "step_chart", "visualizationType": "hbar" } },
22
+ "wgt_table": { "type": "chart", "parameters": { "step": "step_table", "visualizationType": "flatTable" } }
23
+ },
24
+ "steps": {
25
+ "step_filter": {
26
+ "type": "saql",
27
+ "query": "q = load \"{DATASET_NAME}\"; q = foreach q generate '{FILTER_FIELD}' as '{FILTER_FIELD}'; q = limit q 1000;",
28
+ "broadcastFacet": true,
29
+ "useGlobal": true,
30
+ "selectMode": "multi",
31
+ "groups": [],
32
+ "numbers": [],
33
+ "strings": []
34
+ },
35
+ "step_kpi": {
36
+ "type": "saql",
37
+ "query": "q = load \"{DATASET_NAME}\"; q = group q by all; q = foreach q generate sum('{MEASURE_FIELD}') as '{MEASURE_FIELD}';",
38
+ "broadcastFacet": true,
39
+ "useGlobal": true,
40
+ "selectMode": "single",
41
+ "groups": [],
42
+ "numbers": [],
43
+ "strings": []
44
+ },
45
+ "step_chart": {
46
+ "type": "saql",
47
+ "query": "q = load \"{DATASET_NAME}\"; q = group q by '{DIMENSION_FIELD}'; q = foreach q generate '{DIMENSION_FIELD}' as '{DIMENSION_FIELD}', sum('{MEASURE_FIELD}') as '{MEASURE_FIELD}'; q = order q by '{MEASURE_FIELD}' desc; q = limit q 20;",
48
+ "broadcastFacet": true,
49
+ "useGlobal": true,
50
+ "selectMode": "single",
51
+ "groups": [],
52
+ "numbers": [],
53
+ "strings": []
54
+ },
55
+ "step_table": {
56
+ "type": "saql",
57
+ "query": "q = load \"{DATASET_NAME}\"; q = group q by '{DIMENSION_FIELD}'; q = foreach q generate '{DIMENSION_FIELD}' as '{DIMENSION_FIELD}', sum('{MEASURE_FIELD}') as '{MEASURE_FIELD}'; q = order q by '{MEASURE_FIELD}' desc; q = limit q 100;",
58
+ "broadcastFacet": true,
59
+ "useGlobal": true,
60
+ "selectMode": "single",
61
+ "groups": [],
62
+ "numbers": [],
63
+ "strings": []
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <WaveApplication xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <folder>{FOLDER_DEVELOPER_NAME}</folder>
4
+ <masterLabel>{DISPLAY_LABEL}</masterLabel>
5
+ <shares>
6
+ <accessLevel>Manage</accessLevel>
7
+ <sharedTo>{USER_EMAIL}</sharedTo>
8
+ <sharedToType>User</sharedToType>
9
+ </shares>
10
+ </WaveApplication>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <WaveDashboard xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <application>{FOLDER_DEVELOPER_NAME}</application>
4
+ <masterLabel>{DASHBOARD_DISPLAY_LABEL}</masterLabel>
5
+ <description>{OPTIONAL_DESCRIPTION}</description>
6
+ </WaveDashboard>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <WaveRecipe xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <application>{FOLDER_DEVELOPER_NAME}</application>
4
+ <masterLabel>{RECIPE_DISPLAY_LABEL}</masterLabel>
5
+ <targetDatasetAlias>{OUTPUT_DATASET_NAME}</targetDatasetAlias>
6
+ <dataflow>{OUTPUT_DATASET_NAME}</dataflow>
7
+ </WaveRecipe>
8
+
9
+ <!-- DO NOT add: <description>, <label>, <accessType>, <shareType>, <format> -->
10
+ <!-- Each of these triggers a deploy failure -->
11
+ <!-- <dataflow> IS required — set it to the same value as <targetDatasetAlias> -->
@@ -0,0 +1,67 @@
1
+ {
2
+ "dataSourceLinksInfo": {},
3
+ "gridLayouts": [
4
+ {
5
+ "name": "default",
6
+ "numColumns": 24,
7
+ "pages": [
8
+ {
9
+ "label": "Page 1",
10
+ "name": "Page_1",
11
+ "navigationHidden": false,
12
+ "widgets": [
13
+ { "name": "wgt_industry_filter", "row": 0, "column": 0, "colspan": 6, "rowspan": 4 },
14
+ { "name": "wgt_kpi", "row": 0, "column": 6, "colspan": 18, "rowspan": 4 },
15
+ { "name": "wgt_accounts", "row": 4, "column": 0, "colspan": 24, "rowspan": 9 }
16
+ ]
17
+ }
18
+ ]
19
+ }
20
+ ],
21
+ "widgets": {
22
+ "wgt_industry_filter": {
23
+ "type": "listselector",
24
+ "parameters": { "step": "step_industry_sel" }
25
+ },
26
+ "wgt_kpi": {
27
+ "type": "number",
28
+ "parameters": { "step": "step_kpi", "measureField": "TotalRevenue" }
29
+ },
30
+ "wgt_accounts": {
31
+ "type": "chart",
32
+ "parameters": { "step": "step_accounts", "visualizationType": "hbar" }
33
+ }
34
+ },
35
+ "steps": {
36
+ "step_industry_sel": {
37
+ "type": "saql",
38
+ "query": "q = load \"DTS_PipelineByIndustry\"; q = group q by 'Account.Industry'; q = foreach q generate 'Account.Industry' as 'Industry'; q = order q by 'Industry' asc;",
39
+ "broadcastFacet": false,
40
+ "useGlobal": false,
41
+ "selectMode": "single",
42
+ "groups": [],
43
+ "numbers": [],
44
+ "strings": []
45
+ },
46
+ "step_kpi": {
47
+ "type": "saql",
48
+ "query": "q = load \"DTS_PipelineByIndustry\"; q = filter q by {{column(step_industry_sel.selection, [\"Industry\"]).asEquality(\"Account.Industry\")}}; q = group q by all; q = foreach q generate sum('TotalRevenue') as 'TotalRevenue';",
49
+ "broadcastFacet": false,
50
+ "useGlobal": false,
51
+ "selectMode": "single",
52
+ "groups": [],
53
+ "numbers": [],
54
+ "strings": []
55
+ },
56
+ "step_accounts": {
57
+ "type": "saql",
58
+ "query": "q = load \"DTS_PipelineByIndustry\"; q = filter q by {{column(step_industry_sel.selection, [\"Industry\"]).asEquality(\"Account.Industry\")}}; q = group q by 'Account.Name'; q = foreach q generate 'Account.Name' as 'AccountName', sum('TotalRevenue') as 'TotalRevenue'; q = order q by 'TotalRevenue' desc; q = limit q 10;",
59
+ "broadcastFacet": false,
60
+ "useGlobal": false,
61
+ "selectMode": "single",
62
+ "groups": [],
63
+ "numbers": [],
64
+ "strings": []
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,132 @@
1
+ {
2
+ "runMode": "full",
3
+ "nodes": {
4
+ "LOAD_OPPORTUNITY": {
5
+ "action": "load",
6
+ "sources": [],
7
+ "parameters": {
8
+ "dataset": {
9
+ "type": "connectedDataset",
10
+ "label": "Opportunity",
11
+ "connectionName": "SFDC_LOCAL",
12
+ "sourceObjectName": "Opportunity"
13
+ },
14
+ "fields": ["Id", "AccountId", "StageName", "CloseDate", "Amount"],
15
+ "mode": "SYNCED",
16
+ "preserveCurrencyFields": [],
17
+ "sampleDetails": { "type": "TopN", "sortBy": [] }
18
+ }
19
+ },
20
+ "LOAD_ACCOUNT": {
21
+ "action": "load",
22
+ "sources": [],
23
+ "parameters": {
24
+ "dataset": {
25
+ "type": "connectedDataset",
26
+ "label": "Account",
27
+ "connectionName": "SFDC_LOCAL",
28
+ "sourceObjectName": "Account"
29
+ },
30
+ "fields": ["Id", "Name", "Industry"],
31
+ "mode": "SYNCED",
32
+ "preserveCurrencyFields": [],
33
+ "sampleDetails": { "type": "TopN", "sortBy": [] }
34
+ }
35
+ },
36
+ "FILTER_CLOSED_WON": {
37
+ "action": "filter",
38
+ "sources": ["LOAD_OPPORTUNITY"],
39
+ "parameters": {
40
+ "filterExpressions": [
41
+ { "field": "StageName", "operator": "EQUAL", "operands": ["Closed Won"], "type": "TEXT" }
42
+ ]
43
+ }
44
+ },
45
+ "JOIN_ACCOUNT": {
46
+ "action": "join",
47
+ "sources": ["FILTER_CLOSED_WON", "LOAD_ACCOUNT"],
48
+ "parameters": {
49
+ "joinType": "LOOKUP",
50
+ "leftKeys": ["AccountId"],
51
+ "rightKeys": ["Id"],
52
+ "rightQualifier": "Account"
53
+ }
54
+ },
55
+ "COMPUTE_CLOSE_YEAR": {
56
+ "action": "formula",
57
+ "sources": ["JOIN_ACCOUNT"],
58
+ "parameters": {
59
+ "expressionType": "SQL",
60
+ "fields": [{ "name": "CloseYear", "label": "Close Year", "type": "TEXT", "formulaExpression": "date_format(CloseDate, 'yyyy')" }]
61
+ }
62
+ },
63
+ "COMPUTE_CLOSE_MONTH": {
64
+ "action": "formula",
65
+ "sources": ["COMPUTE_CLOSE_YEAR"],
66
+ "parameters": {
67
+ "expressionType": "SQL",
68
+ "fields": [{ "name": "CloseMonth", "label": "Close Month", "type": "TEXT", "formulaExpression": "date_format(CloseDate, 'MM/yyyy')" }]
69
+ }
70
+ },
71
+ "EXTRACT0": {
72
+ "action": "extractGrains",
73
+ "sources": ["COMPUTE_CLOSE_MONTH"],
74
+ "parameters": { "grainExtractions": [] }
75
+ },
76
+ "AGGREGATE_BY_ACCOUNT_MONTH": {
77
+ "action": "aggregate",
78
+ "sources": ["EXTRACT0"],
79
+ "parameters": {
80
+ "aggregations": [{ "name": "TotalRevenue", "label": "Total Revenue", "action": "SUM", "source": "Amount" }],
81
+ "groupings": ["AccountId", "Account.Name", "Account.Industry", "CloseYear", "CloseMonth"],
82
+ "nodeType": "STANDARD",
83
+ "pivots": []
84
+ }
85
+ },
86
+ "OUTPUT_DTS_OPW": {
87
+ "action": "save",
88
+ "sources": ["AGGREGATE_BY_ACCOUNT_MONTH"],
89
+ "parameters": {
90
+ "dataset": {
91
+ "type": "analyticsDataset",
92
+ "label": "DTS_OpportunityRev",
93
+ "name": "DTS_OpportunityRev",
94
+ "folderName": "OpportunityInsights"
95
+ },
96
+ "fields": [],
97
+ "measuresToCurrencies": []
98
+ }
99
+ }
100
+ },
101
+ "ui": {
102
+ "nodes": {
103
+ "LOAD_OPPORTUNITY": { "label": "Opportunity", "type": "LOAD_DATASET", "top": 112, "left": 112, "parameters": { "sampleSize": 2000 } },
104
+ "LOAD_ACCOUNT": { "label": "Account", "type": "LOAD_DATASET", "top": 252, "left": 252, "parameters": { "sampleSize": 2000 } },
105
+ "FILTER_CLOSED_WON": { "label": "FILTER_CLOSED_WON", "type": "FILTER", "top": 112, "left": 252 },
106
+ "JOIN_ACCOUNT": { "label": "JOIN_ACCOUNT", "type": "JOIN", "top": 112, "left": 392 },
107
+ "TRANSFORM0": {
108
+ "label": "TRANSFORM0", "type": "TRANSFORM", "top": 112, "left": 532,
109
+ "graph": {
110
+ "COMPUTE_CLOSE_YEAR": { "parameters": { "type": "BASE_FORMULA_UI" } },
111
+ "COMPUTE_CLOSE_MONTH": { "parameters": { "type": "BASE_FORMULA_UI" } }
112
+ },
113
+ "connectors": [{ "source": "COMPUTE_CLOSE_YEAR", "target": "COMPUTE_CLOSE_MONTH" }]
114
+ },
115
+ "AGGREGATE0": {
116
+ "label": "AGGREGATE0", "type": "AGGREGATE", "top": 112, "left": 672,
117
+ "graph": { "EXTRACT0": null, "AGGREGATE_BY_ACCOUNT_MONTH": null },
118
+ "connectors": [{ "source": "EXTRACT0", "target": "AGGREGATE_BY_ACCOUNT_MONTH" }]
119
+ },
120
+ "OUTPUT_DTS_OPW": { "label": "OUTPUT_DTS_OPW", "type": "OUTPUT", "top": 112, "left": 812 }
121
+ },
122
+ "connectors": [
123
+ { "source": "LOAD_OPPORTUNITY", "target": "FILTER_CLOSED_WON" },
124
+ { "source": "FILTER_CLOSED_WON", "target": "JOIN_ACCOUNT" },
125
+ { "source": "LOAD_ACCOUNT", "target": "JOIN_ACCOUNT" },
126
+ { "source": "JOIN_ACCOUNT", "target": "TRANSFORM0" },
127
+ { "source": "TRANSFORM0", "target": "AGGREGATE0" },
128
+ { "source": "AGGREGATE0", "target": "OUTPUT_DTS_OPW" }
129
+ ],
130
+ "hiddenColumns": []
131
+ }
132
+ }