@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,538 @@
1
+ #!/usr/bin/env bash
2
+ # upsert-and-report.sh - detect + POST (create) or PATCH (align) a QueueRoutingConfig via the Data API; an omitted routing-model preserves an existing record, idempotent (SOQL pre/post-check). Args, env overrides, output contract: SKILL.md.
3
+
4
+ set -euo pipefail
5
+
6
+ if [ $# -lt 1 ]; then
7
+ echo '{"error":"Missing required argument: org-alias","usage":"bash upsert-and-report.sh <org-alias> [routing-model] [developer-name] [master-label] [capacity-weight] [routing-priority]"}' >&2
8
+ exit 1
9
+ fi
10
+
11
+ ORG="$1"
12
+
13
+ # Optional routing-model input: 2nd positional arg, or ROUTING_MODEL_INPUT env var (coordinator path).
14
+ # Empty => preserve existing / use default-on-create (see header).
15
+ REQUESTED_ROUTING_MODEL="${2:-${ROUTING_MODEL_INPUT:-}}"
16
+ ALLOWED_ROUTING_MODELS="MostAvailable LeastActive"
17
+ if [ -n "$REQUESTED_ROUTING_MODEL" ]; then
18
+ case " $ALLOWED_ROUTING_MODELS " in
19
+ *" $REQUESTED_ROUTING_MODEL "*) : ;;
20
+ *) echo "{\"error\":\"Invalid routing-model '$REQUESTED_ROUTING_MODEL'. Allowed: $ALLOWED_ROUTING_MODELS\"}" >&2; exit 1 ;;
21
+ esac
22
+ fi
23
+
24
+ # Routing target drives the canonical naming defaults when no explicit developer-name/label is given.
25
+ ROUTING_TARGET="${QRC_ROUTING_TARGET:-Case}"
26
+ case "$ROUTING_TARGET" in
27
+ Case) TARGET_DEFAULT_DN="Case_Routing_Config"; TARGET_DEFAULT_LABEL="Case Routing Config" ;;
28
+ VoiceCall) TARGET_DEFAULT_DN="Voice_Routing_Config"; TARGET_DEFAULT_LABEL="Voice Routing Config" ;;
29
+ Incident) TARGET_DEFAULT_DN="Incident_Routing_Config"; TARGET_DEFAULT_LABEL="Incident Routing Config" ;;
30
+ MessagingSession) TARGET_DEFAULT_DN="Messaging_Routing_Config"; TARGET_DEFAULT_LABEL="Messaging Routing Config" ;;
31
+ *) echo "{\"error\":\"Invalid QRC_ROUTING_TARGET '$ROUTING_TARGET'. Allowed: Case, VoiceCall, Incident, MessagingSession\"}" >&2; exit 1 ;;
32
+ esac
33
+
34
+ # Resolve DeveloperName / MasterLabel / capacity / priority from positional args, then env, then
35
+ # create-time defaults. On an existing record, omitted values are replaced with persisted values
36
+ # after the pre-check so a targeted rerun does not reset operator-managed configuration.
37
+ DEVELOPER_NAME="${3:-${QRC_DEVELOPER_NAME:-$TARGET_DEFAULT_DN}}"
38
+ MASTER_LABEL="${4:-${QRC_MASTER_LABEL:-$TARGET_DEFAULT_LABEL}}"
39
+ CAPACITY_WEIGHT="${5:-${QRC_CAPACITY_WEIGHT:-5}}"
40
+ ROUTING_PRIORITY="${6:-${QRC_ROUTING_PRIORITY:-1}}"
41
+ MASTER_LABEL_EXPLICIT=false
42
+ { [ -n "${4:-}" ] || [ -n "${QRC_MASTER_LABEL:-}" ]; } && MASTER_LABEL_EXPLICIT=true
43
+ ROUTING_PRIORITY_EXPLICIT=false
44
+ { [ -n "${6:-}" ] || [ -n "${QRC_ROUTING_PRIORITY:-}" ]; } && ROUTING_PRIORITY_EXPLICIT=true
45
+ # Optional fallback/overflow assignee (QueueRoutingConfig.OverflowAssigneeId): a Username or 005 Id.
46
+ # Empty preserves an existing value (never cleared). 7th positional, or QRC_OVERFLOW_ASSIGNEE env.
47
+ OVERFLOW_ASSIGNEE_INPUT="${7:-${QRC_OVERFLOW_ASSIGNEE:-}}"
48
+
49
+ # CapacityWeight and CapacityPercentage are mutually exclusive. The steel-thread
50
+ # contract uses percentage mode (100%); callers that omit it keep weight mode.
51
+ CAPACITY_PERCENTAGE_INPUT="${QRC_CAPACITY_PERCENTAGE:-}"
52
+ WEIGHT_EXPLICIT=false
53
+ { [ -n "${5:-}" ] || [ -n "${QRC_CAPACITY_WEIGHT:-}" ]; } && WEIGHT_EXPLICIT=true
54
+ CAPACITY_MODE="weight"
55
+ if [ -n "$CAPACITY_PERCENTAGE_INPUT" ]; then
56
+ CAPACITY_MODE="percentage"
57
+ if [ "$WEIGHT_EXPLICIT" = "true" ]; then
58
+ echo '{"error":"capacity-weight and capacity-percentage are mutually exclusive; provide only one."}' >&2
59
+ exit 1
60
+ fi
61
+ if ! [[ "$CAPACITY_PERCENTAGE_INPUT" =~ ^[0-9]+([.][0-9]+)?$ ]] \
62
+ || awk "BEGIN{exit !($CAPACITY_PERCENTAGE_INPUT < 0 || $CAPACITY_PERCENTAGE_INPUT > 100)}"; then
63
+ echo "{\"error\":\"Invalid capacity-percentage '$CAPACITY_PERCENTAGE_INPUT'. Must be a number 0..100.\"}" >&2
64
+ exit 1
65
+ fi
66
+ fi
67
+
68
+ # PushTimeout is opt-in. Omitted input preserves an existing value and is left
69
+ # out of a create; an explicit value is written and verified after the write.
70
+ PUSH_TIMEOUT_INPUT="${8:-${QRC_PUSH_TIMEOUT:-}}"
71
+ if [ -n "$PUSH_TIMEOUT_INPUT" ] \
72
+ && { ! [[ "$PUSH_TIMEOUT_INPUT" =~ ^[0-9]+$ ]] || [ "$PUSH_TIMEOUT_INPUT" -gt 3600 ]; }; then
73
+ echo "{\"error\":\"Invalid push-timeout '$PUSH_TIMEOUT_INPUT'. Must be integer seconds 0..3600.\"}" >&2
74
+ exit 1
75
+ fi
76
+
77
+ # Validate DeveloperName grammar (interpolated into SOQL below - reject injection-shaped input).
78
+ if ! [[ "$DEVELOPER_NAME" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
79
+ echo "{\"error\":\"Invalid developer-name '$DEVELOPER_NAME'. Must start with a letter, only letters/digits/underscore (max 80).\"}" >&2
80
+ exit 1
81
+ fi
82
+ if ! [[ "$CAPACITY_WEIGHT" =~ ^[0-9]+$ ]] || [ "$CAPACITY_WEIGHT" -lt 1 ] || [ "$CAPACITY_WEIGHT" -gt 100 ]; then
83
+ echo "{\"error\":\"Invalid capacity-weight '$CAPACITY_WEIGHT'. Must be integer 1..100.\"}" >&2
84
+ exit 1
85
+ fi
86
+ if ! [[ "$ROUTING_PRIORITY" =~ ^[0-9]+$ ]] || [ "$ROUTING_PRIORITY" -lt 1 ]; then
87
+ echo "{\"error\":\"Invalid routing-priority '$ROUTING_PRIORITY'. Must be integer >= 1.\"}" >&2
88
+ exit 1
89
+ fi
90
+ # Validate the overflow assignee (005 Id or Username) before any sf call; it's interpolated into SOQL.
91
+ if [ -n "$OVERFLOW_ASSIGNEE_INPUT" ] \
92
+ && ! [[ "$OVERFLOW_ASSIGNEE_INPUT" =~ ^(005[A-Za-z0-9]{12}([A-Za-z0-9]{3})?|[A-Za-z0-9._@+-]{3,240})$ ]]; then
93
+ echo "{\"error\":\"Invalid overflow-assignee '$OVERFLOW_ASSIGNEE_INPUT'. Provide an active User Username or a 005 User Id.\"}" >&2
94
+ exit 1
95
+ fi
96
+
97
+ DEFAULT_ROUTING_MODEL="MostAvailable"
98
+ # Model for a new record; recomputed below to preserve an existing record's model when unspecified.
99
+ ROUTING_MODEL="${REQUESTED_ROUTING_MODEL:-$DEFAULT_ROUTING_MODEL}"
100
+ # RoutingPriority is required on QueueRoutingConfig (omitting it fails with REQUIRED_FIELD_MISSING);
101
+ # 1 = highest, the canonical demo default.
102
+ IS_ATTRIBUTE_BASED=false
103
+ API_VERSION="v66.0"
104
+
105
+ if ! sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
106
+ echo "{\"error\":\"Org alias '$ORG' is not authenticated.\"}" >&2
107
+ exit 1
108
+ fi
109
+
110
+ # Write guard: POSTs/PATCHes a QueueRoutingConfig, so self-enforce the production-org guard (directly invokable).
111
+ ORG_GUARD_JSON=$(sf data query --target-org "$ORG" \
112
+ --query "SELECT IsSandbox, TrialExpirationDate, OrganizationType FROM Organization LIMIT 1" \
113
+ --json 2>/dev/null) || {
114
+ echo "{\"error\":\"Failed to query Organization for safe_to_write guard: $(printf '%s' "$ORG_GUARD_JSON" | head -c 300)\"}" >&2
115
+ exit 1
116
+ }
117
+ IS_SANDBOX=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].IsSandbox')
118
+ TRIAL_EXP=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].TrialExpirationDate // "null"')
119
+ ORG_TYPE=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].OrganizationType')
120
+
121
+ SAFE_TO_WRITE=false
122
+ if [ "$IS_SANDBOX" = "true" ] \
123
+ || [ "$TRIAL_EXP" != "null" ] \
124
+ || [ "$ORG_TYPE" = "Developer Edition" ] \
125
+ || [ "$ORG_TYPE" = "Base Edition" ]; then
126
+ SAFE_TO_WRITE=true
127
+ fi
128
+
129
+ if [ "$SAFE_TO_WRITE" != "true" ]; then
130
+ echo "{\"skill\":\"service-omni-queue-routing-config-deploy\",\"status\":\"blocked\",\"blocking_issue\":\"Refusing to upsert a QueueRoutingConfig on a real production customer org (IsSandbox=$IS_SANDBOX, TrialExpirationDate=$TRIAL_EXP, OrganizationType=$ORG_TYPE). Target a sandbox, trial CDO, or Developer Edition org.\",\"safe_to_write\":false}" >&2
131
+ exit 1
132
+ fi
133
+
134
+ # Resolve the optional overflow assignee to an active-User Id (read-only). OVERFLOW_REQUESTED
135
+ # distinguishes "set/change overflow" from "leave it alone".
136
+ OVERFLOW_REQUESTED=false
137
+ DESIRED_OVERFLOW_ID=""
138
+ if [ -n "$OVERFLOW_ASSIGNEE_INPUT" ]; then
139
+ OVERFLOW_REQUESTED=true
140
+ if [[ "$OVERFLOW_ASSIGNEE_INPUT" =~ ^005[A-Za-z0-9]{12}([A-Za-z0-9]{3})?$ ]]; then
141
+ OV_JSON=$(sf data query --target-org "$ORG" --json \
142
+ --query "SELECT Id, IsActive FROM User WHERE Id='$OVERFLOW_ASSIGNEE_INPUT'" 2>/dev/null || true)
143
+ else
144
+ OV_JSON=$(sf data query --target-org "$ORG" --json \
145
+ --query "SELECT Id, IsActive FROM User WHERE Username='$OVERFLOW_ASSIGNEE_INPUT'" 2>/dev/null || true)
146
+ fi
147
+ if ! echo "$OV_JSON" | jq -e '.result.totalSize | numbers' >/dev/null 2>&1; then
148
+ echo "{\"skill\":\"service-omni-queue-routing-config-deploy\",\"status\":\"blocked\",\"blocking_issue\":\"Overflow-assignee lookup was inconclusive for '$OVERFLOW_ASSIGNEE_INPUT'. Raw: $(printf '%s' "$OV_JSON" | head -c 200)\"}" >&2
149
+ exit 1
150
+ fi
151
+ if [ "$(echo "$OV_JSON" | jq -r '.result.totalSize')" != "1" ]; then
152
+ echo "{\"skill\":\"service-omni-queue-routing-config-deploy\",\"status\":\"blocked\",\"blocking_issue\":\"Overflow assignee '$OVERFLOW_ASSIGNEE_INPUT' did not resolve to exactly one User.\"}" >&2
153
+ exit 1
154
+ fi
155
+ if [ "$(echo "$OV_JSON" | jq -r '.result.records[0].IsActive')" != "true" ]; then
156
+ echo "{\"skill\":\"service-omni-queue-routing-config-deploy\",\"status\":\"blocked\",\"blocking_issue\":\"Overflow assignee '$OVERFLOW_ASSIGNEE_INPUT' is not an active User; overflow work would never be delivered.\"}" >&2
157
+ exit 1
158
+ fi
159
+ DESIRED_OVERFLOW_ID=$(echo "$OV_JSON" | jq -r '.result.records[0].Id')
160
+ fi
161
+
162
+ # emit read-only plan preview JSON and exit 0 (PLAN_ONLY mode)
163
+ emit_plan() {
164
+ local status="$1"
165
+ local detail="$2"
166
+ jq -n \
167
+ --arg status "$status" \
168
+ --arg detail "$detail" \
169
+ --arg dn "$DEVELOPER_NAME" \
170
+ '{
171
+ skill: "service-omni-queue-routing-config-deploy",
172
+ status: $status,
173
+ plan_mode: true,
174
+ plan_detail: $detail,
175
+ developer_name: $dn,
176
+ manual_actions: [],
177
+ blocking_issue: null
178
+ }'
179
+ exit 0
180
+ }
181
+
182
+ # Step 1 - Pre-check: query existing record by DeveloperName
183
+ QUERY_JSON=$(sf data query --target-org "$ORG" \
184
+ --query "SELECT Id, DeveloperName, MasterLabel, RoutingModel, RoutingPriority, IsAttributeBased, CapacityWeight, CapacityPercentage, PushTimeout, OverflowAssigneeId FROM QueueRoutingConfig WHERE DeveloperName='$DEVELOPER_NAME'" \
185
+ --json 2>/dev/null || true)
186
+
187
+ # Check for OmniChannelSettings-not-enabled error (would surface as INVALID_TYPE)
188
+ if echo "$QUERY_JSON" | jq -e '.name == "INVALID_TYPE"' >/dev/null 2>&1; then
189
+ jq -n '{
190
+ skill: "service-omni-queue-routing-config-deploy",
191
+ status: "blocked",
192
+ developer_name: "'"$DEVELOPER_NAME"'",
193
+ id: null,
194
+ manual_actions: [],
195
+ blocking_issue: "OmniChannelSettings is not enabled on this org. Run service-omni-base-settings-configure first."
196
+ }'
197
+ exit 1
198
+ fi
199
+
200
+ # Only a numeric totalSize is authoritative; an inconclusive read must not be treated as absence
201
+ # (which would trigger a spurious create).
202
+ if ! echo "$QUERY_JSON" | jq -e '.result.totalSize | numbers' >/dev/null 2>&1; then
203
+ jq -n --arg raw "$(printf '%s' "$QUERY_JSON" | head -c 400)" '{
204
+ skill: "service-omni-queue-routing-config-deploy",
205
+ status: "blocked",
206
+ developer_name: "'"$DEVELOPER_NAME"'",
207
+ id: null,
208
+ manual_actions: [],
209
+ blocking_issue: ("QueueRoutingConfig pre-check query was inconclusive (not a clean zero-record result). Not creating on an inconclusive read. Raw: " + $raw)
210
+ }'
211
+ exit 1
212
+ fi
213
+
214
+ RECORD_COUNT=$(echo "$QUERY_JSON" | jq -r '.result.totalSize // 0' 2>/dev/null || echo "0")
215
+
216
+ # Step 2 - Decide action based on existing state
217
+ EXISTING_ID=""
218
+ BEFORE_CAPACITY_WEIGHT="null"
219
+ BEFORE_CAPACITY_PERCENTAGE="null"
220
+ BEFORE_PUSH_TIMEOUT="null"
221
+ BEFORE_ROUTING_MODEL="null"
222
+ BEFORE_MASTER_LABEL="null"
223
+ BEFORE_ROUTING_PRIORITY="null"
224
+ BEFORE_IS_ATTRIBUTE_BASED="false"
225
+ BEFORE_OVERFLOW_ASSIGNEE="null"
226
+ BEFORE_EXISTED=false
227
+ NEEDS_UPDATE=false
228
+
229
+ if [ "$RECORD_COUNT" -gt 0 ]; then
230
+ BEFORE_EXISTED=true
231
+ EXISTING_ID=$(echo "$QUERY_JSON" | jq -r '.result.records[0].Id')
232
+ BEFORE_CAPACITY_WEIGHT=$(echo "$QUERY_JSON" | jq -r '.result.records[0].CapacityWeight // "null"')
233
+ BEFORE_ROUTING_MODEL=$(echo "$QUERY_JSON" | jq -r '.result.records[0].RoutingModel // "null"')
234
+ BEFORE_MASTER_LABEL=$(echo "$QUERY_JSON" | jq -r '.result.records[0].MasterLabel // "null"')
235
+ BEFORE_ROUTING_PRIORITY=$(echo "$QUERY_JSON" | jq -r '.result.records[0].RoutingPriority // "null"')
236
+ BEFORE_IS_ATTRIBUTE_BASED=$(echo "$QUERY_JSON" | jq -r '.result.records[0].IsAttributeBased // false')
237
+ BEFORE_OVERFLOW_ASSIGNEE=$(echo "$QUERY_JSON" | jq -r '.result.records[0].OverflowAssigneeId // "null"')
238
+ BEFORE_CAPACITY_PERCENTAGE=$(echo "$QUERY_JSON" | jq -r '.result.records[0].CapacityPercentage // "null"')
239
+ BEFORE_PUSH_TIMEOUT=$(echo "$QUERY_JSON" | jq -r '.result.records[0].PushTimeout // "null"')
240
+
241
+ # Preserve the org's routing model when none was explicitly requested (an explicit request wins).
242
+ if [ -z "$REQUESTED_ROUTING_MODEL" ] && [ "$BEFORE_ROUTING_MODEL" != "null" ]; then
243
+ ROUTING_MODEL="$BEFORE_ROUTING_MODEL"
244
+ fi
245
+ if [ "$MASTER_LABEL_EXPLICIT" = "false" ] && [ "$BEFORE_MASTER_LABEL" != "null" ]; then
246
+ MASTER_LABEL="$BEFORE_MASTER_LABEL"
247
+ fi
248
+ if [ "$ROUTING_PRIORITY_EXPLICIT" = "false" ] && [ "$BEFORE_ROUTING_PRIORITY" != "null" ]; then
249
+ ROUTING_PRIORITY="$BEFORE_ROUTING_PRIORITY"
250
+ fi
251
+ # This leaf does not expose an attribute-based-routing input; preserve that independent setting.
252
+ IS_ATTRIBUTE_BASED="$BEFORE_IS_ATTRIBUTE_BASED"
253
+ if [ -z "$CAPACITY_PERCENTAGE_INPUT" ] && [ "$WEIGHT_EXPLICIT" = "false" ]; then
254
+ if [ "$BEFORE_CAPACITY_PERCENTAGE" != "null" ]; then
255
+ CAPACITY_MODE="percentage"
256
+ CAPACITY_PERCENTAGE_INPUT="$BEFORE_CAPACITY_PERCENTAGE"
257
+ elif [ "$BEFORE_CAPACITY_WEIGHT" != "null" ]; then
258
+ CAPACITY_MODE="weight"
259
+ CAPACITY_WEIGHT="$BEFORE_CAPACITY_WEIGHT"
260
+ fi
261
+ fi
262
+
263
+ # Compare each field against the effective target values; if any differ, mark for PATCH
264
+ if [ "$BEFORE_ROUTING_MODEL" != "$ROUTING_MODEL" ] \
265
+ || [ "$BEFORE_MASTER_LABEL" != "$MASTER_LABEL" ] \
266
+ || [ "$BEFORE_ROUTING_PRIORITY" != "$ROUTING_PRIORITY" ] \
267
+ || [ "$BEFORE_IS_ATTRIBUTE_BASED" != "$IS_ATTRIBUTE_BASED" ]; then
268
+ NEEDS_UPDATE=true
269
+ fi
270
+ if [ "$CAPACITY_MODE" = "percentage" ]; then
271
+ if [ "$BEFORE_CAPACITY_PERCENTAGE" = "null" ] \
272
+ || awk "BEGIN{exit !($BEFORE_CAPACITY_PERCENTAGE != $CAPACITY_PERCENTAGE_INPUT)}"; then
273
+ NEEDS_UPDATE=true
274
+ fi
275
+ elif [ "$BEFORE_CAPACITY_WEIGHT" != "$CAPACITY_WEIGHT" ]; then
276
+ NEEDS_UPDATE=true
277
+ fi
278
+ if [ -n "$PUSH_TIMEOUT_INPUT" ] && [ "$BEFORE_PUSH_TIMEOUT" != "$PUSH_TIMEOUT_INPUT" ]; then
279
+ NEEDS_UPDATE=true
280
+ fi
281
+ # Overflow drift counts only when an explicit assignee differs from the persisted one.
282
+ if [ "$OVERFLOW_REQUESTED" = "true" ] && [ "$BEFORE_OVERFLOW_ASSIGNEE" != "$DESIRED_OVERFLOW_ID" ]; then
283
+ NEEDS_UPDATE=true
284
+ fi
285
+ fi
286
+
287
+ OVERFLOW_PLAN_NOTE=""
288
+ [ "$OVERFLOW_REQUESTED" = "true" ] && OVERFLOW_PLAN_NOTE=" Fallback/overflow assignee would be set to User $DESIRED_OVERFLOW_ID."
289
+ [ "$CAPACITY_MODE" = "percentage" ] && OVERFLOW_PLAN_NOTE=" Capacity model: percentage=${CAPACITY_PERCENTAGE_INPUT}%.$OVERFLOW_PLAN_NOTE"
290
+ [ -n "$PUSH_TIMEOUT_INPUT" ] && OVERFLOW_PLAN_NOTE=" PushTimeout=${PUSH_TIMEOUT_INPUT}s.$OVERFLOW_PLAN_NOTE"
291
+
292
+ # PLAN_ONLY: Steps 1-2 are read-only. Report what a --run would do and stop before any write.
293
+ if [ "${PLAN_ONLY:-}" = "1" ]; then
294
+ if [ "$BEFORE_EXISTED" = "false" ]; then
295
+ emit_plan "action_needed" "Would create QueueRoutingConfig '$DEVELOPER_NAME' with v1-canonical values.$OVERFLOW_PLAN_NOTE"
296
+ elif [ "$NEEDS_UPDATE" = "true" ]; then
297
+ emit_plan "action_needed" "QueueRoutingConfig '$DEVELOPER_NAME' exists but drifts from v1-canonical values; would PATCH.$OVERFLOW_PLAN_NOTE"
298
+ else
299
+ emit_plan "reused" "QueueRoutingConfig '$DEVELOPER_NAME' already matches v1-canonical values.$OVERFLOW_PLAN_NOTE"
300
+ fi
301
+ fi
302
+
303
+ # Step 3 - POST (create) or PATCH (update) or no-op (reused)
304
+ STATUS="reused"
305
+ FINAL_ID="$EXISTING_ID"
306
+
307
+ if [ "$BEFORE_EXISTED" = "false" ]; then
308
+ # POST new record
309
+ STATUS="created"
310
+ POST_BODY=$(jq -n \
311
+ --arg dn "$DEVELOPER_NAME" \
312
+ --arg ml "$MASTER_LABEL" \
313
+ --arg rm "$ROUTING_MODEL" \
314
+ --argjson rp "$ROUTING_PRIORITY" \
315
+ --argjson iab "$IS_ATTRIBUTE_BASED" \
316
+ '{
317
+ DeveloperName: $dn,
318
+ MasterLabel: $ml,
319
+ RoutingModel: $rm,
320
+ RoutingPriority: $rp,
321
+ IsAttributeBased: $iab
322
+ }')
323
+ if [ "$CAPACITY_MODE" = "percentage" ]; then
324
+ POST_BODY=$(echo "$POST_BODY" | jq --argjson cp "$CAPACITY_PERCENTAGE_INPUT" '. + {CapacityPercentage: $cp}')
325
+ else
326
+ POST_BODY=$(echo "$POST_BODY" | jq --argjson cw "$CAPACITY_WEIGHT" '. + {CapacityWeight: $cw}')
327
+ fi
328
+ if [ -n "$PUSH_TIMEOUT_INPUT" ]; then
329
+ POST_BODY=$(echo "$POST_BODY" | jq --argjson pt "$PUSH_TIMEOUT_INPUT" '. + {PushTimeout: $pt}')
330
+ fi
331
+ # Only set OverflowAssigneeId when explicitly requested (never write a null on create).
332
+ if [ "$OVERFLOW_REQUESTED" = "true" ]; then
333
+ POST_BODY=$(echo "$POST_BODY" | jq --arg oa "$DESIRED_OVERFLOW_ID" '. + {OverflowAssigneeId: $oa}')
334
+ fi
335
+
336
+ # sf api request rest (beta) writes a warning to STDERR; redirect it so stdout is pure JSON.
337
+ POST_STDERR="$(mktemp)"
338
+ POST_RESULT=$(sf api request rest --target-org "$ORG" \
339
+ "/services/data/$API_VERSION/sobjects/QueueRoutingConfig" \
340
+ --method POST \
341
+ --body "$POST_BODY" 2>"$POST_STDERR" || true)
342
+ rm -f "$POST_STDERR"
343
+
344
+ POST_SUCCESS=$(echo "$POST_RESULT" | jq -r '.success // false' 2>/dev/null || echo "false")
345
+ if [ "$POST_SUCCESS" != "true" ]; then
346
+ ERR_MSG=$(echo "$POST_RESULT" | jq -r '.[0].message // .message // "Unknown POST error"' 2>/dev/null)
347
+ jq -n \
348
+ --arg blocking "POST failed: $ERR_MSG" \
349
+ --arg dn "$DEVELOPER_NAME" \
350
+ '{
351
+ skill: "service-omni-queue-routing-config-deploy",
352
+ status: "blocked",
353
+ developer_name: $dn,
354
+ id: null,
355
+ manual_actions: [],
356
+ blocking_issue: $blocking
357
+ }'
358
+ exit 1
359
+ fi
360
+ FINAL_ID=$(echo "$POST_RESULT" | jq -r '.id')
361
+
362
+ elif [ "$NEEDS_UPDATE" = "true" ]; then
363
+ # PATCH existing record
364
+ STATUS="updated"
365
+ PATCH_BODY=$(jq -n \
366
+ --arg ml "$MASTER_LABEL" \
367
+ --arg rm "$ROUTING_MODEL" \
368
+ --argjson rp "$ROUTING_PRIORITY" \
369
+ --argjson iab "$IS_ATTRIBUTE_BASED" \
370
+ '{
371
+ MasterLabel: $ml,
372
+ RoutingModel: $rm,
373
+ RoutingPriority: $rp,
374
+ IsAttributeBased: $iab
375
+ }')
376
+ if [ "$CAPACITY_MODE" = "percentage" ]; then
377
+ PATCH_BODY=$(echo "$PATCH_BODY" | jq --argjson cp "$CAPACITY_PERCENTAGE_INPUT" '. + {CapacityPercentage: $cp}')
378
+ [ "${BEFORE_CAPACITY_WEIGHT:-null}" = "null" ] || PATCH_BODY=$(echo "$PATCH_BODY" | jq '. + {CapacityWeight: null}')
379
+ else
380
+ PATCH_BODY=$(echo "$PATCH_BODY" | jq --argjson cw "$CAPACITY_WEIGHT" '. + {CapacityWeight: $cw}')
381
+ [ "${BEFORE_CAPACITY_PERCENTAGE:-null}" = "null" ] || PATCH_BODY=$(echo "$PATCH_BODY" | jq '. + {CapacityPercentage: null}')
382
+ fi
383
+ if [ -n "$PUSH_TIMEOUT_INPUT" ]; then
384
+ PATCH_BODY=$(echo "$PATCH_BODY" | jq --argjson pt "$PUSH_TIMEOUT_INPUT" '. + {PushTimeout: $pt}')
385
+ fi
386
+ # Only touch OverflowAssigneeId when explicitly requested - an omitted input leaves it as-is.
387
+ if [ "$OVERFLOW_REQUESTED" = "true" ]; then
388
+ PATCH_BODY=$(echo "$PATCH_BODY" | jq --arg oa "$DESIRED_OVERFLOW_ID" '. + {OverflowAssigneeId: $oa}')
389
+ fi
390
+
391
+ # PATCH returns 204 (empty) on success; detect success by the absence of an error field.
392
+ PATCH_STDERR="$(mktemp)"
393
+ PATCH_RESULT=$(sf api request rest --target-org "$ORG" \
394
+ "/services/data/$API_VERSION/sobjects/QueueRoutingConfig/$EXISTING_ID" \
395
+ --method PATCH \
396
+ --body "$PATCH_BODY" 2>"$PATCH_STDERR" || true)
397
+ rm -f "$PATCH_STDERR"
398
+
399
+ # PATCH success = empty result OR result contains success:true. Error = has errorCode or message with error.
400
+ if echo "$PATCH_RESULT" | jq -e 'if type == "array" then .[0].errorCode else .errorCode end' >/dev/null 2>&1; then
401
+ ERR_MSG=$(echo "$PATCH_RESULT" | jq -r 'if type == "array" then .[0].message else .message end' 2>/dev/null)
402
+ jq -n \
403
+ --arg blocking "PATCH failed: $ERR_MSG" \
404
+ --arg dn "$DEVELOPER_NAME" \
405
+ --arg id "$EXISTING_ID" \
406
+ '{
407
+ skill: "service-omni-queue-routing-config-deploy",
408
+ status: "blocked",
409
+ developer_name: $dn,
410
+ id: $id,
411
+ manual_actions: [],
412
+ blocking_issue: $blocking
413
+ }'
414
+ exit 1
415
+ fi
416
+ fi
417
+
418
+ # Step 4 - Post-check: re-query to confirm final state
419
+ FINAL_JSON=$(sf data query --target-org "$ORG" \
420
+ --query "SELECT Id, DeveloperName, MasterLabel, RoutingModel, RoutingPriority, IsAttributeBased, CapacityWeight, CapacityPercentage, PushTimeout, OverflowAssigneeId FROM QueueRoutingConfig WHERE Id='$FINAL_ID'" \
421
+ --json 2>/dev/null)
422
+
423
+ if [ "$(echo "$FINAL_JSON" | jq -r '.result.totalSize')" != "1" ]; then
424
+ jq -n \
425
+ --arg blocking "Post-check re-query returned unexpected count. Expected 1, got $(echo "$FINAL_JSON" | jq -r '.result.totalSize')." \
426
+ --arg dn "$DEVELOPER_NAME" \
427
+ --arg id "$FINAL_ID" \
428
+ '{
429
+ skill: "service-omni-queue-routing-config-deploy",
430
+ status: "blocked",
431
+ developer_name: $dn,
432
+ id: $id,
433
+ manual_actions: [],
434
+ blocking_issue: $blocking
435
+ }'
436
+ exit 1
437
+ fi
438
+
439
+ # Read the persisted values back (reporting input constants would mask a silently coerced field).
440
+ ACTUAL_DN=$(echo "$FINAL_JSON" | jq -r '.result.records[0].DeveloperName // "null"')
441
+ ACTUAL_ML=$(echo "$FINAL_JSON" | jq -r '.result.records[0].MasterLabel // "null"')
442
+ ACTUAL_RM=$(echo "$FINAL_JSON" | jq -r '.result.records[0].RoutingModel // "null"')
443
+ ACTUAL_CW=$(echo "$FINAL_JSON" | jq -r '.result.records[0].CapacityWeight // "null"')
444
+ ACTUAL_CP=$(echo "$FINAL_JSON" | jq -r '.result.records[0].CapacityPercentage // "null"')
445
+ ACTUAL_RP=$(echo "$FINAL_JSON" | jq -r '.result.records[0].RoutingPriority // "null"')
446
+ # IsAttributeBased is boolean; read it explicitly since jq's // would map a genuine false to "null".
447
+ ACTUAL_IAB=$(echo "$FINAL_JSON" | jq -r '.result.records[0].IsAttributeBased | if . == null then "null" else tostring end')
448
+ ACTUAL_PT=$(echo "$FINAL_JSON" | jq -r '.result.records[0].PushTimeout')
449
+ ACTUAL_OAI=$(echo "$FINAL_JSON" | jq -r '.result.records[0].OverflowAssigneeId')
450
+
451
+ # For a create/update, the persisted values must equal what we wrote; a mismatch blocks.
452
+ if [ "$STATUS" = "created" ] || [ "$STATUS" = "updated" ]; then
453
+ MISMATCHES=""
454
+ [ "$ACTUAL_ML" = "$MASTER_LABEL" ] || MISMATCHES="$MISMATCHES MasterLabel(got='$ACTUAL_ML' want='$MASTER_LABEL')"
455
+ [ "$ACTUAL_RM" = "$ROUTING_MODEL" ] || MISMATCHES="$MISMATCHES RoutingModel(got='$ACTUAL_RM' want='$ROUTING_MODEL')"
456
+ if [ "$CAPACITY_MODE" = "percentage" ]; then
457
+ if [ "$ACTUAL_CP" = "null" ] \
458
+ || awk "BEGIN{exit !($ACTUAL_CP != $CAPACITY_PERCENTAGE_INPUT)}"; then
459
+ MISMATCHES="$MISMATCHES CapacityPercentage(got='$ACTUAL_CP' want='$CAPACITY_PERCENTAGE_INPUT')"
460
+ fi
461
+ else
462
+ [ "$ACTUAL_CW" = "$CAPACITY_WEIGHT" ] || MISMATCHES="$MISMATCHES CapacityWeight(got='$ACTUAL_CW' want='$CAPACITY_WEIGHT')"
463
+ fi
464
+ if [ -n "$PUSH_TIMEOUT_INPUT" ]; then
465
+ [ "$ACTUAL_PT" = "$PUSH_TIMEOUT_INPUT" ] || MISMATCHES="$MISMATCHES PushTimeout(got='$ACTUAL_PT' want='$PUSH_TIMEOUT_INPUT')"
466
+ fi
467
+ [ "$ACTUAL_RP" = "$ROUTING_PRIORITY" ] || MISMATCHES="$MISMATCHES RoutingPriority(got='$ACTUAL_RP' want='$ROUTING_PRIORITY')"
468
+ [ "$ACTUAL_IAB" = "$IS_ATTRIBUTE_BASED" ] || MISMATCHES="$MISMATCHES IsAttributeBased(got='$ACTUAL_IAB' want='$IS_ATTRIBUTE_BASED')"
469
+ if [ "$OVERFLOW_REQUESTED" = "true" ]; then
470
+ [ "$ACTUAL_OAI" = "$DESIRED_OVERFLOW_ID" ] || MISMATCHES="$MISMATCHES OverflowAssigneeId(got='$ACTUAL_OAI' want='$DESIRED_OVERFLOW_ID')"
471
+ fi
472
+ if [ -n "$MISMATCHES" ]; then
473
+ jq -n \
474
+ --arg dn "$DEVELOPER_NAME" \
475
+ --arg id "$FINAL_ID" \
476
+ --arg blocking "Post-write verification failed - persisted QueueRoutingConfig does not match intended values:$MISMATCHES" \
477
+ '{
478
+ skill: "service-omni-queue-routing-config-deploy",
479
+ status: "blocked",
480
+ developer_name: $dn,
481
+ id: $id,
482
+ manual_actions: [],
483
+ blocking_issue: $blocking
484
+ }'
485
+ exit 1
486
+ fi
487
+ fi
488
+
489
+ # Step 5 - Emit report JSON (actual persisted values, read back from the org)
490
+ jq -n \
491
+ --arg status "$STATUS" \
492
+ --arg id "$FINAL_ID" \
493
+ --arg actual_dn "$ACTUAL_DN" \
494
+ --arg actual_ml "$ACTUAL_ML" \
495
+ --arg actual_rm "$ACTUAL_RM" \
496
+ --arg actual_cw "$ACTUAL_CW" \
497
+ --arg actual_cp "$ACTUAL_CP" \
498
+ --arg capacity_mode "$CAPACITY_MODE" \
499
+ --arg actual_rp "$ACTUAL_RP" \
500
+ --arg actual_iab "$ACTUAL_IAB" \
501
+ --arg actual_pt "$ACTUAL_PT" \
502
+ --arg actual_oai "$ACTUAL_OAI" \
503
+ --argjson before_existed "$BEFORE_EXISTED" \
504
+ --arg before_cw "$BEFORE_CAPACITY_WEIGHT" \
505
+ --arg before_rm "$BEFORE_ROUTING_MODEL" \
506
+ --arg routing_target "$ROUTING_TARGET" \
507
+ --argjson overflow_requested "$OVERFLOW_REQUESTED" \
508
+ '{
509
+ skill: "service-omni-queue-routing-config-deploy",
510
+ status: $status,
511
+ routing_target: $routing_target,
512
+ overflow_assignee_requested: $overflow_requested,
513
+ developer_name: $actual_dn,
514
+ id: $id,
515
+ master_label: $actual_ml,
516
+ routing_model: $actual_rm,
517
+ capacity_mode: $capacity_mode,
518
+ capacity_weight: (if $actual_cw == "null" then null else ($actual_cw | tonumber?) end),
519
+ capacity_percentage: (if $actual_cp == "null" then null else ($actual_cp | tonumber?) end),
520
+ routing_priority: (if $actual_rp == "null" then null else ($actual_rp | tonumber?) end),
521
+ is_attribute_based: (if $actual_iab == "true" then true elif $actual_iab == "false" then false else null end),
522
+ push_timeout: (if $actual_pt == "null" then null else ($actual_pt | tonumber?) end),
523
+ overflow_assignee_id: (if $actual_oai == "null" then null else $actual_oai end),
524
+ values_source: "requeried",
525
+ before: (
526
+ if $before_existed then
527
+ {
528
+ existed: true,
529
+ capacity_weight: (if $before_cw == "null" then null else ($before_cw | tonumber?) end),
530
+ routing_model: (if $before_rm == "null" then null else $before_rm end)
531
+ }
532
+ else
533
+ {existed: false, capacity_weight: null, routing_model: null}
534
+ end
535
+ ),
536
+ manual_actions: [],
537
+ blocking_issue: null
538
+ }'
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: service-omni-routing-flow-deploy
3
+ description: "Use to deploy an Omni-Channel routing Flow for Case or VoiceCall and verify it routes work items to a queue or by skill. Supports the Case and VoiceCall steel thread with an autolaunched dryRun-gated variant and record-triggered QueueBased and SkillsBased variants. --trigger deploys a record-triggered variant; --routing-type SkillsBased evaluates WorkSkillRouting rules with a non-null skillOption; --runtime-proof checks routing evidence and, for SkillsBased, SkillRequirement rows. Triggers: deploy Omni routing flow for Case, deploy Omni routing flow for VoiceCall, wire VoiceCall routing, create a record-triggered Omni routing flow, verify a Case routing flow, deploy skills-based routing flow. Do not use for screen flows, platform-event-triggered flows, or Flow Tests."
4
+ allowed-tools: Bash Read Write Edit Glob Grep
5
+ metadata:
6
+ version: "1.0"
7
+ domains: ["Service"]
8
+ minApiVersion: "66.0"
9
+ relatedSkills:
10
+ - "service-omni-base-settings-configure"
11
+ - "service-omni-channel-setup-coordinate"
12
+ - "service-omni-presence-status-deploy"
13
+ - "service-omni-queue-deploy"
14
+ - "service-omni-queue-routing-config-deploy"
15
+ - "service-omni-service-channel-configure"
16
+ accessCheck:
17
+ - type: license
18
+ value: ServiceCloud
19
+ cliTools:
20
+ - tool: ["jq"]
21
+ semver: ">=1.6"
22
+ - tool: ["sf"]
23
+ semver: ">=2.139.6"
24
+ ---
25
+
26
+ # service-omni-routing-flow-deploy
27
+
28
+ > Voice runtime proof depends on a provisioned Contact Center. Flow redeploys report `Changed` even when the deployed configuration is unchanged.
29
+
30
+ Deploy an Omni-Channel routing Flow via the Metadata API, confirm it lands Active, and prove it routes work — either by CLI invocation (the autolaunched dry-run) or by observing real routing side effects (the record-triggered variant). `service-omni-channel-setup-coordinate` wires it in after `service-omni-queue-deploy` has produced the target queue and its members are bound.
31
+
32
+ ## Variants
33
+
34
+ Four assets ship under `assets/force-app/main/default/flows/` — an autolaunched and a record-triggered variant for each of Case and VoiceCall. Selection is by `--target Case|VoiceCall` (default Case) and `--trigger`.
35
+
36
+ | Variant | Shape | Proves | Routes real work? |
37
+ |---|---|---|---|
38
+ | `Omni_Route_Cases` / `Omni_Route_VoiceCalls` (default) | Autolaunched, `dryRun` gate | CLI invocability via Actions REST, no side effects | No |
39
+ | `Omni_Route_Case_Trigger` / `Omni_Route_VoiceCall_Trigger` (`--trigger`) | Record-triggered on insert, calls `routeWork` | Real routing — the insert creates `PendingServiceRouting`/`AgentWork` | Yes |
40
+
41
+ The autolaunched variant de-risks the headless CLI path cheaply; the record-triggered variant proves real records reach a queue. A full steel thread deploys the record-triggered variant.
42
+
43
+ With `--trigger --routing-type SkillsBased`, the record-triggered variant is drawn from a sibling `<FlowDN>.SkillsBased.flow-meta.xml` asset that emits `routingType=SkillsBased` plus a non-null `skillOption`. The base trigger flows hardcode `routingType=QueueBased`, so a skills-based-routing org config never takes effect at runtime — this variant is what makes it route by skill (W-24069467).
44
+
45
+ ## Inputs
46
+
47
+ ```bash
48
+ bash scripts/deploy-and-report.sh <org-alias> [flow_developer_name] [--target Case|VoiceCall] [--trigger] [--routing-type QueueBased|SkillsBased] [--skill-option RunSBRRules|DefineSkillRequirements|Both] [--runtime-proof] [--require-proof] [--skip-invoke]
49
+ # autolaunched smoke test (Case):
50
+ bash scripts/deploy-and-report.sh myorg
51
+ # record-triggered Voice routing with runtime proof:
52
+ bash scripts/deploy-and-report.sh myorg --target VoiceCall --trigger --runtime-proof
53
+ # skills-based Case routing (platform evaluates WorkSkillRouting rules):
54
+ bash scripts/deploy-and-report.sh myorg --target Case --trigger --routing-type SkillsBased --runtime-proof
55
+ ```
56
+
57
+ - `--target Case|VoiceCall` (default Case) — selects the flow pair.
58
+ - `--trigger` — deploy the record-triggered variant with dynamic token resolution.
59
+ - `--routing-type QueueBased|SkillsBased` (default QueueBased; env `OMNI_ROUTING_TYPE`) — `SkillsBased` deploys the `<FlowDN>.SkillsBased` variant that calls `routeWork` with `routingType=SkillsBased`, so the org's skills-based routing config takes effect at runtime instead of being inert. It deploys under the same DeveloperName, replacing the QueueBased trigger (never two triggers on one object).
60
+ - `--skill-option RunSBRRules|DefineSkillRequirements|Both` (default `RunSBRRules`; env `OMNI_SKILL_OPTION`; SkillsBased only) — `RunSBRRules`/`Both` make the platform evaluate the org's `WorkSkillRouting` rules server-side and attach matching `SkillRequirement` rows to the PSR; `DefineSkillRequirements` uses only skills the flow itself passes. A SkillsBased deploy with an empty/invalid skillOption is **refused** (exit 2): a null skillOption NPEs the platform `routeWork` action and rolls back the triggering insert (W-24069761), so the asset always carries a non-null value.
61
+ - `--runtime-proof` (implies `--trigger`) — after deploy, insert a target record. QueueBased accepts a `PendingServiceRouting` or `AgentWork` row. SkillsBased requires a `PendingServiceRouting` with at least one `SkillRequirement`, proving that `RunSBRRules` evaluated the active `WorkSkillRouting` rule. Proof is fail-soft unless required. The record is always deleted afterward.
62
+ - `--require-proof` (implies `--runtime-proof`; also `OMNI_RUNTIME_PROOF_REQUIRED=1`) — makes that proof blocking; the release gate for the Voice steel thread.
63
+ - `--skip-invoke` (autolaunched only) — deploy + verify Active, no Actions REST call.
64
+ - Token overrides (`--trigger`): `QUEUE_DEVELOPER_NAME`, `ROUTING_CONFIG_DEVELOPER_NAME`, `SERVICE_CHANNEL_DEVELOPER_NAME`. The coordinator always supplies these three from the resources it established for the target, so the flow binds the same channel/queue/QRC rather than a guessed default.
65
+
66
+ ## Preconditions and safety
67
+
68
+ - Target org authenticated via `sf` CLI, Service Cloud license, `sf` CLI ≥ 2.139.6, `jq` ≥ 1.6.
69
+ - Omni-Channel base settings enabled (`service-omni-base-settings-configure`) — the routing action resolves only when Omni is on; the flow deploys without it but fails at runtime.
70
+ - Record-triggered runs need a deployed ServiceChannel (`service-omni-service-channel-configure`), Queue (`service-omni-queue-deploy`), and QueueRoutingConfig (`service-omni-queue-routing-config-deploy`) to resolve their tokenized IDs; runtime proof additionally needs an agent online with a compatible presence status (`service-omni-presence-status-deploy`).
71
+ - The three-way `safe_to_write` production guard applies.
72
+
73
+ ## Run
74
+
75
+ `deploy-and-report.sh` deploys, verifies, and (per variant) proves routing:
76
+
77
+ 1. **Resolve tokens** (record-triggered only) — the committed asset carries `__QUEUE_ID__`, `__ROUTING_CONFIG_ID__`, `__SERVICE_CHANNEL_ID__`, and channel name/label tokens. The skill resolves each by DeveloperName into a materialized copy that is never committed (the asset stays tokenized for portability). A ServiceChannel that does not resolve fails closed rather than embedding an empty id.
78
+ 2. **Deploy** — `sf project deploy start --metadata "Flow:<FlowDN>"`; the `files[].state` maps to `reused`/`updated`/`created`.
79
+ 3. **Verify Active** — retrieve the Flow and confirm its resource IDs, `routingType`, and SkillsBased `skillOption` still match; otherwise redeploy. Confirm an active version exists.
80
+ 4. **Prove routing** — autolaunched: POST to `/actions/custom/flow/<FlowDN>` with `{"dryRun":true}` and assert `isSuccess` and `dryRunOk`. Record-triggered: insert a target record and observe routing. SkillsBased success additionally requires one or more `SkillRequirement` rows on the PSR.
81
+
82
+ ## Behavior
83
+
84
+ **Two shapes, two verification paths.** "Can we invoke a routing flow headlessly?" and "do real records route to a queue?" are different questions. The autolaunched `dryRun` flow answers the first in one side-effect-free HTTP call — the flow must have a code path that succeeds without prerequisites, so `dryRun=true` takes an assignment step and returns `dryRunOk=true`. Record-triggered flows are not exposed as custom actions, so the only headless proof is inserting the source record and observing `PendingServiceRouting`/`AgentWork`. The PSR row alone proves the flow routed; agent assignment depends on Omni distribution, not the flow.
85
+
86
+ **Active-version round trip.** For the record-triggered variant, component deploy state proves only the transaction, not that an active version exists. The skill prefers `FlowDefinitionView.ActiveVersionId` and falls back to the active Tooling `Flow` row when that view is unavailable. It fails closed when neither source proves an active version, so `flow_active:true` is never reported without evidence.
87
+
88
+ **Binding-drift redeploy.** `reused` means an active version already exists and its embedded queue/QRC/ServiceChannel IDs, routing type, and SkillsBased skill option match the request. If a binding or routing mode drifted, the skill redeploys instead of reusing. `OMNI_FLOW_FORCE=1` forces a redeploy.
89
+
90
+ **Idempotency quirk.** Unlike most metadata types, `Flow` deploys are not byte-idempotent — Salesforce reports `Changed` on identical redeploys because every deploy is a potential version bump. Treat `updated` from this skill as "successfully redeployed," not "operator modified the flow."
91
+
92
+ **Safety.** Deploys use explicit `--metadata "Flow:<DN>"` (never `--source-dir`) so sibling flows are untouched, and the skill never overwrites a Flow it did not author — the coordinator supplies `flow_developer_name` deliberately. `flow_developer_name` is validated against `^[A-Za-z][A-Za-z0-9_]{0,79}$` because it is interpolated into the Actions REST URL path.
93
+
94
+ ## Output contract
95
+
96
+ A single JSON object with `status` ∈ `created` | `reused` | `updated` | `blocked`, `flow_developer_name`, `flow_state`, `flow_active`, `deploy_id`, `manual_actions`, and `blocking_issue`. The autolaunched variant adds `verification_path` and `invocation_smoke_test` (`attempted`, `success`, `endpoint`, `dry_run_ok`). The record-triggered variant adds `mode: "record_triggered"`, `target`, `flow_active_version_id`, `routing_type`, `skill_option` (the skillOption when SkillsBased, else null), `resolved` (queue/routing_config/service_channel), and `runtime_proof` (`attempted`, `required`, `success`, `record_id`, `pending_service_routing`, `agent_work`, `reason`). Under `--routing-type SkillsBased`, `runtime_proof` also carries `skill_requirement_count` and `skill_requirements` — the `SkillRequirement` rows the platform attached to the PSR (the skills-based acceptance signal).
97
+
98
+ - `blocked` — deploy failed; the flow is not Active; the autolaunched invocation failed; a queue/QRC could not be resolved; the ServiceChannel did not resolve; or `--require-proof` was set and the mode-specific proof was not observed. For SkillsBased, a PSR with zero `SkillRequirement` rows is not a passing proof. A plain `--runtime-proof` failure remains fail-soft.
99
+ - `runtime_proof.attempted` is `false` unless `--runtime-proof`/`--require-proof` was passed; the throwaway record is always cleaned up.
100
+
101
+ ## Limitations
102
+
103
+ - Case and VoiceCall only; extend by forking the closest asset and preserving the `dryRun`/`dryRunOk` contract (autolaunched) or the `routeWork` + tokenized-ID pattern (record-triggered).
104
+ - Does not register the Flow on a queue's routing configuration — that binding belongs to `service-omni-queue-routing-config-deploy`.
105
+ - Does not handle screen flows, platform-event-triggered flows, or Flow Tests.
106
+
107
+ ## References
108
+
109
+ | File | When to read |
110
+ |---|---|
111
+ | `references/api-notes.md` | On unexpected deploy or verification behavior — variant comparison, the token-resolution table, Flow schema, the Actions REST response shape, and record-DML verification |