@salesforce/afv-skills 1.30.0 → 1.31.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 (30) hide show
  1. package/package.json +1 -1
  2. package/skills/data360-code-extension-generate/SKILL.md +15 -2
  3. package/skills/dx-pkg-post-install-configure/SKILL.md +184 -0
  4. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +198 -34
  5. package/skills/experience-ui-bundle-app-coordinate/scripts/check-hosting-target.sh +26 -0
  6. package/skills/experience-ui-bundle-app-coordinate/scripts/check-sfdx-project.sh +15 -0
  7. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +19 -8
  8. package/skills/experience-ui-bundle-metadata-generate/references/csp-metadata-format.md +2 -2
  9. package/skills/experience-ui-bundle-metadata-generate/scripts/check-api-version.sh +30 -0
  10. package/skills/platform-custom-field-generate/SKILL.md +12 -11
  11. package/skills/platform-custom-field-generate/references/advanced-picklists.md +17 -17
  12. package/skills/platform-custom-report-type-generate/SKILL.md +259 -0
  13. package/skills/platform-custom-report-type-generate/examples/AccountProjectsWithTasks.reportType-meta.xml +51 -0
  14. package/skills/platform-custom-report-type-generate/examples/AccountsWithIndustry.reportType-meta.xml +27 -0
  15. package/skills/platform-custom-report-type-generate/examples/AccountsWithProjects.reportType-meta.xml +44 -0
  16. package/skills/platform-custom-report-type-generate/references/category-values.md +32 -0
  17. package/skills/platform-custom-report-type-generate/references/errors-and-troubleshooting.md +24 -0
  18. package/skills/platform-dataspace-access-configure/SKILL.md +318 -0
  19. package/skills/platform-docs-get/SKILL.md +4 -0
  20. package/skills/platform-docs-get/scripts/extract_salesforce_doc.py +78 -13
  21. package/skills/platform-encryption-configure/SKILL.md +122 -0
  22. package/skills/platform-encryption-configure/assets/EncryptionKey.settings-meta.xml +17 -0
  23. package/skills/platform-encryption-configure/assets/PlatformEncryption.settings-meta.xml +15 -0
  24. package/skills/platform-encryption-configure/assets/encrypted-field.field-meta.xml +22 -0
  25. package/skills/platform-encryption-configure/examples/cache-only-keys.settings-meta.xml +11 -0
  26. package/skills/platform-encryption-configure/references/encryption-schemes.md +34 -0
  27. package/skills/platform-encryption-configure/references/key-models.md +34 -0
  28. package/skills/platform-encryption-configure/references/tenant-secret-lifecycle.md +23 -0
  29. package/skills/platform-encryption-configure/scripts/validate-encryption-metadata.sh +54 -0
  30. package/skills/platform-metadata-api-context-get/SKILL.md +13 -10
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+ # Check which hosting target is configured in .uibundle-meta.xml files
3
+
4
+ # Find all .uibundle-meta.xml files
5
+ META_FILES=$(find . -name "*.uibundle-meta.xml" -type f 2>/dev/null)
6
+
7
+ if [ -z "$META_FILES" ]; then
8
+ echo "ERROR: No .uibundle-meta.xml files found"
9
+ exit 1
10
+ fi
11
+
12
+ # Check for ExperienceSite target
13
+ if echo "$META_FILES" | xargs grep -q '<target>ExperienceSite</target>' 2>/dev/null; then
14
+ echo "ExperienceSite"
15
+ exit 0
16
+ fi
17
+
18
+ # Check for CustomApplication target
19
+ if echo "$META_FILES" | xargs grep -q '<target>CustomApplication</target>' 2>/dev/null; then
20
+ echo "CustomApplication"
21
+ exit 0
22
+ fi
23
+
24
+ # No valid target found
25
+ echo "ERROR: No valid hosting target found in .uibundle-meta.xml"
26
+ exit 1
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ # Check if sfdx-project.json exists and is valid JSON
3
+
4
+ if [ ! -f sfdx-project.json ]; then
5
+ echo 'ERROR: sfdx-project.json not found'
6
+ exit 1
7
+ fi
8
+
9
+ # Validate JSON format using python (more portable than node)
10
+ if ! python3 -c "import json; json.load(open('sfdx-project.json'))" 2>/dev/null; then
11
+ echo 'ERROR: sfdx-project.json is not valid JSON'
12
+ exit 1
13
+ fi
14
+
15
+ echo 'sfdx-project.json is valid'
@@ -1,8 +1,18 @@
1
1
  ---
2
2
  name: experience-ui-bundle-metadata-generate
3
- description: "MUST activate when the project contains a uiBundles/*/src/ directory and scaffolding a new UI bundle or app, or when editing ui-bundle.json, .uibundle-meta.xml, or CSP trusted site files. Use this skill when scaffolding with sf template generate ui-bundle, configuring ui-bundle.json (routing, headers, outputDir), or registering CSP Trusted Sites. Activate when the task involves files matching *.uibundle-meta.xml, ui-bundle.json, or cspTrustedSites/*.cspTrustedSite-meta.xml."
3
+ description: "Use this skill when adding a front-end React UI bundle to an existing project or configuring UI bundle metadata and config files. TRIGGER when: adding or scaffolding a new UI bundle inside a project that already exists; running sf template generate ui-bundle; editing ui-bundle.json routing, headers, or output directory; working with *.uibundle-meta.xml files; or registering CSP Trusted Sites, resolving blocked images or fonts or external API calls, or editing cspTrustedSites/*.cspTrustedSite-meta.xml files. DO NOT TRIGGER when: creating a brand-new Salesforce project from scratch, where the whole SFDX starter project (UI bundle plus Experience Site metadata and toolchain) is generated together (use experience-ui-bundle-project-generate)."
4
4
  metadata:
5
5
  version: "1.0"
6
+ minApiVersion: "51.0"
7
+ relatedSkills:
8
+ - "experience-ui-bundle-frontend-generate"
9
+ - "experience-ui-bundle-custom-app-generate"
10
+ - "experience-ui-bundle-site-generate"
11
+ cliTools:
12
+ - tool: ["sf"]
13
+ semver: ">=2.0.0"
14
+ - tool: ["jq"]
15
+ semver: ">=1.6"
6
16
  ---
7
17
 
8
18
  # UI Bundle Metadata
@@ -11,9 +21,9 @@ metadata:
11
21
 
12
22
  Use `sf template generate ui-bundle` to create new apps — not create-react-app, Vite, or other generic scaffolds.
13
23
 
14
- **Always pass `--template reactbasic`** to scaffold a React-based bundle.
15
-
16
- **UI bundle name (`-n`):** Alphanumerical only no spaces, hyphens, underscores, or special characters.
24
+ - **Always pass `--template reactbasic`** to scaffold a React-based bundle.
25
+ - **UI bundle name (`-n`):** Alphanumerical only — no spaces, hyphens, underscores, or special characters.
26
+ - Pass `--output-dir` to use a different location for template generation.
17
27
 
18
28
  **Example:**
19
29
  ```bash
@@ -21,10 +31,11 @@ sf template generate ui-bundle -n CoffeeBoutique --template reactbasic
21
31
  ```
22
32
 
23
33
  After generation:
24
- 1. Replace all default boilerplate "React App", "Vite + React", default `<title>`, placeholder text
25
- 2. Populate the home page with real content (landing section, banners, hero, navigation)
26
- 3. Update navigation and placeholders (see the `experience-ui-bundle-frontend-generate` skill)
27
- 4. **Configure a hosting target** a UI bundle without a `<target>` in its meta XML will not be visible in the org. Use `experience-ui-bundle-custom-app-generate` for internal (App Launcher) apps or `experience-ui-bundle-site-generate` for external (Experience Site) apps.
34
+ 1. **Verify API version**run `bash <skill_dir>/scripts/check-api-version.sh` from the project root to ensure `sourceApiVersion` in `sfdx-project.json` is 67.0 or higher. The script will automatically update it if needed.
35
+ 2. Replace all default boilerplate "React App", "Vite + React", default `<title>`, placeholder text
36
+ 3. Populate the home page with real content (landing section, banners, hero, navigation)
37
+ 4. Update navigation and placeholders (see the `experience-ui-bundle-frontend-generate` skill)
38
+ 5. **Configure a hosting target** — a UI bundle without a `<target>` in its meta XML will not be visible in the org. Use `experience-ui-bundle-custom-app-generate` for internal (App Launcher) apps or `experience-ui-bundle-site-generate` for external (Experience Site) apps.
28
39
 
29
40
  Always install dependencies before running any scripts in the UI bundle directory.
30
41
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## File location
4
4
 
5
- ```
5
+ ```text
6
6
  force-app/main/default/cspTrustedSites/{Name}.cspTrustedSite-meta.xml
7
7
  ```
8
8
 
@@ -256,7 +256,7 @@ Some services split resources across multiple subdomains. Create one CSP Trusted
256
256
 
257
257
  If the browser console shows a CSP error like:
258
258
 
259
- ```
259
+ ```text
260
260
  Refused to load the image 'https://example.com/image.png' because it violates
261
261
  the following Content Security Policy directive: "img-src 'self' ..."
262
262
  ```
@@ -0,0 +1,30 @@
1
+ #!/bin/bash
2
+ # Checks that sourceApiVersion in sfdx-project.json is 67.0 or higher.
3
+ # If lower, updates it to "67.0".
4
+
5
+ set -e
6
+
7
+ PROJECT_FILE="sfdx-project.json"
8
+
9
+ if [[ ! -f "$PROJECT_FILE" ]]; then
10
+ echo "ERROR: $PROJECT_FILE not found in current directory"
11
+ exit 1
12
+ fi
13
+
14
+ # Extract sourceApiVersion and check if it's below 67.0
15
+ version=$(jq -r '.sourceApiVersion // "0"' "$PROJECT_FILE")
16
+ is_below=$(jq --arg v "$version" -n '($v | split(".") | .[0] | tonumber) < 67')
17
+
18
+ if [[ "$is_below" == "true" ]]; then
19
+ echo "WARNING: sourceApiVersion is $version (< 67.0)"
20
+ echo "Updating $PROJECT_FILE to set sourceApiVersion to \"67.0\""
21
+
22
+ # Update the file
23
+ jq '.sourceApiVersion = "67.0"' "$PROJECT_FILE" > "$PROJECT_FILE.tmp"
24
+ mv "$PROJECT_FILE.tmp" "$PROJECT_FILE"
25
+
26
+ echo "OK: Updated sourceApiVersion to 67.0"
27
+ exit 0
28
+ fi
29
+
30
+ echo "OK: sourceApiVersion is $version (>= 67.0)"
@@ -4,6 +4,7 @@ description: "Use this skill when users need to create, generate, or validate Sa
4
4
  metadata:
5
5
  version: "1.0"
6
6
  minApiVersion: "51.0"
7
+ relatedSkills: ["platform-value-set-generate", "platform-validation-rule-generate"]
7
8
  ---
8
9
 
9
10
  # Salesforce Custom Field Generator and Validator
@@ -20,7 +21,7 @@ Every generated field must include these tags:
20
21
 
21
22
  | Attribute | Requirement | Notes |
22
23
  |-----------|-------------|-------|
23
- | `<fullName>` | Required | **Field** name only: derive from `<label>` — capitalize each word, replace spaces with `_`, append `__c`. Must start with a letter. E.g., label `Total Contract Value` → `Total_Contract_Value__c`. ⚠️ This rule is for the FIELD name. **Picklist VALUE `<fullName>` is different — keep it exactly as the user spelled it, spaces and all, no `__c`** (e.g. `Closed Won`, NOT `Closed_Won`). See [`references/advanced-picklists.md`](references/advanced-picklists.md) (ref §3). |
24
+ | `<fullName>` | Required | **Field** name only: derive from `<label>` — capitalize each word, replace spaces with `_`, append `__c`. Must start with a letter. E.g., label `Total Contract Value` → `Total_Contract_Value__c`. This rule is for the FIELD name. **Picklist VALUE `<fullName>` is different — keep it exactly as the user spelled it, spaces and all, no `__c`** (e.g. `Closed Won`, NOT `Closed_Won`). See [`references/advanced-picklists.md`](references/advanced-picklists.md) (ref §3). |
24
25
  | `<label>` | Required | The UI name (Title Case) |
25
26
  | `<description>` | Always include | Explain the business reason *why* this field exists. |
26
27
  | `<inlineHelpText>` | Always include | Actionable end-user guidance that adds value beyond the label (e.g., "Enter the value in USD including tax", not "The amount"). |
@@ -49,7 +50,7 @@ To ensure deployment success, follow these mathematical constraints:
49
50
 
50
51
  ### The "Fixed 255" Rule
51
52
 
52
- **TextArea: always include `<length>255</length>` exactly** this literal value is required by the Metadata API and **omitting it fails deployment**, even though the UI exposes no length control. Unlike every other type where `length` is a value you calculate, TextArea's is a fixed constant.
53
+ **TextArea: do NOT include `<length>`**the Metadata API fixes the length at 255 implicitly and **rejects an explicit `<length>` value** with "Can not specify 'length' for a CustomField of type TextArea". Omit `<length>` entirely; the field only needs `<fullName>`, `<label>`, and `<type>TextArea</type>`.
53
54
 
54
55
  ### Visible Lines
55
56
 
@@ -76,7 +77,7 @@ Mandatory for Long/Rich text and Multi-select picklists to control UI height.
76
77
  | Phone | `Phone` | Standardizes phone number formatting |
77
78
  | Picklist | `Picklist` | `valueSet` containing EITHER `valueSetDefinition` (inline) OR `valueSetName` (reference); `restricted` (see "Picklist `restricted` default" below; advanced cases in §3.4) |
78
79
  | Text | `Text` | `length` (Max 255) |
79
- | Text Area | `TextArea` | `<length>255</length>` |
80
+ | Text Area | `TextArea` | None — do NOT include `<length>`; the API fixes length at 255 implicitly |
80
81
  | Text (Long) | `LongTextArea` | `length`, `visibleLines` (default 3) |
81
82
  | Text (Rich) | `Html` | `length`, `visibleLines` (default 25) |
82
83
  | Time | `Time` | Stores time only (no date) |
@@ -112,7 +113,7 @@ Mandatory for Long/Rich text and Multi-select picklists to control UI height.
112
113
 
113
114
  ### 3.4 Advanced Picklists
114
115
 
115
- The inline `<valueSetDefinition>` above is the simple case. Full rules and worked ✅/❌
116
+ The inline `<valueSetDefinition>` above is the simple case. Full rules and worked correct/incorrect
116
117
  examples for everything below are in
117
118
  [`references/advanced-picklists.md`](references/advanced-picklists.md) — load it for any
118
119
  non-trivial picklist. Section numbers in parentheses below (e.g. "ref §1") point to that
@@ -166,12 +167,12 @@ Master-Detail fields have **strict attribute restrictions** that differ from Loo
166
167
 
167
168
  | Attribute | Master-Detail | Lookup |
168
169
  |-----------|---------------|--------|
169
- | `<required>` | FORBIDDEN | Optional |
170
- | `<deleteConstraint>` | FORBIDDEN (always CASCADE) | Required (`SetNull`, `Restrict`, `Cascade`) |
171
- | `<lookupFilter>` | FORBIDDEN | Optional |
172
- | `<relationshipOrder>` | Required (0 or 1) | Not applicable |
173
- | `<reparentableMasterDetail>` | Optional | Not applicable |
174
- | `<writeRequiresMasterRead>` | Optional | Not applicable |
170
+ | `<required>` | FORBIDDEN | Optional |
171
+ | `<deleteConstraint>` | FORBIDDEN (always CASCADE) | Required (`SetNull`, `Restrict`, `Cascade`) |
172
+ | `<lookupFilter>` | FORBIDDEN | Optional |
173
+ | `<relationshipOrder>` | Required (0 or 1) | Not applicable |
174
+ | `<reparentableMasterDetail>` | Optional | Not applicable |
175
+ | `<writeRequiresMasterRead>` | Optional | Not applicable |
175
176
 
176
177
  ### INCORRECT — Master-Detail with forbidden attributes:
177
178
 
@@ -451,7 +452,7 @@ Before generating CustomField XML, verify:
451
452
  - [ ] Is `precision ≤ 18`?
452
453
 
453
454
  ### Text Area Checks
454
- - [ ] For TextArea: Is `<length>255</length>` explicitly included?
455
+ - [ ] For TextArea: Is `<length>` **omitted**? (The API rejects an explicit `<length>` value on TextArea fields.)
455
456
  - [ ] For LongTextArea/Html: Is `<visibleLines>` set?
456
457
 
457
458
  ### Relationship Limit Checks
@@ -21,7 +21,7 @@ A picklist field can either define its values **inline** or **reference an exist
21
21
  value set** (a Global Value Set or a Standard Value Set). The shared set is defined
22
22
  once and reused across many fields.
23
23
 
24
- ### HARD RULE: `<valueSet>` is EITHER a reference OR inline — never both
24
+ ### HARD RULE: `<valueSet>` is EITHER a reference OR inline — never both
25
25
 
26
26
  A `<valueSet>` element must contain **exactly one** of:
27
27
 
@@ -30,7 +30,7 @@ A `<valueSet>` element must contain **exactly one** of:
30
30
 
31
31
  Including both in the same `<valueSet>` is a deployment error.
32
32
 
33
- #### INCORRECT — both reference and inline definition:
33
+ #### INCORRECT — both reference and inline definition:
34
34
 
35
35
  ```xml
36
36
  <CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
@@ -54,7 +54,7 @@ Including both in the same `<valueSet>` is a deployment error.
54
54
 
55
55
  **Error:** `Value set must reference a value set name or define a value set, but not both.`
56
56
 
57
- #### CORRECT — reference a Global Value Set:
57
+ #### CORRECT — reference a Global Value Set:
58
58
 
59
59
  ```xml
60
60
  <CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
@@ -88,7 +88,7 @@ the name you put inside it differs.
88
88
  > local project" warning) is expected org-storage display — keep local metadata on the bare name.
89
89
  > Never append `__c` to a value-set name either.
90
90
 
91
- #### CORRECT — reference a Standard Value Set (bare name, no suffix):
91
+ #### CORRECT — reference a Standard Value Set (bare name, no suffix):
92
92
 
93
93
  ```xml
94
94
  <CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
@@ -126,7 +126,7 @@ A **dependent** picklist filters its available values based on the selected valu
126
126
  **dependent** field via a `<controllingField>` element plus one `<valueSettings>` block
127
127
  per (controlling value → dependent value) pair.
128
128
 
129
- ### Use the MODERN API 38.0+ form ONLY
129
+ ### Use the MODERN API 38.0+ form ONLY
130
130
 
131
131
  | Form | Elements | Status |
132
132
  |------|----------|--------|
@@ -136,7 +136,7 @@ per (controlling value → dependent value) pair.
136
136
  Never emit the legacy `<picklist>`, `<picklistValues>`, or `<controllingFieldValues>`
137
137
  tags. They are not valid against the modern Metadata API and will fail deployment.
138
138
 
139
- ### HARD RULE: both the controlling and dependent field must be `<restricted>true</restricted>`
139
+ ### HARD RULE: both the controlling and dependent field must be `<restricted>true</restricted>`
140
140
 
141
141
  A field dependency requires a fixed, admin-defined value set on **both** ends. **Always emit
142
142
  `<restricted>true</restricted>` inside the `<valueSet>` of the controlling field AND the
@@ -186,7 +186,7 @@ Inside the dependent field's `<valueSet>`, in this order:
186
186
  > EMEA→UK,Germany) you emit **one block for each (controllingValue, dependentValue) pair** — four
187
187
  > pairs = four `<valueSettings>` blocks. And remember: both fields carry `<restricted>true</restricted>`.
188
188
 
189
- ### CORRECT — State dependent on Country (USA → California, Texas)
189
+ ### CORRECT — State dependent on Country (USA → California, Texas)
190
190
 
191
191
  **Controlling field — `Country__c` (a plain restricted picklist):**
192
192
 
@@ -258,7 +258,7 @@ Inside the dependent field's `<valueSet>`, in this order:
258
258
  > `<valueSettings>` block with that country's `<controllingFieldValue>` and
259
259
  > `<valueName>California</valueName>`.
260
260
 
261
- ### INCORRECT — deprecated legacy form:
261
+ ### INCORRECT — deprecated legacy form:
262
262
 
263
263
  ```xml
264
264
  <CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
@@ -282,7 +282,7 @@ legacy dependency elements are not valid in the modern `<valueSet>` structure.
282
282
 
283
283
  ## 3. Enhanced Value Attributes
284
284
 
285
- ### Value-name fidelity — do NOT underscore picklist value names
285
+ ### Value-name fidelity — do NOT underscore picklist value names
286
286
 
287
287
  A **picklist value's `<fullName>` is NOT a field API name** and must NOT be transformed.
288
288
  Use the value text **exactly as the user spelled it**, spaces and all. A value the user
@@ -315,7 +315,7 @@ Inline `<value>` entries (CustomValue subfields) support more than `<fullName>`,
315
315
 
316
316
  These are independent of `<default>` and `<label>` and may be combined freely.
317
317
 
318
- ### CORRECT — Status picklist with colors and an inactive value
318
+ ### CORRECT — Status picklist with colors and an inactive value
319
319
 
320
320
  ```xml
321
321
  <CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
@@ -367,7 +367,7 @@ The Metadata API rejects malformed picklist values at deploy time. Two common fa
367
367
  Two `<value>` entries with the same `<fullName>` inside one `<valueSetDefinition>` are
368
368
  rejected.
369
369
 
370
- #### INCORRECT — duplicate value:
370
+ #### INCORRECT — duplicate value:
371
371
 
372
372
  ```xml
373
373
  <valueSetDefinition>
@@ -398,7 +398,7 @@ The stricter "only alphanumerics and single underscores, no leading digit, no do
398
398
  or trailing underscore" rule applies to the *field* `<fullName>` (the `__c`-suffixed
399
399
  API name), not to picklist value fullNames.
400
400
 
401
- #### INCORRECT — invalid value API name:
401
+ #### INCORRECT — invalid value API name:
402
402
 
403
403
  ```xml
404
404
  <value>
@@ -410,7 +410,7 @@ API name), not to picklist value fullNames.
410
410
 
411
411
  **Error:** `Invalid fullName: must begin with a letter and use only alphanumeric characters and underscores`
412
412
 
413
- #### CORRECT:
413
+ #### CORRECT:
414
414
 
415
415
  ```xml
416
416
  <value>
@@ -454,7 +454,7 @@ include a `<fullName>`** element — the record type's developer name (e.g. `<fu
454
454
  matching the filename `Internal.recordType-meta.xml`. It's bare (no object prefix); the object
455
455
  comes from the `objects/<Object>/` folder path.
456
456
 
457
- ### STEP 1 — Decide if this object needs a BusinessProcess (do this BEFORE writing files)
457
+ ### STEP 1 — Decide if this object needs a BusinessProcess (do this BEFORE writing files)
458
458
 
459
459
  A record type on a **BusinessProcess-gated object — Opportunity, Lead, Case, or Solution —
460
460
  will NOT deploy without a `<businessProcess>` reference**, even when it only filters a *custom*
@@ -472,7 +472,7 @@ developer name instead of generating a new one (confirm via the grounding MCP's
472
472
  if available; otherwise generate a minimal one). For everything else, **do not invent a
473
473
  BusinessProcess** — adding one to a custom-object record type is wrong.
474
474
 
475
- #### CORRECT — Opportunity "Enterprise" record type, two deployable files
475
+ #### CORRECT — Opportunity "Enterprise" record type, two deployable files
476
476
 
477
477
  ```xml
478
478
  <!-- File 1: objects/Opportunity/businessProcesses/Enterprise_Sales_Process.businessProcess-meta.xml -->
@@ -519,7 +519,7 @@ BusinessProcess** — adding one to a custom-object record type is wrong.
519
519
  </RecordType>
520
520
  ```
521
521
 
522
- #### INCORRECT — BusinessProcess file emitted but NOT referenced (most common failure)
522
+ #### INCORRECT — BusinessProcess file emitted but NOT referenced (most common failure)
523
523
 
524
524
  ```xml
525
525
  <!-- File 1 (businessProcesses/Enterprise_Sales_Process...) was generated correctly, BUT -->
@@ -571,7 +571,7 @@ RecordType (filters the field's values; references the BusinessProcess)
571
571
  - For Opportunity/Lead/Case/Solution, the `<businessProcess>` must exist (same package or
572
572
  already in the org) before the RecordType.
573
573
 
574
- ### UI-sync gotcha — values may not auto-display after API deploy
574
+ ### UI-sync gotcha — values may not auto-display after API deploy
575
575
 
576
576
  When `<picklistValues>` are loaded via the Metadata API, the values are correctly associated
577
577
  under the hood, **but they may not automatically appear as "Selected Values" in the Record
@@ -0,0 +1,259 @@
1
+ ---
2
+ name: platform-custom-report-type-generate
3
+ description: "Use this skill when users need to create, generate, or validate Salesforce Custom Report Type metadata. Trigger when users mention custom report types, report types, CRTs, reporting frameworks, cross-object reports, report builder data sources, or ask to expose fields for reporting across related objects. Also use when users mention primary and related objects for reports, inner vs outer joins in reports, report type categories, or encounter deployment errors for .reportType-meta.xml files. Do NOT trigger for: running, editing, or filtering existing reports; creating report folders, dashboards, or list views; or general reporting questions that don't involve authoring a .reportType-meta.xml file."
4
+ metadata:
5
+ version: "1.0"
6
+ minApiVersion: "51.0"
7
+ ---
8
+
9
+ ## Specification
10
+
11
+ # Salesforce Custom Report Type Metadata Knowledge
12
+
13
+ ## Overview
14
+ Custom Report Types (CRTs) define the **data framework** for Salesforce reports. They specify a primary object, up to 3 related objects, the relationship (join) between them, and which fields are available in the report builder.
15
+
16
+ ## Purpose
17
+ - Enable reporting across custom objects and custom relationships not covered by standard report types
18
+ - Curate a focused set of fields for report builders (including fields reached via lookup)
19
+ - Control inner/outer join behavior to include or exclude primary records without related records
20
+
21
+ ## Configuration
22
+
23
+ **File extension:** `.reportType-meta.xml`. The file basename is the report type's developer name (e.g. `AccountsWithProjects.reportType-meta.xml`). Each CRT is a single file, not nested under an object folder.
24
+
25
+ ### Key Elements
26
+
27
+ Top-level `<ReportType>` children:
28
+
29
+ | Element | Required | Notes |
30
+ |---------|----------|-------|
31
+ | `<fullName>` | Yes | API identifier; must match the file name. Letters, numbers, underscores; must begin with a letter; no spaces; no trailing underscore; no consecutive underscores |
32
+ | `<label>` | Yes | Human-friendly name shown in the report type picker |
33
+ | `<description>` | Recommended | State the business "why" — who uses this and what they learn |
34
+ | `<baseObject>` | Yes | API name of the primary object (e.g. `Account`, `Project__c`). Cannot be changed after initial creation. All objects, including custom and external, are supported (external objects from API 38.0+) |
35
+ | `<category>` | Recommended | Report builder category — see `references/category-values.md` |
36
+ | `<deployed>` | Yes | `true` to expose to users; `false` while building/iterating |
37
+ | `<join>` | Conditional | Adds a related object and its join behavior. Nest further `<join>` blocks for deeper relationships |
38
+ | `<sections>` | Recommended | Groups of columns available to the report type. Though not strictly required, a report without columns isn't useful |
39
+
40
+ `<sections>` (group of columns) sub-elements:
41
+
42
+ | Element | Required | Notes |
43
+ |---------|----------|-------|
44
+ | `<masterLabel>` | Yes | Section heading shown in the report builder |
45
+ | `<columns>` | Conditional | One per field exposed in the section |
46
+
47
+ `<columns>` (single field) sub-elements:
48
+
49
+ | Element | Required | Notes |
50
+ |---------|----------|-------|
51
+ | `<field>` | Yes | Field API name (or dotted lookup-traversal path) |
52
+ | `<table>` | Yes | The object the field belongs to — base object name or dotted relationship path |
53
+ | `<checkedByDefault>` | Yes | `true` if the column is selected by default in the report builder |
54
+ | `<displayNameOverride>` | No | Custom column label shown in the report builder, overriding the field's default label |
55
+
56
+ ## Critical Rules (Read First)
57
+
58
+ ### Rule 1: If `<fullName>` Is Present, It Must Match the File Name
59
+ In source format, `fullName` is inherited from `Metadata` and derived from the file name, so the `<fullName>` element is technically optional. The repo convention is to include it. **If you include `<fullName>`, its value must equal the file name (everything before `.reportType-meta.xml`) exactly — same characters, same casing, same underscores.**
60
+
61
+ **Wrong** — file name and `<fullName>` differ:
62
+ - File: `account_projects.reportType-meta.xml`
63
+ - `<fullName>AccountProjects</fullName>`
64
+ (Mismatch: file uses `account_projects`, fullName uses `AccountProjects`)
65
+
66
+ **Right** — file name and `<fullName>` are identical:
67
+ - File: `AccountProjects.reportType-meta.xml`
68
+ - `<fullName>AccountProjects</fullName>`
69
+
70
+ ### Rule 2: Join Semantics — `outerJoin` Controls Inclusion
71
+
72
+ Each `<join>` block has an `<outerJoin>` element that determines which primary records appear in the report:
73
+
74
+ | `<outerJoin>` value | Behavior | Report Builder Label |
75
+ |---------------------|----------|----------------------|
76
+ | `false` | Inner join — only primary records that HAVE at least one related record | "Each 'A' record must have at least one related 'B' record" |
77
+ | `true` | Outer join — all primary records, with or without related records | "'A' records may or may not have related 'B' records" |
78
+
79
+ **Default when unspecified:** Use `true` (outer join) when the user wants to see all primary records regardless of children. Use `false` when the report only makes sense if children exist.
80
+
81
+ ### Rule 3: Each Object Needs Its Own `<sections>` Block
82
+
83
+ Every object in the CRT (primary + each joined object) must have a corresponding `<sections>` block that lists the fields exposed for reporting. Without a section for an object, none of its fields appear in the report builder.
84
+
85
+ - `<masterLabel>` on each section is the section heading in the report builder
86
+ - `<columns>` entries list the fields — each with a `<field>` (API name) and `<table>` (object API name)
87
+ - For fields reached via lookup, use the relationship path in `<field>` (e.g. `Owner.Name` with `<table>` set to the owning object)
88
+
89
+ ### Rule 4: Field API Names, Not Labels
90
+
91
+ Use exact API names for fields: standard fields use their defined names (`Name`, `CreatedDate`, `OwnerId`), custom fields use `Field__c`. Custom objects must include `__c`.
92
+
93
+ **Wrong:**
94
+ - `<field>Account Name</field>`
95
+
96
+ **Right:**
97
+ - `<field>Name</field>` with `<table>Account</table>`
98
+
99
+ ### Rule 5: Relationship Path for Joined Objects
100
+
101
+ When adding a `<join>`, the `<relationship>` element must use the **child relationship name** as defined on the lookup/master-detail field pointing from the child object to the parent. For custom relationships, this typically ends in `__r`.
102
+
103
+ **Wrong:**
104
+ - `<relationship>Project</relationship>` (for a custom child relationship)
105
+
106
+ **Right:**
107
+ - `<relationship>Projects__r</relationship>` (child relationship name)
108
+ - `<relationship>Contacts</relationship>` (standard, non-custom child relationship)
109
+
110
+ ### Rule 6: Maximum 4 Objects Total in a Join Chain
111
+
112
+ A single CRT can join a maximum of **four objects total** (the base object + up to 3 additional objects via nested `<join>` blocks).
113
+
114
+ ### Rule 7: No Inner Join After an Outer Join
115
+
116
+ Once the join chain contains an outer join (`<outerJoin>true</outerJoin>`), every subsequent nested join must also be an outer join. An inner join that follows an outer join earlier in the sequence is not allowed.
117
+
118
+ **Wrong:**
119
+ ```xml
120
+ <join>
121
+ <outerJoin>true</outerJoin> <!-- outer join first -->
122
+ <relationship>Contacts</relationship>
123
+ <join>
124
+ <outerJoin>false</outerJoin> <!-- WRONG: inner join after outer -->
125
+ <relationship>Assets</relationship>
126
+ </join>
127
+ </join>
128
+ ```
129
+
130
+ **Right:**
131
+ ```xml
132
+ <join>
133
+ <outerJoin>true</outerJoin>
134
+ <relationship>Contacts</relationship>
135
+ <join>
136
+ <outerJoin>true</outerJoin> <!-- outer stays outer -->
137
+ <relationship>Assets</relationship>
138
+ </join>
139
+ </join>
140
+ ```
141
+
142
+ ### Rule 8: `<table>` for Joined Objects Uses Dotted Path
143
+
144
+ In `<sections>`, the `<table>` element identifies which object in the join chain each column belongs to. For the base object, use the object name directly (e.g. `Account`). For joined objects, use the **dotted relationship path** from the base object.
145
+
146
+ | Object in chain | `<table>` value |
147
+ |-----------------|-----------------|
148
+ | Base (Account) | `Account` |
149
+ | First join (Account → Contacts) | `Account.Contacts` |
150
+ | Nested join (Account → Contacts → Assets) | `Account.Contacts.Assets` |
151
+
152
+ ### Rule 9: Field Paths Can Traverse Lookups
153
+
154
+ `<field>` values may reference fields reached via lookup relationships using dot notation — for example `Owner.Email` (owner User's email) or `ReportsTo.CreatedBy.Contact.Owner.MobilePhone`. The `<table>` must still be the object that owns the starting field.
155
+
156
+ ### Rule 10: Historical Trending Fields Use `_hst` Suffix
157
+
158
+ For a field with `trackTrending=true`, the API name in `<field>` and `<table>` uses the `_hst` suffix:
159
+
160
+ ```xml
161
+ <columns>
162
+ <checkedByDefault>false</checkedByDefault>
163
+ <field>Field2__c_hst</field>
164
+ <table>CustomTrendedObject__c.CustomTrendedObject__c_hst</table>
165
+ </columns>
166
+ ```
167
+
168
+ ### Rule 11: Primary Object Cannot Be Changed After Deployment
169
+
170
+ Once deployed, the `<baseObject>` of a CRT is locked. To change the primary object, create a new CRT and retire the old one.
171
+
172
+ ### Rule 12: `autogenerated` Is Reserved for Historical Trending
173
+
174
+ The `<autogenerated>` element (API 29.0+) marks CRTs that Salesforce created automatically when historical trending was enabled on an object. Do not set this manually on hand-authored CRTs.
175
+
176
+ ## Generation Workflow
177
+
178
+ ### Step 1: Gather Requirements
179
+ - Primary object API name (e.g. `Account`, `Project__c`)
180
+ - Related objects and the relationship between each (which has the lookup/master-detail to which)
181
+ - For each relationship: inner join (children required) or outer join (children optional)?
182
+ - Which fields to expose per object — aim for task-relevant, not the full field list
183
+ - Audience and category — where should this appear in the report builder picker?
184
+ - Whether this ships as `deployed=true` now or stays `deployed=false` during iteration
185
+
186
+ ### Step 2: Examine Existing Examples
187
+ - Look in the project for in-project CRT patterns
188
+ - If existing report types have been retrieved from an org, compare against those structures
189
+
190
+ ### Step 3: Write the Specification
191
+ Document before authoring:
192
+ - `fullName` and `label`
193
+ - `baseObject`
194
+ - Category and `deployed` state
195
+ - Join chain: for each related object — relationship name, outer vs inner join
196
+ - Section layout: one section per object, ordered list of fields
197
+ - Acceptance criteria: which records should appear when the report runs, which fields are available in the builder
198
+
199
+ ### Step 4: Author the Metadata File
200
+
201
+ Start from the closest example in `examples/` and adapt it to the user's scenario:
202
+
203
+ - Primary object only (no joins) → `examples/AccountsWithIndustry.reportType-meta.xml`
204
+ - Outer join (primary records included even without children) → `examples/AccountsWithProjects.reportType-meta.xml`
205
+ - Nested inner join (every level requires children) → `examples/AccountProjectsWithTasks.reportType-meta.xml`
206
+
207
+ Name the file `<DeveloperName>.reportType-meta.xml`.
208
+
209
+ ### Step 5: Validate
210
+ - Well-formed XML with correct namespace (`xmlns="http://soap.sforce.com/2006/04/metadata"`)
211
+ - File name (without `.reportType-meta.xml`) matches `<fullName>` when `<fullName>` is included
212
+ - `<baseObject>` is a valid API name and the object is deployed
213
+ - Every `<relationship>` uses the correct child relationship name (`__r` suffix for custom)
214
+ - Each object referenced in `<sections>` is part of the CRT (primary or joined)
215
+ - All `<field>` references exist on the parent `<table>` and use API names (not labels)
216
+ - `<category>` is a valid Salesforce category value
217
+ - `<deployed>` is `true` if users need to access the CRT immediately
218
+
219
+ ## Reference File Index
220
+
221
+ | File | When to read |
222
+ |------|--------------|
223
+ | `examples/AccountsWithIndustry.reportType-meta.xml` | Step 2 / Step 4 — primary-object-only template |
224
+ | `examples/AccountsWithProjects.reportType-meta.xml` | Step 2 / Step 4 — outer-join template (primary included even without children) |
225
+ | `examples/AccountProjectsWithTasks.reportType-meta.xml` | Step 2 / Step 4 — nested inner-join template (every level requires children) |
226
+ | `references/category-values.md` | Step 3 — to choose a valid `<category>` value from the `ReportTypeCategory` enum |
227
+ | `references/errors-and-troubleshooting.md` | When fields don't appear in the report builder or join requirements conflict |
228
+
229
+ ## Verification Checklist
230
+
231
+ ### Universal Checks
232
+ - [ ] File extension is `.reportType-meta.xml`
233
+ - [ ] File basename satisfies the developer-name rules (begins with a letter, only letters/numbers/underscores, no spaces, no trailing underscore, no consecutive underscores)
234
+ - [ ] If `<fullName>` is included, it matches the file basename exactly (same characters, casing, and underscores)
235
+ - [ ] `<label>` is human-readable and under 40 characters
236
+ - [ ] `<description>` explains the business purpose
237
+ - [ ] `<baseObject>` uses a valid API name and that object is deployed
238
+ - [ ] `<category>` is a valid `ReportTypeCategory` enum value
239
+ - [ ] `<deployed>` is set appropriately (`true` for user access, `false` for in-progress iteration)
240
+ - [ ] `<autogenerated>` is NOT set manually (reserved for historical-trending CRTs)
241
+
242
+ ### Join Checks
243
+ - [ ] Each `<join>` uses the correct child **relationship name** (not the lookup field API name)
244
+ - [ ] Custom relationships use `__r` suffix
245
+ - [ ] `<outerJoin>` is set intentionally: `true` = optional children, `false` = required children
246
+ - [ ] No inner join (`<outerJoin>false</outerJoin>`) appears after an outer join earlier in the sequence
247
+ - [ ] Total object count (base + joins, including nested) is 4 or fewer
248
+
249
+ ### Section Checks
250
+ - [ ] Every object in the CRT has a corresponding `<sections>` block
251
+ - [ ] `<masterLabel>` on each section is descriptive
252
+ - [ ] Every `<columns>` has both `<field>` (API name) and `<table>` (object API name or dotted path)
253
+ - [ ] `<checkedByDefault>` is set for each column
254
+ - [ ] `<table>` for base object is the object API name (e.g. `Account`)
255
+ - [ ] `<table>` for joined objects uses the dotted relationship path (e.g. `Account.Projects__r`, `Account.Projects__r.Tasks__r`)
256
+ - [ ] Field references use API names (not labels); custom fields use `__c`
257
+ - [ ] Lookup traversal fields use dot notation (e.g. `Owner.Email`) with `<table>` set to the object owning the starting field
258
+ - [ ] Historical trending fields use `_hst` suffix in both `<field>` and `<table>` when applicable
259
+ - [ ] No duplicate fields within a section