@salesforce/afv-skills 1.46.0 → 1.48.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 (317) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-observe/SKILL.md +32 -4
  3. package/skills/agentforce-observe/references/ahm-alerts.md +719 -0
  4. package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +275 -0
  5. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/README.md +32 -0
  6. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls +75 -0
  7. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls-meta.xml +5 -0
  8. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/interview-answers.json +13 -0
  9. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +10 -0
  10. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/create.json +20 -0
  11. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/update.json +16 -0
  12. package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +273 -0
  13. package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +236 -0
  14. package/skills/consumer-goods-promotion-bo-api-deploy/references/reference-example-set-comment-value.md +132 -0
  15. package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +211 -0
  16. package/skills/dx-devops-project-manage/SKILL.md +197 -0
  17. package/skills/dx-devops-project-manage/examples/common-workflows.md +197 -0
  18. package/skills/dx-devops-project-manage/references/cli-commands.md +295 -0
  19. package/skills/dx-devops-project-manage/scripts/create-project.sh +48 -0
  20. package/skills/dx-devops-project-manage/scripts/list-projects.sh +51 -0
  21. package/skills/dx-devops-project-manage/scripts/update-project.sh +96 -0
  22. package/skills/education-cloud-academic-calendar-generate/SKILL.md +225 -0
  23. package/skills/education-cloud-academic-calendar-generate/examples/quarter-calendar.json +47 -0
  24. package/skills/education-cloud-academic-calendar-generate/examples/sample-output.md +57 -0
  25. package/skills/education-cloud-academic-calendar-generate/examples/semester-calendar.json +54 -0
  26. package/skills/education-cloud-academic-calendar-generate/references/calendar-systems.md +127 -0
  27. package/skills/education-cloud-academic-calendar-generate/references/date-validation.md +222 -0
  28. package/skills/education-cloud-academic-calendar-generate/references/foundation_prerequisites.md +40 -0
  29. package/skills/education-cloud-academic-calendar-generate/scripts/validate_calendar_dates.py +143 -0
  30. package/skills/education-cloud-course-catalog-migrate/SKILL.md +321 -0
  31. package/skills/education-cloud-course-catalog-migrate/references/gotchas-detail.md +16 -0
  32. package/skills/education-cloud-course-catalog-migrate/references/gotchas.md +16 -0
  33. package/skills/education-cloud-course-catalog-migrate/references/large-catalog-handling.md +42 -0
  34. package/skills/education-cloud-course-catalog-migrate/scripts/batch_courses.py +36 -0
  35. package/skills/education-cloud-course-catalog-migrate/scripts/detect_linked_courses.py +51 -0
  36. package/skills/education-cloud-course-catalog-migrate/scripts/detect_modality_variants.py +48 -0
  37. package/skills/education-cloud-course-catalog-migrate/scripts/resolve_api_version.py +43 -0
  38. package/skills/education-cloud-course-catalog-migrate/scripts/split_course_code.py +39 -0
  39. package/skills/education-cloud-course-catalog-migrate/scripts/validate_completeness.py +54 -0
  40. package/skills/education-cloud-domain-configure/SKILL.md +234 -0
  41. package/skills/education-cloud-domain-configure/references/dependent-features.md +198 -0
  42. package/skills/education-cloud-domain-configure/references/domain-features/academic-operations.md +386 -0
  43. package/skills/education-cloud-domain-configure/references/domain-features/alumni-relations.md +155 -0
  44. package/skills/education-cloud-domain-configure/references/domain-features/cross-domain-dependencies.md +18 -0
  45. package/skills/education-cloud-domain-configure/references/domain-features/industriessettings-toggle-reference.md +43 -0
  46. package/skills/education-cloud-domain-configure/references/domain-features/mentoring.md +198 -0
  47. package/skills/education-cloud-domain-configure/references/domain-features/recruitment-admissions.md +315 -0
  48. package/skills/education-cloud-domain-configure/references/domain-features/student-management.md +155 -0
  49. package/skills/education-cloud-domain-configure/references/domain-features/student-success.md +249 -0
  50. package/skills/education-cloud-domain-configure/references/domain-features.md +27 -0
  51. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +23 -0
  52. package/skills/education-cloud-domain-configure/references/toggle-write-path.md +67 -0
  53. package/skills/education-cloud-domain-configure/scripts/check_license_provisioned.py +62 -0
  54. package/skills/education-cloud-domain-configure/scripts/check_org_edition.py +44 -0
  55. package/skills/education-cloud-domain-configure/scripts/check_permission_assigned.py +39 -0
  56. package/skills/education-cloud-multi-campus-configure/SKILL.md +7 -0
  57. package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +1 -2
  58. package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +177 -0
  59. package/skills/education-cloud-student-recruitment-agent-configure/references/agent-and-subagents.md +151 -0
  60. package/skills/education-cloud-student-recruitment-agent-configure/references/customer-narration.md +34 -0
  61. package/skills/education-cloud-student-recruitment-agent-configure/references/execution-model.md +54 -0
  62. package/skills/education-cloud-student-recruitment-agent-configure/references/flows.md +82 -0
  63. package/skills/education-cloud-student-recruitment-agent-configure/references/grounding.md +199 -0
  64. package/skills/education-cloud-student-recruitment-agent-configure/references/permissions.md +183 -0
  65. package/skills/education-cloud-student-recruitment-agent-configure/references/platform-enablement.md +82 -0
  66. package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +158 -0
  67. package/skills/education-cloud-student-recruitment-agent-configure/references/routing.md +141 -0
  68. package/skills/experience-cms-brand-apply/SKILL.md +5 -5
  69. package/skills/experience-cms-brand-create/SKILL.md +2 -2
  70. package/skills/experience-cms-content-generate/SKILL.md +1 -0
  71. package/skills/experience-cms-content-render/SKILL.md +173 -0
  72. package/skills/experience-cms-content-render/assets/angular/DetailPage.component.ts +25 -0
  73. package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +133 -0
  74. package/skills/experience-cms-content-render/assets/angular/TypeList.component.ts +38 -0
  75. package/skills/experience-cms-content-render/assets/angular/TypeRenderer.component.ts +90 -0
  76. package/skills/experience-cms-content-render/assets/angular/cms-content.component.ts +248 -0
  77. package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +100 -0
  78. package/skills/experience-cms-content-render/assets/react/DetailPage.tsx +20 -0
  79. package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +129 -0
  80. package/skills/experience-cms-content-render/assets/react/TypeList.tsx +40 -0
  81. package/skills/experience-cms-content-render/assets/react/TypeRenderer.tsx +64 -0
  82. package/skills/experience-cms-content-render/assets/react/heuristicRenderer.tsx +310 -0
  83. package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +129 -0
  84. package/skills/experience-cms-content-render/assets/shared/cmsContentType.ts +49 -0
  85. package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +96 -0
  86. package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +55 -0
  87. package/skills/experience-cms-content-render/references/bulk-loading.md +60 -0
  88. package/skills/experience-cms-content-render/references/codegen-guardrails.md +111 -0
  89. package/skills/experience-cms-content-render/references/detail-pages.md +87 -0
  90. package/skills/experience-cms-content-render/references/embed-recipes.md +127 -0
  91. package/skills/experience-cms-content-render/references/failure-modes.md +96 -0
  92. package/skills/experience-cms-content-render/references/heuristic-render-rules.md +131 -0
  93. package/skills/experience-cms-content-render/references/init-scaffold.md +122 -0
  94. package/skills/experience-cms-content-render/references/interaction-model.md +173 -0
  95. package/skills/experience-cms-content-render/references/package-api.md +106 -0
  96. package/skills/experience-cms-content-render/references/schema-sync.md +114 -0
  97. package/skills/experience-cms-content-render/references/styling-scopes.md +65 -0
  98. package/skills/experience-cms-content-render/references/verify.md +49 -0
  99. package/skills/experience-cms-content-type-generate/SKILL.md +2 -2
  100. package/skills/experience-content-media-stock-image-search/SKILL.md +5 -4
  101. package/skills/experience-search-coordinate/SKILL.md +198 -0
  102. package/skills/experience-search-coordinate/assets/search-payload-template.json +25 -0
  103. package/skills/experience-search-coordinate/references/content-route.md +313 -0
  104. package/skills/experience-search-coordinate/references/content-type-discovery.md +57 -0
  105. package/skills/experience-search-coordinate/references/media-route.md +172 -0
  106. package/skills/experience-search-coordinate/references/scope-resolution.md +14 -0
  107. package/skills/experience-ui-bundle-localize/SKILL.md +1 -1
  108. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +5 -3
  109. package/skills/experience-ui-bundle-project-generate/SKILL.md +18 -14
  110. package/skills/experience-ui-bundle-project-generate/references/angular-project-generate.md +22 -0
  111. package/skills/experience-ui-bundle-project-generate/references/react-project-generate.md +20 -0
  112. package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +58 -54
  113. package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +6 -0
  114. package/skills/experience-ui-bundle-salesforce-data-access/references/graphiti-cli.md +2 -2
  115. package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +6 -0
  116. package/skills/experience-ui-bundle-salesforce-data-access/references/rest-and-integration.md +2 -1
  117. package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +6 -0
  118. package/skills/experience-ui-bundle-site-generate/SKILL.md +59 -8
  119. package/skills/experience-ui-bundle-site-generate/references/configure-metadata-digital-experience.md +8 -3
  120. package/skills/experience-ui-bundle-site-generate/references/configure-metadata-language-settings.md +120 -0
  121. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +336 -0
  122. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +143 -0
  123. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +127 -0
  124. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-deploy-commands.md +116 -0
  125. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +111 -0
  126. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-overview.md +312 -0
  127. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +171 -0
  128. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-state-tracking.md +64 -0
  129. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-trigger-handlers.md +122 -0
  130. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-overview.md +335 -0
  131. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-user-provisioning-details.md +140 -0
  132. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +196 -0
  133. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-metadata-cache-generation.md +155 -0
  134. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +307 -0
  135. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +211 -0
  136. package/skills/life-sciences-fieldsalesrep-coordinate/references/state-machine-and-changes.md +108 -0
  137. package/skills/life-sciences-kam-coordinate/SKILL.md +241 -0
  138. package/skills/life-sciences-kam-coordinate/references/orchestration-flow.md +152 -0
  139. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +79 -0
  140. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-deploy-commands.md +131 -0
  141. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-kam-config-records.md +85 -0
  142. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +112 -0
  143. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-overview.md +202 -0
  144. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +67 -0
  145. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-state-tracking.md +65 -0
  146. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-trigger-handlers.md +123 -0
  147. package/skills/life-sciences-kam-coordinate/references/stage-4-participant-role-and-sprint.md +89 -0
  148. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +337 -0
  149. package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +248 -0
  150. package/skills/life-sciences-kam-coordinate/references/stage-6-ipad-validation-script.md +35 -0
  151. package/skills/life-sciences-kam-coordinate/references/stage-6-metadata-cache-generation.md +155 -0
  152. package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-details.md +146 -0
  153. package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-overview.md +89 -0
  154. package/skills/life-sciences-kam-coordinate/references/state-machine-and-changes.md +114 -0
  155. package/skills/life-sciences-prerequisites-validate/SKILL.md +138 -0
  156. package/skills/life-sciences-prerequisites-validate/references/checks-org-settings.md +190 -0
  157. package/skills/life-sciences-prerequisites-validate/references/checks-user-and-package.md +211 -0
  158. package/skills/life-sciences-territory-configure/SKILL.md +217 -0
  159. package/skills/life-sciences-territory-configure/references/territory-metadata.md +262 -0
  160. package/skills/platform-dsar-policy-manage/SKILL.md +272 -0
  161. package/skills/platform-dsar-policy-manage/references/configure.md +106 -0
  162. package/skills/platform-dsar-policy-manage/references/export-and-history.md +123 -0
  163. package/skills/platform-dsar-policy-manage/references/gap-analysis-guide.md +150 -0
  164. package/skills/platform-dsar-policy-manage/references/gap-scan.md +129 -0
  165. package/skills/platform-dsar-policy-manage/references/headless-sor.md +59 -0
  166. package/skills/platform-dsar-policy-manage/references/report-format.md +59 -0
  167. package/skills/platform-dsar-policy-manage/scripts/tests/__init__.py +0 -0
  168. package/skills/platform-dsar-policy-manage/scripts/tests/test_validate_policy_tree.py +76 -0
  169. package/skills/platform-dsar-policy-manage/scripts/validate-policy-tree.py +130 -0
  170. package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +359 -0
  171. package/skills/platform-salesforce-connect-adapter-generate/references/official-examples.md +69 -0
  172. package/skills/platform-salesforce-connect-adapter-generate/references/scenarios.md +187 -0
  173. package/skills/service-agentforce-channel-configure/SKILL.md +33 -28
  174. package/skills/service-agentforce-human-escalation-configure/SKILL.md +205 -0
  175. package/skills/service-agentforce-human-escalation-configure/assets/escalation-thresholds.instructions.md +37 -0
  176. package/skills/service-agentforce-human-escalation-configure/assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml +173 -0
  177. package/skills/service-agentforce-human-escalation-configure/assets/package.xml +8 -0
  178. package/skills/service-agentforce-human-escalation-configure/assets/sfdx-project.json +5 -0
  179. package/skills/service-agentforce-human-escalation-configure/references/classifier-contracts.md +103 -0
  180. package/skills/service-agentforce-human-escalation-configure/references/escalation-mechanism.md +63 -0
  181. package/skills/service-agentforce-human-escalation-configure/references/failure-policy.md +44 -0
  182. package/skills/service-agentforce-human-escalation-configure/references/nga-escalation.md +57 -0
  183. package/skills/service-agentforce-human-escalation-configure/references/runtime-verification.md +72 -0
  184. package/skills/service-agentforce-human-escalation-configure/scripts/classify-agent-active.mjs +49 -0
  185. package/skills/service-agentforce-human-escalation-configure/scripts/classify-nga-escalation.mjs +166 -0
  186. package/skills/service-agentforce-human-escalation-configure/scripts/extract-outbound-route.mjs +67 -0
  187. package/skills/service-agentforce-human-escalation-configure/scripts/patch-escalation-surfaces.mjs +131 -0
  188. package/skills/service-agentforce-human-escalation-configure/scripts/tests/__init__.py +0 -0
  189. package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +249 -0
  190. package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +899 -0
  191. package/skills/service-agentforce-human-escalation-configure/scripts/verify-and-configure.sh +705 -0
  192. package/skills/service-agentforce-human-escalation-configure/scripts/verify-escalation-config.mjs +89 -0
  193. package/skills/service-de-channel-routing-configure/SKILL.md +13 -5
  194. package/skills/service-de-channel-routing-configure/scripts/tests/test_skill_contract.py +25 -0
  195. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +23 -10
  196. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -48
  197. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +6 -2
  198. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +20 -27
  199. package/skills/service-native-voice-recording-transcription-configure/SKILL.md +47 -27
  200. package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +13 -9
  201. package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +104 -45
  202. package/skills/service-omni-agent-users-create/SKILL.md +96 -0
  203. package/skills/service-omni-agent-users-create/assets/create-users.apex.template +80 -0
  204. package/skills/service-omni-agent-users-create/references/apex-patterns.md +135 -0
  205. package/skills/service-omni-agent-users-create/references/apex-template-notes.md +40 -0
  206. package/skills/service-omni-agent-users-create/scripts/detect-and-create.sh +206 -0
  207. package/skills/service-omni-agent-users-create/scripts/detect-existing.sh +122 -0
  208. package/skills/service-omni-agent-users-create/scripts/run-create.sh +298 -0
  209. package/skills/service-omni-agent-users-create/scripts/tests/test_user_create_security.py +171 -0
  210. package/skills/service-omni-agent-work-sharing-configure/SKILL.md +78 -0
  211. package/skills/service-omni-agent-work-sharing-configure/references/api-notes.md +31 -0
  212. package/skills/service-omni-agent-work-sharing-configure/scripts/configure-and-report.sh +186 -0
  213. package/skills/service-omni-agent-work-sharing-configure/scripts/tests/_bootstrap.py +120 -0
  214. package/skills/service-omni-agent-work-sharing-configure/scripts/tests/test_agent_work_sharing_contracts.py +93 -0
  215. package/skills/service-omni-base-settings-configure/SKILL.md +77 -0
  216. package/skills/service-omni-base-settings-configure/assets/force-app/main/default/settings/OmniChannel.settings-meta.xml +8 -0
  217. package/skills/service-omni-base-settings-configure/assets/package.xml +8 -0
  218. package/skills/service-omni-base-settings-configure/assets/sfdx-project.json +12 -0
  219. package/skills/service-omni-base-settings-configure/references/api-notes.md +121 -0
  220. package/skills/service-omni-base-settings-configure/scripts/configure-and-report.sh +164 -0
  221. package/skills/service-omni-base-settings-configure/scripts/detect-existing.sh +69 -0
  222. package/skills/service-omni-channel-setup-coordinate/SKILL.md +168 -0
  223. package/skills/service-omni-channel-setup-coordinate/examples/output-templates.md +161 -0
  224. package/skills/service-omni-channel-setup-coordinate/references/gap-catalog.md +99 -0
  225. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +1542 -0
  226. package/skills/service-omni-channel-setup-coordinate/scripts/tests/__init__.py +0 -0
  227. package/skills/service-omni-channel-setup-coordinate/scripts/tests/_bootstrap.py +223 -0
  228. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +804 -0
  229. package/skills/service-omni-command-center-analyze/SKILL.md +84 -0
  230. package/skills/service-omni-command-center-analyze/references/api-notes.md +54 -0
  231. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +198 -0
  232. package/skills/service-omni-command-center-analyze/scripts/tests/_bootstrap.py +115 -0
  233. package/skills/service-omni-command-center-analyze/scripts/tests/test_command_center_analyze_contracts.py +165 -0
  234. package/skills/service-omni-permission-set-assign/SKILL.md +88 -0
  235. package/skills/service-omni-permission-set-assign/assets/package.xml +8 -0
  236. package/skills/service-omni-permission-set-assign/references/api-notes.md +143 -0
  237. package/skills/service-omni-permission-set-assign/scripts/verify-and-assign.sh +397 -0
  238. package/skills/service-omni-presence-status-deploy/SKILL.md +87 -0
  239. package/skills/service-omni-presence-status-deploy/assets/package.xml +12 -0
  240. package/skills/service-omni-presence-status-deploy/references/api-notes.md +115 -0
  241. package/skills/service-omni-presence-status-deploy/scripts/deploy-and-report.sh +422 -0
  242. package/skills/service-omni-presence-user-config-deploy/SKILL.md +80 -0
  243. package/skills/service-omni-presence-user-config-deploy/references/api-notes.md +60 -0
  244. package/skills/service-omni-presence-user-config-deploy/scripts/deploy-and-report.sh +261 -0
  245. package/skills/service-omni-presence-user-config-deploy/scripts/tests/_bootstrap.py +106 -0
  246. package/skills/service-omni-presence-user-config-deploy/scripts/tests/test_presence_user_config_contracts.py +142 -0
  247. package/skills/service-omni-queue-deploy/SKILL.md +91 -0
  248. package/skills/service-omni-queue-deploy/references/api-notes.md +140 -0
  249. package/skills/service-omni-queue-deploy/scripts/verify-and-align.sh +470 -0
  250. package/skills/service-omni-queue-members-assign/SKILL.md +93 -0
  251. package/skills/service-omni-queue-members-assign/references/api-notes.md +144 -0
  252. package/skills/service-omni-queue-members-assign/scripts/tests/_bootstrap.py +67 -0
  253. package/skills/service-omni-queue-members-assign/scripts/tests/test_queue_members_contracts.py +73 -0
  254. package/skills/service-omni-queue-members-assign/scripts/verify-and-bind.sh +362 -0
  255. package/skills/service-omni-queue-routing-config-deploy/SKILL.md +101 -0
  256. package/skills/service-omni-queue-routing-config-deploy/references/api-notes.md +127 -0
  257. package/skills/service-omni-queue-routing-config-deploy/scripts/tests/_bootstrap.py +90 -0
  258. package/skills/service-omni-queue-routing-config-deploy/scripts/tests/test_queue_routing_config_contracts.py +70 -0
  259. package/skills/service-omni-queue-routing-config-deploy/scripts/upsert-and-report.sh +538 -0
  260. package/skills/service-omni-routing-flow-deploy/SKILL.md +111 -0
  261. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.SkillsBased.flow-meta.xml +94 -0
  262. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.flow-meta.xml +88 -0
  263. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Cases.flow-meta.xml +144 -0
  264. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.SkillsBased.flow-meta.xml +94 -0
  265. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.flow-meta.xml +88 -0
  266. package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCalls.flow-meta.xml +144 -0
  267. package/skills/service-omni-routing-flow-deploy/assets/package.xml +11 -0
  268. package/skills/service-omni-routing-flow-deploy/assets/sfdx-project.json +11 -0
  269. package/skills/service-omni-routing-flow-deploy/references/api-notes.md +265 -0
  270. package/skills/service-omni-routing-flow-deploy/scripts/deploy-and-report.sh +684 -0
  271. package/skills/service-omni-service-channel-configure/SKILL.md +83 -0
  272. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Cases.serviceChannel-meta.xml +9 -0
  273. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Incidents.serviceChannel-meta.xml +9 -0
  274. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/MessagingSessions.serviceChannel-meta.xml +9 -0
  275. package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/VoiceCalls.serviceChannel-meta.xml +9 -0
  276. package/skills/service-omni-service-channel-configure/assets/package.xml +11 -0
  277. package/skills/service-omni-service-channel-configure/assets/sfdx-project.json +12 -0
  278. package/skills/service-omni-service-channel-configure/references/api-notes.md +84 -0
  279. package/skills/service-omni-service-channel-configure/scripts/deploy-and-report.sh +383 -0
  280. package/skills/service-omni-sidebar-configure/SKILL.md +77 -0
  281. package/skills/service-omni-sidebar-configure/references/api-notes.md +51 -0
  282. package/skills/service-omni-sidebar-configure/scripts/enable-and-report.sh +315 -0
  283. package/skills/service-omni-sidebar-configure/scripts/tests/_bootstrap.py +142 -0
  284. package/skills/service-omni-sidebar-configure/scripts/tests/test_sidebar_contracts.py +139 -0
  285. package/skills/service-omni-skills-based-routing-configure/SKILL.md +81 -0
  286. package/skills/service-omni-skills-based-routing-configure/references/api-notes.md +50 -0
  287. package/skills/service-omni-skills-based-routing-configure/scripts/configure-and-report.sh +270 -0
  288. package/skills/service-omni-skills-based-routing-configure/scripts/tests/_bootstrap.py +120 -0
  289. package/skills/service-omni-skills-based-routing-configure/scripts/tests/test_sbr_contracts.py +129 -0
  290. package/skills/service-omni-supervisor-config-deploy/SKILL.md +97 -0
  291. package/skills/service-omni-supervisor-config-deploy/assets/force-app/main/default/omniSupervisorConfigs/Omni_Supervisor.omniSupervisorConfig-meta.xml +9 -0
  292. package/skills/service-omni-supervisor-config-deploy/assets/package.xml +8 -0
  293. package/skills/service-omni-supervisor-config-deploy/assets/sfdx-project.json +12 -0
  294. package/skills/service-omni-supervisor-config-deploy/references/api-notes.md +205 -0
  295. package/skills/service-omni-supervisor-config-deploy/scripts/deploy-and-report.sh +586 -0
  296. package/skills/service-omni-supervisor-config-deploy/scripts/tests/_bootstrap.py +153 -0
  297. package/skills/service-omni-supervisor-config-deploy/scripts/tests/test_supervisor_config_contracts.py +116 -0
  298. package/skills/service-omni-supervisor-permset-assign/SKILL.md +88 -0
  299. package/skills/service-omni-supervisor-permset-assign/references/api-notes.md +48 -0
  300. package/skills/service-omni-supervisor-permset-assign/scripts/verify-and-assign.sh +316 -0
  301. package/skills/service-omni-supervisor-surface-deploy/SKILL.md +85 -0
  302. package/skills/service-omni-supervisor-surface-deploy/references/api-notes.md +47 -0
  303. package/skills/service-omni-supervisor-surface-deploy/scripts/deploy-and-report.sh +216 -0
  304. package/skills/service-omni-supervisor-surface-deploy/scripts/tests/_bootstrap.py +95 -0
  305. package/skills/service-omni-supervisor-surface-deploy/scripts/tests/test_supervisor_surface_contracts.py +154 -0
  306. package/skills/service-omni-supervisor-users-create/SKILL.md +92 -0
  307. package/skills/service-omni-supervisor-users-create/assets/create-supervisors.apex.template +72 -0
  308. package/skills/service-omni-supervisor-users-create/references/apex-patterns.md +73 -0
  309. package/skills/service-omni-supervisor-users-create/references/apex-template-notes.md +41 -0
  310. package/skills/service-omni-supervisor-users-create/scripts/detect-and-create.sh +197 -0
  311. package/skills/service-omni-supervisor-users-create/scripts/detect-existing.sh +112 -0
  312. package/skills/service-omni-supervisor-users-create/scripts/run-create.sh +296 -0
  313. package/skills/service-omni-work-skill-routing-configure/SKILL.md +80 -0
  314. package/skills/service-omni-work-skill-routing-configure/references/api-notes.md +67 -0
  315. package/skills/service-omni-work-skill-routing-configure/scripts/configure-and-report.sh +262 -0
  316. package/skills/service-omni-work-skill-routing-configure/scripts/tests/_bootstrap.py +121 -0
  317. package/skills/service-omni-work-skill-routing-configure/scripts/tests/test_work_skill_routing_contracts.py +161 -0
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env bash
2
+ # configure-and-report.sh - author a WorkSkillRouting rule (field-value -> required Omni Skill) for a related entity via the Metadata API; detect-before-write, idempotent, graceful-degrade when the feature is unavailable. Args, env, exit codes: SKILL.md.
3
+
4
+ set -euo pipefail
5
+
6
+ if [ $# -lt 5 ]; then
7
+ echo '{"error":"Usage: bash configure-and-report.sh <org-alias> <related_entity> <field> <skill> <value> [rule_master_label]"}' >&2
8
+ exit 1
9
+ fi
10
+
11
+ ORG="$1"
12
+ RELATED_ENTITY="$2"
13
+ FIELD="$3"
14
+ SKILL="$4"
15
+ VALUE="$5"
16
+ MASTER_LABEL_EXPLICIT=false
17
+ { [ -n "${6:-}" ] || [ -n "${RULE_MASTER_LABEL:-}" ]; } && MASTER_LABEL_EXPLICIT=true
18
+ IS_ACTIVE_EXPLICIT=false
19
+ [ -n "${IS_ACTIVE:-}" ] && IS_ACTIVE_EXPLICIT=true
20
+ RULE_MASTER_LABEL="${6:-${RULE_MASTER_LABEL:-$RELATED_ENTITY Skill Routing}}"
21
+
22
+ RULE_DN="${RULE_DEVELOPER_NAME:-$RELATED_ENTITY}"
23
+ IS_ACTIVE="${IS_ACTIVE:-true}"
24
+ SKILL_LEVEL="${SKILL_LEVEL:-}"
25
+ SKILL_PRIORITY="${SKILL_PRIORITY:-}"
26
+ IS_ADDITIONAL_SKILL="${IS_ADDITIONAL_SKILL:-}"
27
+
28
+ # ---- validation (reject before any sf call) ----
29
+ if ! [[ "$RELATED_ENTITY" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
30
+ echo "{\"error\":\"Invalid related_entity '$RELATED_ENTITY'. Must be an sObject API name (letters, digits, underscores; max 80).\"}" >&2
31
+ exit 1
32
+ fi
33
+ if ! [[ "$FIELD" =~ ^[A-Za-z][A-Za-z0-9_.]{0,120}$ ]]; then
34
+ echo "{\"error\":\"Invalid field '$FIELD'. Use a field API name, optionally Entity.Field (letters, digits, underscores, dots).\"}" >&2
35
+ exit 1
36
+ fi
37
+ if ! [[ "$SKILL" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
38
+ echo "{\"error\":\"Invalid skill '$SKILL'. Must be a Skill DeveloperName (letters, digits, underscores; max 80).\"}" >&2
39
+ exit 1
40
+ fi
41
+ if ! [[ "$VALUE" =~ ^[A-Za-z0-9][A-Za-z0-9\ _./-]{0,254}$ ]]; then
42
+ echo "{\"error\":\"Invalid value '$VALUE'. Provide a plain field value (letters, digits, space, . _ / -); XML-reserved characters are not supported.\"}" >&2
43
+ exit 1
44
+ fi
45
+ if ! [[ "$RULE_DN" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
46
+ echo "{\"error\":\"Invalid RULE_DEVELOPER_NAME '$RULE_DN'.\"}" >&2
47
+ exit 1
48
+ fi
49
+ if ! [[ "$RULE_MASTER_LABEL" =~ ^[A-Za-z0-9][A-Za-z0-9\ _-]{0,79}$ ]]; then
50
+ echo "{\"error\":\"Invalid rule_master_label '$RULE_MASTER_LABEL'.\"}" >&2
51
+ exit 1
52
+ fi
53
+ if [ "$IS_ACTIVE" != "true" ] && [ "$IS_ACTIVE" != "false" ]; then
54
+ echo "{\"error\":\"Invalid IS_ACTIVE '$IS_ACTIVE' - must be true or false.\"}" >&2
55
+ exit 1
56
+ fi
57
+ if [ -n "$SKILL_LEVEL" ] && { ! [[ "$SKILL_LEVEL" =~ ^[0-9]+$ ]] || [ "$SKILL_LEVEL" -gt 10 ]; }; then
58
+ echo "{\"error\":\"Invalid SKILL_LEVEL '$SKILL_LEVEL' - must be an integer 0..10.\"}" >&2
59
+ exit 1
60
+ fi
61
+ if [ -n "$SKILL_PRIORITY" ] && { ! [[ "$SKILL_PRIORITY" =~ ^[0-9]+$ ]] || [ "$SKILL_PRIORITY" -gt 10 ]; }; then
62
+ echo "{\"error\":\"Invalid SKILL_PRIORITY '$SKILL_PRIORITY' - must be an integer 0..10.\"}" >&2
63
+ exit 1
64
+ fi
65
+ if [ -n "$IS_ADDITIONAL_SKILL" ] && [ "$IS_ADDITIONAL_SKILL" != "true" ] && [ "$IS_ADDITIONAL_SKILL" != "false" ]; then
66
+ echo "{\"error\":\"Invalid IS_ADDITIONAL_SKILL '$IS_ADDITIONAL_SKILL' - must be true or false.\"}" >&2
67
+ exit 1
68
+ fi
69
+
70
+ # The Metadata API requires WorkSkillRoutingAttribute.field to be entity-qualified.
71
+ # Bare standard fields are otherwise resolved as CustomField metadata and fail to deploy.
72
+ if [[ "$FIELD" != *.* ]]; then
73
+ FIELD="${RELATED_ENTITY}.${FIELD}"
74
+ fi
75
+
76
+ if ! sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
77
+ echo "{\"error\":\"Org alias '$ORG' is not authenticated. Run: sf org login web -a $ORG -r <my-domain-url>\"}" >&2
78
+ exit 1
79
+ fi
80
+
81
+ emit_blocked() {
82
+ local msg="$1"; local target_skill="${2:-}"
83
+ local manual_actions='[]'
84
+ if [ -n "$target_skill" ]; then
85
+ manual_actions=$(jq -n --arg ts "$target_skill" '[{id:"MISSING_PREREQ",title:"Run prerequisite skill",target_skill:$ts}]')
86
+ fi
87
+ jq -n --arg msg "$msg" --arg rdn "$RULE_DN" --arg ent "$RELATED_ENTITY" --argjson ma "$manual_actions" \
88
+ '{skill:"service-omni-work-skill-routing-configure",status:"blocked",
89
+ rule:{developer_name:$rdn,related_entity:$ent,state:"not_deployed"},
90
+ attribute:null,deploy_id:null,manual_actions:$ma,blocking_issue:$msg}'
91
+ exit 1
92
+ }
93
+
94
+ emit_plan() {
95
+ local detail="$1"
96
+ jq -n --arg detail "$detail" --arg rdn "$RULE_DN" --arg ent "$RELATED_ENTITY" \
97
+ '{skill:"service-omni-work-skill-routing-configure",status:"action_needed",plan_mode:true,
98
+ plan_detail:$detail,rule:{developer_name:$rdn,related_entity:$ent},manual_actions:[],blocking_issue:null}'
99
+ exit 0
100
+ }
101
+
102
+ # Step 1 - Organization + safe_to_write guard
103
+ ORG_JSON=$(sf data query --target-org "$ORG" \
104
+ --query "SELECT IsSandbox, TrialExpirationDate, OrganizationType FROM Organization LIMIT 1" --json 2>/dev/null) \
105
+ || emit_blocked "Failed to query Organization for the safe_to_write guard."
106
+ IS_SANDBOX=$(echo "$ORG_JSON" | jq -r '.result.records[0].IsSandbox')
107
+ TRIAL_EXP=$(echo "$ORG_JSON" | jq -r '.result.records[0].TrialExpirationDate // "null"')
108
+ ORG_TYPE=$(echo "$ORG_JSON" | jq -r '.result.records[0].OrganizationType')
109
+ SAFE_TO_WRITE=false
110
+ if [ "$IS_SANDBOX" = "true" ] || [ "$TRIAL_EXP" != "null" ] \
111
+ || [ "$ORG_TYPE" = "Developer Edition" ] || [ "$ORG_TYPE" = "Base Edition" ]; then
112
+ SAFE_TO_WRITE=true
113
+ fi
114
+ [ "$SAFE_TO_WRITE" = "true" ] || emit_blocked "Refusing to author a routing rule on a real production customer org (IsSandbox=$IS_SANDBOX, TrialExpirationDate=$TRIAL_EXP, OrganizationType=$ORG_TYPE). Target a sandbox, trial CDO, or Developer Edition org."
115
+
116
+ # Step 2 - Referenced Skill must exist (a WorkSkillRoutingAttribute.skill points at a Skill DeveloperName)
117
+ SKILL_JSON=$(sf data query --target-org "$ORG" --json \
118
+ --query "SELECT Id FROM Skill WHERE DeveloperName='$SKILL'" 2>/dev/null || echo '{}')
119
+ SKILL_COUNT=$(echo "$SKILL_JSON" | jq -r '.result.records | length' 2>/dev/null || echo 0)
120
+ [ "${SKILL_COUNT:-0}" -ge 1 ] || emit_blocked "Referenced Skill '$SKILL' does not exist; deploy it first so the rule can bind to it." "service-omni-skills-based-routing-configure"
121
+
122
+ # A metadata deploy replaces the full one-per-entity rule. Read and merge the
123
+ # current attribute array so adding Origin does not erase an existing Priority rule.
124
+ MERGE_MODE=true
125
+ [ "${WSR_REPLACE:-}" = "1" ] && MERGE_MODE=false
126
+ EXISTING_ATTRS='[]'
127
+ MERGED_FROM_EXISTING=false
128
+ if [ "$MERGE_MODE" = "true" ]; then
129
+ WSR_META_JSON=$(sf data query --target-org "$ORG" --use-tooling-api --json \
130
+ --query "SELECT Metadata FROM WorkSkillRouting WHERE DeveloperName='$RULE_DN'" 2>/dev/null || true)
131
+ if echo "$WSR_META_JSON" | jq -e '.result.totalSize == 1' >/dev/null 2>&1; then
132
+ MERGED_FROM_EXISTING=true
133
+ EXISTING_ATTRS=$(echo "$WSR_META_JSON" | jq -c '.result.records[0].Metadata.workSkillRoutingAttributes // []')
134
+ PRESERVED_LABEL=$(echo "$WSR_META_JSON" | jq -r '.result.records[0].Metadata.masterLabel // ""')
135
+ PRESERVED_ACTIVE=$(echo "$WSR_META_JSON" | jq -r '.result.records[0].Metadata.isActive // empty')
136
+ [ "$MASTER_LABEL_EXPLICIT" = "true" ] || [ -z "$PRESERVED_LABEL" ] || RULE_MASTER_LABEL="$PRESERVED_LABEL"
137
+ [ "$IS_ACTIVE_EXPLICIT" = "true" ] || [ -z "$PRESERVED_ACTIVE" ] || IS_ACTIVE="$PRESERVED_ACTIVE"
138
+ elif echo "$WSR_META_JSON" | jq -e '.result.totalSize == 0' >/dev/null 2>&1; then
139
+ :
140
+ elif ! echo "$WSR_META_JSON" | grep -qiE 'INVALID_TYPE|not available|no such|Cannot find'; then
141
+ emit_blocked "Could not read existing WorkSkillRouting '$RULE_DN' for a merge. Refusing a destructive whole-rule replace; retry the read or set WSR_REPLACE=1 to overwrite intentionally."
142
+ fi
143
+ fi
144
+
145
+ NEW_ATTR=$(jq -n \
146
+ --arg field "$FIELD" --arg skill "$SKILL" --arg value "$VALUE" \
147
+ --arg lvl "$SKILL_LEVEL" --arg pri "$SKILL_PRIORITY" --arg add "$IS_ADDITIONAL_SKILL" \
148
+ '{field:$field,skill:$skill,value:$value}
149
+ + (if $add=="" then {} else {isAdditionalSkill:($add=="true")} end)
150
+ + (if $lvl=="" then {} else {skillLevel:($lvl|tonumber)} end)
151
+ + (if $pri=="" then {} else {skillPriority:($pri|tonumber)} end)')
152
+ MERGED_ATTRS=$(jq -c -n --argjson existing "$EXISTING_ATTRS" --argjson new "$NEW_ATTR" \
153
+ '($existing | map(select((.field==$new.field and .value==$new.value) | not))) + [$new]')
154
+ ATTR_COUNT=$(echo "$MERGED_ATTRS" | jq -r 'length')
155
+ PRESERVED_COUNT=$((ATTR_COUNT - 1))
156
+
157
+ # PLAN_ONLY - read-only; report intent and stop before any write.
158
+ if [ "${PLAN_ONLY:-}" = "1" ]; then
159
+ emit_plan "Would deploy WorkSkillRouting '$RULE_DN' on $RELATED_ENTITY (isActive=$IS_ACTIVE) mapping $FIELD='$VALUE' -> Skill '$SKILL'."
160
+ fi
161
+
162
+ # Step 3 - Materialize the WorkSkillRouting in XSD (alphabetical) order
163
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
164
+ WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
165
+ cp "$SCRIPT_DIR/../assets/sfdx-project.json" "$WORK/sfdx-project.json" 2>/dev/null \
166
+ || echo '{"packageDirectories":[{"path":"force-app","default":true}],"sourceApiVersion":"66.0"}' > "$WORK/sfdx-project.json"
167
+ WSR_DIR="$WORK/force-app/main/default/workSkillRoutings"
168
+ mkdir -p "$WSR_DIR"
169
+
170
+ ATTR_XML=$(echo "$MERGED_ATTRS" | jq -r '
171
+ def xe: tostring | gsub("&";"&amp;") | gsub("<";"&lt;") | gsub(">";"&gt;");
172
+ .[] |
173
+ " <workSkillRoutingAttributes>",
174
+ " <field>\(.field|xe)</field>",
175
+ (if has("isAdditionalSkill") then " <isAdditionalSkill>\(.isAdditionalSkill)</isAdditionalSkill>" else empty end),
176
+ " <skill>\(.skill|xe)</skill>",
177
+ (if .skillLevel != null then " <skillLevel>\(.skillLevel)</skillLevel>" else empty end),
178
+ (if .skillPriority != null then " <skillPriority>\(.skillPriority)</skillPriority>" else empty end),
179
+ " <value>\(.value|xe)</value>",
180
+ " </workSkillRoutingAttributes>"
181
+ ')
182
+
183
+ cat > "$WSR_DIR/${RULE_DN}.workSkillRouting-meta.xml" <<XML
184
+ <?xml version="1.0" encoding="UTF-8"?>
185
+ <WorkSkillRouting xmlns="http://soap.sforce.com/2006/04/metadata">
186
+ <isActive>${IS_ACTIVE}</isActive>
187
+ <masterLabel>${RULE_MASTER_LABEL}</masterLabel>
188
+ <relatedEntity>${RELATED_ENTITY}</relatedEntity>
189
+ ${ATTR_XML}
190
+ </WorkSkillRouting>
191
+ XML
192
+
193
+ # Step 4 - Deploy (async + poll to terminal state)
194
+ START_JSON=$(cd "$WORK" && sf project deploy start --target-org "$ORG" \
195
+ --metadata "WorkSkillRouting:${RULE_DN}" --async --json 2>/dev/null || true)
196
+ DEPLOY_ID=$(echo "$START_JSON" | jq -r '.result.id // ""' 2>/dev/null)
197
+ DEPLOY_JSON="$START_JSON"
198
+
199
+ if [ -n "$DEPLOY_ID" ] && [ "$DEPLOY_ID" != "null" ]; then
200
+ POLL_STATUS=""
201
+ for _ in $(seq 1 60); do
202
+ REPORT_JSON=$(cd "$WORK" && sf project deploy report --job-id "$DEPLOY_ID" --target-org "$ORG" --json 2>/dev/null || true)
203
+ CUR_STATUS=$(echo "$REPORT_JSON" | jq -r '.result.status // ""' 2>/dev/null)
204
+ case "$CUR_STATUS" in
205
+ Succeeded|Failed|SucceededPartial|Canceled) DEPLOY_JSON="$REPORT_JSON"; POLL_STATUS="$CUR_STATUS"; break ;;
206
+ esac
207
+ sleep 15
208
+ done
209
+ [ -n "$POLL_STATUS" ] || emit_blocked "WorkSkillRouting deploy job $DEPLOY_ID did not reach a terminal state within the poll budget. Not reporting success."
210
+ fi
211
+
212
+ DEPLOY_SUCCESS=$(echo "$DEPLOY_JSON" | jq -r '.result.success // false' 2>/dev/null)
213
+ DEPLOY_ID=$(echo "$DEPLOY_JSON" | jq -r '.result.id // ""' 2>/dev/null)
214
+
215
+ if [ "$DEPLOY_SUCCESS" != "true" ]; then
216
+ FAILURES=$(echo "$DEPLOY_JSON" | jq -r '.result.details.componentFailures // [] | map(.problem) | join("; ")' 2>/dev/null)
217
+ [ -n "$FAILURES" ] && [ "$FAILURES" != "null" ] || FAILURES=$(echo "$DEPLOY_JSON" | jq -r '.message // .result.errorMessage // "Unknown deploy error"' 2>/dev/null)
218
+ case "$FAILURES" in
219
+ *INVALID_TYPE*|*"Cannot find"*|*"not available"*|*"WorkSkillRouting"*)
220
+ emit_blocked "WorkSkillRouting is not available on this org (skills-based routing may be off, or the feature is not provisioned on this trial/CDO): $FAILURES" "service-omni-base-settings-configure" ;;
221
+ *"$SKILL"*)
222
+ emit_blocked "Deploy failed: Skill '$SKILL' could not be resolved by the rule. $FAILURES" "service-omni-skills-based-routing-configure" ;;
223
+ *) emit_blocked "WorkSkillRouting deploy failed: $FAILURES" ;;
224
+ esac
225
+ fi
226
+
227
+ STATE_WSR=$(echo "$DEPLOY_JSON" | jq -r --arg fn "$RULE_DN" \
228
+ '.result.files[] | select(.fullName==$fn and .type=="WorkSkillRouting") | .state' 2>/dev/null | head -1)
229
+ STATE_WSR="${STATE_WSR:-unknown}"
230
+ case "$STATE_WSR" in
231
+ Created|Changed|Unchanged) ;;
232
+ *) emit_blocked "Deploy reported success overall but WorkSkillRouting '$RULE_DN' has an unrecognized/absent state \"$STATE_WSR\" (expected Created, Changed, or Unchanged). Not reporting success." ;;
233
+ esac
234
+
235
+ STATUS="reused"
236
+ [ "$STATE_WSR" = "Created" ] && STATUS="created"
237
+ [ "$STATE_WSR" = "Changed" ] && STATUS="updated"
238
+
239
+ jq -n \
240
+ --arg status "$STATUS" \
241
+ --arg rdn "$RULE_DN" --arg lbl "$RULE_MASTER_LABEL" --arg ent "$RELATED_ENTITY" --arg act "$IS_ACTIVE" --arg state "$STATE_WSR" \
242
+ --arg field "$FIELD" --arg skill "$SKILL" --arg value "$VALUE" \
243
+ --arg lvl "$SKILL_LEVEL" --arg pri "$SKILL_PRIORITY" --arg add "$IS_ADDITIONAL_SKILL" \
244
+ --arg deploy_id "$DEPLOY_ID" \
245
+ --argjson merged_from_existing "$MERGED_FROM_EXISTING" \
246
+ --argjson attribute_count "$ATTR_COUNT" \
247
+ --argjson preserved_count "$PRESERVED_COUNT" \
248
+ '{
249
+ skill: "service-omni-work-skill-routing-configure",
250
+ status: $status,
251
+ rule: {developer_name:$rdn, master_label:$lbl, related_entity:$ent, is_active:($act=="true"), state:$state},
252
+ merge: {merged_from_existing:$merged_from_existing, attribute_count:$attribute_count, preserved_count:$preserved_count},
253
+ attribute: {
254
+ field:$field, skill:$skill, value:$value,
255
+ skill_level:(if $lvl=="" then null else ($lvl|tonumber) end),
256
+ skill_priority:(if $pri=="" then null else ($pri|tonumber) end),
257
+ is_additional_skill:(if $add=="" then null else ($add=="true") end)
258
+ },
259
+ deploy_id: (if $deploy_id=="" then null else $deploy_id end),
260
+ manual_actions: [],
261
+ blocking_issue: null
262
+ }'
@@ -0,0 +1,121 @@
1
+ """Shared paths + fake-`sf` harness for the work-skill-routing contract suite.
2
+
3
+ Drives ``configure-and-report.sh`` via ``subprocess`` and asserts on exit codes + emitted JSON.
4
+ Org-free and network-free. ``jq`` comes from the real PATH.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ import subprocess
10
+ import tempfile
11
+ from pathlib import Path
12
+
13
+ SKILLS_ROOT = Path(__file__).resolve().parents[3]
14
+ SKILL_DIR = "service-omni-work-skill-routing-configure"
15
+ SCRIPT = f"{SKILL_DIR}/scripts/configure-and-report.sh"
16
+
17
+ # PRODUCTION org - safe_to_write must refuse before any deploy.
18
+ _FAKE_SF_PROD = r"""#!/usr/bin/env bash
19
+ sub="${1:-} ${2:-}"; args="$*"
20
+ case "$sub" in
21
+ "org display") echo '{"status":0,"result":{"id":"00Dxx0000000000EAA"}}'; exit 0;;
22
+ "data query")
23
+ if printf '%s' "$args" | grep -q "FROM Organization"; then
24
+ echo '{"status":0,"result":{"records":[{"IsSandbox":false,"TrialExpirationDate":null,"OrganizationType":"Enterprise Edition"}]}}'
25
+ else
26
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
27
+ fi
28
+ exit 0;;
29
+ "project deploy") echo '{"status":1,"result":{"success":false},"message":"FAKE_SF_DEPLOY_SHOULD_NOT_BE_REACHED"}'; exit 1;;
30
+ *) echo '{"status":0,"result":{}}'; exit 0;;
31
+ esac
32
+ """
33
+
34
+ # SANDBOX org. Tunables:
35
+ # FAKE_STATE_WSR -> Created|Changed|Unchanged (default Created).
36
+ # FAKE_SKILL_PRESENT -> 1|0 (default 1); 0 blocks on the missing-Skill prereq.
37
+ # FAKE_DEPLOY_FAIL=1 -> deploy fails with a feature-unavailable (INVALID_TYPE) problem.
38
+ # FAKE_RULE_DN -> fullName echoed in the report (default Case).
39
+ _FAKE_SF_SANDBOX = r"""#!/usr/bin/env bash
40
+ sub="${1:-} ${2:-}"; args="$*"
41
+ STATE_WSR="${FAKE_STATE_WSR:-Created}"
42
+ RULE_DN="${FAKE_RULE_DN:-Case}"
43
+ if [ -n "${FAKE_SF_CALLLOG:-}" ]; then printf '%s\n' "$args" >> "$FAKE_SF_CALLLOG"; fi
44
+ case "$sub" in
45
+ "org display") echo '{"status":0,"result":{"id":"00Dxx0000000000EAA"}}'; exit 0;;
46
+ "data query")
47
+ if printf '%s' "$args" | grep -q "FROM Organization"; then
48
+ echo '{"status":0,"result":{"records":[{"IsSandbox":true,"TrialExpirationDate":null,"OrganizationType":"Developer Edition"}]}}'
49
+ elif printf '%s' "$args" | grep -q "FROM WorkSkillRouting"; then
50
+ if [ "${FAKE_WSR_INCONCLUSIVE:-0}" = "1" ]; then
51
+ echo '{"status":1,"message":"Transient failure reading WorkSkillRouting metadata"}'
52
+ elif [ "${FAKE_WSR_EXISTING:-0}" = "1" ]; then
53
+ if [ "${FAKE_WSR_NULL_OPTIONALS:-0}" = "1" ]; then
54
+ echo '{"status":0,"result":{"totalSize":1,"records":[{"Metadata":{"masterLabel":"Existing Label","isActive":true,"relatedEntity":"Case","workSkillRoutingAttributes":[{"field":"Case.Priority","skill":"Priority_Support","value":"High","skillLevel":null,"skillPriority":null}]}}]}}'
55
+ else
56
+ echo '{"status":0,"result":{"totalSize":1,"records":[{"Metadata":{"masterLabel":"Existing Label","isActive":true,"relatedEntity":"Case","workSkillRoutingAttributes":[{"field":"Case.Priority","skill":"Priority_Support","value":"High"}]}}]}}'
57
+ fi
58
+ else
59
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
60
+ fi
61
+ elif printf '%s' "$args" | grep -q "FROM Skill"; then
62
+ if [ "${FAKE_SKILL_PRESENT:-1}" = "1" ]; then
63
+ echo '{"status":0,"result":{"records":[{"Id":"0C9xx0000000001"}],"totalSize":1}}'
64
+ else
65
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
66
+ fi
67
+ else
68
+ echo '{"status":0,"result":{"records":[],"totalSize":0}}'
69
+ fi
70
+ exit 0;;
71
+ "project deploy")
72
+ if printf '%s' "$args" | grep -q "report"; then
73
+ if grep -R -q '>null<' force-app/main/default/workSkillRoutings 2>/dev/null; then
74
+ echo '{"status":0,"result":{"success":false,"id":"0AfXX0000000001","status":"Failed","details":{"componentFailures":[{"problem":"null is not a valid value for the type xsd:int"}]}}}'
75
+ elif [ "${FAKE_DEPLOY_FAIL:-0}" = "1" ]; then
76
+ echo '{"status":0,"result":{"success":false,"id":"0AfXX0000000001","status":"Failed","details":{"componentFailures":[{"problem":"INVALID_TYPE: WorkSkillRouting not available"}]}}}'
77
+ else
78
+ printf '{"status":0,"result":{"success":true,"id":"0AfXX0000000001","status":"Succeeded","files":[{"fullName":"%s","type":"WorkSkillRouting","state":"%s"}]}}' "$RULE_DN" "$STATE_WSR"
79
+ fi
80
+ else
81
+ echo '{"status":0,"result":{"id":"0AfXX0000000001","status":"Queued"}}'
82
+ fi
83
+ exit 0;;
84
+ *) echo '{"status":0,"result":{}}'; exit 0;;
85
+ esac
86
+ """
87
+
88
+
89
+ def run(args, path_prefix=None, scrub_sf=False, extra_env=None):
90
+ env = dict(os.environ)
91
+ if scrub_sf:
92
+ kept = [d for d in env.get("PATH", "").split(os.pathsep)
93
+ if d and not os.path.exists(os.path.join(d, "sf"))]
94
+ env["PATH"] = os.pathsep.join(kept)
95
+ if path_prefix:
96
+ env["PATH"] = path_prefix + os.pathsep + env.get("PATH", "")
97
+ if extra_env:
98
+ env.update({k: str(v) for k, v in extra_env.items()})
99
+ proc = subprocess.run(
100
+ ["bash", str(SKILLS_ROOT / SCRIPT), *args],
101
+ capture_output=True, text=True, env=env, cwd=str(SKILLS_ROOT),
102
+ )
103
+ return proc.returncode, (proc.stdout or "") + (proc.stderr or "")
104
+
105
+
106
+ class FakeSf:
107
+ def __init__(self, body=_FAKE_SF_PROD):
108
+ self._body = body
109
+ self._tmp = None
110
+
111
+ def __enter__(self):
112
+ self._tmp = tempfile.mkdtemp(prefix="wsr-fake-sf-")
113
+ sf = Path(self._tmp) / "sf"
114
+ sf.write_text(self._body)
115
+ sf.chmod(0o755)
116
+ return self._tmp
117
+
118
+ def __exit__(self, *exc):
119
+ import shutil
120
+ if self._tmp:
121
+ shutil.rmtree(self._tmp, ignore_errors=True)
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env python3
2
+ """Contract tests for service-omni-work-skill-routing-configure/scripts/configure-and-report.sh."""
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ import unittest
8
+ from pathlib import Path
9
+
10
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
11
+ from _bootstrap import ( # noqa: E402
12
+ FakeSf,
13
+ _FAKE_SF_PROD,
14
+ _FAKE_SF_SANDBOX,
15
+ run,
16
+ )
17
+
18
+
19
+ def _last_json(out: str) -> dict:
20
+ dec = json.JSONDecoder()
21
+ obj, i, n = None, 0, len(out)
22
+ while i < n:
23
+ if out[i] in "{[":
24
+ try:
25
+ cand, end = dec.raw_decode(out, i)
26
+ if isinstance(cand, dict):
27
+ obj = cand
28
+ i = end
29
+ continue
30
+ except json.JSONDecodeError:
31
+ pass
32
+ i += 1
33
+ assert obj is not None, f"no JSON object found in output:\n{out}"
34
+ return obj
35
+
36
+
37
+ class InputContractTests(unittest.TestCase):
38
+ def test_missing_args_rejected(self):
39
+ rc, out = run(["myorg", "Case", "Origin"], scrub_sf=True)
40
+ self.assertEqual(rc, 1, out)
41
+ self.assertIn("Usage", out)
42
+
43
+ def test_injection_shaped_skill_rejected(self):
44
+ rc, out = run(["myorg", "Case", "Origin", "Bad; DROP", "Phone"], scrub_sf=True)
45
+ self.assertEqual(rc, 1, out)
46
+ self.assertIn("Invalid skill", out)
47
+
48
+ def test_xml_injection_value_rejected(self):
49
+ rc, out = run(["myorg", "Case", "Origin", "Voice", "<x>&bad"], scrub_sf=True)
50
+ self.assertEqual(rc, 1, out)
51
+ self.assertIn("Invalid value", out)
52
+
53
+ def test_bad_related_entity_rejected(self):
54
+ rc, out = run(["myorg", "Case Origin", "Origin", "Voice", "Phone"], scrub_sf=True)
55
+ self.assertEqual(rc, 1, out)
56
+ self.assertIn("Invalid related_entity", out)
57
+
58
+ def test_out_of_range_skill_level_rejected(self):
59
+ rc, out = run(["myorg", "Case", "Origin", "Voice", "Phone"], scrub_sf=True,
60
+ extra_env={"SKILL_LEVEL": "11"})
61
+ self.assertEqual(rc, 1, out)
62
+ self.assertIn("Invalid SKILL_LEVEL", out)
63
+
64
+
65
+ class ProductionGuardTests(unittest.TestCase):
66
+ def test_refuses_production_org(self):
67
+ with FakeSf(_FAKE_SF_PROD) as p:
68
+ rc, out = run(["prodorg", "Case", "Origin", "Voice", "Phone"], path_prefix=p)
69
+ self.assertEqual(rc, 1, out)
70
+ v = _last_json(out)
71
+ self.assertEqual(v["status"], "blocked", out)
72
+ self.assertIn("production", v["blocking_issue"].lower())
73
+
74
+
75
+ class ConfigureTests(unittest.TestCase):
76
+ def test_create_deploys_rule(self):
77
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
78
+ rc, out = run(["sandbox", "Case", "Case.Origin", "Voice", "Phone"], path_prefix=p,
79
+ extra_env={"FAKE_STATE_WSR": "Created"})
80
+ self.assertEqual(rc, 0, out)
81
+ v = _last_json(out)
82
+ self.assertEqual(v["status"], "created", out)
83
+ self.assertEqual(v["rule"]["related_entity"], "Case", out)
84
+ self.assertEqual(v["attribute"]["skill"], "Voice", out)
85
+ self.assertEqual(v["attribute"]["value"], "Phone", out)
86
+
87
+ def test_bare_standard_field_is_entity_qualified(self):
88
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
89
+ rc, out = run(["sandbox", "Case", "Priority", "Voice", "High"], path_prefix=p,
90
+ extra_env={"FAKE_STATE_WSR": "Created"})
91
+ self.assertEqual(rc, 0, out)
92
+ self.assertEqual(_last_json(out)["attribute"]["field"], "Case.Priority", out)
93
+
94
+ def test_reused_when_unchanged(self):
95
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
96
+ rc, out = run(["sandbox", "Case", "Origin", "Voice", "Phone"], path_prefix=p,
97
+ extra_env={"FAKE_STATE_WSR": "Unchanged"})
98
+ self.assertEqual(rc, 0, out)
99
+ v = _last_json(out)
100
+ self.assertEqual(v["status"], "reused", out)
101
+
102
+ def test_blocks_when_referenced_skill_missing(self):
103
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
104
+ rc, out = run(["sandbox", "Case", "Origin", "Voice", "Phone"], path_prefix=p,
105
+ extra_env={"FAKE_SKILL_PRESENT": "0"})
106
+ self.assertEqual(rc, 1, out)
107
+ v = _last_json(out)
108
+ self.assertEqual(v["status"], "blocked", out)
109
+ self.assertTrue(any(a.get("target_skill") == "service-omni-skills-based-routing-configure"
110
+ for a in v["manual_actions"]), out)
111
+
112
+ def test_graceful_degrade_when_feature_unavailable(self):
113
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
114
+ rc, out = run(["sandbox", "Case", "Origin", "Voice", "Phone"], path_prefix=p,
115
+ extra_env={"FAKE_DEPLOY_FAIL": "1"})
116
+ self.assertEqual(rc, 1, out)
117
+ v = _last_json(out)
118
+ self.assertEqual(v["status"], "blocked", out)
119
+ self.assertIn("not available", v["blocking_issue"].lower())
120
+
121
+ def test_plan_only_reports_without_writing(self):
122
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
123
+ calllog = str(Path(p) / "calls.log")
124
+ rc, out = run(["sandbox", "Case", "Origin", "Voice", "Phone"], path_prefix=p,
125
+ extra_env={"PLAN_ONLY": "1", "FAKE_SF_CALLLOG": calllog})
126
+ log = Path(calllog).read_text() if Path(calllog).exists() else ""
127
+ self.assertEqual(rc, 0, out)
128
+ v = _last_json(out)
129
+ self.assertTrue(v.get("plan_mode"), out)
130
+ self.assertEqual(v["status"], "action_needed", out)
131
+ self.assertNotIn("project deploy", log, "plan mode must not deploy")
132
+
133
+
134
+ class MergeSafeTests(unittest.TestCase):
135
+ def test_preserves_existing_mapping(self):
136
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
137
+ rc, out = run(["sandbox", "Case", "Origin", "Voice_Support", "Phone"], path_prefix=p,
138
+ extra_env={"FAKE_WSR_EXISTING": "1", "FAKE_STATE_WSR": "Changed"})
139
+ self.assertEqual(rc, 0, out)
140
+ merge = _last_json(out)["merge"]
141
+ self.assertEqual(merge["attribute_count"], 2, out)
142
+ self.assertEqual(merge["preserved_count"], 1, out)
143
+
144
+ def test_omits_null_optional_values_from_preserved_mapping(self):
145
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
146
+ rc, out = run(["sandbox", "Case", "Origin", "Voice_Support", "Phone"], path_prefix=p,
147
+ extra_env={"FAKE_WSR_EXISTING": "1", "FAKE_WSR_NULL_OPTIONALS": "1",
148
+ "FAKE_STATE_WSR": "Changed"})
149
+ self.assertEqual(rc, 0, out)
150
+ self.assertEqual(_last_json(out)["merge"]["preserved_count"], 1, out)
151
+
152
+ def test_inconclusive_read_blocks_destructive_replace(self):
153
+ with FakeSf(_FAKE_SF_SANDBOX) as p:
154
+ rc, out = run(["sandbox", "Case", "Origin", "Voice_Support", "Phone"], path_prefix=p,
155
+ extra_env={"FAKE_WSR_INCONCLUSIVE": "1"})
156
+ self.assertEqual(rc, 1, out)
157
+ self.assertIn("WSR_REPLACE", _last_json(out)["blocking_issue"])
158
+
159
+
160
+ if __name__ == "__main__":
161
+ unittest.main(verbosity=2)