@salesforce/afv-skills 1.55.0 → 1.56.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 (220) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-generate/SKILL.md +8 -0
  3. package/skills/agentforce-generate/references/actions-reference.md +2 -2
  4. package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
  5. package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
  6. package/skills/agentforce-observe/SKILL.md +13 -9
  7. package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
  8. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
  9. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
  10. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
  11. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
  12. package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
  13. package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
  14. package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
  15. package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
  16. package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
  17. package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
  18. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
  19. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
  20. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
  21. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
  22. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
  23. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
  24. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
  25. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
  26. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
  27. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
  28. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
  29. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
  30. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
  31. package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
  32. package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
  33. package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
  34. package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
  35. package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
  36. package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
  37. package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
  38. package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
  39. package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
  40. package/skills/dx-org-analyze/SKILL.md +0 -1
  41. package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
  42. package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
  43. package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
  44. package/skills/dx-org-manage/SKILL.md +6 -50
  45. package/skills/dx-org-manage/examples/README.md +10 -16
  46. package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
  47. package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
  48. package/skills/dx-org-shape-manage/SKILL.md +4 -3
  49. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
  50. package/skills/experience-accessibility-validate/SKILL.md +8 -5
  51. package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
  52. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
  53. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
  54. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
  55. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
  56. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
  57. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
  58. package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
  59. package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
  60. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
  61. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
  62. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
  63. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
  64. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
  65. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
  66. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
  67. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
  68. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
  69. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
  70. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
  71. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
  72. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
  73. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
  74. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
  75. package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
  76. package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
  77. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
  78. package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
  79. package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
  80. package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
  81. package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
  82. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
  83. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
  84. package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
  85. package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
  86. package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
  87. package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
  88. package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
  89. package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
  90. package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
  91. package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
  92. package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
  93. package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
  94. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
  95. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
  96. package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
  97. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
  98. package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
  99. package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
  100. package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
  101. package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
  102. package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
  103. package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
  104. package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
  105. package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
  106. package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
  107. package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
  108. package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
  109. package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
  110. package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
  111. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
  112. package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
  113. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
  114. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
  115. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
  116. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
  117. package/skills/mobile-apps-create/SKILL.md +1 -1
  118. package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
  119. package/skills/platform-sandbox-configure/SKILL.md +108 -133
  120. package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
  121. package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
  122. package/skills/platform-widget-generate/SKILL.md +26 -18
  123. package/skills/platform-widget-generate/examples/formulas.json +191 -0
  124. package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
  125. package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
  126. package/skills/sales-call-scoring-configure/SKILL.md +318 -0
  127. package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
  128. package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
  129. package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
  130. package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
  131. package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
  132. package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
  133. package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
  134. package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
  135. package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
  136. package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
  137. package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
  138. package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
  139. package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
  140. package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
  141. package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
  142. package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
  143. package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
  144. package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
  145. package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
  146. package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
  147. package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
  148. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
  149. package/skills/service-de-channel-activate/SKILL.md +6 -5
  150. package/skills/service-de-channel-create/SKILL.md +3 -3
  151. package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
  152. package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
  153. package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
  154. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
  155. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
  156. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
  157. package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
  158. package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
  159. package/skills/service-helpagent-coordinate/SKILL.md +3 -2
  160. package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
  161. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
  162. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
  163. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
  164. package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
  165. package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
  166. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
  167. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
  168. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
  169. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
  170. package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
  171. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
  172. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
  173. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
  174. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
  175. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
  176. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
  177. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
  178. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
  179. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
  180. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
  181. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
  182. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
  183. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
  184. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
  185. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
  186. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
  187. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
  188. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
  189. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
  190. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
  191. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
  192. package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
  193. package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
  194. package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
  195. package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
  196. package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
  197. package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
  198. package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
  199. package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
  200. package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
  201. package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
  202. package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
  203. package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
  204. package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
  205. package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
  206. package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
  207. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
  208. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
  209. package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
  210. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  211. package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
  212. package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
  213. package/skills/dx-org-manage/references/cli_flags.md +0 -67
  214. package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
  215. package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
  216. package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
  217. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
  218. package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
  219. /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
  220. /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
@@ -20,7 +20,7 @@ Use this reference when the user wants to:
20
20
  5. [Enumerate `_mtc` metrics and screen for time-grain fit](#enumerate-_mtc-metrics-and-screen-for-time-grain-fit)
21
21
  6. [List the agents available for alert filters](#list-the-agents-available-for-alert-filters)
22
22
  7. [Create an alert](#create-an-alert) — POST vs GET field names, `filterContext`, thresholds, full schema, enums, troubleshooting
23
- 8. [Update an alert](#update-an-alert) (not yet available — delete + recreate)
23
+ 8. [Update an alert](#update-an-alert) — PUT in place
24
24
  9. [Delete an alert](#delete-an-alert)
25
25
  10. [Trigger history — inspect triggered notifications](#trigger-history--inspect-triggered-notifications)
26
26
  11. [Verify metric value via Semantic Engine Gateway](#verify-metric-value-via-semantic-engine-gateway-condensed)
@@ -100,13 +100,13 @@ AHM alerts are a special flavor of Salesforce data alerts — `dataAlertType: "a
100
100
 
101
101
  They are **not** served from `/wave/dataAlerts` (CRM Analytics) or `/analytics/dataAlerts`. The "tableau" namespace here is Tableau Next / Data Cloud analytics, distinct from classic Wave — `/tableau/dataAlerts` does **not** require Wave / CRM Analytics.
102
102
 
103
- | Method | Path | Purpose |
104
- |--------|------|---------|
103
+ | Method | Path | Purpose |
104
+ |--------|------|---------------------------------------------|
105
105
  | GET | `/services/data/v66.0/tableau/dataAlerts?ownerId={userId}` | List alerts for a user (requires `ownerId`) |
106
- | POST | `/services/data/v66.0/tableau/dataAlerts` | Create |
107
- | PUT | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Update (not yet available — see below) |
108
- | DELETE | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Delete |
109
- | GET | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Not supported — 405 Method Not Allowed |
106
+ | POST | `/services/data/v66.0/tableau/dataAlerts` | Create |
107
+ | PUT | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Update (same input representation as POST) |
108
+ | DELETE | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Delete |
109
+ | GET | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Not supported — 405 Method Not Allowed |
110
110
 
111
111
  Single-alert GET is not supported — list all and filter client-side. Omitting `ownerId` on the list GET returns `400 MISSING_PARAM: Owner ID cannot be empty`. There is no "list all alerts across the org" shape; always scope to a user.
112
112
 
@@ -602,7 +602,68 @@ Conventional encoded format the AHM UI parses (the backend accepts arbitrary str
602
602
 
603
603
  ## Update an alert
604
604
 
605
- The PUT API (`PUT /services/data/v66.0/tableau/dataAlerts/{alertId}`) is **not yet available** (expected later 2026). Until then, **delete** the existing alert and **create** a new one with the updated configuration.
605
+ `PUT /services/data/v66.0/tableau/dataAlerts/{alertId}` updates an alert **in place** — same `id`, no delete + recreate. It takes the **same input representation as POST** (PascalCase `type` discriminators, `utterance` not `alertName`, `RawValue`/`Metric`/`EveryNMinutes`, threshold as a raw-ratio string, etc. — see [Create an alert](#create-an-alert)). Send the **full** alert body, not a partial patch: the representation is replaced, so include every field you want to keep (schedule, content, all `thresholds.conditions`, `deliveryConfigurations`). Returns **HTTP 200** with the updated alert in GET-shape (lowercased enums); `id` is unchanged, `lastModifiedDate` is bumped.
606
+
607
+ > **Addressed by alert id.** Like `DELETE`, `PUT` targets the alert via its `{alertId}` in the **path** (`/tableau/dataAlerts/{alertId}`) — the id is *not* in the body. A metricId (`1HU…`) in the path 404s; use the alert id (`3VR…`).
608
+ >
609
+ > **Read-modify-write.** Fetch the current alert first (`GET tableau/dataAlerts?ownerId=$USER_ID`, filter by `id`), change only the fields you intend to, and PUT the whole thing back — translating the GET response's field names/casing to the POST input names (`alertName`→`utterance`, lowercase `type`s→PascalCase). Keep the existing `metricId` in the body: PUT preserves it (no sub-metric churn) as long as `filterContext` is unchanged.
610
+
611
+ ### Example: change a threshold (e.g. `80` → `85`)
612
+
613
+ Build the full body in a private temp file (see the `mktemp` idiom above), then PUT it. This mirrors the POST schema with the alert's current values, changing only `rightOperand.value` (and, optionally, `utterance`):
614
+
615
+ ```bash
616
+ ALERT_ID="3VRSG0000001uBR4AY"
617
+ body=$(mktemp) && chmod 600 "$body"
618
+ trap 'rm -f "$body"' EXIT
619
+ cat > "$body" <<'JSON'
620
+ {
621
+ "utterance": "Task Resolution Rate Less Than 85% for AgentRuntimeInsights262",
622
+ "dataAlertType": "agenthealthmonitoring",
623
+ "schedule": { "type": "EveryNMinutes", "minuteLevelFrequency": 10 },
624
+ "content": { "type": "Metric", "modelApiNameOrId": ["2SMSG000000bPXf4AM"] },
625
+ "thresholds": {
626
+ "conditions": [
627
+ {
628
+ "leftOperand": {
629
+ "type": "Metric",
630
+ "modelApiNameOrId": "2SMSG000000bPXf4AM",
631
+ "metricId": "1HUSG0000010Un74AE",
632
+ "insightType": "Popc",
633
+ "factKey": "FACT_KEY_TARGET_PERIOD_VALUE",
634
+ "params": {},
635
+ "filterContext": [],
636
+ "timeContext": { "operator": "LastNMinutes", "values": ["15"] }
637
+ },
638
+ "operator": "LessThan",
639
+ "rightOperand": { "type": "RawValue", "dataType": "Number", "value": "85" }
640
+ }
641
+ ],
642
+ "customLogicalOperation": "1"
643
+ },
644
+ "deliveryConfigurations": {
645
+ "receivers": [
646
+ { "type": "Notification", "recipients": ["005SG00000ZEQePYAX"] },
647
+ { "type": "Email", "recipients": ["005SG00000ZEQePYAX"] }
648
+ ]
649
+ }
650
+ }
651
+ JSON
652
+
653
+ sf api request rest "/services/data/v66.0/tableau/dataAlerts/$ALERT_ID" \
654
+ -X PUT -H "Content-Type: application/json" -b "@$body" -o <org> \
655
+ | python3 -m json.tool
656
+ ```
657
+
658
+ When the new name/threshold/recipients come from **data** rather than being typed by hand, do not string-substitute into the heredoc — reject values containing `"`/`\` then build the JSON with the same `python3` JSON-dump idiom shown under [Create an alert](#create-an-alert) (the injection risk is identical for PUT).
659
+
660
+ **Verified (org `ahm-test-notifications`, 2026-09-02):** PUT on alert `3VRSG0000001uBR4AY` with the POST-style body above returned HTTP 200, changed `rightOperand.value` `80`→`85` and `alertName`, kept the same `id` and `metricId` (`1HUSG0000010Un74AE`), and bumped `lastModifiedDate` — a true in-place update. Reverting to `80` via a second PUT worked identically.
661
+
662
+ ### Troubleshooting
663
+
664
+ - **Same `JSON_PARSER_ERROR` / casing / array-shape errors as POST** — the input grammar is identical; see [Create an alert → Troubleshooting](#troubleshooting).
665
+ - **`404` on the PUT** — confirm the `{alertId}` is a real alert `id` (`3VR…`) from the list; a metricId (`1HU…`) will 404. If the id is valid and it still 404s, retry at the org's current API version.
666
+ - **Fields silently reverting** — PUT replaces the representation; a field you omitted reverts to server default. Always send the full body (read-modify-write), not a partial patch.
606
667
 
607
668
  ---
608
669
 
@@ -619,28 +680,146 @@ Returns **HTTP 204** (No Content) on success — no response body. Use `--includ
619
680
 
620
681
  ## Trigger history — inspect triggered notifications
621
682
 
622
- When an alert's condition is met it generates a **system notification** and optionally an **email** (per `deliveryConfigurations.receivers`).
683
+ When an alert's condition is met it generates a **system notification** and optionally an **email** (per `deliveryConfigurations.receivers`). AHM alert notifications carry the notification `type` **`templatized_data_alert`** and their `target` field holds the **sub-metric id** (`1HU…`) — the same value as the alert's `thresholds.conditions[0].leftOperand.metricId`.
684
+
685
+ **Attribute by alert id only.** The notification's `targetPageRef` (an HTML-encoded JSON string) embeds the owning alert id under `state.c__alertId` (or `state.c__dataAlertId`, or nested `attributes.pageRef.state.c__alertId`/`c__dataAlertId`). That id is the **precise** attribution key — match on it. **Do not fall back to the `target == metricId` join**: it is only correct when there's one alert per metric — two alerts on the same bare `_mtc` (both with empty `filterContext`) share a `metricId`, so the metricId join returns the **union** of their firings and cannot tell them apart. A notification whose alert id can't be recovered is simply **not attributed** (see the null-handling note below), not routed through metricId.
686
+
687
+ ### Extracting and matching the alert id
688
+
689
+ ```python
690
+ import json, html
691
+
692
+ def extract_alert_id(n):
693
+ """Return the owning alert id from a notification, or None. Degrades to None
694
+ (never raises) on missing / non-JSON / key-absent targetPageRef → 'no match'."""
695
+ ref = n.get("targetPageRef")
696
+ if not ref:
697
+ return None
698
+ try:
699
+ if isinstance(ref, str):
700
+ ref = json.loads(html.unescape(ref)) # API HTML-encodes the quotes (&quot;)
701
+ except Exception:
702
+ return None
703
+ top = (ref or {}).get("state") or {}
704
+ if top.get("c__dataAlertId"): return top["c__dataAlertId"]
705
+ if top.get("c__alertId"): return top["c__alertId"]
706
+ nested = (((ref or {}).get("attributes") or {}).get("pageRef") or {}).get("state") or {}
707
+ return nested.get("c__dataAlertId") or nested.get("c__alertId")
708
+
709
+ def ids_match(a, b):
710
+ """15/18-char-safe Salesforce id equality."""
711
+ if not a or not b: # None from a failed extraction ⇒ no match, drop
712
+ return False
713
+ return a == b or a[:15] == b[:15] # 18-char = 15-char + 3-char checksum suffix
714
+ ```
623
715
 
624
- ### Check notification counts via CLI (org-global signal only)
716
+ - **`None` extraction is a non-match, not an error.** A missing/malformed/key-absent `targetPageRef` yields `None`; `ids_match` returns `False` on a `None` operand, so the notification is simply **dropped** (not attributed to any alert) — nothing throws, and it is **not** routed through a metricId fallback.
717
+ - **Compare the first 15 chars.** The id embedded in `targetPageRef` is frequently the **15-char** form; the alert id from `GET tableau/dataAlerts` is the **18-char** form (15-char + 3-char case-insensitive checksum). Strict `===` fails on the same record (`3VRSG0000001vHB` vs `3VRSG0000001vHB4AY`); try exact first, then `a[:15] == b[:15]`.
718
+ - **Casing caveat.** The 15-char id is case-sensitive and the 18-char checksum encodes that casing; `ids_match` does **not** case-normalize, so truncating to 15 is correct only because both sides preserve the true casing (they do in this flow — neither source upper/lower-cases the id). Do not add a `.lower()` on either side.
719
+ - **Verified (org `ahm-test-notifications`):** all fired notifications carried `state.c__alertId` (18-char, e.g. `3VRSG0000001vHB4AY`) matching a listed alert 1:1; the nested branches and `c__dataAlertId` are defensive fallbacks for other page-ref shapes.
720
+
721
+ ### The `X-UNS-Type-Filter: all` header is REQUIRED to see AHM notifications
722
+
723
+ The Connect Notifications API (`connect/notifications*`) **defaults to returning *custom* notification types only**. AHM/Tableau-Next alerts are **standard/platform** types (`templatized_data_alert`), so a plain call returns an empty list / empty types even when alerts have genuinely fired. **Always pass `-H "X-UNS-Type-Filter: all"`** on every `connect/notifications*` call to include standard/platform types:
625
724
 
626
725
  ```bash
627
- sf api request rest "/services/data/v66.0/connect/notifications/status" -o <org> | python3 -m json.tool
726
+ # Status (counts). WITHOUT the header these are custom-types-only and read 0 for AHM.
727
+ sf api request rest "/services/data/v66.0/connect/notifications/status" \
728
+ -H "X-UNS-Type-Filter: all" -o <org> | python3 -m json.tool
729
+
730
+ # List all notifications (includes AHM). The header is what surfaces them.
731
+ sf api request rest "/services/data/v66.0/connect/notifications" \
732
+ -H "X-UNS-Type-Filter: all" -o <org> | python3 -m json.tool
733
+
734
+ # Which notification types the org exposes (verify templatized_data_alert is present).
735
+ sf api request rest "/services/data/v66.0/connect/notifications/types" \
736
+ -H "X-UNS-Type-Filter: all" -o <org> | python3 -m json.tool
628
737
  ```
629
738
 
630
- Returns `unreadCount` and `unseenCount`. **These are org-global counts across *all* notification types — not per-alert, and not AHM-specific.** A non-zero count means *some* notification was generated (it could be any alert or any other notification), so treat it only as a coarse "something fired" signal, never as confirmation that *this* alert fired. For per-alert trigger history, use the UI Incidents tab below.
739
+ > **Symptom of the missing header:** `connect/notifications` → `{ "notifications": [] }`, `connect/notifications/types` → `{ "notificationTypes": [] }`, and `status` → `unreadCount: 0` — even though the alerts fired and are visible in the UI bell. This empty result is a **false negative**, not proof of "not fired." (The UI bell uses the internal Aura controller `ui-notifications-components-notifications-controller.Notifications.getNotifications`, which reads the full store; the CLI must opt in with the header.)
740
+
741
+ Each notification object includes: `type`, `messageTitle` (metric + agent + current value), `messageBody` (`The current value (X) is above/below the alert condition (Y)`), `lastModified` (fire time), `read`/`seen`, `target` (the sub-metric id), and `targetPageRef` (an HTML-encoded JSON string). The **owning alert id** is in `targetPageRef.state.c__alertId` — that, not `target`, is the attribution key (see "Extracting and matching the alert id" above).
742
+
743
+ ### Answering "get notifications" (default response)
744
+
745
+ When the user asks to get / list / check notifications for an org, report **both**:
746
+ 1. **Status** — `unreadCount` / `unseenCount` from the `status` call (with the header).
747
+ 2. **Notifications** — the list from the `notifications` call (with the header), summarized (title, body, fire time, type).
748
+
749
+ Filter to `type == "templatized_data_alert"` to isolate AHM/agent-health alerts (the type is shared with other templatized Tableau data alerts, so it is **not** AHM-exclusive — filter, don't assume).
750
+
751
+ ### Answering "notifications for a specific alert"
752
+
753
+ There is **no per-alert notification endpoint** (`…/dataAlerts/{id}/notifications` → 404). Instead, fetch **all** notifications once, then **filter client-side by alert id** (exact — see "Extracting and matching the alert id" above):
754
+
755
+ 1. Fetch all notifications with the header (above).
756
+ 2. For each notification, `extract_alert_id(n)` and keep those where `ids_match(extracted, targetAlertId)` (15/18-char-safe). Those are the firings of that alert.
757
+ 3. A notification whose `targetPageRef` is missing/unparseable (`extract_alert_id` → `None`) is **dropped**, not attributed. Do **not** fall back to a `target == metricId` join — it mis-attributes when two alerts share a metric.
758
+
759
+ Present title / current value / threshold / `lastModified` per firing.
760
+
761
+ ```bash
762
+ # Attribute every notification to its owning alert (by alert id from targetPageRef — NOT metricId).
763
+ ORG=<org>
764
+ USER_ID=$(sf org display user --target-org "$ORG" --json | python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["id"])')
765
+ sf api request rest "/services/data/v66.0/tableau/dataAlerts?ownerId=$USER_ID" -o "$ORG" > /tmp/alerts.json
766
+ sf api request rest "/services/data/v66.0/connect/notifications" -H "X-UNS-Type-Filter: all" -o "$ORG" > /tmp/notifs.json
767
+ python3 - <<'PY'
768
+ import json, html
769
+ from collections import defaultdict
770
+
771
+ def extract_alert_id(n):
772
+ ref = n.get("targetPageRef")
773
+ if not ref:
774
+ return None
775
+ try:
776
+ if isinstance(ref, str):
777
+ ref = json.loads(html.unescape(ref))
778
+ except Exception:
779
+ return None
780
+ top = (ref or {}).get("state") or {}
781
+ if top.get("c__dataAlertId"): return top["c__dataAlertId"]
782
+ if top.get("c__alertId"): return top["c__alertId"]
783
+ nested = (((ref or {}).get("attributes") or {}).get("pageRef") or {}).get("state") or {}
784
+ return nested.get("c__dataAlertId") or nested.get("c__alertId")
785
+
786
+ def norm(x):
787
+ return x[:15] if x else x # 15/18-char-safe: truncate both sides to the 15-char id
788
+
789
+ alerts = json.load(open('/tmp/alerts.json')).get("dataAlerts", [])
790
+ notifs = json.load(open('/tmp/notifs.json')).get("notifications", [])
791
+ by_alert = {norm(a.get("id")): a for a in alerts} # key on the alert id (15-char-normalized)
792
+ groups = defaultdict(list)
793
+ for n in notifs:
794
+ groups[norm(extract_alert_id(n))].append(n) # None-key bucket = no recoverable alert id
795
+ for aid, a in by_alert.items():
796
+ ns = sorted(groups.get(aid, []), key=lambda x: x.get("lastModified", ""))
797
+ print(f"\nALERT: {a.get('alertName')} (id={a.get('id')}) -> {len(ns)} fired")
798
+ for n in ns:
799
+ print(" %s | %s" % (n.get("lastModified", "")[:19], html.unescape(n.get("messageTitle", ""))))
800
+ # notifications whose extracted alert id matches no listed alert (deleted/renamed), or None (no recoverable id — dropped, NOT routed to metricId)
801
+ for aid, ns in groups.items():
802
+ if aid in by_alert:
803
+ continue
804
+ for n in ns:
805
+ label = ("UNMATCHED alertId=%s" % aid) if aid else "UNATTRIBUTED (no alert id in targetPageRef)"
806
+ print("%s | %s" % (label, html.unescape(n.get("messageTitle", ""))))
807
+ PY
808
+ ```
631
809
 
632
810
  ### Other verification paths
633
811
 
634
- - **UI (per-alert trigger history)** — `/lightning/n/standard-AgentforceStudio?c__nav=alerts` → **Incidents** tab is the only surface with the full, per-alert notification list and details. Route the user here whenever they need to confirm a *specific* alert fired or see its history.
812
+ - **UI (per-alert trigger history)** — `/lightning/n/standard-AgentforceStudio?c__nav=alerts` → **Incidents** tab shows the full, per-alert notification list with details. Use it to corroborate the CLI attribution above or when the header path is unavailable.
635
813
  - **Email** — if `deliveryConfigurations` includes `"type": "Email"`, the owner receives an email when it fires.
636
814
  - **Metric value** — confirm the underlying metric actually crosses the threshold (see next section).
637
815
 
638
816
  ### Troubleshooting alerts that don't fire
639
817
 
640
- `unreadCount: 0` is a weak signal (it means no *unread* notifications org-wide — already-read ones don't count). Confirm in the Incidents tab that the specific alert has no history, then check:
818
+ First rule out the **missing-header false negative**: if the list is empty, retry with `-H "X-UNS-Type-Filter: all"` before concluding the alert never fired. If it is still empty (no notification attributed to that alert id — see attribution above), then check:
641
819
  1. **Does the SDM return data?** If the semantic gateway (below) returns null/0, the alert evaluation also returns null and the condition is never met.
642
820
  2. **Is the schedule running?** Alerts evaluate on their `minuteLevelFrequency`; a freshly created alert may take a few cycles.
643
821
  3. **Does the threshold make sense?** A 10% rate with threshold `>= 0.5` (50%) never fires — remember the raw-0–1-ratio scale.
822
+ 4. **Does the filter match live data?** An agent name in `filterContext` that never had a session selects zero rows — nothing to alert on.
644
823
 
645
824
  ---
646
825
 
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: automation-sandbox-post-copy-config-generate
3
- description: "Generate the JSON config file that the Salesforce sandbox post-copy automation tool consumes, from a customer SOP in any format (PDF, xlsx, csv, JSON, docx, Markdown, plain text, or a screenshot of an endpoint table). Use when the user asks to create, build, generate, produce, or convert a post-copy or post-refresh automation config — turning a sandbox-refresh SOP into a JSON array of OutboundMessages and RemoteSiteSettings entries with ConfigurationName, Label, Fields, IsActive, and ExecutionOrder. Also trigger for phrasings like \"post-copy config\", \"post-refresh automation JSON\", \"update the outbound message (OBM) endpoints after refresh\", \"convert this SOP to config\", \"remote site settings JSON\", \"refresh planner to JSON\", or \"sandbox refresh config\". DO NOT TRIGGER when: user wants to deploy the generated config to an org (use platform-metadata-deploy), or apply/execute/run/dry-run the post-copy automation JSON against a sandbox (use automation-sandbox-post-copy-configure)."
3
+ description: "Generate the JSON config file that the Salesforce sandbox post-copy automation tool consumes, from a customer SOP in any format (PDF, xlsx, csv, JSON, docx, Markdown, plain text, or a screenshot of an endpoint table). Use when the user asks to create, build, generate, produce, or convert a post-copy or post-refresh automation config — turning a sandbox-refresh SOP into a JSON array of OutboundMessages, RemoteSiteSettings, and ScheduledApex entries with ConfigurationName, Label, Fields, IsActive, and ExecutionOrder. Also trigger for phrasings like \"post-copy config\", \"post-refresh automation JSON\", \"update the outbound message (OBM) endpoints after refresh\", \"convert this SOP to config\", \"remote site settings JSON\", \"refresh planner to JSON\", or \"sandbox refresh config\". DO NOT TRIGGER when: user wants to deploy the generated config to an org (use platform-metadata-deploy), or apply/execute/run/dry-run the post-copy automation JSON against a sandbox (use automation-sandbox-post-copy-configure)."
4
4
  metadata:
5
5
  relatedSkills:
6
6
  - "automation-sandbox-post-copy-configure"
7
7
  - "platform-metadata-deploy"
8
- version: "1.0"
8
+ version: "1.1"
9
9
  domains: ["Automation"]
10
10
  ---
11
11
 
@@ -20,7 +20,7 @@ fields are involved, whether it is active, and what order it runs in.
20
20
 
21
21
  Do **not** compose the output from memory. Before you write the file, you
22
22
  MUST open and read `assets/config_template.json` and copy an entry from it
23
- for each action. Every output entry is exactly one of these two shapes —
23
+ for each action. Every output entry is exactly one of these three shapes —
24
24
  five top-level keys, no others, no wrapper object:
25
25
 
26
26
  ```json
@@ -38,12 +38,19 @@ five top-level keys, no others, no wrapper object:
38
38
  "Fields": { "RemoteSiteUrl": "https://uat.example.com" },
39
39
  "IsActive": true,
40
40
  "ExecutionOrder": 2
41
+ },
42
+ {
43
+ "ConfigurationName": "ScheduledApex",
44
+ "Label": "Nightly Data Sync",
45
+ "Fields": { "ApexClassName": "NightlyDataSyncScheduler", "CronExpression": "0 0 2 * * ?", "JobName": "Nightly Data Sync" },
46
+ "IsActive": true,
47
+ "ExecutionOrder": 3
41
48
  }
42
49
  ]
43
50
  ```
44
51
 
45
- - `ConfigurationName`: exactly `OutboundMessages` or `RemoteSiteSettings` — never `Type`, `Name`, or `Operation`.
46
- - OBM `Fields`: `EndpointUrl` + `Object` (both required). RemoteSite `Fields`: `RemoteSiteUrl` only — never `Url`/`RemoteSiteURL`.
52
+ - `ConfigurationName`: exactly `OutboundMessages`, `RemoteSiteSettings`, or `ScheduledApex` — never `Type`, `Name`, or `Operation`.
53
+ - OBM `Fields`: `EndpointUrl` + `Object` (both required). RemoteSite `Fields`: `RemoteSiteUrl` only — never `Url`/`RemoteSiteURL`. ScheduledApex `Fields`: `ApexClassName` + `CronExpression` + `JobName` (all three required).
47
54
  - Top level is a JSON array. No `steps`/`actions`/`records` wrapper. No `<…>` or `REPLACE_WITH_…` placeholder ever survives into the output.
48
55
 
49
56
  If you announce "I will now write …" without having read the template and
@@ -127,18 +134,20 @@ having read `assets/config_template.json` and
127
134
 
128
135
  3. **Map each action to a `ConfigurationName`** — load
129
136
  `references/configuration_catalog.md`. The catalog currently supports
130
- only `OutboundMessages` and `RemoteSiteSettings`. Any action that
131
- targets a different configuration type is out of scope: skip it and
132
- list it in the response so the user can extend the catalog later.
137
+ `OutboundMessages`, `RemoteSiteSettings`, and `ScheduledApex`. Any
138
+ action that targets a different configuration type is out of scope:
139
+ skip it and list it in the response so the user can extend the
140
+ catalog later.
133
141
 
134
142
  4. **Read the JSON template** — load `assets/config_template.json`. It
135
- shows the exact required shape of one `OutboundMessages` entry and one
136
- `RemoteSiteSettings` entry, with `<…>` placeholder slots. Copy an
137
- entry, replace every `<…>` slot with the concrete SOP value, and keep
138
- the exact top-level keys (`ConfigurationName`, `Label`, `Fields`,
139
- `IsActive`, `ExecutionOrder`) — never rename them to `Type`, `Name`,
140
- `Operation`, etc. Never emit an entry that still contains a `<…>`
141
- placeholder; if you cannot fill a slot, skip the entry (see Rules).
143
+ shows the exact required shape of one `OutboundMessages` entry, one
144
+ `RemoteSiteSettings` entry, and one `ScheduledApex` entry, with
145
+ `<…>` placeholder slots. Copy an entry, replace every `<…>` slot
146
+ with the concrete SOP value, and keep the exact top-level keys
147
+ (`ConfigurationName`, `Label`, `Fields`, `IsActive`, `ExecutionOrder`)
148
+ — never rename them to `Type`, `Name`, `Operation`, etc. Never emit
149
+ an entry that still contains a `<…>` placeholder; if you cannot fill
150
+ a slot, skip the entry (see Rules).
142
151
 
143
152
  5. **Validate against the schema** — load `assets/json_schema.json`. Every
144
153
  entry must conform: `ConfigurationName` is one of the catalog values,
@@ -17,5 +17,16 @@
17
17
  },
18
18
  "IsActive": true,
19
19
  "ExecutionOrder": 2
20
+ },
21
+ {
22
+ "ConfigurationName": "ScheduledApex",
23
+ "Label": "<Human-readable job name from the SOP — typically the Setup > Scheduled Jobs display name>",
24
+ "Fields": {
25
+ "ApexClassName": "<Bare Apex identifier of the Schedulable class, e.g. NightlyDataSyncScheduler>",
26
+ "CronExpression": "<6- or 7-field Salesforce cron expression, e.g. 0 0 2 * * ?>",
27
+ "JobName": "<Setup > Scheduled Jobs display name, must equal CronJobDetail.Name post-schedule>"
28
+ },
29
+ "IsActive": true,
30
+ "ExecutionOrder": 3
20
31
  }
21
32
  ]
@@ -51,6 +51,37 @@
51
51
  }
52
52
  }
53
53
  }
54
+ },
55
+ {
56
+ "if": {
57
+ "properties": { "ConfigurationName": { "const": "ScheduledApex" } }
58
+ },
59
+ "then": {
60
+ "properties": {
61
+ "Fields": {
62
+ "type": "object",
63
+ "additionalProperties": false,
64
+ "required": ["ApexClassName", "CronExpression", "JobName"],
65
+ "properties": {
66
+ "ApexClassName": {
67
+ "description": "Bare Apex identifier of the class that implements Schedulable. Must match ^[A-Za-z_][A-Za-z0-9_]*$.",
68
+ "type": "string",
69
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
70
+ },
71
+ "CronExpression": {
72
+ "description": "Salesforce cron expression — 6 or 7 whitespace-separated fields.",
73
+ "type": "string",
74
+ "minLength": 1
75
+ },
76
+ "JobName": {
77
+ "description": "Human-readable job name. Appears in Setup > Scheduled Jobs and equals CronTrigger.CronJobDetail.Name post-schedule.",
78
+ "type": "string",
79
+ "minLength": 1
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
54
85
  }
55
86
  ],
56
87
  "properties": {
@@ -59,7 +90,8 @@
59
90
  "type": "string",
60
91
  "enum": [
61
92
  "OutboundMessages",
62
- "RemoteSiteSettings"
93
+ "RemoteSiteSettings",
94
+ "ScheduledApex"
63
95
  ]
64
96
  },
65
97
  "Label": {
@@ -4,10 +4,11 @@ The fixed catalog of supported `ConfigurationName` values for the post-copy
4
4
  automation config. **Never invent a new value** — if an SOP step does not
5
5
  fit a row below, raise it to the user.
6
6
 
7
- The catalog currently covers two types only:
7
+ The catalog currently covers three types only:
8
8
 
9
9
  1. `OutboundMessages`
10
10
  2. `RemoteSiteSettings`
11
+ 3. `ScheduledApex`
11
12
 
12
13
  Anything else (Custom Labels, Connected Apps, Named Credentials, SSO, etc.)
13
14
  is out of scope for this skill at present — surface them to the user and
@@ -65,6 +66,36 @@ For each post-copy action extracted from the SOP:
65
66
  - **Skip when**: the SOP names the remote site but does not give a new
66
67
  URL.
67
68
 
69
+ ### ScheduledApex
70
+
71
+ - **SOP signal phrases**: "scheduled apex", "scheduled job", "cron
72
+ trigger", "System.schedule", "Setup > Scheduled Jobs", "re-schedule
73
+ the nightly job", "schedule the batch class after refresh".
74
+ - **Field keys** (all three required):
75
+ - `ApexClassName` — the bare Apex identifier of the class that
76
+ implements `Schedulable`. Must match `^[A-Za-z_][A-Za-z0-9_]*$` —
77
+ no dots, no spaces, no namespace prefix in this field, no `.cls`
78
+ suffix. Example: `NightlyDataSyncScheduler`.
79
+ - `CronExpression` — a 6- or 7-field Salesforce cron expression. The
80
+ apply-side skill validates the shape, but do not emit a
81
+ single-word / malformed value here. Example: `0 0 2 * * ?`
82
+ (every day at 2:00 AM).
83
+ - `JobName` — the human-readable job name that appears in Setup >
84
+ Scheduled Jobs. Post-schedule this equals
85
+ `CronTrigger.CronJobDetail.Name`; the apply-side skill uses it
86
+ both as the `System.schedule` argument and as the SOQL key when
87
+ verifying the job materialized. Typically the same string the
88
+ customer uses to refer to the job.
89
+ - **Do NOT add**:
90
+ - Any other Field key (e.g. `NextFireTime`, `State`, `CronTriggerId`)
91
+ — `CronTrigger` is read-only and computed by the platform, so only
92
+ the three inputs to `System.schedule` belong in `Fields`.
93
+ - **Skip when**: the SOP names a scheduled job but does not give all
94
+ three of `ApexClassName`, `CronExpression`, and `JobName`. Never
95
+ fabricate one from the others (for example, do not derive a JobName
96
+ from the class name or vice versa) — surface the incomplete entry
97
+ in the response so the user can supply the missing piece.
98
+
68
99
  ## Adding a new ConfigurationName
69
100
 
70
101
  If a customer SOP introduces a configuration type not in the list above,