@salesforce/afv-skills 1.45.0 → 1.47.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 (171) 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/automation-flow-generate/SKILL.md +11 -5
  5. package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +275 -0
  6. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/README.md +32 -0
  7. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls +75 -0
  8. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls-meta.xml +5 -0
  9. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/interview-answers.json +13 -0
  10. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +10 -0
  11. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/create.json +20 -0
  12. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/update.json +16 -0
  13. package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +273 -0
  14. package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +236 -0
  15. package/skills/consumer-goods-promotion-bo-api-deploy/references/reference-example-set-comment-value.md +132 -0
  16. package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +211 -0
  17. package/skills/dx-code-analyzer-configure/scripts/validate-config.sh +14 -10
  18. package/skills/dx-code-analyzer-run/scripts/apply-fixes.js +45 -4
  19. package/skills/dx-code-analyzer-run/scripts/describe-rule.js +52 -32
  20. package/skills/dx-devops-project-manage/SKILL.md +197 -0
  21. package/skills/dx-devops-project-manage/examples/common-workflows.md +197 -0
  22. package/skills/dx-devops-project-manage/references/cli-commands.md +295 -0
  23. package/skills/dx-devops-project-manage/scripts/create-project.sh +48 -0
  24. package/skills/dx-devops-project-manage/scripts/list-projects.sh +51 -0
  25. package/skills/dx-devops-project-manage/scripts/update-project.sh +96 -0
  26. package/skills/education-cloud-academic-calendar-generate/SKILL.md +225 -0
  27. package/skills/education-cloud-academic-calendar-generate/examples/quarter-calendar.json +47 -0
  28. package/skills/education-cloud-academic-calendar-generate/examples/sample-output.md +57 -0
  29. package/skills/education-cloud-academic-calendar-generate/examples/semester-calendar.json +54 -0
  30. package/skills/education-cloud-academic-calendar-generate/references/calendar-systems.md +127 -0
  31. package/skills/education-cloud-academic-calendar-generate/references/date-validation.md +222 -0
  32. package/skills/education-cloud-academic-calendar-generate/references/foundation_prerequisites.md +40 -0
  33. package/skills/education-cloud-academic-calendar-generate/scripts/validate_calendar_dates.py +143 -0
  34. package/skills/education-cloud-course-catalog-migrate/SKILL.md +321 -0
  35. package/skills/education-cloud-course-catalog-migrate/references/gotchas-detail.md +16 -0
  36. package/skills/education-cloud-course-catalog-migrate/references/gotchas.md +16 -0
  37. package/skills/education-cloud-course-catalog-migrate/references/large-catalog-handling.md +42 -0
  38. package/skills/education-cloud-course-catalog-migrate/scripts/batch_courses.py +36 -0
  39. package/skills/education-cloud-course-catalog-migrate/scripts/detect_linked_courses.py +51 -0
  40. package/skills/education-cloud-course-catalog-migrate/scripts/detect_modality_variants.py +48 -0
  41. package/skills/education-cloud-course-catalog-migrate/scripts/resolve_api_version.py +43 -0
  42. package/skills/education-cloud-course-catalog-migrate/scripts/split_course_code.py +39 -0
  43. package/skills/education-cloud-course-catalog-migrate/scripts/validate_completeness.py +54 -0
  44. package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +3 -5
  45. package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +177 -0
  46. package/skills/education-cloud-student-recruitment-agent-configure/references/agent-and-subagents.md +151 -0
  47. package/skills/education-cloud-student-recruitment-agent-configure/references/customer-narration.md +34 -0
  48. package/skills/education-cloud-student-recruitment-agent-configure/references/execution-model.md +54 -0
  49. package/skills/education-cloud-student-recruitment-agent-configure/references/flows.md +82 -0
  50. package/skills/education-cloud-student-recruitment-agent-configure/references/grounding.md +199 -0
  51. package/skills/education-cloud-student-recruitment-agent-configure/references/permissions.md +183 -0
  52. package/skills/education-cloud-student-recruitment-agent-configure/references/platform-enablement.md +82 -0
  53. package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +158 -0
  54. package/skills/education-cloud-student-recruitment-agent-configure/references/routing.md +141 -0
  55. package/skills/experience-cms-brand-apply/SKILL.md +5 -5
  56. package/skills/experience-cms-brand-create/SKILL.md +2 -2
  57. package/skills/experience-cms-content-generate/SKILL.md +1 -0
  58. package/skills/experience-cms-content-render/SKILL.md +173 -0
  59. package/skills/experience-cms-content-render/assets/angular/DetailPage.component.ts +25 -0
  60. package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +133 -0
  61. package/skills/experience-cms-content-render/assets/angular/TypeList.component.ts +38 -0
  62. package/skills/experience-cms-content-render/assets/angular/TypeRenderer.component.ts +90 -0
  63. package/skills/experience-cms-content-render/assets/angular/cms-content.component.ts +248 -0
  64. package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +100 -0
  65. package/skills/experience-cms-content-render/assets/react/DetailPage.tsx +20 -0
  66. package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +129 -0
  67. package/skills/experience-cms-content-render/assets/react/TypeList.tsx +40 -0
  68. package/skills/experience-cms-content-render/assets/react/TypeRenderer.tsx +64 -0
  69. package/skills/experience-cms-content-render/assets/react/heuristicRenderer.tsx +310 -0
  70. package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +129 -0
  71. package/skills/experience-cms-content-render/assets/shared/cmsContentType.ts +49 -0
  72. package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +96 -0
  73. package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +55 -0
  74. package/skills/experience-cms-content-render/references/bulk-loading.md +60 -0
  75. package/skills/experience-cms-content-render/references/codegen-guardrails.md +111 -0
  76. package/skills/experience-cms-content-render/references/detail-pages.md +87 -0
  77. package/skills/experience-cms-content-render/references/embed-recipes.md +127 -0
  78. package/skills/experience-cms-content-render/references/failure-modes.md +96 -0
  79. package/skills/experience-cms-content-render/references/heuristic-render-rules.md +131 -0
  80. package/skills/experience-cms-content-render/references/init-scaffold.md +122 -0
  81. package/skills/experience-cms-content-render/references/interaction-model.md +173 -0
  82. package/skills/experience-cms-content-render/references/package-api.md +106 -0
  83. package/skills/experience-cms-content-render/references/schema-sync.md +114 -0
  84. package/skills/experience-cms-content-render/references/styling-scopes.md +65 -0
  85. package/skills/experience-cms-content-render/references/verify.md +49 -0
  86. package/skills/experience-cms-content-type-generate/SKILL.md +2 -2
  87. package/skills/experience-content-media-stock-image-search/SKILL.md +5 -4
  88. package/skills/experience-search-coordinate/SKILL.md +198 -0
  89. package/skills/experience-search-coordinate/assets/search-payload-template.json +25 -0
  90. package/skills/experience-search-coordinate/references/content-route.md +313 -0
  91. package/skills/experience-search-coordinate/references/content-type-discovery.md +57 -0
  92. package/skills/experience-search-coordinate/references/media-route.md +172 -0
  93. package/skills/experience-search-coordinate/references/scope-resolution.md +14 -0
  94. package/skills/experience-ui-bundle-localize/SKILL.md +1 -1
  95. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +5 -3
  96. package/skills/experience-ui-bundle-project-generate/SKILL.md +18 -14
  97. package/skills/experience-ui-bundle-project-generate/references/angular-project-generate.md +22 -0
  98. package/skills/experience-ui-bundle-project-generate/references/react-project-generate.md +20 -0
  99. package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +58 -54
  100. package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +6 -0
  101. package/skills/experience-ui-bundle-salesforce-data-access/references/graphiti-cli.md +2 -2
  102. package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +6 -0
  103. package/skills/experience-ui-bundle-salesforce-data-access/references/rest-and-integration.md +2 -1
  104. package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +6 -0
  105. package/skills/experience-ui-bundle-site-generate/SKILL.md +59 -8
  106. package/skills/experience-ui-bundle-site-generate/references/configure-metadata-digital-experience.md +8 -3
  107. package/skills/experience-ui-bundle-site-generate/references/configure-metadata-language-settings.md +120 -0
  108. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +336 -0
  109. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +143 -0
  110. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +127 -0
  111. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-deploy-commands.md +116 -0
  112. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +111 -0
  113. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-overview.md +312 -0
  114. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +171 -0
  115. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-state-tracking.md +64 -0
  116. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-trigger-handlers.md +122 -0
  117. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-overview.md +335 -0
  118. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-user-provisioning-details.md +140 -0
  119. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +196 -0
  120. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-metadata-cache-generation.md +155 -0
  121. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +307 -0
  122. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +211 -0
  123. package/skills/life-sciences-fieldsalesrep-coordinate/references/state-machine-and-changes.md +108 -0
  124. package/skills/life-sciences-kam-coordinate/SKILL.md +241 -0
  125. package/skills/life-sciences-kam-coordinate/references/orchestration-flow.md +152 -0
  126. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +79 -0
  127. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-deploy-commands.md +131 -0
  128. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-kam-config-records.md +85 -0
  129. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +112 -0
  130. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-overview.md +202 -0
  131. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +67 -0
  132. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-state-tracking.md +65 -0
  133. package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-trigger-handlers.md +123 -0
  134. package/skills/life-sciences-kam-coordinate/references/stage-4-participant-role-and-sprint.md +89 -0
  135. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +337 -0
  136. package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +248 -0
  137. package/skills/life-sciences-kam-coordinate/references/stage-6-ipad-validation-script.md +35 -0
  138. package/skills/life-sciences-kam-coordinate/references/stage-6-metadata-cache-generation.md +155 -0
  139. package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-details.md +146 -0
  140. package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-overview.md +89 -0
  141. package/skills/life-sciences-kam-coordinate/references/state-machine-and-changes.md +114 -0
  142. package/skills/life-sciences-prerequisites-validate/SKILL.md +138 -0
  143. package/skills/life-sciences-prerequisites-validate/references/checks-org-settings.md +190 -0
  144. package/skills/life-sciences-prerequisites-validate/references/checks-user-and-package.md +211 -0
  145. package/skills/life-sciences-territory-configure/SKILL.md +217 -0
  146. package/skills/life-sciences-territory-configure/references/territory-metadata.md +262 -0
  147. package/skills/platform-apex-logs-debug/SKILL.md +7 -7
  148. package/skills/platform-custom-application-generate/SKILL.md +4 -4
  149. package/skills/platform-custom-object-generate/SKILL.md +7 -7
  150. package/skills/platform-custom-tab-generate/SKILL.md +1 -1
  151. package/skills/platform-dsar-policy-manage/SKILL.md +272 -0
  152. package/skills/platform-dsar-policy-manage/references/configure.md +106 -0
  153. package/skills/platform-dsar-policy-manage/references/export-and-history.md +123 -0
  154. package/skills/platform-dsar-policy-manage/references/gap-analysis-guide.md +150 -0
  155. package/skills/platform-dsar-policy-manage/references/gap-scan.md +129 -0
  156. package/skills/platform-dsar-policy-manage/references/headless-sor.md +59 -0
  157. package/skills/platform-dsar-policy-manage/references/report-format.md +59 -0
  158. package/skills/platform-dsar-policy-manage/scripts/tests/__init__.py +0 -0
  159. package/skills/platform-dsar-policy-manage/scripts/tests/test_validate_policy_tree.py +76 -0
  160. package/skills/platform-dsar-policy-manage/scripts/validate-policy-tree.py +130 -0
  161. package/skills/platform-flexipage-generate/SKILL.md +4 -0
  162. package/skills/platform-list-view-generate/SKILL.md +1 -0
  163. package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +359 -0
  164. package/skills/platform-salesforce-connect-adapter-generate/references/official-examples.md +69 -0
  165. package/skills/platform-salesforce-connect-adapter-generate/references/scenarios.md +187 -0
  166. package/skills/platform-soql-query/SKILL.md +8 -8
  167. package/skills/platform-value-set-generate/SKILL.md +2 -2
  168. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +20 -27
  169. package/skills/service-native-voice-recording-transcription-configure/SKILL.md +47 -27
  170. package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +13 -9
  171. package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +104 -45
@@ -80,28 +80,22 @@ CMDB Setup (via service-itsm-agentic-setup-cmdb-coordinate)
80
80
  Target org: <org> (all steps below run against this org)
81
81
 
82
82
  CMDB is enabled in ordered layers. Each must succeed before the next:
83
+ ```
83
84
 
84
- ┌───┬───────────────────────────┬──────────────────────────────────────────────┬──────────┐
85
- │ # │ Layer │ What it does │ Status │
86
- ├───┼───────────────────────────┼──────────────────────────────────────────────┼──────────┤
87
- │ 0 │ License check │ Confirm this org is licensed for CMDB │ Pending │
88
- │ │ (prerequisite) │ (set by edition/license — can't be turned on) │ │
89
- ├───┼───────────────────────────┼──────────────────────────────────────────────┼──────────┤
90
- │ 1 │ Provision & Enable CMDB │ Set up the CMDB service, then turn on the │ Pending │
91
- │ 2 │ │ CMDB feature (makes CMDB available to use) │ │
92
- ├───┼───────────────────────────┼──────────────────────────────────────────────┼──────────┤
93
- │ 3 │ Assign user access │ Grant CMDB access to the chosen users │ Pending │
94
- ├───┼───────────────────────────┼──────────────────────────────────────────────┼──────────┤
95
- │ 4 │ Install content bundle │ Install the CMDB Foundation (base) content │ Pending │
96
- ├───┼───────────────────────────┼──────────────────────────────────────────────┼──────────┤
97
- │ 5 │ Enable Asset Discovery │ Turn on asset discovery and grant Discovery │ Pending │
98
- │ │ │ page access (final step — needs the steps │ │
99
- │ │ │ above done first) │ │
100
- └───┴───────────────────────────┴──────────────────────────────────────────────┴──────────┘
85
+ | # | Layer | What it does | Status |
86
+ | --- | ------------------------ | ------------------------------------------------------------ | ------- |
87
+ | 0 | License check | Confirm this org is licensed for CMDB (set by edition/license — can't be turned on) | Pending |
88
+ | 1–2 | Provision & enable CMDB | Set up the CMDB service, then turn on the CMDB feature so it's available to use | Pending |
89
+ | 3 | Assign user access | Grant CMDB access to the chosen users | Pending |
90
+ | 4 | Install content bundle | Install the CMDB Foundation (base) content | Pending |
91
+ | 5 | Enable Asset Discovery | Turn on asset discovery and grant Discovery page access (final step — needs the steps above done first) | Pending |
101
92
 
93
+ ```text
102
94
  I recommend running these in order. Where would you like to start (or shall I run 1 → 5)?
103
95
  ```
104
96
 
97
+ Render one row per layer with a single-line cell — do not split a layer across stacked rows or wrap
98
+ a cell onto a second line. Present the combined provision-and-enable step as the single row `1–2`.
105
99
  Only show layers that have a corresponding, working child skill. Do NOT show placeholder layers.
106
100
 
107
101
  ### 4. Delegate to child skills
@@ -134,18 +128,17 @@ When all available layers are done (or the user stops), present a final summary:
134
128
  CMDB Setup — Complete (via service-itsm-agentic-setup-cmdb-coordinate)
135
129
 
136
130
  Target org: <org>
131
+ ```
137
132
 
138
- ┌───┬───────────────────────────┬──────────┐
139
- │ # │ Layer │ Status │
140
- ├───┼───────────────────────────┼──────────┤
141
- │ 0 │ License check │ Done │
142
- │ 1 │ CMDB service setup │ Done │
143
- │ 2 │ CMDB feature turned on │ Done │
144
- │ 3 │ User access │ Done │
145
- │ 4 │ Content bundle (base) │ Done │
146
- │ 5 │ Asset Discovery + access │ Done │
147
- └───┴───────────────────────────┴──────────┘
133
+ | # | Layer | Status |
134
+ | --- | ------------------------ | ------ |
135
+ | 0 | License check | Done |
136
+ | 1–2 | Provision & enable CMDB | Done |
137
+ | 3 | User access | Done |
138
+ | 4 | Content bundle (base) | Done |
139
+ | 5 | Asset Discovery + access | Done |
148
140
 
141
+ ```text
149
142
  CMDB is enabled and ready. Users with the assigned permission sets can now work with
150
143
  Configuration Items. Next, you can model CIs, identification rules, and relationships.
151
144
  ```
@@ -1,10 +1,13 @@
1
1
  ---
2
2
  name: service-native-voice-recording-transcription-configure
3
- description: "Enable call recording and call transcription for Native Voice (Thunderbird Voice) programmatically via the Metadata API, for headless / API-driven support where no human uses the Service Console UI. Use this skill when a user asks to turn on, enable, or configure Native Voice recording and/or transcription for headless or automated voice calls. TRIGGER when: users mention Thunderbird Voice, Native Voice, ThunderbirdVoiceSettings, enableCallRecording, enableCallTranscription, 'enable recording and transcription', 'turn on call recording via API', headless voice recording, or configuring voice-call capture without clicking through Setup. DO NOT TRIGGER when: the user wants to redact or scrub PII from existing recordings, transcribe a supplied audio file, or author a Flow/Apex — those are out of scope for this skill."
3
+ description: "Configure (enable OR disable) call recording and call transcription for Native Voice (Thunderbird Voice) programmatically via the Metadata API, for headless / API-driven support where no human uses the Service Console UI. Either preference can be turned on or off independently. Use this skill when a user asks to turn on/off, enable/disable, or configure Native Voice recording and/or transcription for headless or automated voice calls. TRIGGER when: users mention Thunderbird Voice, Native Voice, ThunderbirdVoiceSettings, enableCallRecording, enableCallTranscription, 'enable recording and transcription', 'turn off call recording', 'disable transcription', 'turn on call recording via API', headless voice recording, or configuring voice-call capture without clicking through Setup. DO NOT TRIGGER when: the user wants to redact or scrub PII from existing recordings, transcribe a supplied audio file, or author a Flow/Apex — those are out of scope for this skill."
4
4
  metadata:
5
- version: "1.1"
5
+ version: "1.2"
6
6
  domains: ["Service"]
7
7
  minApiVersion: "68.0"
8
+ accessCheck:
9
+ - type: "orgPerm"
10
+ value: "ThunderbirdVoice.orgHasNativeVoiceAllowed"
8
11
  cliTools:
9
12
  - tool: ["jq"]
10
13
  semver: ">=1.6"
@@ -12,20 +15,23 @@ metadata:
12
15
  semver: ">=2.0.0"
13
16
  ---
14
17
 
15
- # Enable Native Voice Recording & Transcription (headless)
18
+ # Configure Native Voice Recording & Transcription (headless)
16
19
 
17
- Turn on call **recording** and call **transcription** for Native Voice (Thunderbird
18
- Voice) on an org, programmatically, so they take effect for headless / API-driven
19
- support sessions that never touch the Service Console UI. Recording and transcription
20
- are **two separate preferences** — this skill enables both and returns a structured
21
- JSON confirmation.
20
+ Turn call **recording** and call **transcription** for Native Voice (Thunderbird
21
+ Voice) **on or off** on an org, programmatically, so the change takes effect for
22
+ headless / API-driven support sessions that never touch the Service Console UI.
23
+ Recording and transcription are **two separate preferences** — this skill can set
24
+ either or both to the requested state (leaving the other untouched) and returns a
25
+ structured JSON confirmation.
22
26
 
23
27
  ## Scope
24
28
 
25
- - **In scope**: enabling `enableCallRecording` and `enableCallTranscription` on the
29
+ - **In scope**: setting `enableCallRecording` and/or `enableCallTranscription` to a
30
+ requested state — **`true` (enable) or `false` (disable)** — on the
26
31
  `ThunderbirdVoiceSettings` metadata via an sf CLI Metadata API deploy (the generic
27
32
  `Settings` container, member `ThunderbirdVoice`); returning a JSON confirmation of
28
- the resulting configuration.
33
+ the resulting configuration. A preference the user does not name is left at its
34
+ current value.
29
35
  - **Out of scope**: changing any other `ThunderbirdVoiceSettings` boolean (e.g.
30
36
  call-recording redaction, SIP recording) — siblings are read only to be preserved,
31
37
  never set or reported; redacting/scrubbing PII from existing recordings or
@@ -43,8 +49,11 @@ Gather or infer before proceeding:
43
49
  uses this existing `sf` login; no session id or access token is requested. Default:
44
50
  the org's default target. Confirm connectivity with `sf org display -o <alias>`.
45
51
  - **Which preferences** — recording, transcription, or both. Default: **both**, since
46
- headless capture typically needs recording *and* transcription. Confirm only if the
47
- user named just one.
52
+ headless capture typically needs recording *and* transcription. If the user named
53
+ just one, target only that one and leave the other at its current value.
54
+ - **Desired state** — enable (`on`/`true`) or disable (`off`/`false`) for each named
55
+ preference. Default: **enable**, unless the user asks to turn something off. Honor a
56
+ disable request literally — never re-enable a preference the user asked to turn off.
48
57
 
49
58
  ---
50
59
 
@@ -58,13 +67,18 @@ missing org context.
58
67
  booleans on the same metadata type — never collapse them into one combined flag.
59
68
  The final confirmation must report each independently.
60
69
 
61
- 2. **Run the enable script.** Run
62
- `scripts/enable-recording-transcription.sh --target-org <alias>`. It retrieves
63
- `ThunderbirdVoiceSettings` (via the `Settings` container), checks the org-perm gate,
64
- flips the two target flags to `true` while preserving every sibling, validates with
65
- a dry-run deploy, deploys, and verifies by round-trip. Use `--dry-run` to retrieve +
66
- validate without persisting. Run
67
- `scripts/enable-recording-transcription.sh --help` for all options.
70
+ 2. **Run the configure script.** Run
71
+ `scripts/enable-recording-transcription.sh --target-org <alias>` with the desired
72
+ state per preference: `--recording on|off` and/or `--transcription on|off`. With no
73
+ preference flags it defaults to turning **both on**. Examples: turn both off →
74
+ `--recording off --transcription off`; enable recording only →
75
+ `--recording on` (transcription left as-is); disable transcription only →
76
+ `--transcription off`. The script retrieves `ThunderbirdVoiceSettings` (via the
77
+ `Settings` container), checks the org-perm gate, sets **only the named** flag(s) to
78
+ the requested value while preserving every other field, validates with a dry-run
79
+ deploy, deploys, and verifies by round-trip. Use `--dry-run` to retrieve + validate
80
+ without persisting. Run `scripts/enable-recording-transcription.sh --help` for all
81
+ options.
68
82
 
69
83
  3. **For the Metadata API contract**, read `references/thunderbird-voice-settings.md` —
70
84
  it documents the type, the two booleans, the `Settings`-container manifest, the
@@ -75,15 +89,20 @@ missing org context.
75
89
  > mdApiType **without** the `Settings` suffix (`ThunderbirdVoice`), and the manifest
76
90
  > version MUST be **>= 68.0** — the fields are not exposed at any lower version.
77
91
 
78
- 4. **Change only the two target fields.** The deployed settings file sets exactly
79
- `enableCallRecording` and `enableCallTranscription` to `true`; every other
80
- `ThunderbirdVoiceSettings` boolean keeps its retrieved value. Do not add or flip
81
- other booleans — they are out of this skill's scope.
92
+ 4. **Change only the named target field(s), to the requested state.** The deployed
93
+ settings file sets exactly the preference(s) the user named — `enableCallRecording`
94
+ and/or `enableCallTranscription` — to the requested value (`true` to enable, `false`
95
+ to disable); every other `ThunderbirdVoiceSettings` boolean, INCLUDING a target
96
+ preference the user did not name, keeps its retrieved value. Do not add or flip
97
+ other booleans, and never coerce a disable request back to `true` — they are out of
98
+ this skill's scope or contrary to the request.
82
99
 
83
100
  5. **Return the confirmation JSON.** Emit a single JSON object matching
84
101
  `assets/confirmation-output.json`: the metadata `type` and `fullName`, that it was
85
102
  applied via a headless/API deploy, and — for **each** of the two preferences
86
- separately — its field name and enabled state, plus an overall `status`. It also
103
+ separately — its field name and its resulting enabled state (`true`/`false`, as it
104
+ now stands after the deploy, whether it was changed or left as-is), plus an overall
105
+ `status` (`ENABLED`, `DISABLED`, or `CONFIGURED`). It also
87
106
  includes a **`preserved`** array echoing every sibling boolean (e.g.
88
107
  `enableCallRecRedaction`, `enableSipRecording`) with the value it was retrieved
89
108
  with — this attests the siblings were carried through **unchanged**, which
@@ -102,8 +121,9 @@ missing org context.
102
121
  | Deploy through the generic `Settings` container with member `ThunderbirdVoice` | `ThunderbirdVoiceSettings` is not a retrievable top-level type name; the `Settings` container (member = mdApiType minus the `Settings` suffix) is the sf CLI path and uses the org's `sf` login — no session id. |
103
122
  | Manifest version must be >= 68.0 | `ThunderbirdVoiceSettings` fields are only exposed at API v68.0+; below that the retrieve returns the type empty/absent. |
104
123
  | Retrieve before deploy; preserve all sibling fields | The deploy is a full-file replace — starting from the retrieved file keeps other prefs intact and lets the gate be checked. |
105
- | Report the preserved siblings in a `preserved` block, but never change them | Compliance callers need explicit "nothing else moved" assurance; the confirmation echoes each sibling's retrieved value unchanged. Echoing a value is not setting it — the deployed file still flips only the two target fields. |
106
- | Verify by round-trip before reporting `ENABLED` | A "Succeeded" deploy is not proof; only claim enabled after a re-retrieve returns both `true`. |
124
+ | Report the preserved siblings in a `preserved` block, but never change them | Compliance callers need explicit "nothing else moved" assurance; the confirmation echoes each sibling's retrieved value unchanged. Echoing a value is not setting it — the deployed file changes only the named target field(s). |
125
+ | Honor the requested state literally — enable means `true`, disable means `false` | A disable request that silently re-enables the preference (or reports success while leaving it on) is a correctness failure; set exactly what was asked and report the value the org actually returns. |
126
+ | Verify by round-trip before reporting the status | A "Succeeded" deploy is not proof; only report the resulting state after a re-retrieve returns each named preference at its requested value. |
107
127
  | Output must be a single valid JSON object | The confirmation is consumed programmatically by headless callers; prose-only answers are not usable. |
108
128
  | Do not include a session id, access token, phone number, recording URL, or transcript content in the output | These are secrets / PII; the confirmation reports configuration state only. |
109
129
 
@@ -140,7 +160,7 @@ project and reports the result.
140
160
 
141
161
  | File | When to read |
142
162
  |------|-------------|
143
- | `scripts/enable-recording-transcription.sh` | Step 2 — run to retrieve → flip → validate → deploy → verify (supports `--dry-run`, `--api-version`, `--help`). |
163
+ | `scripts/enable-recording-transcription.sh` | Step 2 — run to retrieve → set named field(s) → validate → deploy → verify (supports `--recording on\|off`, `--transcription on\|off`, `--dry-run`, `--api-version`, `--help`; defaults to both on). |
144
164
  | `references/thunderbird-voice-settings.md` | Step 3 — for the metadata type, the two booleans, the `Settings` manifest, the v68.0+ floor, and the gate check. |
145
165
  | `assets/package.xml` | Step 3 — the Metadata API manifest template the script generates; read to understand or hand-build the retrieve/deploy. |
146
166
  | `assets/confirmation-output.json` | Step 5 — the exact shape of the JSON confirmation to return. |
@@ -1,8 +1,9 @@
1
1
  # ThunderbirdVoiceSettings — Metadata API contract
2
2
 
3
- Reference for enabling Native Voice (Thunderbird Voice) recording and transcription
4
- via the Metadata API, using the sf CLI `Settings`-container retrieve/deploy path.
5
- Read this when constructing or debugging `scripts/enable-recording-transcription.sh`.
3
+ Reference for configuring — enabling **or disabling** — Native Voice (Thunderbird
4
+ Voice) recording and transcription via the Metadata API, using the sf CLI
5
+ `Settings`-container retrieve/deploy path. Read this when constructing or debugging
6
+ `scripts/enable-recording-transcription.sh`.
6
7
 
7
8
  ## The metadata type
8
9
 
@@ -16,12 +17,13 @@ This skill manages exactly two boolean preferences:
16
17
 
17
18
  | Field | Meaning | This skill |
18
19
  |-------|---------|------------|
19
- | `enableCallRecording` | Capture audio of Native Voice calls | **set `true`** (target) |
20
- | `enableCallTranscription` | Produce transcripts of Native Voice calls | **set `true`** (target) |
20
+ | `enableCallRecording` | Capture audio of Native Voice calls | **set to the requested state** (`true`/`false`) when named (target) |
21
+ | `enableCallTranscription` | Produce transcripts of Native Voice calls | **set to the requested state** (`true`/`false`) when named (target) |
21
22
 
22
23
  Recording and transcription are independent — a call can be recorded without being
23
24
  transcribed and vice versa — so they are always modeled and reported as two separate
24
- preferences.
25
+ preferences, and each can be enabled or disabled on its own. A target preference the
26
+ caller does not name is left at its retrieved value, exactly like a sibling.
25
27
 
26
28
  The type may expose other booleans (e.g. `enableCallRecRedaction`,
27
29
  `enableRealTimeStreamingRecording`, `enableSipRecording`). They are **out of scope** to
@@ -74,11 +76,13 @@ SFDX project). Two things matter:
74
76
  (no fields), the org-perm gate `ThunderbirdVoice.orgHasNativeVoiceAllowed` is **off**
75
77
  — Native Voice is not provisioned. Stop; this is enabled via provisioning/Blacktab,
76
78
  not this skill. If the fields are listed, the gate is on — proceed.
77
- 3. **Flip** `enableCallRecording` and `enableCallTranscription` to `true`; leave every
78
- sibling field at its retrieved value.
79
+ 3. **Set** each named target (`enableCallRecording` and/or `enableCallTranscription`)
80
+ to its requested value (`true` to enable, `false` to disable); leave every other
81
+ field — siblings and any un-named target — at its retrieved value.
79
82
  4. **Validate** with `deploy --dry-run` (never skip).
80
83
  5. **Deploy**.
81
- 6. **Verify** by re-retrieving and confirming the org *returns* both `true`.
84
+ 6. **Verify** by re-retrieving and confirming the org *returns* each named field at
85
+ its requested value.
82
86
 
83
87
  ## Distinguishing the two failure modes
84
88
 
@@ -1,27 +1,35 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
- # Enable Native Voice (Thunderbird Voice) call recording + transcription via the
4
- # Metadata API, using the sf CLI Settings-container retrieve/deploy path.
3
+ # Configure Native Voice (Thunderbird Voice) call recording + transcription via
4
+ # the Metadata API, using the sf CLI Settings-container retrieve/deploy path.
5
5
  #
6
- # Scope: this skill enables exactly two preferences — enableCallRecording and
7
- # enableCallTranscription. Every other ThunderbirdVoiceSettings boolean is read
8
- # only to be preserved verbatim — none is changed. The siblings ARE reported
9
- # back (unchanged) in a `preserved` block so compliance callers can confirm
10
- # nothing else moved; reporting a value is not the same as setting it.
6
+ # Scope: this skill configures exactly two preferences — enableCallRecording and
7
+ # enableCallTranscription — to a requested ON or OFF state. Either or both may be
8
+ # targeted (`--recording on|off`, `--transcription on|off`); a preference that is
9
+ # NOT named is left at its retrieved value (preserved), so you can turn one on or
10
+ # off without disturbing the other. With NO preference flags the script defaults
11
+ # to turning BOTH on (the original enable-both behavior). Every OTHER
12
+ # ThunderbirdVoiceSettings boolean is read only to be preserved verbatim — none
13
+ # is changed. The siblings ARE reported back (unchanged) in a `preserved` block
14
+ # so compliance callers can confirm nothing else moved; reporting a value is not
15
+ # the same as setting it.
11
16
  #
12
17
  # Why sf CLI (not raw SOAP): the generic `Settings` metadata type IS in the sf
13
18
  # CLI registry (member = the type name without the `Settings` suffix, i.e.
14
19
  # `ThunderbirdVoice`). Deploying through it authenticates with the org's existing
15
20
  # `sf` login — no browser session id / access token is ever handled. Retrieving
16
- # first lets us (a) check the org-perm gate and (b) preserve sibling prefs.
21
+ # first lets us (a) check the org-perm gate and (b) preserve non-targeted prefs.
17
22
  #
18
- # Sequence: retrieve -> gate check -> flip the two target fields -> validate
19
- # (dry-run) -> deploy -> verify by round-trip -> print a JSON confirmation.
23
+ # Sequence: retrieve -> gate check -> set the targeted field(s) to the requested
24
+ # state -> validate (dry-run) -> deploy -> verify by round-trip -> print a JSON
25
+ # confirmation reporting the resulting state of BOTH preferences.
20
26
  #
21
27
  # Deps: sf, jq. (No curl, no session id.)
22
28
  #
23
29
  set -euo pipefail
24
30
 
31
+ fail() { echo "ERROR: $*" >&2; exit 1; }
32
+
25
33
  # ThunderbirdVoiceSettings fields are only exposed at API v68.0+; below that the
26
34
  # Settings type does not appear in a retrieve at all. Default to exactly 68.0 — the
27
35
  # field-exposure floor. A retrieve/deploy at a version ABOVE the org's max supported
@@ -31,6 +39,9 @@ set -euo pipefail
31
39
  API_VERSION="68.0"
32
40
  TARGET_ORG=""
33
41
  DRY_RUN="false"
42
+ # Desired state per target field: "" = not targeted (preserve), else "true"/"false".
43
+ REC_STATE=""
44
+ TRANS_STATE=""
34
45
 
35
46
  # mdApiType ThunderbirdVoiceSettings -> package.xml member without the "Settings" suffix.
36
47
  MEMBER="ThunderbirdVoice"
@@ -38,39 +49,60 @@ SETTINGS_FILE_REL="force-app/main/default/settings/${MEMBER}.settings-meta.xml"
38
49
 
39
50
  usage() {
40
51
  cat <<'EOF'
41
- Enable Native Voice call recording + transcription (sf CLI Settings deploy).
52
+ Configure Native Voice call recording + transcription (sf CLI Settings deploy).
42
53
 
43
54
  Usage:
44
- enable-recording-transcription.sh --target-org <alias> [--dry-run] [--api-version X.Y]
55
+ enable-recording-transcription.sh --target-org <alias> \
56
+ [--recording on|off] [--transcription on|off] [--dry-run] [--api-version X.Y]
45
57
 
46
58
  Options:
47
- --target-org <alias> Org alias/username the sf CLI is authenticated to. Required.
48
- --api-version <X.Y> Metadata API version (default: 68.0; must be >= 68.0, the
49
- version at which ThunderbirdVoiceSettings is exposed). Do NOT
50
- exceed the target org's max supported version — a retrieve
51
- above it silently returns empty.
52
- --dry-run Retrieve + validate the change (deploy --dry-run) but do NOT
53
- persist it. Prints the JSON confirmation with status VALIDATED.
54
- -h, --help Show this help.
55
-
56
- On success prints a JSON confirmation object: the two enabled preferences reported
57
- separately, plus a `preserved` block echoing every sibling boolean (unchanged) so
58
- callers can confirm nothing else moved.
59
+ --target-org <alias> Org alias/username the sf CLI is authenticated to. Required.
60
+ --recording on|off Desired state for call recording (enableCallRecording).
61
+ --transcription on|off Desired state for call transcription (enableCallTranscription).
62
+ If NEITHER --recording nor --transcription is given, the
63
+ script defaults to turning BOTH on. A preference that is
64
+ not named is left at its current (retrieved) value.
65
+ --api-version <X.Y> Metadata API version (default: 68.0; must be >= 68.0, the
66
+ version at which ThunderbirdVoiceSettings is exposed). Do NOT
67
+ exceed the target org's max supported version — a retrieve
68
+ above it silently returns empty.
69
+ --dry-run Retrieve + validate the change (deploy --dry-run) but do NOT
70
+ persist it. Prints the JSON confirmation with status VALIDATED.
71
+ -h, --help Show this help.
72
+
73
+ On success prints a JSON confirmation object: BOTH preferences reported separately
74
+ with their resulting state, plus a `preserved` block echoing every sibling boolean
75
+ (unchanged) so callers can confirm nothing else moved.
59
76
  Uses the org's sf login; no session id / access token is handled or printed.
60
77
  EOF
61
78
  }
62
79
 
80
+ # Normalize an on/off style value to the literal "true"/"false" used in the XML.
81
+ onoff() { # $1 = flag name (for errors), $2 = value
82
+ case "$2" in
83
+ on|true|enable|enabled) echo "true" ;;
84
+ off|false|disable|disabled) echo "false" ;;
85
+ *) fail "invalid value for $1: '$2' (expected on|off)." ;;
86
+ esac
87
+ }
88
+
63
89
  while [ $# -gt 0 ]; do
64
90
  case "$1" in
65
91
  --target-org) TARGET_ORG="$2"; shift 2 ;;
66
92
  --api-version) API_VERSION="$2"; shift 2 ;;
93
+ --recording) REC_STATE="$(onoff --recording "$2")"; shift 2 ;;
94
+ --transcription) TRANS_STATE="$(onoff --transcription "$2")"; shift 2 ;;
67
95
  --dry-run) DRY_RUN="true"; shift ;;
68
96
  -h|--help) usage; exit 0 ;;
69
97
  *) echo "ERROR: unknown argument: $1" >&2; usage >&2; exit 2 ;;
70
98
  esac
71
99
  done
72
100
 
73
- fail() { echo "ERROR: $*" >&2; exit 1; }
101
+ # Default: if neither preference was named, target BOTH on — the original
102
+ # enable-both behavior, so existing callers keep working unchanged.
103
+ if [ -z "$REC_STATE" ] && [ -z "$TRANS_STATE" ]; then
104
+ REC_STATE="true"; TRANS_STATE="true"
105
+ fi
74
106
 
75
107
  command -v sf >/dev/null 2>&1 || fail "sf CLI not found (required)."
76
108
  command -v jq >/dev/null 2>&1 || fail "jq not found (required)."
@@ -100,6 +132,15 @@ EOF
100
132
 
101
133
  SETTINGS_FILE="$PROJ/$SETTINGS_FILE_REL"
102
134
 
135
+ # Read the literal true/false a boolean field currently holds in the settings
136
+ # file; echoes "false" if the field is absent (guarded so an empty grep under
137
+ # `set -o pipefail` does not abort the script).
138
+ read_pref() { # $1 = settings xml file, $2 = field name
139
+ local v
140
+ v="$({ grep -oE "<$2>(true|false)</$2>" "$1" || true; } | sed -E "s#<$2>([a-z]+)</$2>#\1#" | head -n1)"
141
+ echo "${v:-false}"
142
+ }
143
+
103
144
  # --- step 1: retrieve current settings (gate check + sibling snapshot) --------
104
145
  ( cd "$PROJ" && sf project retrieve start -x package.xml -o "$TARGET_ORG" >/dev/null 2>&1 ) \
105
146
  || fail "retrieve failed for org '$TARGET_ORG'. Check the alias is connected (sf org display -o $TARGET_ORG)."
@@ -113,24 +154,27 @@ if ! grep -q '<enableCallRecording>' "$SETTINGS_FILE"; then
113
154
  fail "Native Voice is not provisioned on '$TARGET_ORG' (ThunderbirdVoiceSettings returned no fields). This is an org-perm gate enabled through provisioning/Blacktab, not something this skill can flip."
114
155
  fi
115
156
 
116
- # --- step 2: flip only the two target fields, preserve every sibling ----------
117
- # Version-suffixed sed -i for BSD (macOS) + GNU portability.
118
- sed -E -i.bak \
119
- -e 's|<enableCallRecording>[^<]*</enableCallRecording>|<enableCallRecording>true</enableCallRecording>|' \
120
- -e 's|<enableCallTranscription>[^<]*</enableCallTranscription>|<enableCallTranscription>true</enableCallTranscription>|' \
121
- "$SETTINGS_FILE"
157
+ # --- step 2: set ONLY the targeted field(s) to the requested state ------------
158
+ # A field that is not targeted is left at its retrieved value (preserved), just
159
+ # like the siblings. Version-suffixed sed -i for BSD (macOS) + GNU portability.
160
+ SED_ARGS=()
161
+ [ -n "$REC_STATE" ] && SED_ARGS+=(-e "s|<enableCallRecording>[^<]*</enableCallRecording>|<enableCallRecording>${REC_STATE}</enableCallRecording>|")
162
+ [ -n "$TRANS_STATE" ] && SED_ARGS+=(-e "s|<enableCallTranscription>[^<]*</enableCallTranscription>|<enableCallTranscription>${TRANS_STATE}</enableCallTranscription>|")
163
+ sed -E -i.bak "${SED_ARGS[@]}" "$SETTINGS_FILE"
122
164
  rm -f "${SETTINGS_FILE}.bak"
123
165
 
124
- grep -q '<enableCallRecording>true</enableCallRecording>' "$SETTINGS_FILE" || fail "could not set enableCallRecording in the retrieved file."
125
- grep -q '<enableCallTranscription>true</enableCallTranscription>' "$SETTINGS_FILE" || fail "could not set enableCallTranscription in the retrieved file."
166
+ # A targeted field must now hold the requested value; if the tag was absent the
167
+ # sed matched nothing (a provisioned org exposes both, so this is an edge case).
168
+ [ -z "$REC_STATE" ] || grep -q "<enableCallRecording>${REC_STATE}</enableCallRecording>" "$SETTINGS_FILE" || fail "could not set enableCallRecording=${REC_STATE} in the retrieved file (field absent?)."
169
+ [ -z "$TRANS_STATE" ] || grep -q "<enableCallTranscription>${TRANS_STATE}</enableCallTranscription>" "$SETTINGS_FILE" || fail "could not set enableCallTranscription=${TRANS_STATE} in the retrieved file (field absent?)."
126
170
 
127
171
  # --- step 3: validate (dry-run). Never skip. ---------------------------------
128
172
  ( cd "$PROJ" && sf project deploy start -x package.xml -o "$TARGET_ORG" --dry-run --ignore-conflicts >/dev/null 2>&1 ) \
129
173
  || fail "validation (deploy --dry-run) failed — the org rejected the change. Re-check the gate (step 1)."
130
174
 
131
175
  # Collect every boolean sibling (<tag>true|false</tag>) EXCEPT the two target
132
- # prefs from the retrieved settings file, as a JSON array [{name, enabled}], so
133
- # the confirmation can attest each sibling was retrieved and carried through
176
+ # prefs from the settings file, as a JSON array [{name, enabled}], so the
177
+ # confirmation can attest each sibling was retrieved and carried through
134
178
  # unchanged. `grep -o` isolates each pair; `#`-delimited sed avoids colliding
135
179
  # with the `|` alternation; the `|| true` guards keep an empty result (no
136
180
  # siblings) from tripping `set -o pipefail`.
@@ -141,8 +185,16 @@ extract_preserved() { # $1 = settings xml file -> JSON array on stdout
141
185
  | jq -R -s 'split("\n") | map(select(length > 0) | split(" ") | { name: .[0], enabled: (.[1] == "true") })'
142
186
  }
143
187
 
144
- emit_json() { # $1 = status, $2 = preserved siblings JSON array
145
- jq -n --arg apiVersion "$API_VERSION" --arg status "$1" --argjson preserved "$2" \
188
+ # Overall status word from the resulting state of the two target preferences.
189
+ compute_status() { # $1 = recording bool, $2 = transcription bool
190
+ if [ "$1" = "true" ] && [ "$2" = "true" ]; then echo "ENABLED"
191
+ elif [ "$1" = "false" ] && [ "$2" = "false" ]; then echo "DISABLED"
192
+ else echo "CONFIGURED"; fi
193
+ }
194
+
195
+ emit_json() { # $1 = recording bool, $2 = transcription bool, $3 = status, $4 = preserved JSON
196
+ jq -n --arg apiVersion "$API_VERSION" --arg status "$3" \
197
+ --argjson recording "$1" --argjson transcription "$2" --argjson preserved "$4" \
146
198
  '{
147
199
  type: "ThunderbirdVoiceSettings",
148
200
  fullName: "ThunderbirdVoice",
@@ -150,8 +202,8 @@ emit_json() { # $1 = status, $2 = preserved siblings JSON array
150
202
  method: "sf project deploy (Settings/ThunderbirdVoice)",
151
203
  headless: true,
152
204
  preferences: [
153
- { name: "enableCallRecording", enabled: true },
154
- { name: "enableCallTranscription", enabled: true }
205
+ { name: "enableCallRecording", enabled: $recording },
206
+ { name: "enableCallTranscription", enabled: $transcription }
155
207
  ],
156
208
  preserved: $preserved,
157
209
  status: $status
@@ -159,9 +211,11 @@ emit_json() { # $1 = status, $2 = preserved siblings JSON array
159
211
  }
160
212
 
161
213
  if [ "$DRY_RUN" = "true" ]; then
162
- # Siblings are still at their retrieved values in the local file (only the two
163
- # targets were flipped in step 2); report them as preserved.
164
- emit_json "VALIDATED" "$(extract_preserved "$SETTINGS_FILE")"
214
+ # The local file already reflects the requested change (targets flipped, the
215
+ # rest at their retrieved values); report both prefs from it.
216
+ REC_FINAL="$(read_pref "$SETTINGS_FILE" enableCallRecording)"
217
+ TRANS_FINAL="$(read_pref "$SETTINGS_FILE" enableCallTranscription)"
218
+ emit_json "$REC_FINAL" "$TRANS_FINAL" "VALIDATED" "$(extract_preserved "$SETTINGS_FILE")"
165
219
  exit 0
166
220
  fi
167
221
 
@@ -173,9 +227,14 @@ fi
173
227
  rm -f "$SETTINGS_FILE"
174
228
  ( cd "$PROJ" && sf project retrieve start -x package.xml -o "$TARGET_ORG" >/dev/null 2>&1 ) \
175
229
  || fail "post-deploy retrieve failed; cannot verify."
176
- grep -q '<enableCallRecording>true</enableCallRecording>' "$SETTINGS_FILE" || fail "verification failed: org did not return enableCallRecording=true."
177
- grep -q '<enableCallTranscription>true</enableCallTranscription>' "$SETTINGS_FILE" || fail "verification failed: org did not return enableCallTranscription=true."
230
+
231
+ REC_FINAL="$(read_pref "$SETTINGS_FILE" enableCallRecording)"
232
+ TRANS_FINAL="$(read_pref "$SETTINGS_FILE" enableCallTranscription)"
233
+ # Each targeted field must come back at the requested state (untargeted fields
234
+ # are whatever the org holds — reported, not asserted).
235
+ [ -z "$REC_STATE" ] || [ "$REC_FINAL" = "$REC_STATE" ] || fail "verification failed: org did not return enableCallRecording=${REC_STATE} (got ${REC_FINAL})."
236
+ [ -z "$TRANS_STATE" ] || [ "$TRANS_FINAL" = "$TRANS_STATE" ] || fail "verification failed: org did not return enableCallTranscription=${TRANS_STATE} (got ${TRANS_FINAL})."
178
237
 
179
238
  # Report siblings from the post-deploy round-tripped file — their real, current
180
239
  # (unchanged) values, proving nothing else moved.
181
- emit_json "ENABLED" "$(extract_preserved "$SETTINGS_FILE")"
240
+ emit_json "$REC_FINAL" "$TRANS_FINAL" "$(compute_status "$REC_FINAL" "$TRANS_FINAL")" "$(extract_preserved "$SETTINGS_FILE")"