@salesforce/afv-skills 1.41.0 → 1.43.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.
- package/package.json +2 -6
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +3 -0
- package/skills/automation-sandbox-post-copy-configure/SKILL.md +4 -0
- package/skills/design-systems-slds-validate/SKILL.md +2 -2
- package/skills/dx-app-analytics-query/SKILL.md +1 -3
- package/skills/dx-devops-conflict-resolve/SKILL.md +205 -0
- package/skills/dx-devops-conflict-resolve/examples/conflict-workflows.md +165 -0
- package/skills/dx-devops-conflict-resolve/references/deploy-failure-resolution.md +75 -0
- package/skills/dx-devops-conflict-resolve/references/git-conflict-resolution.md +124 -0
- package/skills/dx-devops-conflict-resolve/scripts/detect-conflicts.sh +91 -0
- package/skills/dx-devops-conflict-resolve/scripts/diagnose-deploy-failure.sh +156 -0
- package/skills/dx-devops-request-status/SKILL.md +160 -0
- package/skills/dx-devops-request-status/examples/polling-workflows.md +101 -0
- package/skills/dx-devops-request-status/references/cli-commands.md +176 -0
- package/skills/dx-devops-request-status/scripts/poll-status.sh +134 -0
- package/skills/dx-devops-test-failures-analyze/SKILL.md +6 -6
- package/skills/dx-devops-test-pipeline-configure/SKILL.md +7 -7
- package/skills/dx-devops-test-suite-assignments-configure/SKILL.md +6 -6
- package/skills/dx-devops-test-suite-run/SKILL.md +6 -6
- package/skills/dx-devops-work-item-manage/SKILL.md +2 -0
- package/skills/dx-org-manage/references/creating-scratch-org.md +5 -2
- package/skills/dx-org-manage/references/creating-snapshot.md +1 -0
- package/skills/dx-org-shape-manage/SKILL.md +152 -0
- package/skills/dx-org-shape-manage/examples/create_error_output.json +9 -0
- package/skills/dx-org-shape-manage/examples/create_success_output.json +9 -0
- package/skills/dx-org-shape-manage/examples/delete_output.json +11 -0
- package/skills/dx-org-shape-manage/examples/list_inactive_output.json +32 -0
- package/skills/dx-org-shape-manage/examples/list_output.json +24 -0
- package/skills/dx-org-shape-manage/references/cli_flags.md +135 -0
- package/skills/dx-org-switch/SKILL.md +2 -2
- package/skills/dx-org-trial-expiration-check/SKILL.md +2 -0
- package/skills/dx-pkg-post-install-configure/SKILL.md +3 -0
- package/skills/education-cloud-multi-campus-configure/SKILL.md +211 -0
- package/skills/education-cloud-multi-campus-configure/examples/hierarchy_visualization.md +174 -0
- package/skills/education-cloud-multi-campus-configure/examples/output_examples.md +54 -0
- package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input.csv +29 -0
- package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input_edgecases.csv +16 -0
- package/skills/education-cloud-multi-campus-configure/references/account_recordtype_prerequisite.md +44 -0
- package/skills/education-cloud-multi-campus-configure/references/delta_computation.md +33 -0
- package/skills/education-cloud-multi-campus-configure/references/error_handling.md +198 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +54 -0
- package/skills/education-cloud-multi-campus-configure/references/gotchas.md +16 -0
- package/skills/education-cloud-multi-campus-configure/references/hierarchy_parsing_rules.md +133 -0
- package/skills/education-cloud-multi-campus-configure/references/mcp-invocation.md +75 -0
- package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/SKILL.md +1 -1
- package/skills/experience-cms-brand-apply/SKILL.md +4 -1
- package/skills/experience-cms-brand-create/SKILL.md +197 -0
- package/skills/experience-cms-brand-create/assets/brand-template.json +329 -0
- package/skills/experience-cms-brand-create/references/brand-anatomy.md +120 -0
- package/skills/experience-cms-brand-create/references/disk-contract.md +84 -0
- package/skills/experience-cms-content-generate/SKILL.md +190 -0
- package/skills/experience-cms-content-generate/assets/display-formats.md +84 -0
- package/skills/experience-cms-content-generate/assets/payloads/create-content-bulk.json +11 -0
- package/skills/experience-cms-content-generate/assets/payloads/create-content-single.json +11 -0
- package/skills/experience-cms-content-generate/assets/payloads/publish-content.json +4 -0
- package/skills/experience-cms-content-generate/assets/payloads/update-content.json +9 -0
- package/skills/experience-cms-content-generate/assets/questions.md +241 -0
- package/skills/experience-cms-content-generate/examples/create-content-call.md +81 -0
- package/skills/experience-cms-content-generate/references/bulk-batching.md +56 -0
- package/skills/experience-cms-content-generate/references/content-type-classification.md +88 -0
- package/skills/experience-cms-content-generate/references/content-write-tool.md +95 -0
- package/skills/experience-cms-content-generate/references/delegation-protocol.md +37 -0
- package/skills/experience-cms-content-generate/references/edit-publish-workflow.md +63 -0
- package/skills/experience-cms-content-generate/references/error-recovery.md +72 -0
- package/skills/experience-cms-content-generate/references/identifier-resolution.md +26 -0
- package/skills/experience-cms-content-generate/references/intent-routing.md +45 -0
- package/skills/experience-cms-content-generate/references/principles.md +19 -0
- package/skills/experience-cms-content-generate/references/ux-rules.md +68 -0
- package/skills/experience-cms-content-generate/references/workspace-resolution.md +33 -0
- package/skills/experience-cms-content-type-generate/SKILL.md +449 -0
- package/skills/experience-cms-content-type-generate/assets/discovery-prompts.md +79 -0
- package/skills/experience-cms-content-type-generate/assets/schema-example.json +70 -0
- package/skills/experience-cms-content-type-generate/references/agent-checklist.md +50 -0
- package/skills/experience-cms-content-type-generate/references/anti-patterns.md +39 -0
- package/skills/experience-cms-content-type-generate/references/deployment-errors.md +27 -0
- package/skills/experience-cms-content-type-generate/references/discovery-details.md +106 -0
- package/skills/experience-cms-content-type-generate/references/discovery-query-rules.md +85 -0
- package/skills/experience-cms-content-type-generate/references/edit-fields-loop.md +48 -0
- package/skills/experience-cms-content-type-generate/references/pre-deploy-checklist.md +21 -0
- package/skills/experience-cms-content-type-generate/references/retrieve-and-reconcile.md +105 -0
- package/skills/experience-cms-content-type-generate/references/schema-rules.md +51 -0
- package/skills/experience-cms-content-type-generate/references/schema-summary-format.md +60 -0
- package/skills/experience-lds-graphql-generate/SKILL.md +4 -0
- package/skills/experience-lwc-accessibility-jest-run/SKILL.md +79 -0
- package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +205 -0
- package/skills/experience-lwc-design-generate/SKILL.md +5 -4
- package/skills/experience-lwc-generate/SKILL.md +10 -9
- package/skills/experience-ui-bundle-deploy/references/dev-preview.md +21 -0
- package/skills/experience-ui-bundle-deploy/references/social-login.md +1 -0
- package/skills/experience-ui-bundle-file-upload-generate/SKILL.md +6 -6
- package/skills/experience-ui-bundle-localize/SKILL.md +30 -37
- package/skills/experience-ui-bundle-localize/references/gotchas.md +23 -7
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +48 -4
- package/skills/experience-ui-bundle-localize/references/label-xml.md +22 -8
- package/skills/experience-ui-bundle-localize/references/verifying.md +39 -11
- package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +118 -15
- package/skills/experience-ui-bundle-localize/scripts/tests/test-detect-bundle-type.sh +187 -0
- package/skills/experience-ui-bundle-mfa-configure/SKILL.md +13 -2
- package/skills/experience-ui-bundle-mfa-configure/references/social-login.md +1 -0
- package/skills/experience-ui-bundle-project-generate/SKILL.md +2 -2
- package/skills/integration-connectivity-generate/SKILL.md +11 -10
- package/skills/integration-eventing-cdc-configure/SKILL.md +6 -6
- package/skills/integration-eventing-subscription-configure/SKILL.md +6 -5
- package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +2 -2
- package/skills/mobile-platform-offline-validate/scripts/package.json +1 -1
- package/skills/platform-agentexchange-partner-offers-configure/SKILL.md +8 -8
- package/skills/platform-custom-application-generate/SKILL.md +2 -0
- package/skills/platform-custom-field-generate/SKILL.md +8 -6
- package/skills/platform-custom-metadata-type-generate/SKILL.md +460 -0
- package/skills/platform-custom-metadata-type-generate/references/cmdt-records.md +251 -0
- package/skills/platform-custom-metadata-type-generate/scripts/sanitize-developer-name.sh +46 -0
- package/skills/platform-custom-setting-generate/SKILL.md +419 -0
- package/skills/platform-dataspace-access-configure/SKILL.md +3 -3
- package/skills/platform-sharing-owd-configure/SKILL.md +3 -0
- package/skills/platform-value-set-generate/SKILL.md +2 -0
- package/skills/service-agentforce-channel-configure/SKILL.md +29 -20
- package/skills/service-agentforce-channel-configure/assets/BotEmailDefinition.botEmailDefinition-meta.xml +36 -0
- package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email +17 -0
- package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email-meta.xml +29 -0
- package/skills/service-agentforce-channel-configure/assets/mdapi-package.xml +25 -0
- package/skills/service-agentforce-channel-configure/assets/settings-mdapi-package.xml +30 -0
- package/skills/service-agentforce-channel-configure/references/agent-wiring.md +20 -0
- package/skills/service-agentforce-channel-configure/references/botemaildefinition.md +107 -0
- package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +203 -50
- package/skills/service-agentforce-channel-configure/references/channel-types.md +30 -3
- package/skills/service-agentforce-channel-configure/references/queue-resolution.md +17 -8
- package/skills/service-agentforce-channel-configure/references/routing-flow.md +14 -3
- package/skills/service-agentforce-channel-configure/scripts/validate-botemaildefinition.py +126 -0
- package/skills/service-agentforce-channel-configure/scripts/validate-emailtemplate.py +133 -0
- package/skills/service-de-channel-activate/SKILL.md +271 -0
- package/skills/service-de-channel-activate/references/gotchas.md +31 -0
- package/skills/service-de-channel-activate/references/phone-verification.md +96 -0
- package/skills/service-de-channel-activate/references/worked-examples.md +14 -0
- package/skills/service-de-channel-consent-configure/SKILL.md +230 -0
- package/skills/service-de-channel-consent-configure/references/gotchas.md +41 -0
- package/skills/service-de-channel-consent-configure/references/language-keywords.md +84 -0
- package/skills/service-de-channel-consent-configure/references/worked-examples.md +134 -0
- package/skills/service-de-channel-create/SKILL.md +369 -0
- package/skills/service-de-channel-create/references/apple.md +59 -0
- package/skills/service-de-channel-create/references/connect-insert.md +84 -0
- package/skills/service-de-channel-create/references/facebook.md +154 -0
- package/skills/service-de-channel-create/references/line.md +66 -0
- package/skills/service-de-channel-create/references/sms.md +98 -0
- package/skills/service-de-channel-create/references/whatsapp.md +98 -0
- package/skills/service-de-channel-create/references/worked-examples.md +70 -0
- package/skills/service-de-channel-routing-configure/SKILL.md +345 -0
- package/skills/service-de-channel-routing-configure/references/asa-routing.md +90 -0
- package/skills/service-de-channel-routing-configure/references/gotchas.md +27 -0
- package/skills/service-de-channel-routing-configure/references/queue-creation.md +123 -0
- package/skills/service-de-channel-routing-configure/references/target-locate.md +98 -0
- package/skills/service-de-channel-routing-configure/references/worked-examples.md +174 -0
- package/skills/service-de-headless-channel-configure/SKILL.md +305 -0
- package/skills/service-de-headless-channel-configure/references/gotchas.md +27 -0
- package/skills/service-de-headless-channel-configure/references/inputs.md +49 -0
- package/skills/service-de-headless-channel-configure/references/output-envelopes.md +53 -0
- package/skills/service-de-headless-channel-configure/references/partial-success.md +24 -0
- package/skills/service-de-headless-channel-configure/references/terms-and-conditions.md +54 -0
- package/skills/service-de-headless-channel-configure/references/worked-examples.md +108 -0
- package/skills/service-de-waba-integrate/SKILL.md +227 -0
- package/skills/service-digital-engagement-channel-configure/scripts/check-api-version.sh +29 -0
- package/skills/service-digital-engagement-messaging-site-integrate/SKILL.md +10 -9
- package/skills/service-digital-engagement-messaging-site-integrate/references/lwr_patch.md +39 -21
- package/skills/service-digital-engagement-messaging-site-integrate/scripts/patch_lwr_bundle.sh +157 -96
- package/skills/service-email-to-case-configure/SKILL.md +190 -0
- package/skills/service-email-to-case-configure/assets/CaseSettings.settings-meta.xml +45 -0
- package/skills/service-email-to-case-configure/examples/CaseSettings-two-addresses.settings-meta.xml +36 -0
- package/skills/service-email-to-case-configure/references/apply-mechanics.md +73 -0
- package/skills/service-email-to-case-configure/references/routing_address_reference.md +73 -0
- package/skills/service-email-to-case-configure/references/troubleshooting.md +25 -0
- package/skills/service-email-to-case-configure/scripts/apply-casesettings.py +1315 -0
- package/skills/service-email-to-case-configure/scripts/check-agent-email-capability.sh +26 -0
- package/skills/service-email-to-case-configure/scripts/tests/__init__.py +0 -0
- package/skills/service-email-to-case-configure/scripts/tests/_bootstrap.py +45 -0
- package/skills/service-email-to-case-configure/scripts/tests/_fakeorg.py +366 -0
- package/skills/service-email-to-case-configure/scripts/tests/_run.py +84 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_apply_org_scenarios.py +655 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_get_session.py +134 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_new_capabilities.py +193 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_validate_casesettings.py +170 -0
- package/skills/service-email-to-case-configure/scripts/validate-casesettings.py +229 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +33 -15
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -47
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +17 -12
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +5 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +11 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +8 -6
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +3 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +11 -3
- package/skills/service-itsm-agentic-setup-configure/SKILL.md +5 -5
- package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +5 -5
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +2 -2
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +11 -7
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +20 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +10 -6
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +7 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +11 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +24 -17
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +98 -58
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +20 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/strip-release-management.mjs +176 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +19 -19
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +3 -4
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +6 -6
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +3 -10
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +7 -9
- package/skills/service-itsm-incident-mgmt-configure/SKILL.md +2 -0
- package/skills/service-itsm-incident-priority-configure/SKILL.md +15 -12
- package/skills/service-itsm-incident-priority-configure/examples/matrix-operations.md +27 -1
- package/skills/service-itsm-incident-priority-configure/references/sf-cli-invocation.md +37 -6
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +146 -0
- package/skills/service-native-voice-recording-transcription-configure/assets/confirmation-output.json +16 -0
- package/skills/service-native-voice-recording-transcription-configure/assets/package.xml +22 -0
- package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +101 -0
- package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +181 -0
- package/skills/data360-activate/README.md +0 -38
- package/skills/data360-activate/SKILL.md +0 -127
- package/skills/data360-connect/README.md +0 -57
- package/skills/data360-connect/SKILL.md +0 -163
- package/skills/data360-connect/examples/connections/heroku-postgres.json +0 -15
- package/skills/data360-connect/examples/connections/ingest-api-connection.json +0 -5
- package/skills/data360-connect/examples/connections/ingest-api-schema.json +0 -31
- package/skills/data360-connect/examples/connections/redshift.json +0 -16
- package/skills/data360-connect/examples/connections/sharepoint-unstructured.json +0 -20
- package/skills/data360-connect/examples/connections/snowflake-connection.json +0 -42
- package/skills/data360-harmonize/README.md +0 -31
- package/skills/data360-harmonize/SKILL.md +0 -126
- package/skills/data360-orchestrate/README.md +0 -120
- package/skills/data360-orchestrate/SKILL.md +0 -263
- package/skills/data360-orchestrate/assets/definitions/activation-target.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/activation.template.json +0 -7
- package/skills/data360-orchestrate/assets/definitions/calculated-insight.template.json +0 -7
- package/skills/data360-orchestrate/assets/definitions/data-action-target.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/data-action.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/data-graph.template.json +0 -21
- package/skills/data360-orchestrate/assets/definitions/data-stream.template.json +0 -55
- package/skills/data360-orchestrate/assets/definitions/dmo.template.json +0 -17
- package/skills/data360-orchestrate/assets/definitions/identity-resolution.template.json +0 -30
- package/skills/data360-orchestrate/assets/definitions/mapping.template.json +0 -14
- package/skills/data360-orchestrate/assets/definitions/relationship.template.json +0 -12
- package/skills/data360-orchestrate/assets/definitions/search-index.template.json +0 -9
- package/skills/data360-orchestrate/assets/definitions/segment.template.json +0 -16
- package/skills/data360-orchestrate/references/feature-readiness.md +0 -157
- package/skills/data360-orchestrate/references/plugin-setup.md +0 -138
- package/skills/data360-orchestrate/scripts/bootstrap-plugin.sh +0 -53
- package/skills/data360-orchestrate/scripts/diagnose-org.mjs +0 -511
- package/skills/data360-orchestrate/scripts/generate-manifest.mjs +0 -68
- package/skills/data360-orchestrate/scripts/verify-plugin.sh +0 -58
- package/skills/data360-prepare/README.md +0 -50
- package/skills/data360-prepare/SKILL.md +0 -203
- package/skills/data360-prepare/examples/ingestion-api/.env.example +0 -8
- package/skills/data360-prepare/examples/ingestion-api/README.md +0 -48
- package/skills/data360-prepare/examples/ingestion-api/send-data.py +0 -144
- package/skills/data360-query/README.md +0 -43
- package/skills/data360-query/SKILL.md +0 -128
- package/skills/data360-query/examples/search-indexes/hybrid-structured.json +0 -44
- package/skills/data360-query/examples/search-indexes/vector-knowledge.json +0 -43
- package/skills/data360-segment/README.md +0 -35
- package/skills/data360-segment/SKILL.md +0 -123
- package/skills/service-helpagent-coordinate/references/channel-help-portal.md +0 -22
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-1-1-non-text-content.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-i-lists.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-ii-tables.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iii-form-labels.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iv-regions.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-v-groups.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-5-identify-input.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-4-3-contrast.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-1-1-keyboard.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-4-link-purpose.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-6-headings-labels.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-3-label-in-name.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-7-dragging-movement.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-1-on-focus.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-2-on-input.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-1-error-identification.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-2-labels-instructions.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-3-error-suggestion.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-i-name.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-ii-role.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-iii-value.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-1-1-non-text-content.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-1-use-of-color.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-10-resize-reflow.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-11-non-text-contrast.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-3-contrast.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/scripts/contrast-ratio.py +0 -0
|
@@ -56,15 +56,15 @@ sf data create record --target-org $ORG \
|
|
|
56
56
|
|
|
57
57
|
## Step 4 — Resolve or create a Routing Configuration
|
|
58
58
|
|
|
59
|
-
The queue must have a `QueueRoutingConfig` so Omni-Channel knows how to handle capacity.
|
|
59
|
+
The queue must have a `QueueRoutingConfig` so Omni-Channel knows how to handle capacity. `QueueRoutingConfig` has **no `QueueId` column** — the link is the reverse direction (the queue's `Group` record points at the config via `QueueRoutingConfigId`), so resolve an existing config through the `Group` record, not by filtering the config on a queue Id:
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
sf data query --target-org $ORG --json \
|
|
63
|
-
--query "SELECT
|
|
63
|
+
--query "SELECT QueueRoutingConfigId FROM Group WHERE Id='{QUEUE_ID}'"
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
- **
|
|
67
|
-
-
|
|
66
|
+
- **`QueueRoutingConfigId` is non-null** → the queue already has a routing config; use it as-is and skip creation.
|
|
67
|
+
- **`QueueRoutingConfigId` is null** → create one using the capacity percentage for the channel type, then link it to the queue:
|
|
68
68
|
|
|
69
69
|
| Channel type | `CapacityPercentage` |
|
|
70
70
|
|---|---|
|
|
@@ -74,17 +74,26 @@ sf data query --target-org $ORG --json \
|
|
|
74
74
|
|
|
75
75
|
All use `RoutingModel: LeastActive`.
|
|
76
76
|
|
|
77
|
+
`QueueRoutingConfig` uses `MasterLabel` (there is **no `Name` field**), has **no `QueueId`**, and **requires `RoutingPriority`** — a non-nillable integer with no default; set `1`. Create the config standalone, capture its Id, then point the queue's `Group` record at it:
|
|
78
|
+
|
|
77
79
|
```bash
|
|
78
|
-
sf data create record --target-org $ORG \
|
|
80
|
+
QRC_ID=$(sf data create record --target-org $ORG \
|
|
79
81
|
--sobject QueueRoutingConfig \
|
|
80
|
-
--values "
|
|
82
|
+
--values "MasterLabel='{QUEUE_NAME} Routing' DeveloperName='{QUEUE_DEVELOPER_NAME}_Routing' CapacityPercentage={CAPACITY_PERCENTAGE} RoutingModel='LeastActive' RoutingPriority=1 IsAttributeBased=false" \
|
|
83
|
+
--json | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['id'])")
|
|
84
|
+
|
|
85
|
+
# Link is the REVERSE direction — the queue's Group record points at the config:
|
|
86
|
+
sf data update record --target-org $ORG \
|
|
87
|
+
--sobject Group --record-id "{QUEUE_ID}" \
|
|
88
|
+
--values "QueueRoutingConfigId='$QRC_ID'"
|
|
81
89
|
```
|
|
82
90
|
|
|
83
|
-
Verify the
|
|
91
|
+
Verify the queue now references the config:
|
|
84
92
|
```bash
|
|
85
93
|
sf data query --target-org $ORG --json \
|
|
86
|
-
--query "SELECT Id,
|
|
94
|
+
--query "SELECT Id, QueueRoutingConfigId FROM Group WHERE Id='{QUEUE_ID}'"
|
|
87
95
|
```
|
|
96
|
+
`QueueRoutingConfigId` must be non-null.
|
|
88
97
|
|
|
89
98
|
## Step 5 — Capture for use in Phase 2
|
|
90
99
|
|
|
@@ -7,13 +7,13 @@ Two distinct RoutingFlows are used when wiring an agent to a channel:
|
|
|
7
7
|
| Inbound | `RoutingFlow` | `Copilot` | Routes arriving work items *to* the agent; queue is fallback when agent unavailable |
|
|
8
8
|
| Outbound | `RoutingFlow` | `QueueBased` | Routes *from* the agent to a human queue when the customer requests escalation |
|
|
9
9
|
|
|
10
|
-
Both flows reference the same fallback queue resolved in Phase 1. The inbound flow is created in Phase 2 (
|
|
10
|
+
Both flows reference the same fallback queue resolved in Phase 1. The inbound flow is created in Phase 2 for **Branch B (Voice)**, and for **Branch C (Email-to-Case)** when the user picks the Omni-Channel flow path over direct case-owner assignment, and Branch A uses `sessionHandlerAsa`. The outbound flow is created in Phase 3 if the user opts into outbound escalation.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
## Part 1 — Inbound RoutingFlow (Copilot routing)
|
|
15
15
|
|
|
16
|
-
Used for Voice (Branch B) and Email-to-Case (Branch C). When a work item arrives on the channel, it routes directly to the Agentforce Agent, with the fallback queue used when the agent is unavailable.
|
|
16
|
+
Used for Voice (Branch B), and for Email-to-Case (Branch C) on the Omni-Channel flow path. When a work item arrives on the channel, it routes directly to the Agentforce Agent, with the fallback queue used when the agent is unavailable.
|
|
17
17
|
|
|
18
18
|
The `routingType` is `Copilot` (the platform token for Agentforce Agent routing). The agent is referenced by its **label** (`copilotLabel`), not its DeveloperName. The queue fallback is expressed via `queueLabel` (the queue's `Name` field) and `queueId` (the queue's 18-char record Id).
|
|
19
19
|
|
|
@@ -22,7 +22,7 @@ The `routingType` is `Copilot` (the platform token for Agentforce Agent routing)
|
|
|
22
22
|
| Channel | Flow Label | DeveloperName |
|
|
23
23
|
|---|---|---|
|
|
24
24
|
| Voice | `{AgentLabel} Inbound Voice Flow` | `{AgentDevName}_Inbound_Voice_Flow` |
|
|
25
|
-
| Email-to-Case | `{AgentLabel} Inbound Email Flow` | `{AgentDevName}_Inbound_Email_Flow` |
|
|
25
|
+
| Service Email (Email-to-Case) | `{AgentLabel} Inbound Email Flow` | `{AgentDevName}_Inbound_Email_Flow` |
|
|
26
26
|
|
|
27
27
|
## Lookup the queue Id before writing the XML
|
|
28
28
|
|
|
@@ -168,6 +168,7 @@ Used in Phase 3 for all channel types. When the agent escalates to a human, this
|
|
|
168
168
|
| Enhanced Chat | `{AgentLabel} Outbound Enhanced Chat Flow` | `{AgentDevName}_Outbound_Enhanced_Chat_Flow` |
|
|
169
169
|
| Enhanced Messaging | `{AgentLabel} Outbound Messaging Flow` | `{AgentDevName}_Outbound_Messaging_Flow` |
|
|
170
170
|
| Voice | `{AgentLabel} Outbound Voice Flow` | `{AgentDevName}_Outbound_Voice_Flow` |
|
|
171
|
+
| Service Email (Email-to-Case) | `{AgentLabel} Outbound Email Flow` | `{AgentDevName}_Outbound_Email_Flow` |
|
|
171
172
|
|
|
172
173
|
### Check before creating
|
|
173
174
|
|
|
@@ -184,6 +185,16 @@ If a row exists with non-null `ActiveVersionId`, reuse it — capture the `ApiNa
|
|
|
184
185
|
|---|---|---|
|
|
185
186
|
| Enhanced Chat / Enhanced Messaging | `sfdc_livemessage` | `Messaging` |
|
|
186
187
|
| Voice | `sfdc_phone` | `Phone` |
|
|
188
|
+
| Service Email (Email-to-Case) | *(org-specific — the Case-based ServiceChannel; resolve or provision it, see below)* | *(its `MasterLabel`)* |
|
|
189
|
+
|
|
190
|
+
For **Email-to-Case (Branch C)**, there is no fixed platform token — the outbound flow needs the org's **Case-based `ServiceChannel`** (`RelatedEntity = 'Case'`). Resolve it here, and provision it if absent, before writing the flow:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
sf data query --target-org $ORG --json \
|
|
194
|
+
--query "SELECT DeveloperName, MasterLabel FROM ServiceChannel WHERE RelatedEntity='Case' LIMIT 1"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Use the returned `DeveloperName` as `SERVICE_CHANNEL_DEV_NAME` and `MasterLabel` as `SERVICE_CHANNEL_LABEL`. If no row is returned, provision one first — see `references/channel-types.md` § "Provision a Case ServiceChannel".
|
|
187
198
|
|
|
188
199
|
### XML template
|
|
189
200
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate a BotEmailDefinition source file for Email-to-Case Agentforce replies.
|
|
3
|
+
|
|
4
|
+
BotEmailDefinition is the Metadata API type (API v68.0+) that links an
|
|
5
|
+
Agentforce Service Agent to Service Email. It is deployed in METADATA format
|
|
6
|
+
(--metadata-dir), then referenced from an Email-to-Case routing address via the
|
|
7
|
+
routing address's <botEmailDefinition> field.
|
|
8
|
+
|
|
9
|
+
The WSDL complexType extends Metadata with SIX child fields, none marked
|
|
10
|
+
minOccurs="0" -- so all are required on create, and there are no
|
|
11
|
+
platform-generated read-only fields to strip:
|
|
12
|
+
|
|
13
|
+
botDefinition - developer/API name of the AgentforceServiceAgent (Bot) to link.
|
|
14
|
+
emailTemplate - FOLDER-QUALIFIED developer name of the reply email template.
|
|
15
|
+
label - display label for the email definition.
|
|
16
|
+
legalDisclaimer - legal disclosure / footer text (>= 10 chars).
|
|
17
|
+
replyAll - boolean; whether the agent replies-all vs. reply-to-sender.
|
|
18
|
+
signature - email signature block (>= 10 chars).
|
|
19
|
+
|
|
20
|
+
Checks the structural rules the platform's save-time validation enforces, so
|
|
21
|
+
they hold before deploying:
|
|
22
|
+
- Well-formed XML with the BotEmailDefinition root and correct namespace.
|
|
23
|
+
- All six required fields are present and non-empty.
|
|
24
|
+
- legalDisclaimer and signature are each at least 10 characters
|
|
25
|
+
(save-time MinLengthNotMet check -- these are silently accepted by a naive
|
|
26
|
+
template but rejected by the org).
|
|
27
|
+
- emailTemplate is folder-qualified (contains a '/'); a bare name fails the
|
|
28
|
+
deploy with "no EmailTemplate named X found".
|
|
29
|
+
- replyAll is a boolean literal ('true' or 'false').
|
|
30
|
+
- No <fullName> element inside the file (the fullName is the file name for a
|
|
31
|
+
top-level component; a <fullName> child is rejected on deploy).
|
|
32
|
+
|
|
33
|
+
Usage: python3 validate-botemaildefinition.py <path-to-file> (--help for this text)
|
|
34
|
+
Exits non-zero and prints ERROR lines to stderr if any check fails.
|
|
35
|
+
"""
|
|
36
|
+
import sys
|
|
37
|
+
import xml.etree.ElementTree as ET
|
|
38
|
+
|
|
39
|
+
NS = "http://soap.sforce.com/2006/04/metadata"
|
|
40
|
+
Q = f"{{{NS}}}"
|
|
41
|
+
|
|
42
|
+
REQUIRED_FIELDS = (
|
|
43
|
+
"botDefinition",
|
|
44
|
+
"emailTemplate",
|
|
45
|
+
"label",
|
|
46
|
+
"legalDisclaimer",
|
|
47
|
+
"replyAll",
|
|
48
|
+
"signature",
|
|
49
|
+
)
|
|
50
|
+
BOOLEAN_FIELDS = {"replyAll"}
|
|
51
|
+
# Save-time MinLengthNotMet check on the org rejects these below 10 chars.
|
|
52
|
+
MIN_LENGTH_FIELDS = {"legalDisclaimer": 10, "signature": 10}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def local(tag):
|
|
56
|
+
return tag.split("}", 1)[-1]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def child_text(parent, name):
|
|
60
|
+
el = parent.find(f"{Q}{name}")
|
|
61
|
+
return el.text.strip() if el is not None and el.text else None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def validate(path):
|
|
65
|
+
errors = []
|
|
66
|
+
try:
|
|
67
|
+
tree = ET.parse(path)
|
|
68
|
+
except ET.ParseError as exc:
|
|
69
|
+
return [f"XML is not well-formed: {exc}"]
|
|
70
|
+
except OSError as exc:
|
|
71
|
+
return [f"Cannot read file: {exc}"]
|
|
72
|
+
|
|
73
|
+
root = tree.getroot()
|
|
74
|
+
if local(root.tag) != "BotEmailDefinition":
|
|
75
|
+
errors.append(
|
|
76
|
+
f"Root element must be BotEmailDefinition, found '{local(root.tag)}'.")
|
|
77
|
+
if not root.tag.startswith(Q):
|
|
78
|
+
errors.append(f"Root must use namespace '{NS}'.")
|
|
79
|
+
|
|
80
|
+
# fullName is the file name for a top-level component; a <fullName> child
|
|
81
|
+
# in the source file is rejected on deploy.
|
|
82
|
+
if root.find(f"{Q}fullName") is not None:
|
|
83
|
+
errors.append(
|
|
84
|
+
"Remove the <fullName> element — for a top-level component the "
|
|
85
|
+
"fullName is the file name (e.g. My_Email_Def in "
|
|
86
|
+
"My_Email_Def.botEmailDefinition-meta.xml), not a child element.")
|
|
87
|
+
|
|
88
|
+
for field in REQUIRED_FIELDS:
|
|
89
|
+
value = child_text(root, field)
|
|
90
|
+
if not value:
|
|
91
|
+
errors.append(f"Missing required field '{field}'.")
|
|
92
|
+
continue
|
|
93
|
+
if field in BOOLEAN_FIELDS and value not in ("true", "false"):
|
|
94
|
+
errors.append(
|
|
95
|
+
f"Field '{field}' must be 'true' or 'false', found '{value}'.")
|
|
96
|
+
if field in MIN_LENGTH_FIELDS and len(value) < MIN_LENGTH_FIELDS[field]:
|
|
97
|
+
errors.append(
|
|
98
|
+
f"Field '{field}' must be at least {MIN_LENGTH_FIELDS[field]} "
|
|
99
|
+
f"characters (save-time MinLengthNotMet check); found "
|
|
100
|
+
f"{len(value)} ('{value}').")
|
|
101
|
+
if field == "emailTemplate" and "/" not in value:
|
|
102
|
+
errors.append(
|
|
103
|
+
f"Field 'emailTemplate' must be folder-qualified "
|
|
104
|
+
f"(e.g. unfiled$public/{value}); a bare name fails the deploy "
|
|
105
|
+
f"with 'no EmailTemplate named {value} found'.")
|
|
106
|
+
|
|
107
|
+
return errors
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def main():
|
|
111
|
+
args = [a for a in sys.argv[1:] if a not in ("--help", "-h")]
|
|
112
|
+
if len(sys.argv) == 1 or "--help" in sys.argv or "-h" in sys.argv:
|
|
113
|
+
print(__doc__)
|
|
114
|
+
return 0 if "--help" in sys.argv or "-h" in sys.argv else 2
|
|
115
|
+
path = args[0]
|
|
116
|
+
errors = validate(path)
|
|
117
|
+
if errors:
|
|
118
|
+
for e in errors:
|
|
119
|
+
print(f"ERROR: {e}", file=sys.stderr)
|
|
120
|
+
return 1
|
|
121
|
+
print(f"OK: {path} passed BotEmailDefinition validation.")
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
sys.exit(main())
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate an SFX EmailTemplate source pair for Agentforce Service Agent email.
|
|
3
|
+
|
|
4
|
+
BotEmailDefinition only accepts a template that is a Lightning (SFX) email
|
|
5
|
+
template, has an HTML body, contains two literal ASA placeholder tokens, and
|
|
6
|
+
lives in a public folder. This script validates the source artifact Branch C
|
|
7
|
+
deploys (the fallback-template create branch) so those rules hold before deploy:
|
|
8
|
+
|
|
9
|
+
- The .email-meta.xml root is EmailTemplate in the metadata namespace.
|
|
10
|
+
- uiType is 'SFX' (a classic 'Aloha' template is rejected by the platform;
|
|
11
|
+
uiType is also set-once, so it must be correct at create time).
|
|
12
|
+
- type is 'custom' (an HTML/custom body; 'text'/'visualforce' won't carry the
|
|
13
|
+
HTML tokens).
|
|
14
|
+
- The companion .email body (same base name) exists, is non-blank, and
|
|
15
|
+
contains BOTH literal tokens, exactly:
|
|
16
|
+
[[[GENERATED_CONTENT]]] and [[[LEGAL_DISCLOSURE]]].
|
|
17
|
+
|
|
18
|
+
Public-folder placement (folder must be public, e.g. unfiled$public) is a
|
|
19
|
+
deploy-path/permission concern, not visible in the file pair, so it is checked
|
|
20
|
+
against the file's directory only as a warning.
|
|
21
|
+
|
|
22
|
+
Usage: python3 validate-emailtemplate.py <path-to-.email-meta.xml> (--help for this text)
|
|
23
|
+
Exits non-zero and prints ERROR lines to stderr if any check fails.
|
|
24
|
+
"""
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
import xml.etree.ElementTree as ET
|
|
28
|
+
|
|
29
|
+
NS = "http://soap.sforce.com/2006/04/metadata"
|
|
30
|
+
Q = f"{{{NS}}}"
|
|
31
|
+
|
|
32
|
+
REQUIRED_TOKENS = ("[[[GENERATED_CONTENT]]]", "[[[LEGAL_DISCLOSURE]]]")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def local(tag):
|
|
36
|
+
return tag.split("}", 1)[-1]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def child_text(parent, name):
|
|
40
|
+
el = parent.find(f"{Q}{name}")
|
|
41
|
+
return el.text.strip() if el is not None and el.text else None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def validate(meta_path):
|
|
45
|
+
errors = []
|
|
46
|
+
try:
|
|
47
|
+
tree = ET.parse(meta_path)
|
|
48
|
+
except ET.ParseError as exc:
|
|
49
|
+
return [f"XML is not well-formed: {exc}"]
|
|
50
|
+
except OSError as exc:
|
|
51
|
+
return [f"Cannot read file: {exc}"]
|
|
52
|
+
|
|
53
|
+
root = tree.getroot()
|
|
54
|
+
if local(root.tag) != "EmailTemplate":
|
|
55
|
+
errors.append(
|
|
56
|
+
f"Root element must be EmailTemplate, found '{local(root.tag)}'.")
|
|
57
|
+
if not root.tag.startswith(Q):
|
|
58
|
+
errors.append(f"Root must use namespace '{NS}'.")
|
|
59
|
+
|
|
60
|
+
ui_type = child_text(root, "uiType")
|
|
61
|
+
if ui_type != "SFX":
|
|
62
|
+
errors.append(
|
|
63
|
+
f"uiType must be 'SFX' (Lightning email template) for Agentforce "
|
|
64
|
+
f"Service Agent email; found '{ui_type}'. A classic 'Aloha' "
|
|
65
|
+
f"template is rejected, and uiType cannot be changed after create.")
|
|
66
|
+
|
|
67
|
+
tmpl_type = child_text(root, "type")
|
|
68
|
+
if tmpl_type != "custom":
|
|
69
|
+
errors.append(
|
|
70
|
+
f"type must be 'custom' (an HTML body) to carry the ASA tokens; "
|
|
71
|
+
f"found '{tmpl_type}'.")
|
|
72
|
+
|
|
73
|
+
# Companion .email body: same base name, .email extension.
|
|
74
|
+
if meta_path.endswith(".email-meta.xml"):
|
|
75
|
+
body_path = meta_path[: -len("-meta.xml")]
|
|
76
|
+
else:
|
|
77
|
+
body_path = os.path.splitext(meta_path)[0] + ".email"
|
|
78
|
+
try:
|
|
79
|
+
with open(body_path, encoding="utf-8") as fh:
|
|
80
|
+
body = fh.read()
|
|
81
|
+
except OSError:
|
|
82
|
+
errors.append(
|
|
83
|
+
f"Companion HTML body '{os.path.basename(body_path)}' not found "
|
|
84
|
+
f"next to the meta file — the SFX template needs an .email body.")
|
|
85
|
+
body = ""
|
|
86
|
+
|
|
87
|
+
if body_path and not body.strip():
|
|
88
|
+
errors.append(
|
|
89
|
+
"The email body is blank — BotEmailDefinition rejects a template "
|
|
90
|
+
"with no HTML value (EmailTemplateDoesntContainHtmlValue).")
|
|
91
|
+
|
|
92
|
+
for token in REQUIRED_TOKENS:
|
|
93
|
+
if token not in body:
|
|
94
|
+
errors.append(
|
|
95
|
+
f"The email body is missing the required ASA token '{token}' — "
|
|
96
|
+
f"BotEmailDefinition rejects a template without it. Add it "
|
|
97
|
+
f"verbatim (it is a literal token, not a merge field).")
|
|
98
|
+
|
|
99
|
+
return errors
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def folder_warning(meta_path):
|
|
103
|
+
"""Public-folder placement can't be proven from the file, but a private
|
|
104
|
+
(user) folder is a common mistake — nudge if the path looks non-public."""
|
|
105
|
+
parent = os.path.basename(os.path.dirname(os.path.abspath(meta_path)))
|
|
106
|
+
if parent and parent != "unfiled$public" and not parent.endswith("$public"):
|
|
107
|
+
return (f"NOTE: template is under folder '{parent}'. BotEmailDefinition "
|
|
108
|
+
f"requires a PUBLIC folder (e.g. unfiled$public or a folder "
|
|
109
|
+
f"whose AccessType is Public). Confirm the deploy target folder "
|
|
110
|
+
f"is public.")
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def main():
|
|
115
|
+
args = [a for a in sys.argv[1:] if a not in ("--help", "-h")]
|
|
116
|
+
if len(sys.argv) == 1 or "--help" in sys.argv or "-h" in sys.argv:
|
|
117
|
+
print(__doc__)
|
|
118
|
+
return 0 if "--help" in sys.argv or "-h" in sys.argv else 2
|
|
119
|
+
path = args[0]
|
|
120
|
+
errors = validate(path)
|
|
121
|
+
if errors:
|
|
122
|
+
for e in errors:
|
|
123
|
+
print(f"ERROR: {e}", file=sys.stderr)
|
|
124
|
+
return 1
|
|
125
|
+
note = folder_warning(path)
|
|
126
|
+
if note:
|
|
127
|
+
print(note, file=sys.stderr)
|
|
128
|
+
print(f"OK: {path} passed SFX EmailTemplate ASA validation.")
|
|
129
|
+
return 0
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if __name__ == "__main__":
|
|
133
|
+
sys.exit(main())
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-de-channel-activate
|
|
3
|
+
description: "Activate an Enhanced `MessagingChannel` (WhatsApp / Apple / Facebook / SMS / RCS) by PATCHing `MessagingChannelUsage.DeploymentStatus` from `Disabled` to `Provisioning` via the standard REST sobject endpoint. The UDD save-hook fires on the REST write, dispatches by `MessageType` for the external callout (`WHATS_APP` → Meta `/register`, `FACEBOOK` → `subscribeFacebookPage`, `TEXT` → `registerCsotSms`; Apple, LINE, and others need no external call), writes the terminal `Active` or `Error`, and flips `MessagingChannel.IsActive`. The chain is synchronous on the HTTP response — WhatsApp returns 204 only after Meta confirms registration (~15-21s); channels with no external callout are near-instant (~1s on Apple). No Aura RPC; no CSRF cookies. Use this skill when the user needs to activate an already-inserted Enhanced messaging channel headlessly via REST."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
minApiVersion: "67.0"
|
|
7
|
+
domains: ["Service"]
|
|
8
|
+
cliTools:
|
|
9
|
+
- tool: ["jq"]
|
|
10
|
+
semver: ">=1.6.0"
|
|
11
|
+
- tool: ["node"]
|
|
12
|
+
semver: ">=20.0.0"
|
|
13
|
+
- tool: ["sf"]
|
|
14
|
+
semver: ">=2.0.0"
|
|
15
|
+
relatedSkills:
|
|
16
|
+
- "service-de-channel-consent-configure"
|
|
17
|
+
- "service-de-channel-create"
|
|
18
|
+
- "service-de-channel-routing-configure"
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Activating a Messaging Channel
|
|
22
|
+
|
|
23
|
+
## What this skill does
|
|
24
|
+
|
|
25
|
+
Given a `{CHANNEL_ID}`, reads the channel's `MessagingChannelUsage.Id`, then fires `PATCH /services/data/v{V}/sobjects/MessagingChannelUsage/{MCU_ID}` with body `{"DeploymentStatus":"Provisioning"}`. The server-side chain:
|
|
26
|
+
|
|
27
|
+
1. `MessagingChannelUsageFunctions.validateBeforeSave` runs `validateDeploymentStatus` → `validateChannelReadinessOnProvisioning`. For WhatsApp this confirms consent is configured. Rejected writes return HTTP 400 `FIELD_INTEGRITY_EXCEPTION`.
|
|
28
|
+
2. `MessagingChannelUsageFunctions.saveHook_PostStmtExecuteOnce` fires unconditionally after the UPDATE statement. It calls `MessagingChannelUsageFunctionsHelper.handlePostSave` which registers a post-commit `TransactionObserver`.
|
|
29
|
+
3. At commit, the observer calls `ConversationChannelUsageDeploymentStatusService.handleProvisioning` (inherited from `AbstractChannelUsageDeploymentStatusService`), which:
|
|
30
|
+
- Calls `runProvisioning` — `switch`-dispatches by `MessageType` for the external callout:
|
|
31
|
+
- `WHATS_APP` → `registerCsotWhatsAppNumber` → `LiveMessageSetupApi.registerWhatsAppNumber` → Meta `/register` + status verification. 15-21s wall-clock.
|
|
32
|
+
- `FACEBOOK` → `metaGraphApiService.subscribeFacebookPage`.
|
|
33
|
+
- `TEXT` → `registerCsotSms`.
|
|
34
|
+
- `AppleBusinessChat`, `Line`, everything else → `default` branch, no external callout, no network wait.
|
|
35
|
+
- On success: writes `DeploymentStatus = 'Active'` via PLSQL.
|
|
36
|
+
- On failure: writes `DeploymentStatus = 'Error'` plus `ErrorReason` / `ErrorDetails`.
|
|
37
|
+
4. Inside the same observer, a second pass syncs `MessagingChannel.IsActive = true` once MCU reaches `Active` (the `isTransitioningStatus` flag skips the flip while status is still `Provisioning`).
|
|
38
|
+
|
|
39
|
+
All synchronous within the PATCH request — the 204 response only comes back after the full chain completes. WhatsApp: ~15-21s (Meta `/register` round-trip). Apple / Line: ~1s (no external call; just the local save-hook + observer + PLSQL write). Verified on wadtesting 2026-04-30.
|
|
40
|
+
|
|
41
|
+
## Reference File Index
|
|
42
|
+
|
|
43
|
+
| Reference file | Load when |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `references/phone-verification.md` | Stage 3 comes back with `ErrorReason === "VERIFICATION_REQUIRED"` (WhatsApp only) — the phone-number OTP verification sub-flow. |
|
|
46
|
+
| `references/worked-examples.md` | You want a reference run of the WhatsApp happy-path, Apple activation, a readiness failure, or the already-active no-op. |
|
|
47
|
+
| `references/gotchas.md` | Troubleshooting an unexpected result, or before modifying this skill — the eleven known gotchas. |
|
|
48
|
+
|
|
49
|
+
## Why REST PATCH instead of Apex?
|
|
50
|
+
|
|
51
|
+
A direct REST PATCH produces the identical save-hook chain as the old `activateChannelUsage` Apex method, with substantially less machinery — no CSRF cookie acquisition, no bootstrap fetch, no Aura response parsing, no double-wrapped `returnValue`. REST semantics are honest: 204 means the transition succeeded; 4xx means it didn't.
|
|
52
|
+
|
|
53
|
+
Code proof: `MessagingChannelUsageFunctions.saveHook_PostStmtExecuteOnce` fires on any DML path (REST, SOAP, Apex, Metadata API) — there is no Apex-specific gate. The entity XML (`MessagingChannelUsage.entity.xml`) marks `DeploymentStatus` as `editAccess="always"` with no `<readonly>` attribute. The transition validator (`getValidAPIStatusTransitions`) allows `Disabled → Provisioning` (and `New → Provisioning`, and `Error → Provisioning | Deprovisioning`, and `Active → Deprovisioning`). The DB-only transitions `Provisioning → Active | Error` are reserved for the observer's PLSQL call — that's why we write `Provisioning` and let the server pick the terminal state.
|
|
54
|
+
|
|
55
|
+
## When NOT to use this skill
|
|
56
|
+
|
|
57
|
+
- **The channel is already `IsActive=true`.** Re-firing is blocked by the API transition validator (`Active → Provisioning` is not in `getValidAPIStatusTransitions()`) — the PATCH would return 400. The Stage 1 precondition check catches this and emits `noop:true`.
|
|
58
|
+
- **Routing isn't configured.** `activateChannelUsage` used to fail with `LiveMessageSetupException` / `nullQueueId` at the Apex entry point. With the REST path the same guard lives in `validateChannelReadinessOnProvisioning` — write with `SessionHandlerId=null && FallbackQueueId=null` → 400 `FIELD_INTEGRITY_EXCEPTION`. Run `service-de-channel-routing-configure` first. The Stage 1 check still runs defensively.
|
|
59
|
+
- **The MCU doesn't exist.** Can't PATCH a row that's missing. Run the insertion skill first — it always creates the MCU as a side-effect of `addChannel`.
|
|
60
|
+
|
|
61
|
+
## Inputs (from caller)
|
|
62
|
+
|
|
63
|
+
- `{CHANNEL_ID}` — a 15- or 18-char `MessagingChannel.Id` (prefix `0Mj`). The channel must already exist with a non-null `SessionHandlerId` or `FallbackQueueId`.
|
|
64
|
+
- `{ORG_ALIAS}` — optional; the `sf` CLI target-org alias. Default: whatever `sf config get target-org` returns. Used for OAuth and SOQL reads.
|
|
65
|
+
- `{API_VERSION}` — optional; REST API version. Default: `68.0`. Any version where `MessagingChannelUsage` is addressable as a standard sobject is fine (v50+ should work; not exhaustively tested).
|
|
66
|
+
|
|
67
|
+
Unlike the old Aura-based version of this skill, there are no `{POLL_TIMEOUT_S}` / `{POLL_INTERVAL_S}` inputs — the PATCH is synchronous end-to-end.
|
|
68
|
+
|
|
69
|
+
## Output (to caller)
|
|
70
|
+
|
|
71
|
+
**Success — channel is live:**
|
|
72
|
+
```json
|
|
73
|
+
{"ok": true, "channelId": "0Mj...", "mcuId": "0gL...", "deploymentStatus": "Active", "isActive": true, "messageType": "WhatsApp", "durationMs": 20934}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Success — no-op (already active):**
|
|
77
|
+
```json
|
|
78
|
+
{"ok": true, "noop": true, "channelId": "0Mj...", "message": "Channel already active"}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Failure — precondition not met:**
|
|
82
|
+
```json
|
|
83
|
+
{"ok": false, "kind": "no-routing", "hint": "run service-de-channel-routing-configure first"}
|
|
84
|
+
{"ok": false, "kind": "no-mcu", "hint": "no MessagingChannelUsage row for this channel — run the insertion skill first"}
|
|
85
|
+
{"ok": false, "kind": "channel-missing","hint": "MessagingChannel id not found"}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Failure — validator or server-side provisioning error:**
|
|
89
|
+
```json
|
|
90
|
+
{"ok": false, "kind": "readiness-failed", "errorCode": "FIELD_INTEGRITY_EXCEPTION",
|
|
91
|
+
"message": "...", "hint": "validateChannelReadinessOnProvisioning rejected the write — most commonly missing consent; run service-de-channel-consent-configure first"}
|
|
92
|
+
{"ok": false, "kind": "provisioning-error", "mcuId": "0gL...", "errorReason": "MetaRegistrationFailed", "errorDetails": "..."}
|
|
93
|
+
{"ok": false, "kind": "verification-failed", "hint": "WhatsApp phone number verification failed or was declined by user"}
|
|
94
|
+
{"ok": false, "kind": "verification-request-failed", "errorCode": "...", "message": "...", "hint": "Could not request verification code from Meta"}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Failure — auth / transport:**
|
|
98
|
+
```json
|
|
99
|
+
{"ok": false, "kind": "auth", "hint": "OAuth token invalid / expired — run 'sf org login web'"}
|
|
100
|
+
{"ok": false, "kind": "transport", "status": 500, "message": "..."}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Stage 1: Precondition checks
|
|
106
|
+
|
|
107
|
+
Read the channel, then its MCU, as two separate SOQL calls. A combined subquery would be cheaper but fails on orgs where the child relationship is unnameable (see gotcha #4).
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
111
|
+
--query "SELECT Id, DeveloperName, MessageType, IsActive, SessionHandlerId, FallbackQueueId, MessagingPlatformKey FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" \
|
|
112
|
+
--json > /tmp/amc-precheck-channel.json
|
|
113
|
+
|
|
114
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
115
|
+
--query "SELECT Id, DeploymentStatus, DeploymentType, ErrorReason, ErrorDetails FROM MessagingChannelUsage WHERE MessagingChannelId = '{CHANNEL_ID}'" \
|
|
116
|
+
--json > /tmp/amc-precheck-mcu.json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Let `channel = /tmp/amc-precheck-channel.json records[0]` and `mcu = /tmp/amc-precheck-mcu.json records[0]`:
|
|
120
|
+
|
|
121
|
+
| Condition | Envelope |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| Channel query returned 0 records | `{ok:false, kind:"channel-missing", hint:"MessagingChannel id not found"}` |
|
|
124
|
+
| `channel.IsActive === true` | `{ok:true, noop:true, channelId, message:"Channel already active"}` — return |
|
|
125
|
+
| `channel.SessionHandlerId == null && channel.FallbackQueueId == null` | `{ok:false, kind:"no-routing", hint:"run service-de-channel-routing-configure first"}` |
|
|
126
|
+
| MCU query returned 0 records | `{ok:false, kind:"no-mcu", hint:"no MessagingChannelUsage row for this channel — run the insertion skill first"}` |
|
|
127
|
+
| Otherwise | Record `{MCU_ID} = mcu.Id`, `{MESSAGE_TYPE} = channel.MessageType`, `{MESSAGING_PLATFORM_KEY} = channel.MessagingPlatformKey`, `{INITIAL_MCU_STATUS} = mcu.DeploymentStatus` and continue to Stage 2. |
|
|
128
|
+
|
|
129
|
+
Also record `{T0}` (epoch ms at start of Stage 2) so the final envelope can report `durationMs`.
|
|
130
|
+
|
|
131
|
+
### Stage 1.1: Fast path for already-provisioning MCU
|
|
132
|
+
|
|
133
|
+
If `{INITIAL_MCU_STATUS} === "Provisioning"` — the MCU is already mid-flight from a prior call in this transaction window. Skip Stage 2 (firing the PATCH) entirely and jump to Stage 3 (verification). This is a rare race guard: the observer is synchronous with the PATCH, so by the time the caller sees the 204 the status is already terminal (`Active` or `Error`) — `Provisioning` should be invisible from outside. If we do see it in the precheck, something wrote `Provisioning` in a separate DML and the observer is still mid-flight — don't fire a second PATCH.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Stage 2: PATCH `MessagingChannelUsage.DeploymentStatus = "Provisioning"`
|
|
138
|
+
|
|
139
|
+
Use `sf api request rest` so authentication stays inside the CLI's transport — no OAuth token is ever extracted into shell state.
|
|
140
|
+
|
|
141
|
+
Fire the PATCH. **This call can take 15-30 seconds for WhatsApp** — the observer runs synchronously, including Meta's `/register` round trip. `sf api request rest` has no separate client-side timeout to raise; it waits on the underlying HTTP call.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
sf api request rest \
|
|
145
|
+
"/services/data/v{API_VERSION}/sobjects/MessagingChannelUsage/{MCU_ID}" \
|
|
146
|
+
--method PATCH \
|
|
147
|
+
--target-org '{ORG_ALIAS}' \
|
|
148
|
+
--header 'Content-Type: application/json' \
|
|
149
|
+
--body '{"DeploymentStatus":"Provisioning"}' \
|
|
150
|
+
--include \
|
|
151
|
+
> /tmp/amc-patch-response.txt 2>&1
|
|
152
|
+
HTTP_CODE=$(head -1 /tmp/amc-patch-response.txt | grep -oE '[0-9]{3}')
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`--include` prints the HTTP status/headers block ahead of the (typically empty, on 204) body — read the status line from that block rather than a `-w`-style trailing marker.
|
|
156
|
+
|
|
157
|
+
Classify by HTTP status:
|
|
158
|
+
|
|
159
|
+
| Status | Body | Handling |
|
|
160
|
+
| --- | --- | --- |
|
|
161
|
+
| 204 | empty | Success. The observer ran to completion; MCU is `Active` or `Error`. Continue to Stage 3 to read the terminal state. |
|
|
162
|
+
| 400 | `[{errorCode:"FIELD_INTEGRITY_EXCEPTION", message:"..."}]` | Validator rejection. See table below. |
|
|
163
|
+
| 400 | `[{errorCode:"INVALID_FIELD_FOR_INSERT_UPDATE" or "MALFORMED_ID", ...}]` | Skill bug — the MCU_ID from Stage 1 was wrong, or the body shape is off. Emit `{ok:false, kind:"transport", status:400, message}`. |
|
|
164
|
+
| 401 | (usually empty) | Bearer token invalid. Emit `{ok:false, kind:"auth", hint:"OAuth token invalid / expired — run 'sf org login web'"}`. |
|
|
165
|
+
| 403 | `[{errorCode:"INSUFFICIENT_ACCESS"}]` | User lacks perm to write `DeploymentStatus`. Emit `{ok:false, kind:"business", errorCode:"INSUFFICIENT_ACCESS", message}`. |
|
|
166
|
+
| 5xx | varies | Transport. The observer may have partially committed — Stage 3's SOQL is the source of truth. Read MCU state; if it's `Active`, report success with a warning; if `Error` or still `Disabled`, classify as `{ok:false, kind:"transport", status, message}`. |
|
|
167
|
+
|
|
168
|
+
**Known 400 `FIELD_INTEGRITY_EXCEPTION` messages:**
|
|
169
|
+
|
|
170
|
+
| Message fragment | Meaning | Envelope |
|
|
171
|
+
| --- | --- | --- |
|
|
172
|
+
| "invalid deployment status transition" | The current status doesn't allow `→ Provisioning` (e.g. MCU is already `Active` — Stage 1 should have caught this, but there's a race window). | Re-read MCU; if now `Active`, emit success-noop. If `Provisioning`, Stage 3 poll. Otherwise emit `{ok:false, kind:"readiness-failed", ...}`. |
|
|
173
|
+
| "consent" / "keyword" / mentions of STOP/HELP | `validateChannelReadinessOnProvisioning` rejected — channel doesn't have required consent configured. | `{ok:false, kind:"readiness-failed", errorCode:"FIELD_INTEGRITY_EXCEPTION", message, hint:"channel requires a ConsentType and a matching MsgChannelLanguageKeyword record (opt-out keyword + confirmation) before activation — run service-de-channel-consent-configure"}`. |
|
|
174
|
+
| "routing" / "queue" / "SessionHandler" | Routing precondition (Stage 1 should have caught, but the validator re-checks). | `{ok:false, kind:"no-routing", message, hint:"run service-de-channel-routing-configure first"}`. |
|
|
175
|
+
| other | Unrecognized validator error. | `{ok:false, kind:"readiness-failed", errorCode, message}`. |
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Stage 3: Read the terminal MCU state
|
|
180
|
+
|
|
181
|
+
The PATCH is synchronous, so by the time we're here the MCU is `Active` or `Error` — no polling. Read once:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
185
|
+
--query "SELECT Id, DeploymentStatus, ErrorReason, ErrorDetails FROM MessagingChannelUsage WHERE Id = '{MCU_ID}'" \
|
|
186
|
+
--json > /tmp/amc-poststate-mcu.json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
| Status | Handling |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `Active` | Continue to Stage 4. |
|
|
192
|
+
| `Error` with `ErrorReason === "VERIFICATION_REQUIRED"` | For WhatsApp channels only: phone number needs OTP verification. Continue to Stage 3.2 (WhatsApp verification flow). |
|
|
193
|
+
| `Error` (other) | Emit `{ok:false, kind:"provisioning-error", mcuId, errorReason, errorDetails}`. |
|
|
194
|
+
| `Provisioning` | Unexpected — the observer was supposed to terminate before the PATCH returned 204. Fall through to a defensive poll (see Stage 3.1). |
|
|
195
|
+
| `Disabled` | The PATCH returned 204 but the write didn't take? Emit `{ok:false, kind:"transport", message:"PATCH returned 204 but MCU is still Disabled — observer didn't commit"}`. |
|
|
196
|
+
|
|
197
|
+
### Stage 3.2: WhatsApp phone number verification (only if ErrorReason === "VERIFICATION_REQUIRED")
|
|
198
|
+
|
|
199
|
+
This sub-flow only runs for WhatsApp channels when activation fails with `ErrorReason === "VERIFICATION_REQUIRED"` — the phone number needs OTP verification with Meta before it can be registered. **If the MCU comes back with `ErrorReason === "VERIFICATION_REQUIRED"` (WhatsApp only), load `references/phone-verification.md` and follow it** to drive the phone-number verification sub-flow (request code → prompt user → validate code → retry activation once).
|
|
200
|
+
|
|
201
|
+
### Stage 3.1: Defensive poll (only if Stage 3 saw `Provisioning`)
|
|
202
|
+
|
|
203
|
+
The observer's external-callout block (WhatsApp/Facebook/SMS) runs synchronously inside the PATCH request — there's no async queue indirection in `runProvisioning` for any message type (verified against the switch/case in `ConversationChannelUsageDeploymentStatusService`). So a `Provisioning` status at Stage 3 should not happen in steady state. Possible causes if it does: an exception thrown *after* the external call succeeded but before the PLSQL terminal-write ran; unusual instance config with async observer execution; or a future MessageType whose dispatch behavior we haven't accounted for. If Stage 3 returned `Provisioning`, loop:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
for i in 1 2 3 4 5 6 7 8 9 10; do
|
|
207
|
+
sleep 3
|
|
208
|
+
STATUS=$(sf data query --target-org '{ORG_ALIAS}' \
|
|
209
|
+
--query "SELECT DeploymentStatus FROM MessagingChannelUsage WHERE Id='{MCU_ID}'" --json \
|
|
210
|
+
| node -e 'console.log(JSON.parse(require("fs").readFileSync(0,"utf8")).result.records[0].DeploymentStatus)')
|
|
211
|
+
case "$STATUS" in
|
|
212
|
+
Provisioning) continue ;;
|
|
213
|
+
Active|Error) break ;;
|
|
214
|
+
esac
|
|
215
|
+
done
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Max wait: 30s. If still `Provisioning` after the loop: emit `{ok:false, kind:"timeout", mcuId, lastStatus:"Provisioning", elapsedS:30}`. Verified on WhatsApp/wadtesting 2026-04-30: this loop should never actually iterate.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Stage 4: Verify `MessagingChannel.IsActive`
|
|
223
|
+
|
|
224
|
+
The observer flips `IsActive` inside the same callback as the status terminal write. In principle this is set by the time the PATCH returns. Confirm:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
228
|
+
--query "SELECT Id, IsActive FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" --json > /tmp/amc-verify-channel.json
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
If `IsActive === true`: compute `durationMs = Date.now() - T0` and emit success. If `IsActive !== true` despite MCU `Active`: the `IsActive` sync pass skipped (e.g. `isTransitioningStatus` was true when the observer ran, which shouldn't happen post-terminal). Emit:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{"ok": false, "kind": "provisioning-error", "mcuId": "...", "errorReason": "mcu-active-but-channel-inactive",
|
|
235
|
+
"errorDetails": "MCU DeploymentStatus=Active but MessagingChannel.IsActive=false — observer's IsActive sync pass didn't run. Inspect MessagingChannelUsageFunctionsHelper.handlePostSave."}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Stage 5: Report to caller
|
|
241
|
+
|
|
242
|
+
Build the success envelope:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{"ok": true, "channelId": "{CHANNEL_ID}", "mcuId": "{MCU_ID}", "deploymentStatus": "Active",
|
|
246
|
+
"isActive": true, "messageType": "{MESSAGE_TYPE}", "durationMs": 20934}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
If this skill is the leaf (user invoked it directly), render:
|
|
250
|
+
|
|
251
|
+
- `Success — Activated — MessagingChannel {CHANNEL_ID} ({messageType}) is now live. MCU DeploymentStatus=Active, IsActive=true. (~{durationMs/1000}s)`
|
|
252
|
+
- `Info: Already active — MessagingChannel {CHANNEL_ID} is IsActive=true. No changes.` (no-op path)
|
|
253
|
+
- `Error: Routing not configured — run 'service-de-channel-routing-configure' skill first.` (no-routing)
|
|
254
|
+
- `Error: No MessagingChannelUsage row — run the insertion skill first.` (no-mcu)
|
|
255
|
+
- `Error: Readiness check failed: {message} — if it mentions consent/keywords, run 'service-de-channel-consent-configure' first.` (readiness-failed)
|
|
256
|
+
- `Error: Activation failed: {errorReason} — {errorDetails}` (provisioning-error)
|
|
257
|
+
- `Error: WhatsApp phone number verification failed: {hint}` (verification-failed)
|
|
258
|
+
- `Error: Could not request verification code: {message}` (verification-request-failed)
|
|
259
|
+
- `Timeout: Activation timed out after {elapsedS}s with MCU.DeploymentStatus={lastStatus}. Defensive-poll limit hit; this is unusual. Check MCU {mcuId} in Setup.` (timeout)
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Worked examples
|
|
264
|
+
|
|
265
|
+
For end-to-end activation traces (WhatsApp happy-path, Apple activation, a readiness-validator failure, and the already-active no-op), see `references/worked-examples.md`.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Gotchas
|
|
270
|
+
|
|
271
|
+
Eleven known gotchas — synchronous PATCH timing for WhatsApp, valid API status transitions, the `IsActive` sync pass, relationship-name variance across orgs, consent preconditions, `DeploymentStatus` picklist casing, the REST-vs-Apex equivalence, `ErrorReason` values, `VERIFICATION_REQUIRED` handling, OAuth token extraction, and the Status-code-409 Admin API conflict. Before troubleshooting an unexpected result or modifying this skill, load `references/gotchas.md` and follow it.
|