@salesforce/afv-skills 1.55.0 → 1.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-generate/SKILL.md +8 -0
  3. package/skills/agentforce-generate/references/actions-reference.md +2 -2
  4. package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
  5. package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
  6. package/skills/agentforce-observe/SKILL.md +13 -9
  7. package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
  8. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
  9. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
  10. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
  11. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
  12. package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
  13. package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
  14. package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
  15. package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
  16. package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
  17. package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
  18. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
  19. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
  20. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
  21. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
  22. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
  23. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
  24. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
  25. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
  26. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
  27. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
  28. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
  29. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
  30. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
  31. package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
  32. package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
  33. package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
  34. package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
  35. package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
  36. package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
  37. package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
  38. package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
  39. package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
  40. package/skills/dx-org-analyze/SKILL.md +0 -1
  41. package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
  42. package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
  43. package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
  44. package/skills/dx-org-manage/SKILL.md +6 -50
  45. package/skills/dx-org-manage/examples/README.md +10 -16
  46. package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
  47. package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
  48. package/skills/dx-org-shape-manage/SKILL.md +4 -3
  49. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
  50. package/skills/experience-accessibility-validate/SKILL.md +8 -5
  51. package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
  52. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
  53. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
  54. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
  55. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
  56. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
  57. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
  58. package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
  59. package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
  60. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
  61. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
  62. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
  63. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
  64. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
  65. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
  66. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
  67. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
  68. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
  69. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
  70. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
  71. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
  72. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
  73. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
  74. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
  75. package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
  76. package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
  77. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
  78. package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
  79. package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
  80. package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
  81. package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
  82. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
  83. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
  84. package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
  85. package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
  86. package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
  87. package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
  88. package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
  89. package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
  90. package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
  91. package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
  92. package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
  93. package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
  94. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
  95. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
  96. package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
  97. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
  98. package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
  99. package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
  100. package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
  101. package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
  102. package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
  103. package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
  104. package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
  105. package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
  106. package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
  107. package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
  108. package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
  109. package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
  110. package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
  111. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
  112. package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
  113. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
  114. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
  115. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
  116. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
  117. package/skills/mobile-apps-create/SKILL.md +1 -1
  118. package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
  119. package/skills/platform-sandbox-configure/SKILL.md +108 -133
  120. package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
  121. package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
  122. package/skills/platform-widget-generate/SKILL.md +26 -18
  123. package/skills/platform-widget-generate/examples/formulas.json +191 -0
  124. package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
  125. package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
  126. package/skills/sales-call-scoring-configure/SKILL.md +318 -0
  127. package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
  128. package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
  129. package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
  130. package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
  131. package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
  132. package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
  133. package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
  134. package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
  135. package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
  136. package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
  137. package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
  138. package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
  139. package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
  140. package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
  141. package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
  142. package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
  143. package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
  144. package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
  145. package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
  146. package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
  147. package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
  148. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
  149. package/skills/service-de-channel-activate/SKILL.md +6 -5
  150. package/skills/service-de-channel-create/SKILL.md +3 -3
  151. package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
  152. package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
  153. package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
  154. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
  155. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
  156. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
  157. package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
  158. package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
  159. package/skills/service-helpagent-coordinate/SKILL.md +3 -2
  160. package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
  161. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
  162. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
  163. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
  164. package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
  165. package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
  166. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
  167. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
  168. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
  169. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
  170. package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
  171. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
  172. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
  173. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
  174. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
  175. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
  176. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
  177. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
  178. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
  179. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
  180. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
  181. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
  182. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
  183. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
  184. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
  185. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
  186. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
  187. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
  188. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
  189. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
  190. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
  191. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
  192. package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
  193. package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
  194. package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
  195. package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
  196. package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
  197. package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
  198. package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
  199. package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
  200. package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
  201. package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
  202. package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
  203. package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
  204. package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
  205. package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
  206. package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
  207. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
  208. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
  209. package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
  210. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  211. package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
  212. package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
  213. package/skills/dx-org-manage/references/cli_flags.md +0 -67
  214. package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
  215. package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
  216. package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
  217. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
  218. package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
  219. /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
  220. /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
@@ -0,0 +1,413 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Authoritative FSM Flow <-> Data Capture capability-parity catalog.
4
+
5
+ This module is the single source of truth for the migration skill's decisions
6
+ about which Field Service Mobile (FSM) Flow screen components have a Data
7
+ Capture (DC) equivalent, which do not, and which DC components have no FSM
8
+ source at all. It is consumed by analyze_flow.py (gap reporting) and is the
9
+ data behind reference/fsm-dc-capability-catalog.md. WI-7/WI-10 import it too.
10
+
11
+ KEYED ON THE REAL FSM VOCABULARY
12
+ --------------------------------
13
+ FSM Flow screen fields use the standard Flow `FlowScreenFieldType` enum
14
+ (`InputField`, `DropdownBox`, `RadioButtons`, `MultiSelectCheckboxes`,
15
+ `DisplayText`, `LargeTextArea`, `PasswordField`, ...) — NOT Lightning component
16
+ names. A `lightning:*` / `forceContent:*` / `c:*` name only ever appears as the
17
+ `<extensionName>` INSIDE a `ComponentInstance` field. This catalog therefore
18
+ classifies by `fieldType` first and only inspects `extensionName` for
19
+ `ComponentInstance`.
20
+
21
+ VERIFIED AGAINST CORE (core-2206 / 264 release line)
22
+ ----------------------------------------------------
23
+ - FSM vocabulary: `FlowScreenField.FlowScreenFieldType` enum
24
+ core/interaction/submodules/udd/java/src/interaction/definition/mdapi/FlowScreenField.java
25
+ - FSM screen fields use a DENYLIST (not an allowlist): hook_isFlowScreenFieldTypeAllowed
26
+ blocks only Component*/Region/RegionContainer/Repeater/ObjectProvided, so the
27
+ full native vocabulary (InputField, DropdownBox, RadioButtons, MultiSelectPicklist,
28
+ MultiSelectCheckboxes, ...) is a valid migration SOURCE:
29
+ .../mdapi/processtype/FieldServiceMobileProcessTypeHandler.java
30
+ Separately, an FSM ComponentInstance may embed exactly one whitelisted extension:
31
+ .../mdapi/validation/FieldServiceMobileFlowValidator.java
32
+ -> supportedLightningComponents = ImmutableSet.of("forceContent:fileUpload")
33
+ - DC-allowed screen fields + actions:
34
+ .../mdapi/processtype/DataCaptureFlowProcessTypeHandler.java
35
+ allowedScreenFieldTypes = {DisplayText, ComponentInstance, ComponentChoice,
36
+ ComponentMultiChoice, RegionContainer, Region, Repeater, ObjectProvided}
37
+ allowedActionTypes = {AGX_FLOW} -> no user-facing action buttons
38
+ hook_isEnvironmentAllowed -> Offline only
39
+
40
+ TWO CORRECTIONS THIS CATALOG MAKES (see reference doc for the full write-up)
41
+ ----------------------------------------------------------------------------
42
+ 1. SIGNATURE is DC-only. Core exposes NO native FSM signature component. The
43
+ earlier decision (WI-3 / W-23364096) that "signature is a supported FSM->DC
44
+ migration target" was WRONG and is reversed here.
45
+ 2. FILE-UPLOAD is NOT DC-only (the assessment spreadsheet is wrong on this).
46
+ FSM allows exactly one screen extension — `forceContent:fileUpload` — and it
47
+ maps cleanly to `dcUpFile`. The migration keys off that extension name, never
48
+ the `lightning:fileUpload` name the old skill assumed.
49
+ """
50
+
51
+ from typing import Dict, List, Optional, Tuple
52
+
53
+ # DC screen-field categories (how the target renders in DataCaptureFlow XML).
54
+ COMPONENT_INSTANCE = "ComponentInstance"
55
+ COMPONENT_CHOICE = "ComponentChoice"
56
+ COMPONENT_MULTI_CHOICE = "ComponentMultiChoice"
57
+
58
+ DC_NS = "runtime_service_fieldservice:"
59
+
60
+ # Classification status values.
61
+ MAPPED = "mapped" # clean FSM -> DC conversion exists
62
+ NO_DC_EQUIVALENT = "no-DC-equivalent" # FSM component with nothing on the DC side
63
+ DC_ONLY = "DC-only" # DC component with no FSM source (reference)
64
+ UNKNOWN = "unknown" # not a recognised FSM screen field
65
+
66
+
67
+ # ──────────────────────────────────────────────────────────────────────────
68
+ # 1. FSM fieldType (standalone, no extensionName) -> DC target
69
+ # A `dc_target` of None means "no DC equivalent"; the field cannot migrate.
70
+ # ──────────────────────────────────────────────────────────────────────────
71
+
72
+ # (dc_extension, dc_fieldType) for InputField, resolved by the field's dataType.
73
+ # Matches transform_flow.py FIELD_TYPE_MAP exactly (catalog <-> transformer must
74
+ # agree). NOTE the Boolean discrepancy documented at the bottom of this file.
75
+ INPUTFIELD_BY_DATATYPE: Dict[str, Tuple[str, str]] = {
76
+ "Boolean": ("dcCheckbox", COMPONENT_INSTANCE),
77
+ "String": ("dcTextInput", COMPONENT_INSTANCE),
78
+ "Number": ("dcNumeric", COMPONENT_INSTANCE),
79
+ "Double": ("dcNumeric", COMPONENT_INSTANCE),
80
+ "Currency": ("dcNumeric", COMPONENT_INSTANCE),
81
+ "Date": ("dcDate", COMPONENT_INSTANCE),
82
+ "DateTime": ("dcDateTime", COMPONENT_INSTANCE),
83
+ "Email": ("dcEmail", COMPONENT_INSTANCE),
84
+ "Phone": ("dcPhone", COMPONENT_INSTANCE),
85
+ "TextArea": ("dcLongText", COMPONENT_INSTANCE),
86
+ "LongTextArea": ("dcLongText", COMPONENT_INSTANCE),
87
+ "Picklist": ("dcPicklist", COMPONENT_CHOICE),
88
+ "MultiselectPicklist": ("dcCbGroup", COMPONENT_MULTI_CHOICE),
89
+ "Name": ("dcName", COMPONENT_INSTANCE),
90
+ "Toggle": ("dcToggle", COMPONENT_INSTANCE),
91
+ "Address": ("dcAddress", COMPONENT_INSTANCE),
92
+ "Lookup": ("dcLookup", COMPONENT_INSTANCE),
93
+ "Counter": ("dcCounter", COMPONENT_INSTANCE),
94
+ "Matrix": ("dcMatrix", COMPONENT_MULTI_CHOICE),
95
+ }
96
+ # InputField with an unrecognised/absent dataType falls back to a text input.
97
+ INPUTFIELD_DEFAULT = ("dcTextInput", COMPONENT_INSTANCE)
98
+
99
+ # Standalone FSM screen field types (no extensionName involved).
100
+ # value = (status, dc_extension_or_None, dc_fieldType_or_None, note)
101
+ FSM_FIELDTYPE_CATALOG: Dict[str, Tuple[str, Optional[str], Optional[str], str]] = {
102
+ # ── Mapped: clean FSM -> DC path ──────────────────────────────────────
103
+ "DisplayText": (MAPPED, None, "DisplayText",
104
+ "Only field type valid in BOTH flow types; passes through. "
105
+ "Complex formulas fail at DC mobile runtime — pre-calculate."),
106
+ "LargeTextArea": (MAPPED, "dcLongText", COMPONENT_INSTANCE, ""),
107
+ "DropdownBox": (MAPPED, "dcPicklist", COMPONENT_CHOICE, ""),
108
+ "RadioButtons": (MAPPED, "dcRbGroup", COMPONENT_CHOICE, ""),
109
+ "MultiSelectCheckboxes": (MAPPED, "dcCbGroup", COMPONENT_MULTI_CHOICE, ""),
110
+ "MultiSelectPicklist": (MAPPED, "dcCbGroup", COMPONENT_MULTI_CHOICE,
111
+ "Multi-select choice field. Maps to dcCbGroup "
112
+ "(ComponentMultiChoice), matching transform_flow.py. "
113
+ "dcPicklist in multi mode is a closer-UX alternative "
114
+ "(also ComponentMultiChoice)."),
115
+ "RadioButtonGroup": (MAPPED, "dcRbGroup", COMPONENT_CHOICE,
116
+ "Single-select radio (grouped/segmented rendering); "
117
+ "same DC target as RadioButtons. Note: FSM reports the "
118
+ "RadioButtonGroup *feature* as unsupported, so it is "
119
+ "uncommon in genuine FSM sources."),
120
+ # InputField is resolved separately by dataType (see classify()).
121
+ "InputField": (MAPPED, None, COMPONENT_INSTANCE,
122
+ "Resolved by dataType to a dc* component."),
123
+
124
+ # ── No DC equivalent: FSM-only, cannot migrate automatically ──────────
125
+ "PasswordField": (NO_DC_EQUIVALENT, None, None,
126
+ "No DC password component. Rare in field forms; capture as "
127
+ "dcTextInput only if the value is non-secret, else drop."),
128
+ "DisplayRichText": (NO_DC_EQUIVALENT, None, None,
129
+ "No DC rich-text display. Downgrade to DisplayText (plain/basic "
130
+ "HTML) manually; rich formatting is lost."),
131
+ "PlainButtons": (NO_DC_EQUIVALENT, None, None,
132
+ "CAPABILITY GAP: Data Capture has no user-facing action buttons "
133
+ "(only AGX_FLOW actions). No migration path — remove or redesign."),
134
+ }
135
+
136
+ # ──────────────────────────────────────────────────────────────────────────
137
+ # 2. ComponentInstance extensionName -> DC target
138
+ # Only `forceContent:fileUpload` is natively allowed in an FSM flow (core
139
+ # whitelist). Everything else is a custom/unsupported component.
140
+ # ──────────────────────────────────────────────────────────────────────────
141
+
142
+ # Extensions with a real FSM -> DC migration path.
143
+ EXTENSION_CATALOG: Dict[str, Tuple[str, str, str, str]] = {
144
+ # (status, dc_extension, dc_fieldType, note)
145
+ "forceContent:fileUpload": (MAPPED, "dcUpFile", COMPONENT_INSTANCE,
146
+ "The ONLY screen extension core allows inside an FSM "
147
+ "flow. Maps cleanly to dcUpFile."),
148
+ # imageUpload is handled by transform_flow.py; core only whitelists fileUpload
149
+ # for FSM, so a real FSM source rarely contains it, but map it if present.
150
+ "forceContent:imageUpload": (MAPPED, "dcUpImage", COMPONENT_INSTANCE,
151
+ "Maps to dcUpImage. Note: core's FSM whitelist only "
152
+ "lists forceContent:fileUpload, so imageUpload is "
153
+ "uncommon in genuine FSM sources."),
154
+ }
155
+
156
+ # Field-already-DC: an FSM flow that was partially hand-migrated may already
157
+ # carry runtime_service_fieldservice:dc* extensions. Treat as already-DC.
158
+ DC_EXTENSION_PREFIX = DC_NS + "dc"
159
+
160
+ # Named unsupported extensions. These are real, standard-shipped Lightning flow
161
+ # screen components (fieldType=ComponentInstance) that CAN legitimately appear on
162
+ # a Field Service Mobile flow screen when the relevant org feature is enabled,
163
+ # but have NO Data Capture equivalent. They would otherwise fall into the generic
164
+ # "custom/unsupported component" bucket; naming them lets the gap report explain
165
+ # exactly what was found and why it can't migrate, instead of a vague "unknown
166
+ # component" line. Keyed by extensionName; value = (status, note).
167
+ #
168
+ # `runtime_industries_csv_dataimport:advCsv` — palette label "Advanced CSV
169
+ # Import"; imports CSV data via Data Processing Engine (DPE) templates. Exposed
170
+ # as a lightning__FlowScreen component and surfaced in the FSM flow builder
171
+ # palette when Industries CSV Data Import is enabled. Its DPE-backed bulk-import
172
+ # model is outside Data Capture's per-record form model, so there is no dc*
173
+ # equivalent. `...:impBeg` is the simpler "beginner" variant of the same feature.
174
+ # Verified against core (core-2206): the advCsv/impBeg LWCs live in the
175
+ # runtime_industries_csv_dataimport module with <target>lightning__FlowScreen</target>.
176
+ KNOWN_UNSUPPORTED_EXTENSIONS: Dict[str, Tuple[str, str]] = {
177
+ "runtime_industries_csv_dataimport:advCsv": (
178
+ NO_DC_EQUIVALENT,
179
+ "'Advanced CSV Import' (Industries CSV Data Import / DPE-backed bulk "
180
+ "import). No Data Capture equivalent — DC has no CSV/bulk-import screen "
181
+ "component and its per-record form model is a different paradigm. Rebuild "
182
+ "the data-loading step outside the flow (e.g. Data Processing Engine) or "
183
+ "drop it; it cannot be migrated automatically.",
184
+ ),
185
+ "runtime_industries_csv_dataimport:impBeg": (
186
+ NO_DC_EQUIVALENT,
187
+ "'CSV Import' (beginner variant of Industries CSV Data Import). Same "
188
+ "capability gap as runtime_industries_csv_dataimport:advCsv — no Data "
189
+ "Capture equivalent; cannot be migrated automatically.",
190
+ ),
191
+ # Two more standard extensions that appear in the FSM palette gold file
192
+ # (flowExtensionsForFieldServiceMobile) with no DC equivalent.
193
+ "industries_common:intelligentDocumentUpload": (
194
+ NO_DC_EQUIVALENT,
195
+ "'Intelligent Document Upload' (Industries). Document capture/extraction "
196
+ "component with no Data Capture equivalent. For plain file capture use "
197
+ "dcUpFile; the intelligent-extraction behaviour cannot be migrated.",
198
+ ),
199
+ "healthcloud:censusMembersMgmtWrapper": (
200
+ NO_DC_EQUIVALENT,
201
+ "Health Cloud census-members management component. Domain-specific, with "
202
+ "no Data Capture equivalent; cannot be migrated automatically.",
203
+ ),
204
+ }
205
+
206
+ # ──────────────────────────────────────────────────────────────────────────
207
+ # 3. DC-only components (no FSM source maps to them) — reference / for docs.
208
+ # Used to explain "these DC capabilities cannot be produced by migration."
209
+ # ──────────────────────────────────────────────────────────────────────────
210
+
211
+ DC_ONLY_COMPONENTS: Dict[str, str] = {
212
+ "dcSignature": "Signature capture. DC-only — FSM has NO native signature component.",
213
+ "dcEmail": "Email input. DC-only as a dedicated component (FSM uses InputField/Email).",
214
+ "dcPhone": "Phone input. DC-only as a dedicated component (FSM uses InputField/Phone).",
215
+ "dcName": "Structured name input. DC-only.",
216
+ "dcAddress": "Address input (optional GPS). DC-only.",
217
+ "dcLookup": "Record lookup. DC-only.",
218
+ "dcMatrix": "Matrix / grid input. DC-only.",
219
+ "dcCounter": "Numeric stepper. DC-only.",
220
+ "dcCheckbox": "Single checkbox. DC-only as a distinct component — see the "
221
+ "Boolean discrepancy note below.",
222
+ "dcFileView": "Read-only file/image viewer. DC-only.",
223
+ "dcUpImage": "Image upload. DC-only as a dedicated component.",
224
+ }
225
+
226
+ # Layout / structural DC constructs FSM does not allow (core FSM handler
227
+ # disallows Region/RegionContainer/Repeater/ObjectProvided).
228
+ DC_ONLY_LAYOUT: Dict[str, str] = {
229
+ "Region": "Layout region. DC-only (FSM disallows).",
230
+ "RegionContainer": "Layout region container. DC-only (FSM disallows).",
231
+ "Repeater": "Native repeating section. DC-only (FSM disallows; FSM used "
232
+ "Loop + screens instead).",
233
+ "ObjectProvided": "Object-provided fields. DC-only (FSM disallows).",
234
+ }
235
+
236
+
237
+ # ──────────────────────────────────────────────────────────────────────────
238
+ # 4. Capability-level gaps (beyond individual components)
239
+ # ──────────────────────────────────────────────────────────────────────────
240
+
241
+ def capability_gaps() -> List[Dict[str, str]]:
242
+ """Capability-level differences that no per-component mapping can preserve.
243
+
244
+ Returned as a list of {gap, detail, evidence} dicts so callers (analyze_flow,
245
+ WI-10 summary) can render them uniformly.
246
+ """
247
+ return [
248
+ {
249
+ "gap": "No actions / buttons",
250
+ "detail": "Data Capture has no user-facing action buttons. FSM's "
251
+ "PlainButtons has no DC equivalent; the only invocable action "
252
+ "a DC flow permits is AGX_FLOW. Button-driven FSM logic must "
253
+ "be redesigned.",
254
+ "evidence": "DataCaptureFlowProcessTypeHandler.java: allowedScreenFieldTypes "
255
+ "omits PlainButtons; allowedActionTypes = {AGX_FLOW}.",
256
+ },
257
+ {
258
+ "gap": "Offline-only environment",
259
+ "detail": "DataCaptureFlow runs only in the Offline environment. Flows "
260
+ "that relied on online-only behaviour (Apex, real-time callouts) "
261
+ "cannot be preserved.",
262
+ "evidence": "DataCaptureFlowProcessTypeHandler.hook_isEnvironmentAllowed "
263
+ "returns true only for FlowEnvironment.Offline.",
264
+ },
265
+ {
266
+ "gap": "No Apex / action calls",
267
+ "detail": "DataCaptureFlow forbids <actionCalls> and Apex data types. "
268
+ "FSM flows calling Apex must convert that logic to record "
269
+ "lookups or move it out of the flow.",
270
+ "evidence": "DataCaptureFlowProcessTypeHandler action allowlist "
271
+ "(AGX_FLOW only); ActionCall / FlowDataType.Apex rejected.",
272
+ },
273
+ {
274
+ "gap": "CUD only at end of flow",
275
+ "detail": "All record create/update/delete must follow the last screen. "
276
+ "FSM allowed CUD mid-flow. The transformer restructures the "
277
+ "graph; some patterns (create->lookup cycles) need redesign.",
278
+ "evidence": "DataCaptureFlowValidator CUD-at-end BFS.",
279
+ },
280
+ {
281
+ "gap": "No fault paths",
282
+ "detail": "Fault connectors are unsupported. FSM fault handling is "
283
+ "stripped and cannot be preserved.",
284
+ "evidence": "navigateToFault() is an empty stub; $Flow.FaultMessage "
285
+ "global blocked (see dc-conditional-logic-sources.md).",
286
+ },
287
+ {
288
+ "gap": "Subflows: DC-type only, no CUD",
289
+ "detail": "A DC flow can only call another DataCaptureFlow, and subflows "
290
+ "cannot contain CUD. FSM AutoLaunched subflows must be inlined "
291
+ "or converted.",
292
+ "evidence": "DataCaptureFlowProcessTypeHandler subflow-type check.",
293
+ },
294
+ ]
295
+
296
+
297
+ # ──────────────────────────────────────────────────────────────────────────
298
+ # 5. Field classification entry point
299
+ # ──────────────────────────────────────────────────────────────────────────
300
+
301
+ def classify(field_type: Optional[str],
302
+ data_type: Optional[str] = None,
303
+ extension_name: Optional[str] = None) -> Dict[str, Optional[str]]:
304
+ """Classify one FSM screen field against the DC catalog.
305
+
306
+ Args:
307
+ field_type: the FSM `<fieldType>` (e.g. 'InputField', 'DropdownBox',
308
+ 'ComponentInstance', 'DisplayText').
309
+ data_type: the field's `<dataType>` (only meaningful for 'InputField').
310
+ extension_name: the `<extensionName>` (only meaningful for
311
+ 'ComponentInstance').
312
+
313
+ Returns a dict:
314
+ {
315
+ 'status': mapped | no-DC-equivalent | DC-only | unknown,
316
+ 'dc_extension': 'runtime_service_fieldservice:dcX' or None,
317
+ 'dc_field_type': 'ComponentInstance'|'ComponentChoice'|... or None,
318
+ 'note': human-readable guidance,
319
+ }
320
+ """
321
+ ft = (field_type or "").strip()
322
+ dt = (data_type or "").strip() or None
323
+ ext = (extension_name or "").strip() or None
324
+
325
+ # ── ComponentInstance: classify by extensionName ──────────────────────
326
+ if ft == COMPONENT_INSTANCE or (ext and not ft):
327
+ # Already a DC component (partially hand-migrated source).
328
+ if ext and ext.startswith(DC_EXTENSION_PREFIX):
329
+ return _result(MAPPED, ext, COMPONENT_INSTANCE,
330
+ "Already a Data Capture component; passes through.")
331
+ if ext in EXTENSION_CATALOG:
332
+ status, dc_ext, dc_ft, note = EXTENSION_CATALOG[ext]
333
+ return _result(status, DC_NS + dc_ext, dc_ft, note)
334
+ # Known standard extension with no DC equivalent (e.g. Advanced CSV
335
+ # Import) — name it explicitly so the gap report is specific.
336
+ if ext in KNOWN_UNSUPPORTED_EXTENSIONS:
337
+ status, note = KNOWN_UNSUPPORTED_EXTENSIONS[ext]
338
+ return _result(status, None, None, note)
339
+ # Any other extension (lightning:*, c:*, forceChatter:*, ...) is a
340
+ # custom/unsupported component with no DC extension point.
341
+ return _result(
342
+ NO_DC_EQUIVALENT, None, None,
343
+ f"Custom/unsupported component '{ext or 'unknown'}'. Data Capture has "
344
+ f"no custom-component extension point; rebuild with a native dc* "
345
+ f"component or flag as unmigratable.",
346
+ )
347
+
348
+ # ── InputField: resolve by dataType ───────────────────────────────────
349
+ if ft == "InputField":
350
+ dc_ext, dc_ft = INPUTFIELD_BY_DATATYPE.get(dt, INPUTFIELD_DEFAULT)
351
+ note = "" if dt in INPUTFIELD_BY_DATATYPE else (
352
+ f"InputField dataType '{dt}' not recognised; defaulted to dcTextInput."
353
+ )
354
+ return _result(MAPPED, DC_NS + dc_ext, dc_ft, note)
355
+
356
+ # ── Standalone FSM field types ────────────────────────────────────────
357
+ if ft in FSM_FIELDTYPE_CATALOG:
358
+ status, dc_ext, dc_ft, note = FSM_FIELDTYPE_CATALOG[ft]
359
+ full_ext = (DC_NS + dc_ext) if dc_ext else None
360
+ return _result(status, full_ext, dc_ft, note)
361
+
362
+ # ── DC-only layout constructs appearing in a source (unusual) ─────────
363
+ if ft in DC_ONLY_LAYOUT:
364
+ return _result(MAPPED, None, ft,
365
+ f"{DC_ONLY_LAYOUT[ft]} Passes through unchanged.")
366
+
367
+ # ── Unrecognised ──────────────────────────────────────────────────────
368
+ return _result(UNKNOWN, None, None,
369
+ f"Unrecognised FSM fieldType '{ft}'. Not in the capability "
370
+ f"catalog; treat as unsupported and flag for manual review.")
371
+
372
+
373
+ def _result(status: str, dc_extension: Optional[str],
374
+ dc_field_type: Optional[str], note: str) -> Dict[str, Optional[str]]:
375
+ return {
376
+ "status": status,
377
+ "dc_extension": dc_extension,
378
+ "dc_field_type": dc_field_type,
379
+ "note": note,
380
+ }
381
+
382
+
383
+ def is_gap(status: str) -> bool:
384
+ """True if this classification represents a functionality gap (won't migrate)."""
385
+ return status in (NO_DC_EQUIVALENT, UNKNOWN)
386
+
387
+
388
+ # ──────────────────────────────────────────────────────────────────────────
389
+ # KNOWN DISCREPANCY — Boolean InputField target (dcCheckbox vs dcToggle)
390
+ # ──────────────────────────────────────────────────────────────────────────
391
+ # The shipping transformer (transform_flow.py) maps `InputField`/`Boolean` to
392
+ # `dcCheckbox`, and this catalog matches it so catalog and code stay in sync.
393
+ # The assessment spreadsheet instead recommends `dcToggle` for a Boolean
394
+ # ("Boolean/Toggle is dcToggle, NOT dcCheckbox; single Checkbox is DC-only").
395
+ # Both dcCheckbox and dcToggle bind to Boolean and deploy successfully; the
396
+ # difference is UX (checkbox vs. toggle switch). This is an unresolved product
397
+ # decision, not a correctness bug. If the mapping should change to dcToggle,
398
+ # update INPUTFIELD_BY_DATATYPE here AND transform_flow.py's FIELD_TYPE_MAP
399
+ # together. Flagged in W-23364461.
400
+ BOOLEAN_TARGET_DISCREPANCY = (
401
+ "InputField/Boolean -> dcCheckbox (matches shipping transform_flow.py). "
402
+ "Assessment spreadsheet recommended dcToggle. Unresolved product decision."
403
+ )
404
+
405
+
406
+ if __name__ == "__main__":
407
+ # Tiny self-demo: print the catalog as a table.
408
+ import json
409
+ demo = {
410
+ "capability_gaps": capability_gaps(),
411
+ "boolean_discrepancy": BOOLEAN_TARGET_DISCREPANCY,
412
+ }
413
+ print(json.dumps(demo, indent=2))
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env python3
2
+ """WI-10 — Render a human-readable migration summary (migration-summary.md)
3
+ from the spec JSON produced by convert_to_dc_spec.py.
4
+
5
+ Deterministic and offline: reads a local JSON file, writes a local markdown
6
+ file. Emits no AI-generated prose (NFR-003) — AI content belongs only inside
7
+ the labeled Advisory placeholder. Status indicators are text prefixes, never
8
+ color (NFR-006).
9
+
10
+ Usage: generate_summary.py <spec.json> <output.md>
11
+ Exit: 0 success | 1 unreadable/invalid spec JSON | 2 usage error
12
+ """
13
+ import json
14
+ import sys
15
+ from typing import Any, Dict, List
16
+
17
+
18
+ AGX_NOTE = (
19
+ "- **AGX cards / actions:** A DataCaptureFlow has no user-facing action "
20
+ "buttons; the only action a DC flow can invoke is an `AGX_FLOW` action. "
21
+ "Button-driven FSM logic (`PlainButtons`) has no DC equivalent and is "
22
+ "dropped — redesign it in Flow Builder. "
23
+ "See `reference/fsm-dc-capability-catalog.md`.")
24
+
25
+ LDS_WARNING = (
26
+ "DataCaptureFlow forms are offline-first and rely on Lightning Data Service "
27
+ "with Briefcase priming. Any object or field the migrated flow reads or "
28
+ "writes must be added to the Briefcase and have correct FLS/sharing, or it "
29
+ "will be unavailable on-device. Verify Briefcase, FLS, and OWD before "
30
+ "activating. See `configure-field-service-mobile` and "
31
+ "`reference/migration-checklist.md`.")
32
+
33
+
34
+ def load_spec(path: str) -> Dict[str, Any]:
35
+ try:
36
+ with open(path) as f:
37
+ return json.load(f)
38
+ except (OSError, json.JSONDecodeError) as e:
39
+ print(f"[ERROR] Cannot read spec JSON '{path}': {e}", file=sys.stderr)
40
+ sys.exit(1)
41
+
42
+
43
+ def _incomplete_names(spec: Dict[str, Any]) -> set:
44
+ return {e["name"] for e in spec.get("_incompleteMigration", [])}
45
+
46
+
47
+ def field_status(field: Dict[str, Any], incomplete_names: set) -> str:
48
+ label = field.get("fieldLabel", "")
49
+ if label.startswith("[UNSUPPORTED:") or field.get("fieldName") in incomplete_names:
50
+ return "[ERROR]"
51
+ if field.get("_migrationNote"):
52
+ return "[WARNING]"
53
+ return "[OK]"
54
+
55
+
56
+ def _note_for(field: Dict[str, Any]) -> str:
57
+ note = field.get("_migrationNote", "")
58
+ if note:
59
+ return note.replace("\n", " ").strip()
60
+ return "migrated"
61
+
62
+
63
+ def render_mapping_table(spec: Dict[str, Any]) -> str:
64
+ names = _incomplete_names(spec)
65
+ lines = ["## Component Mapping", "",
66
+ "| Component | Type | Status | Notes |",
67
+ "|---|---|---|---|"]
68
+ for fields in spec.get("screens", {}).values():
69
+ for f in fields:
70
+ lines.append(
71
+ f"| {f.get('fieldName', '')} | {f.get('fieldType', '')} "
72
+ f"| {field_status(f, names)} | {_note_for(f)} |")
73
+ lines.append("")
74
+ return "\n".join(lines)
75
+
76
+
77
+ def render_conditional_logic(spec: Dict[str, Any]) -> str:
78
+ lines = ["## Conditional Logic", "",
79
+ "| Construct | Migration status | Notes |", "|---|---|---|"]
80
+ rows = 0
81
+ for c in spec.get("_collapsedDecisions", []):
82
+ target = c.get("section") or c.get("field") or "field visibility"
83
+ fields = ", ".join(c.get("fields", []))
84
+ lines.append(
85
+ f"| Decision `{c.get('decision', '')}` | [OK] collapsed into "
86
+ f"visibility on {target} | fields: {fields} |")
87
+ rows += 1
88
+ for d in spec.get("_decisionFlags", []):
89
+ lines.append(
90
+ f"| Decision `{d.get('decision', '')}` | [WARNING] kept as decision "
91
+ f"| {d.get('reason', '').replace(chr(10), ' ')} |")
92
+ rows += 1
93
+ if rows == 0:
94
+ return "## Conditional Logic\n\nNone detected.\n"
95
+ lines.append("")
96
+ return "\n".join(lines)
97
+
98
+
99
+ def render_unsupported(spec: Dict[str, Any]) -> str:
100
+ entries = spec.get("_incompleteMigration", [])
101
+ if not entries:
102
+ return "## Unsupported Components\n\nNone detected.\n"
103
+ lines = ["## Unsupported Components", "",
104
+ "| Type | Name | Reason |", "|---|---|---|"]
105
+ for e in entries:
106
+ reason = e.get("reason", "").replace("\n", " ").strip()
107
+ lines.append(f"| {e.get('type', '')} | {e.get('name', '')} | {reason} |")
108
+ lines.append("")
109
+ return "\n".join(lines)
110
+
111
+
112
+ def render_behavioral_notes(spec: Dict[str, Any]) -> str:
113
+ lines = ["## Behavioral Notes", ""]
114
+ had_plain_buttons = any(
115
+ e.get("type") == "PlainButtons"
116
+ for e in spec.get("_incompleteMigration", []))
117
+ if had_plain_buttons:
118
+ lines.append(AGX_NOTE)
119
+ cud = spec.get("_cudAnalysis", {})
120
+ for m in cud.get("manualRestructure", []):
121
+ lines.append(f"- **CUD `{m.get('node')}`:** "
122
+ f"{m.get('reason', '').replace(chr(10), ' ')}")
123
+ for c in cud.get("consolidated", []):
124
+ lines.append(f"- **CUD `{c.get('node')}`:** auto-consolidated to "
125
+ f"end-of-flow ({c.get('from')} → {c.get('to')}).")
126
+ for cf in spec.get("_ceilingFlags", []):
127
+ lines.append(f"- **`{cf.get('name')}`:** "
128
+ f"{cf.get('reason', '').replace(chr(10), ' ')}")
129
+ if spec.get("_subflowNote"):
130
+ lines.append(f"- **Subflows:** {spec['_subflowNote']}")
131
+ if spec.get("_automationNote"):
132
+ lines.append(f"- **Automation:** {spec['_automationNote']}")
133
+ if spec.get("_migrationNote"):
134
+ lines.append(f"- **Note:** {spec['_migrationNote']}")
135
+ if len(lines) == 2:
136
+ lines.append("None specific to this flow.")
137
+ lines.append("")
138
+ return "\n".join(lines)
139
+
140
+
141
+ def render_review_checklist(spec: Dict[str, Any]) -> str:
142
+ items = [
143
+ "Open the migrated flow in Flow Builder and confirm it opens without errors",
144
+ "Verify Briefcase priming, FLS, and sharing for every object/field the flow touches",
145
+ "Test the flow on a real device before activating",
146
+ ]
147
+ if spec.get("_incompleteMigration"):
148
+ items.append("Rebuild each unsupported component listed above in Flow Builder")
149
+ if spec.get("_cudAnalysis", {}).get("manualRestructure"):
150
+ items.append("Manually restructure the flagged CUD operations to run after the last screen")
151
+ if spec.get("_decisionFlags"):
152
+ items.append("Review decisions that could not be collapsed into visibility")
153
+ lines = ["## Review Checklist", ""]
154
+ lines += [f"- [ ] {i}" for i in items]
155
+ lines.append("")
156
+ return "\n".join(lines)
157
+
158
+
159
+ def render_lds_warning() -> str:
160
+ return ("## Lightning Data Service (LDS) Warning\n\n" + LDS_WARNING + "\n")
161
+
162
+
163
+ def render_advisory() -> str:
164
+ return (
165
+ "## Advisory (AI-generated — review before relying on)\n\n"
166
+ "> **Advisory (AI-generated, review before relying on):**\n"
167
+ "> _(placeholder — flow-specific advisory prose may be appended here.)_\n")
168
+
169
+
170
+ def render_summary(spec: Dict[str, Any]) -> str:
171
+ parts = [f"# Migration Summary: {spec.get('formTitle', 'Untitled')}", ""]
172
+ parts.append(render_mapping_table(spec))
173
+ parts.append(render_conditional_logic(spec))
174
+ parts.append(render_unsupported(spec))
175
+ parts.append(render_behavioral_notes(spec))
176
+ parts.append(render_lds_warning())
177
+ parts.append(render_review_checklist(spec))
178
+ parts.append(render_advisory())
179
+ return "\n".join(parts) + "\n"
180
+
181
+
182
+ def main(argv: List[str]) -> int:
183
+ if len(argv) != 3:
184
+ print(f"Usage: {argv[0]} <spec.json> <output.md>", file=sys.stderr)
185
+ return 2
186
+ spec = load_spec(argv[1])
187
+ markdown = render_summary(spec)
188
+ try:
189
+ with open(argv[2], "w") as f:
190
+ f.write(markdown)
191
+ except OSError as e:
192
+ print(f"[ERROR] Cannot write output '{argv[2]}': {e}", file=sys.stderr)
193
+ return 1
194
+ print(f"Migration summary written to: {argv[2]}")
195
+ return 0
196
+
197
+
198
+ if __name__ == "__main__":
199
+ sys.exit(main(sys.argv))