@salesforce/afv-skills 1.14.0 → 1.15.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 (332) hide show
  1. package/package.json +1 -1
  2. package/skills/applying-slds/SKILL.md +322 -0
  3. package/skills/applying-slds/checklists.md +83 -0
  4. package/skills/applying-slds/examples.md +283 -0
  5. package/skills/applying-slds/guidance/README.md +83 -0
  6. package/skills/applying-slds/guidance/blueprints-index.md +213 -0
  7. package/skills/applying-slds/guidance/icons-guidance.md +186 -0
  8. package/skills/applying-slds/guidance/overviews/borders.md +236 -0
  9. package/skills/applying-slds/guidance/overviews/color.md +266 -0
  10. package/skills/applying-slds/guidance/overviews/display-density.md +366 -0
  11. package/skills/applying-slds/guidance/overviews/icons.md +240 -0
  12. package/skills/applying-slds/guidance/overviews/illustrations.md +235 -0
  13. package/skills/applying-slds/guidance/overviews/shadows.md +176 -0
  14. package/skills/applying-slds/guidance/overviews/spacing.md +216 -0
  15. package/skills/applying-slds/guidance/overviews/typography.md +323 -0
  16. package/skills/applying-slds/guidance/overviews/utilities.md +542 -0
  17. package/skills/applying-slds/guidance/slds-development-guide.md +288 -0
  18. package/skills/applying-slds/guidance/styling-hooks/borders.md +202 -0
  19. package/skills/applying-slds/guidance/styling-hooks/color/expressive-palette-hooks.md +153 -0
  20. package/skills/applying-slds/guidance/styling-hooks/color/index.md +171 -0
  21. package/skills/applying-slds/guidance/styling-hooks/color/semantic/accent-hooks.md +204 -0
  22. package/skills/applying-slds/guidance/styling-hooks/color/semantic/feedback-hooks.md +768 -0
  23. package/skills/applying-slds/guidance/styling-hooks/color/semantic/surface-hooks.md +337 -0
  24. package/skills/applying-slds/guidance/styling-hooks/color/system-hooks.md +132 -0
  25. package/skills/applying-slds/guidance/styling-hooks/index.md +327 -0
  26. package/skills/applying-slds/guidance/styling-hooks/shadows.md +238 -0
  27. package/skills/applying-slds/guidance/styling-hooks/spacing.md +254 -0
  28. package/skills/applying-slds/guidance/styling-hooks/typography.md +448 -0
  29. package/skills/applying-slds/guidance/utilities/alignment.md +119 -0
  30. package/skills/applying-slds/guidance/utilities/borders.md +131 -0
  31. package/skills/applying-slds/guidance/utilities/box.md +125 -0
  32. package/skills/applying-slds/guidance/utilities/color.md +165 -0
  33. package/skills/applying-slds/guidance/utilities/dark-mode.md +111 -0
  34. package/skills/applying-slds/guidance/utilities/description-list.md +168 -0
  35. package/skills/applying-slds/guidance/utilities/floats.md +117 -0
  36. package/skills/applying-slds/guidance/utilities/grid.md +264 -0
  37. package/skills/applying-slds/guidance/utilities/horizontal-list.md +110 -0
  38. package/skills/applying-slds/guidance/utilities/hyphenation.md +84 -0
  39. package/skills/applying-slds/guidance/utilities/index.md +205 -0
  40. package/skills/applying-slds/guidance/utilities/interactions.md +89 -0
  41. package/skills/applying-slds/guidance/utilities/layout.md +109 -0
  42. package/skills/applying-slds/guidance/utilities/line-clamp.md +131 -0
  43. package/skills/applying-slds/guidance/utilities/margin.md +155 -0
  44. package/skills/applying-slds/guidance/utilities/media-object.md +161 -0
  45. package/skills/applying-slds/guidance/utilities/name-value-list.md +152 -0
  46. package/skills/applying-slds/guidance/utilities/padding.md +155 -0
  47. package/skills/applying-slds/guidance/utilities/position.md +177 -0
  48. package/skills/applying-slds/guidance/utilities/print.md +114 -0
  49. package/skills/applying-slds/guidance/utilities/scrollable.md +126 -0
  50. package/skills/applying-slds/guidance/utilities/sizing.md +190 -0
  51. package/skills/applying-slds/guidance/utilities/themes.md +121 -0
  52. package/skills/applying-slds/guidance/utilities/truncate.md +127 -0
  53. package/skills/applying-slds/guidance/utilities/typography.md +166 -0
  54. package/skills/applying-slds/guidance/utilities/vertical-list.md +166 -0
  55. package/skills/applying-slds/guidance/utilities/visibility.md +228 -0
  56. package/skills/applying-slds/metadata/README.md +84 -0
  57. package/skills/applying-slds/metadata/blueprints/components/accordion.yaml +304 -0
  58. package/skills/applying-slds/metadata/blueprints/components/activity-timeline.yaml +92 -0
  59. package/skills/applying-slds/metadata/blueprints/components/alert.yaml +103 -0
  60. package/skills/applying-slds/metadata/blueprints/components/app-launcher.yaml +94 -0
  61. package/skills/applying-slds/metadata/blueprints/components/avatar-group.yaml +81 -0
  62. package/skills/applying-slds/metadata/blueprints/components/avatar.yaml +97 -0
  63. package/skills/applying-slds/metadata/blueprints/components/badges.yaml +102 -0
  64. package/skills/applying-slds/metadata/blueprints/components/brand-band.yaml +198 -0
  65. package/skills/applying-slds/metadata/blueprints/components/breadcrumbs.yaml +95 -0
  66. package/skills/applying-slds/metadata/blueprints/components/builder-header.yaml +192 -0
  67. package/skills/applying-slds/metadata/blueprints/components/button-groups.yaml +82 -0
  68. package/skills/applying-slds/metadata/blueprints/components/button-icons.yaml +295 -0
  69. package/skills/applying-slds/metadata/blueprints/components/buttons.yaml +230 -0
  70. package/skills/applying-slds/metadata/blueprints/components/cards.yaml +124 -0
  71. package/skills/applying-slds/metadata/blueprints/components/carousel.yaml +140 -0
  72. package/skills/applying-slds/metadata/blueprints/components/chat.yaml +179 -0
  73. package/skills/applying-slds/metadata/blueprints/components/checkbox-button-group.yaml +192 -0
  74. package/skills/applying-slds/metadata/blueprints/components/checkbox-button.yaml +204 -0
  75. package/skills/applying-slds/metadata/blueprints/components/checkbox-toggle.yaml +177 -0
  76. package/skills/applying-slds/metadata/blueprints/components/checkbox.yaml +108 -0
  77. package/skills/applying-slds/metadata/blueprints/components/color-picker.yaml +172 -0
  78. package/skills/applying-slds/metadata/blueprints/components/combobox.yaml +136 -0
  79. package/skills/applying-slds/metadata/blueprints/components/counter.yaml +147 -0
  80. package/skills/applying-slds/metadata/blueprints/components/data-tables.yaml +157 -0
  81. package/skills/applying-slds/metadata/blueprints/components/datepickers.yaml +130 -0
  82. package/skills/applying-slds/metadata/blueprints/components/datetime-picker.yaml +155 -0
  83. package/skills/applying-slds/metadata/blueprints/components/docked-composer.yaml +201 -0
  84. package/skills/applying-slds/metadata/blueprints/components/docked-form-footer.yaml +161 -0
  85. package/skills/applying-slds/metadata/blueprints/components/docked-utility-bar.yaml +175 -0
  86. package/skills/applying-slds/metadata/blueprints/components/drop-zone.yaml +115 -0
  87. package/skills/applying-slds/metadata/blueprints/components/dueling-picklist.yaml +196 -0
  88. package/skills/applying-slds/metadata/blueprints/components/dynamic-icons.yaml +128 -0
  89. package/skills/applying-slds/metadata/blueprints/components/dynamic-menu.yaml +141 -0
  90. package/skills/applying-slds/metadata/blueprints/components/expandable-section.yaml +115 -0
  91. package/skills/applying-slds/metadata/blueprints/components/expression.yaml +143 -0
  92. package/skills/applying-slds/metadata/blueprints/components/feeds.yaml +125 -0
  93. package/skills/applying-slds/metadata/blueprints/components/file-selector.yaml +154 -0
  94. package/skills/applying-slds/metadata/blueprints/components/files.yaml +119 -0
  95. package/skills/applying-slds/metadata/blueprints/components/form-element.yaml +145 -0
  96. package/skills/applying-slds/metadata/blueprints/components/global-header.yaml +120 -0
  97. package/skills/applying-slds/metadata/blueprints/components/global-navigation.yaml +100 -0
  98. package/skills/applying-slds/metadata/blueprints/components/icons.yaml +138 -0
  99. package/skills/applying-slds/metadata/blueprints/components/illustration.yaml +205 -0
  100. package/skills/applying-slds/metadata/blueprints/components/input.yaml +151 -0
  101. package/skills/applying-slds/metadata/blueprints/components/list-builder.yaml +127 -0
  102. package/skills/applying-slds/metadata/blueprints/components/lookups.yaml +132 -0
  103. package/skills/applying-slds/metadata/blueprints/components/map.yaml +118 -0
  104. package/skills/applying-slds/metadata/blueprints/components/menus.yaml +134 -0
  105. package/skills/applying-slds/metadata/blueprints/components/modals.yaml +152 -0
  106. package/skills/applying-slds/metadata/blueprints/components/notifications.yaml +88 -0
  107. package/skills/applying-slds/metadata/blueprints/components/page-headers.yaml +135 -0
  108. package/skills/applying-slds/metadata/blueprints/components/panels.yaml +149 -0
  109. package/skills/applying-slds/metadata/blueprints/components/path.yaml +154 -0
  110. package/skills/applying-slds/metadata/blueprints/components/picklist.yaml +125 -0
  111. package/skills/applying-slds/metadata/blueprints/components/pills.yaml +154 -0
  112. package/skills/applying-slds/metadata/blueprints/components/popovers.yaml +120 -0
  113. package/skills/applying-slds/metadata/blueprints/components/progress-bar.yaml +110 -0
  114. package/skills/applying-slds/metadata/blueprints/components/progress-indicator.yaml +133 -0
  115. package/skills/applying-slds/metadata/blueprints/components/progress-ring.yaml +102 -0
  116. package/skills/applying-slds/metadata/blueprints/components/prompt.yaml +126 -0
  117. package/skills/applying-slds/metadata/blueprints/components/publishers.yaml +178 -0
  118. package/skills/applying-slds/metadata/blueprints/components/radio-button-group.yaml +172 -0
  119. package/skills/applying-slds/metadata/blueprints/components/radio-group.yaml +112 -0
  120. package/skills/applying-slds/metadata/blueprints/components/rich-text-editor.yaml +135 -0
  121. package/skills/applying-slds/metadata/blueprints/components/scoped-notifications.yaml +188 -0
  122. package/skills/applying-slds/metadata/blueprints/components/scoped-tabs.yaml +97 -0
  123. package/skills/applying-slds/metadata/blueprints/components/select.yaml +127 -0
  124. package/skills/applying-slds/metadata/blueprints/components/setup-assistant.yaml +152 -0
  125. package/skills/applying-slds/metadata/blueprints/components/slider.yaml +111 -0
  126. package/skills/applying-slds/metadata/blueprints/components/spinners.yaml +135 -0
  127. package/skills/applying-slds/metadata/blueprints/components/split-view.yaml +112 -0
  128. package/skills/applying-slds/metadata/blueprints/components/summary-detail.yaml +103 -0
  129. package/skills/applying-slds/metadata/blueprints/components/tabs.yaml +138 -0
  130. package/skills/applying-slds/metadata/blueprints/components/textarea.yaml +116 -0
  131. package/skills/applying-slds/metadata/blueprints/components/tiles.yaml +108 -0
  132. package/skills/applying-slds/metadata/blueprints/components/timepicker.yaml +111 -0
  133. package/skills/applying-slds/metadata/blueprints/components/toast.yaml +154 -0
  134. package/skills/applying-slds/metadata/blueprints/components/tooltips.yaml +107 -0
  135. package/skills/applying-slds/metadata/blueprints/components/tree-grid.yaml +116 -0
  136. package/skills/applying-slds/metadata/blueprints/components/trees.yaml +116 -0
  137. package/skills/applying-slds/metadata/blueprints/components/trial-bar.yaml +112 -0
  138. package/skills/applying-slds/metadata/blueprints/components/vertical-navigation.yaml +130 -0
  139. package/skills/applying-slds/metadata/blueprints/components/vertical-tabs.yaml +140 -0
  140. package/skills/applying-slds/metadata/blueprints/components/visual-picker.yaml +150 -0
  141. package/skills/applying-slds/metadata/blueprints/components/welcome-mat.yaml +136 -0
  142. package/skills/applying-slds/metadata/hooks-index.json +6272 -0
  143. package/skills/applying-slds/metadata/icon-metadata.json +38466 -0
  144. package/skills/applying-slds/metadata/utilities-index.json +21912 -0
  145. package/skills/applying-slds/references/component-selection.md +112 -0
  146. package/skills/applying-slds/references/icons-decision-guide.md +124 -0
  147. package/skills/applying-slds/references/styling-decision-guide.md +228 -0
  148. package/skills/applying-slds/references/utilities-quick-ref.md +125 -0
  149. package/skills/applying-slds/scripts/search-blueprints.cjs +117 -0
  150. package/skills/applying-slds/scripts/search-hooks.cjs +139 -0
  151. package/skills/applying-slds/scripts/search-icons.cjs +174 -0
  152. package/skills/applying-slds/scripts/search-utilities.cjs +161 -0
  153. package/skills/generating-custom-lightning-type/SKILL.md +17 -39
  154. package/skills/generating-custom-lightning-type/assets/primitive-types-and-constraints.md +41 -0
  155. package/skills/generating-custom-lightning-type/references/widget-rendition.md +124 -0
  156. package/skills/investigating-agentforce-architecture/README.md +156 -0
  157. package/skills/investigating-agentforce-architecture/SKILL.md +230 -0
  158. package/skills/investigating-agentforce-architecture/assets/cli/describe_sobject.yaml +16 -0
  159. package/skills/investigating-agentforce-architecture/assets/cli/describe_tooling_sobject.yaml +17 -0
  160. package/skills/investigating-agentforce-architecture/assets/cli/list_metadata_genaiprompttemplate.yaml +17 -0
  161. package/skills/investigating-agentforce-architecture/assets/cli/org_display.yaml +15 -0
  162. package/skills/investigating-agentforce-architecture/assets/cli/retrieve_genai_plugin.yaml +18 -0
  163. package/skills/investigating-agentforce-architecture/assets/cli/show_access_token.yaml +27 -0
  164. package/skills/investigating-agentforce-architecture/assets/mermaid/action_tree.mmd +20 -0
  165. package/skills/investigating-agentforce-architecture/assets/mermaid/data_flow.mmd +19 -0
  166. package/skills/investigating-agentforce-architecture/assets/mermaid/dependency_graph.mmd +19 -0
  167. package/skills/investigating-agentforce-architecture/assets/mermaid/invocation_sequence.mmd +20 -0
  168. package/skills/investigating-agentforce-architecture/assets/mermaid/planner_state.mmd +18 -0
  169. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_ids.soql +3 -0
  170. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_names.soql +3 -0
  171. package/skills/investigating-agentforce-architecture/assets/soql/bot_definition_details.soql +3 -0
  172. package/skills/investigating-agentforce-architecture/assets/soql/bot_version_lookup.soql +4 -0
  173. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_by_ids.soql +3 -0
  174. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_ids_by_names.soql +3 -0
  175. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_view_by_durable_ids.soql +4 -0
  176. package/skills/investigating-agentforce-architecture/assets/soql/flow_metadata_by_id.soql +3 -0
  177. package/skills/investigating-agentforce-architecture/assets/soql/functions_by_plugins.soql +5 -0
  178. package/skills/investigating-agentforce-architecture/assets/soql/planner_attrs_by_parent_ids.soql +3 -0
  179. package/skills/investigating-agentforce-architecture/assets/soql/planner_bundle_functions.soql +3 -0
  180. package/skills/investigating-agentforce-architecture/assets/soql/planner_definition_by_agent_chain.soql +3 -0
  181. package/skills/investigating-agentforce-architecture/assets/soql/plugin_functions_by_plugin_ids.soql +3 -0
  182. package/skills/investigating-agentforce-architecture/assets/soql/plugin_instructions_by_plugin_ids.soql +3 -0
  183. package/skills/investigating-agentforce-architecture/assets/soql/plugins_by_planner.soql +4 -0
  184. package/skills/investigating-agentforce-architecture/references/architecture_sections.md +243 -0
  185. package/skills/investigating-agentforce-architecture/references/contract.json +244 -0
  186. package/skills/investigating-agentforce-architecture/references/soql_fields.md +512 -0
  187. package/skills/investigating-agentforce-architecture/scripts/_shared/__init__.py +1 -0
  188. package/skills/investigating-agentforce-architecture/scripts/_shared/fs_guard.py +329 -0
  189. package/skills/investigating-agentforce-architecture/scripts/_shared/paths.py +110 -0
  190. package/skills/investigating-agentforce-architecture/scripts/_shared/runtime.py +59 -0
  191. package/skills/investigating-agentforce-architecture/scripts/_shared/sql.py +10 -0
  192. package/skills/investigating-agentforce-architecture/scripts/cache_check.py +234 -0
  193. package/skills/investigating-agentforce-architecture/scripts/config.py +131 -0
  194. package/skills/investigating-agentforce-architecture/scripts/fetch_soql.py +689 -0
  195. package/skills/investigating-agentforce-architecture/scripts/finalize.py +295 -0
  196. package/skills/investigating-agentforce-architecture/scripts/main.py +2835 -0
  197. package/skills/investigating-agentforce-architecture/scripts/metadata_listing.py +265 -0
  198. package/skills/investigating-agentforce-architecture/scripts/parallel_retrieve.py +69 -0
  199. package/skills/investigating-agentforce-architecture/scripts/parse_bundle.py +215 -0
  200. package/skills/investigating-agentforce-architecture/scripts/parse_wave.py +845 -0
  201. package/skills/investigating-agentforce-architecture/scripts/probe_channels.py +302 -0
  202. package/skills/investigating-agentforce-architecture/scripts/render_architecture.py +1043 -0
  203. package/skills/investigating-agentforce-architecture/scripts/resolve_bot.py +255 -0
  204. package/skills/investigating-agentforce-architecture/scripts/resolve_invocation_target.py +130 -0
  205. package/skills/investigating-agentforce-architecture/scripts/rest_client.py +763 -0
  206. package/skills/investigating-agentforce-architecture/scripts/retrieve_planner.py +13 -0
  207. package/skills/investigating-agentforce-architecture/scripts/sf_cli.py +242 -0
  208. package/skills/investigating-agentforce-architecture/scripts/soql_loader.py +253 -0
  209. package/skills/investigating-agentforce-architecture/scripts/summarize_tree.py +143 -0
  210. package/skills/investigating-agentforce-architecture/scripts/tests/__init__.py +0 -0
  211. package/skills/investigating-agentforce-architecture/scripts/tests/_bootstrap.py +23 -0
  212. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/__init__.py +0 -0
  213. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/genai_payloads.py +400 -0
  214. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check.py +307 -0
  215. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check_main.py +283 -0
  216. package/skills/investigating-agentforce-architecture/scripts/tests/test_config.py +115 -0
  217. package/skills/investigating-agentforce-architecture/scripts/tests/test_end_to_end_fixture.py +651 -0
  218. package/skills/investigating-agentforce-architecture/scripts/tests/test_finalize.py +278 -0
  219. package/skills/investigating-agentforce-architecture/scripts/tests/test_flow_children_inflation.py +582 -0
  220. package/skills/investigating-agentforce-architecture/scripts/tests/test_fs_guard.py +113 -0
  221. package/skills/investigating-agentforce-architecture/scripts/tests/test_iterative_wave_b.py +478 -0
  222. package/skills/investigating-agentforce-architecture/scripts/tests/test_main_pipeline.py +3359 -0
  223. package/skills/investigating-agentforce-architecture/scripts/tests/test_parallel_retrieve.py +131 -0
  224. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_bundle.py +400 -0
  225. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave.py +644 -0
  226. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py +224 -0
  227. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_helpers.py +380 -0
  228. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_main.py +397 -0
  229. package/skills/investigating-agentforce-architecture/scripts/tests/test_per_branch_visited.py +244 -0
  230. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_channels.py +359 -0
  231. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_cli_recipes.py +185 -0
  232. package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py +810 -0
  233. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_bot.py +203 -0
  234. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_creds.py +157 -0
  235. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_invocation_target.py +145 -0
  236. package/skills/investigating-agentforce-architecture/scripts/tests/test_rest_client.py +1253 -0
  237. package/skills/investigating-agentforce-architecture/scripts/tests/test_runtime_override.py +100 -0
  238. package/skills/investigating-agentforce-architecture/scripts/tests/test_sf_cli.py +261 -0
  239. package/skills/investigating-agentforce-architecture/scripts/tests/test_signature_stamping.py +466 -0
  240. package/skills/investigating-agentforce-architecture/scripts/tests/test_soql_loader.py +501 -0
  241. package/skills/investigating-agentforce-architecture/scripts/tests/test_summarize_tree.py +241 -0
  242. package/skills/investigating-agentforce-architecture/scripts/tests/test_write_emit_ctx.py +480 -0
  243. package/skills/investigating-agentforce-architecture/tools/emit_env.py +157 -0
  244. package/skills/investigating-agentforce-architecture/tools/emit_result.py +262 -0
  245. package/skills/investigating-agentforce-architecture/tools/sanitize.py +33 -0
  246. package/skills/investigating-agentforce-architecture/tools/write_emit_ctx.py +332 -0
  247. package/skills/investigating-agentforce-d360/README.md +123 -0
  248. package/skills/investigating-agentforce-d360/SKILL.md +163 -0
  249. package/skills/investigating-agentforce-d360/assets/dc/app_generation.sql +51 -0
  250. package/skills/investigating-agentforce-d360/assets/dc/content_category.sql +44 -0
  251. package/skills/investigating-agentforce-d360/assets/dc/content_quality.sql +41 -0
  252. package/skills/investigating-agentforce-d360/assets/dc/discover_sessions.sql +36 -0
  253. package/skills/investigating-agentforce-d360/assets/dc/feedback.sql +47 -0
  254. package/skills/investigating-agentforce-d360/assets/dc/feedback_details.sql +38 -0
  255. package/skills/investigating-agentforce-d360/assets/dc/gateway_records.sql +45 -0
  256. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_llm.sql +50 -0
  257. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_metadata.sql +44 -0
  258. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_tags.sql +42 -0
  259. package/skills/investigating-agentforce-d360/assets/dc/gateway_requests.sql +89 -0
  260. package/skills/investigating-agentforce-d360/assets/dc/gateway_responses.sql +43 -0
  261. package/skills/investigating-agentforce-d360/assets/dc/generations.sql +52 -0
  262. package/skills/investigating-agentforce-d360/assets/dc/interactions.sql +53 -0
  263. package/skills/investigating-agentforce-d360/assets/dc/messages.sql +53 -0
  264. package/skills/investigating-agentforce-d360/assets/dc/messaging_session.sql +37 -0
  265. package/skills/investigating-agentforce-d360/assets/dc/moment_interactions.sql +34 -0
  266. package/skills/investigating-agentforce-d360/assets/dc/moments.sql +39 -0
  267. package/skills/investigating-agentforce-d360/assets/dc/participants.sql +48 -0
  268. package/skills/investigating-agentforce-d360/assets/dc/sessions.sql +78 -0
  269. package/skills/investigating-agentforce-d360/assets/dc/steps.sql +64 -0
  270. package/skills/investigating-agentforce-d360/assets/dc/tag_associations.sql +46 -0
  271. package/skills/investigating-agentforce-d360/assets/dc/tag_definition_associations.sql +37 -0
  272. package/skills/investigating-agentforce-d360/assets/dc/tag_definitions.sql +50 -0
  273. package/skills/investigating-agentforce-d360/assets/dc/tags.sql +37 -0
  274. package/skills/investigating-agentforce-d360/assets/dc/telemetry_spans.sql +55 -0
  275. package/skills/investigating-agentforce-d360/references/artifacts.md +50 -0
  276. package/skills/investigating-agentforce-d360/references/dc_dmo_fields.md +823 -0
  277. package/skills/investigating-agentforce-d360/references/dc_pipeline_contract.md +608 -0
  278. package/skills/investigating-agentforce-d360/scripts/_shared/__init__.py +2 -0
  279. package/skills/investigating-agentforce-d360/scripts/_shared/cli_override.py +98 -0
  280. package/skills/investigating-agentforce-d360/scripts/_shared/fs_guard.py +334 -0
  281. package/skills/investigating-agentforce-d360/scripts/_shared/paths.py +155 -0
  282. package/skills/investigating-agentforce-d360/scripts/_shared/runtime.py +59 -0
  283. package/skills/investigating-agentforce-d360/scripts/_shared/sql.py +14 -0
  284. package/skills/investigating-agentforce-d360/scripts/assemble_dc.py +1624 -0
  285. package/skills/investigating-agentforce-d360/scripts/config.py +45 -0
  286. package/skills/investigating-agentforce-d360/scripts/dc.py +188 -0
  287. package/skills/investigating-agentforce-d360/scripts/discover_sessions.py +556 -0
  288. package/skills/investigating-agentforce-d360/scripts/fetch_dc.py +1045 -0
  289. package/skills/investigating-agentforce-d360/scripts/render_dc.py +1750 -0
  290. package/skills/investigating-agentforce-d360/scripts/resolve_session.py +264 -0
  291. package/skills/investigating-agentforce-d360/scripts/storage.py +92 -0
  292. package/skills/investigating-agentforce-d360/scripts/tests/__init__.py +0 -0
  293. package/skills/investigating-agentforce-d360/scripts/tests/_bootstrap.py +15 -0
  294. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/__init__.py +0 -0
  295. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/synthetic_session.py +424 -0
  296. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_bootstrap_and_mode.py +115 -0
  297. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct.py +220 -0
  298. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct_integration.py +158 -0
  299. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_helpers.py +287 -0
  300. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_integration.py +247 -0
  301. package/skills/investigating-agentforce-d360/scripts/tests/test_dc_and_resolve_session.py +433 -0
  302. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions.py +458 -0
  303. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions_grep_ci.py +193 -0
  304. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_helpers.py +266 -0
  305. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_identity.py +528 -0
  306. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_main.py +251 -0
  307. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall.py +229 -0
  308. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall_full.py +283 -0
  309. package/skills/investigating-agentforce-d360/scripts/tests/test_identity_coherence.py +327 -0
  310. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_branches.py +256 -0
  311. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_gateway_direct.py +130 -0
  312. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_helpers.py +291 -0
  313. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_integration.py +220 -0
  314. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_planner_llm_calls.py +284 -0
  315. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_show_prompts_gating.py +215 -0
  316. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_from_disk.py +100 -0
  317. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_session_main.py +149 -0
  318. package/skills/investigating-agentforce-d360/scripts/tests/test_runtime_override.py +104 -0
  319. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape.py +95 -0
  320. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape_dropped_by_stdm.py +85 -0
  321. package/skills/managing-managed-event-subscription/SKILL.md +152 -0
  322. package/skills/managing-managed-event-subscription/assets/managed-event-subscription-template.xml +20 -0
  323. package/skills/managing-managed-event-subscription/references/delete-guide.md +57 -0
  324. package/skills/managing-managed-event-subscription/references/topic-name-formats.md +26 -0
  325. package/skills/managing-managed-event-subscription/references/update-constraints.md +30 -0
  326. package/skills/uplifting-components-to-slds2/SKILL.md +3 -2
  327. package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +30 -9
  328. package/skills/uplifting-components-to-slds2/references/examples.md +24 -6
  329. package/skills/validating-slds/SKILL.md +262 -0
  330. package/skills/validating-slds/references/quality-checks.md +308 -0
  331. package/skills/validating-slds/references/report-format.md +302 -0
  332. package/skills/validating-slds/scripts/analyze-quality.cjs +521 -0
@@ -0,0 +1,131 @@
1
+ """Tests for parallel_retrieve.fetch_bodies_parallel uses as_completed
2
+ with (ok, result_or_exc) tuples — one failure MUST NOT abort the whole run.
3
+ `executor.map()` would raise on first failure and silently cancel the rest;
4
+ the whole point of this module is to never do that.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import threading
9
+ import unittest
10
+ import urllib.error
11
+
12
+ from . import _bootstrap # noqa: F401
13
+
14
+ import parallel_retrieve # type: ignore
15
+
16
+
17
+ class AllSucceedTests(unittest.TestCase):
18
+ def test_five_out_of_five_ok(self):
19
+ tasks = [lambda i=i: i * 10 for i in range(5)]
20
+ results = parallel_retrieve.fetch_bodies_parallel(tasks, max_workers=5)
21
+ self.assertEqual(len(results), 5)
22
+ for ok, _ in results:
23
+ self.assertTrue(ok)
24
+ # Order is preserved: results[i] corresponds to tasks[i].
25
+ self.assertEqual([r for _, r in results], [0, 10, 20, 30, 40])
26
+
27
+
28
+ class OneFailureTests(unittest.TestCase):
29
+ def test_four_ok_one_fail(self):
30
+ def boom():
31
+ raise RuntimeError("task-3-failed")
32
+
33
+ tasks = [
34
+ lambda: "a",
35
+ lambda: "b",
36
+ lambda: "c",
37
+ boom,
38
+ lambda: "e",
39
+ ]
40
+ results = parallel_retrieve.fetch_bodies_parallel(tasks, max_workers=3)
41
+ self.assertEqual(len(results), 5)
42
+ oks = [r[0] for r in results]
43
+ self.assertEqual(oks, [True, True, True, False, True])
44
+ # The failure slot carries the RuntimeError object, not a string.
45
+ self.assertIsInstance(results[3][1], RuntimeError)
46
+ self.assertIn("task-3-failed", str(results[3][1]))
47
+
48
+ def test_function_does_not_raise_on_failure(self):
49
+ """Top-level contract: one bad task does NOT abort the run."""
50
+ tasks = [lambda: 1, lambda: (_ for _ in ()).throw(ValueError("x"))]
51
+ # Must not raise.
52
+ results = parallel_retrieve.fetch_bodies_parallel(tasks, max_workers=2)
53
+ self.assertEqual(len(results), 2)
54
+
55
+
56
+ class AllFailTests(unittest.TestCase):
57
+ def test_all_failures_function_still_returns(self):
58
+ def fail(n):
59
+ raise RuntimeError(f"task-{n}")
60
+
61
+ tasks = [lambda n=i: fail(n) for i in range(5)]
62
+ results = parallel_retrieve.fetch_bodies_parallel(tasks, max_workers=5)
63
+ self.assertEqual(len(results), 5)
64
+ for ok, exc in results:
65
+ self.assertFalse(ok)
66
+ self.assertIsInstance(exc, RuntimeError)
67
+
68
+
69
+ class ExceptionIdentityPreservedTests(unittest.TestCase):
70
+ """Boundary: we must return the SAME exception object so
71
+ callers can run it through `rest_client.redact_error` at log time.
72
+ Auto-stringifying here would strip structured attrs (HTTPError.code,
73
+ .headers) and potentially leak tokens before redaction runs."""
74
+
75
+ def test_httperror_object_identity_preserved(self):
76
+ # HTTPError with a body containing a bearer token — caller is
77
+ # responsible for redacting at log time; we must not stringify.
78
+ sentinel_body = b'{"access_token":"eyJtok.secret"}'
79
+ exc = urllib.error.HTTPError(
80
+ url="https://example.com/x",
81
+ code=429,
82
+ msg="Too Many Requests",
83
+ hdrs=None, # type: ignore[arg-type]
84
+ fp=None,
85
+ )
86
+ # Stash the body where a caller could read it later.
87
+ exc.body_bytes = sentinel_body # type: ignore[attr-defined]
88
+
89
+ def raises():
90
+ raise exc
91
+
92
+ results = parallel_retrieve.fetch_bodies_parallel([raises], max_workers=1)
93
+ self.assertEqual(len(results), 1)
94
+ ok, returned_exc = results[0]
95
+ self.assertFalse(ok)
96
+ # Identity check — the SAME object came back.
97
+ self.assertIs(returned_exc, exc)
98
+ # Structured attrs are intact for downstream triage.
99
+ self.assertEqual(returned_exc.code, 429)
100
+ self.assertEqual(returned_exc.body_bytes, sentinel_body)
101
+
102
+
103
+ class MaxWorkersOneSerializesTests(unittest.TestCase):
104
+ def test_max_workers_one_ordering_deterministic(self):
105
+ """With max_workers=1, tasks run one at a time — ordering is fully
106
+ deterministic (and matches input order since results are indexed).
107
+ """
108
+ order_seen: list[int] = []
109
+ lock = threading.Lock()
110
+
111
+ def make_task(n):
112
+ def task():
113
+ with lock:
114
+ order_seen.append(n)
115
+ return n
116
+ return task
117
+
118
+ tasks = [make_task(i) for i in range(10)]
119
+ results = parallel_retrieve.fetch_bodies_parallel(tasks, max_workers=1)
120
+ self.assertEqual([r for _, r in results], list(range(10)))
121
+ # Serialized execution: tasks ran in submission order.
122
+ self.assertEqual(order_seen, list(range(10)))
123
+
124
+
125
+ class EmptyTasksTests(unittest.TestCase):
126
+ def test_empty_list_returns_empty_list(self):
127
+ self.assertEqual(parallel_retrieve.fetch_bodies_parallel([]), [])
128
+
129
+
130
+ if __name__ == "__main__":
131
+ unittest.main()
@@ -0,0 +1,400 @@
1
+ """Tests for ``parse_bundle`` — Bot.bot + GenAiPlannerBundle parsing.
2
+
3
+ Covers the four public functions:
4
+
5
+ - ``classify_generation`` pure str → str
6
+ - ``extract_planner_name`` reads bots/*.bot from a tmp WORK_DIR
7
+ - ``extract_bundle`` reads genAiPlannerBundles/*.genAiPlannerBundle
8
+ - ``atomic_write`` tmp-file + os.replace semantics
9
+
10
+ All filesystem fixtures are constructed inline under ``tmp_path`` — no
11
+ external fixture files needed. XML mirrors the real Salesforce metadata
12
+ shape (sf: namespace) so the parser exercises its real code path.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import os
18
+ import unittest
19
+ from pathlib import Path
20
+ from tempfile import TemporaryDirectory
21
+
22
+ from . import _bootstrap # noqa: F401 — sys.path setup
23
+
24
+ import parse_bundle # type: ignore
25
+
26
+
27
+ SF_NS = "http://soap.sforce.com/2006/04/metadata"
28
+
29
+
30
+ def _bot_xml(version: str, planner_name: str | None = "MyPlanner") -> str:
31
+ """Minimal Bot.bot XML with one botVersions block.
32
+
33
+ If ``planner_name`` is None, omit the <conversationDefinitionPlanners>
34
+ block — caller can use this to exercise the empty-planner branch.
35
+ """
36
+ cdp_block = (
37
+ f""" <conversationDefinitionPlanners>
38
+ <genAiPlannerName>{planner_name}</genAiPlannerName>
39
+ </conversationDefinitionPlanners>"""
40
+ if planner_name is not None
41
+ else ""
42
+ )
43
+ return f"""<?xml version="1.0" encoding="UTF-8"?>
44
+ <Bot xmlns="{SF_NS}">
45
+ <botVersions>
46
+ <fullName>{version}</fullName>
47
+ {cdp_block}
48
+ </botVersions>
49
+ </Bot>
50
+ """
51
+
52
+
53
+ def _bundle_xml(
54
+ *,
55
+ planner_type: str = "Atlas__Reasoning",
56
+ description: str = "demo",
57
+ master_label: str = "Demo",
58
+ topics: list[dict] | None = None,
59
+ planner_actions: list[dict] | None = None,
60
+ ) -> str:
61
+ """Build a genAiPlannerBundle XML body.
62
+
63
+ Each topic dict supports keys ``name``, ``actions`` (list of dicts with
64
+ keys ``name``, ``invocationTarget``, ``invocationTargetType``).
65
+ """
66
+ topics = topics or []
67
+ planner_actions = planner_actions or []
68
+
69
+ def _topic_xml(t: dict) -> str:
70
+ actions = "".join(
71
+ f""" <localActions>
72
+ <developerName>{a['name']}</developerName>
73
+ <invocationTarget>{a.get('invocationTarget', '')}</invocationTarget>
74
+ <invocationTargetType>{a.get('invocationTargetType', '')}</invocationTargetType>
75
+ </localActions>
76
+ """
77
+ for a in t.get("actions", [])
78
+ )
79
+ return f""" <localTopics>
80
+ <developerName>{t['name']}</developerName>
81
+ <masterLabel>{t.get('masterLabel', '')}</masterLabel>
82
+ <canEscalate>{str(t.get('canEscalate', False)).lower()}</canEscalate>
83
+ {actions} </localTopics>
84
+ """
85
+
86
+ def _planner_action_xml(a: dict) -> str:
87
+ return f""" <plannerActions>
88
+ <developerName>{a['name']}</developerName>
89
+ <invocationTarget>{a.get('invocationTarget', '')}</invocationTarget>
90
+ <invocationTargetType>{a.get('invocationTargetType', '')}</invocationTargetType>
91
+ </plannerActions>
92
+ """
93
+
94
+ return f"""<?xml version="1.0" encoding="UTF-8"?>
95
+ <GenAiPlannerBundle xmlns="{SF_NS}">
96
+ <plannerType>{planner_type}</plannerType>
97
+ <description>{description}</description>
98
+ <masterLabel>{master_label}</masterLabel>
99
+ {''.join(_topic_xml(t) for t in topics)}{''.join(_planner_action_xml(a) for a in planner_actions)}</GenAiPlannerBundle>
100
+ """
101
+
102
+
103
+ # -----------------------------------------------------------------------------
104
+ # classify_generation — pure str → str
105
+ # -----------------------------------------------------------------------------
106
+
107
+
108
+ class ClassifyGenerationTests(unittest.TestCase):
109
+
110
+ def test_aicopilot_prefix_classifies_as_classic(self):
111
+ self.assertEqual(parse_bundle.classify_generation("AiCopilot__Foo"), "classic")
112
+
113
+ def test_atlas_prefix_classifies_as_nga(self):
114
+ self.assertEqual(parse_bundle.classify_generation("Atlas__Reasoning"), "nga")
115
+
116
+ def test_unknown_prefix_classifies_as_unknown(self):
117
+ self.assertEqual(parse_bundle.classify_generation("SomethingElse"), "unknown")
118
+
119
+ def test_empty_string_classifies_as_unknown(self):
120
+ self.assertEqual(parse_bundle.classify_generation(""), "unknown")
121
+
122
+ def test_none_classifies_as_unknown(self):
123
+ self.assertEqual(parse_bundle.classify_generation(None), "unknown") # type: ignore[arg-type]
124
+
125
+
126
+ # -----------------------------------------------------------------------------
127
+ # extract_planner_name — reads bots/*.bot under WORK_DIR
128
+ # -----------------------------------------------------------------------------
129
+
130
+
131
+ class ExtractPlannerNameTests(unittest.TestCase):
132
+
133
+ def _setup_bot(self, tmp: Path, version: str, planner_name: str | None = "MyPlanner") -> Path:
134
+ bots_dir = tmp / "sf_meta" / "wave1_bot" / "unpackaged" / "bots"
135
+ bots_dir.mkdir(parents=True)
136
+ bot_file = bots_dir / "MyAgent.bot"
137
+ bot_file.write_text(_bot_xml(version, planner_name))
138
+ return tmp
139
+
140
+ def test_returns_planner_name_when_botversion_matches(self):
141
+ with TemporaryDirectory() as t:
142
+ tmp = self._setup_bot(Path(t), "v1", "MyPlanner")
143
+ self.assertEqual(parse_bundle.extract_planner_name(tmp, "v1"), "MyPlanner")
144
+
145
+ def test_returns_empty_when_botversion_does_not_match(self):
146
+ with TemporaryDirectory() as t:
147
+ tmp = self._setup_bot(Path(t), "v1", "MyPlanner")
148
+ self.assertEqual(parse_bundle.extract_planner_name(tmp, "v99"), "")
149
+
150
+ def test_returns_empty_when_no_planner_block(self):
151
+ # Bot has the right version but no <conversationDefinitionPlanners>
152
+ with TemporaryDirectory() as t:
153
+ tmp = self._setup_bot(Path(t), "v1", planner_name=None)
154
+ self.assertEqual(parse_bundle.extract_planner_name(tmp, "v1"), "")
155
+
156
+ def test_returns_empty_when_bots_dir_missing(self):
157
+ with TemporaryDirectory() as t:
158
+ tmp = Path(t) # no sf_meta/wave1_bot/...
159
+ self.assertEqual(parse_bundle.extract_planner_name(tmp, "v1"), "")
160
+
161
+ def test_returns_empty_when_xml_is_malformed(self):
162
+ with TemporaryDirectory() as t:
163
+ tmp = Path(t)
164
+ bots_dir = tmp / "sf_meta" / "wave1_bot" / "unpackaged" / "bots"
165
+ bots_dir.mkdir(parents=True)
166
+ (bots_dir / "Broken.bot").write_text("<<<not xml>>>")
167
+ self.assertEqual(parse_bundle.extract_planner_name(tmp, "v1"), "")
168
+
169
+ def test_falls_back_to_plannerName_then_fullName(self):
170
+ # Build a bot with sf:plannerName instead of sf:genAiPlannerName.
171
+ with TemporaryDirectory() as t:
172
+ tmp = Path(t)
173
+ bots_dir = tmp / "sf_meta" / "wave1_bot" / "unpackaged" / "bots"
174
+ bots_dir.mkdir(parents=True)
175
+ (bots_dir / "Fallback.bot").write_text(f"""<?xml version="1.0" encoding="UTF-8"?>
176
+ <Bot xmlns="{SF_NS}">
177
+ <botVersions>
178
+ <fullName>v1</fullName>
179
+ <conversationDefinitionPlanners>
180
+ <plannerName>FallbackPlanner</plannerName>
181
+ </conversationDefinitionPlanners>
182
+ </botVersions>
183
+ </Bot>
184
+ """)
185
+ self.assertEqual(
186
+ parse_bundle.extract_planner_name(tmp, "v1"),
187
+ "FallbackPlanner",
188
+ )
189
+
190
+
191
+ # -----------------------------------------------------------------------------
192
+ # extract_bundle — reads genAiPlannerBundles/*.genAiPlannerBundle
193
+ # -----------------------------------------------------------------------------
194
+
195
+
196
+ class ExtractBundleTests(unittest.TestCase):
197
+
198
+ def _setup_bundle(self, tmp: Path, body: str, dirname: str = "MyPlanner") -> None:
199
+ bundle_dir = (
200
+ tmp / "sf_meta" / "wave1_bundle" / "unpackaged" / "genAiPlannerBundles" / dirname
201
+ )
202
+ bundle_dir.mkdir(parents=True)
203
+ (bundle_dir / "MyPlanner.genAiPlannerBundle").write_text(body)
204
+
205
+ def test_returns_skeleton_when_planner_dir_missing(self):
206
+ with TemporaryDirectory() as t:
207
+ out = parse_bundle.extract_bundle(Path(t), "MyPlanner")
208
+ self.assertEqual(out["topics"], [])
209
+ self.assertEqual(out["plannerActions"], [])
210
+ self.assertEqual(out["plannerType"], None)
211
+ self.assertEqual(out["plannerName"], "MyPlanner")
212
+ self.assertEqual(out["generation"], "unknown")
213
+
214
+ def test_extracts_planner_type_description_and_master_label(self):
215
+ with TemporaryDirectory() as t:
216
+ tmp = Path(t)
217
+ self._setup_bundle(tmp, _bundle_xml(
218
+ planner_type="Atlas__Reasoning",
219
+ description="My agent",
220
+ master_label="My Agent",
221
+ ))
222
+ out = parse_bundle.extract_bundle(tmp, "MyPlanner")
223
+ self.assertEqual(out["plannerType"], "Atlas__Reasoning")
224
+ self.assertEqual(out["description"], "My agent")
225
+ self.assertEqual(out["masterLabel"], "My Agent")
226
+ self.assertEqual(out["generation"], "nga")
227
+
228
+ def test_extracts_topics_with_localActions(self):
229
+ with TemporaryDirectory() as t:
230
+ tmp = Path(t)
231
+ self._setup_bundle(tmp, _bundle_xml(topics=[{
232
+ "name": "Greetings",
233
+ "masterLabel": "Greetings",
234
+ "actions": [
235
+ {"name": "Hello", "invocationTarget": "say_hello",
236
+ "invocationTargetType": "apex"},
237
+ {"name": "Bye", "invocationTarget": "say_bye",
238
+ "invocationTargetType": "apex"},
239
+ ],
240
+ }]))
241
+ out = parse_bundle.extract_bundle(tmp, "MyPlanner")
242
+ self.assertEqual(len(out["topics"]), 1)
243
+ self.assertEqual(out["topics"][0]["name"], "Greetings")
244
+ self.assertEqual(len(out["topics"][0]["actions"]), 2)
245
+ self.assertEqual(out["topics"][0]["actions"][0]["name"], "Hello")
246
+ self.assertEqual(out["topics"][0]["actions"][0]["invocationTargetType"], "apex")
247
+
248
+ def test_extracts_plannerActions_classic_shape(self):
249
+ with TemporaryDirectory() as t:
250
+ tmp = Path(t)
251
+ self._setup_bundle(tmp, _bundle_xml(
252
+ planner_type="AiCopilot__Foo",
253
+ planner_actions=[
254
+ {"name": "Search", "invocationTarget": "search",
255
+ "invocationTargetType": "flow"},
256
+ ],
257
+ ))
258
+ out = parse_bundle.extract_bundle(tmp, "MyPlanner")
259
+ self.assertEqual(out["generation"], "classic")
260
+ self.assertEqual(len(out["plannerActions"]), 1)
261
+ self.assertEqual(out["plannerActions"][0]["name"], "Search")
262
+
263
+ def test_skips_topic_without_developerName(self):
264
+ # Build XML where one topic has no <developerName> — should be dropped.
265
+ with TemporaryDirectory() as t:
266
+ tmp = Path(t)
267
+ broken = f"""<?xml version="1.0" encoding="UTF-8"?>
268
+ <GenAiPlannerBundle xmlns="{SF_NS}">
269
+ <plannerType>Atlas__Reasoning</plannerType>
270
+ <localTopics>
271
+ <masterLabel>NoName</masterLabel>
272
+ </localTopics>
273
+ <localTopics>
274
+ <developerName>Good</developerName>
275
+ </localTopics>
276
+ </GenAiPlannerBundle>
277
+ """
278
+ self._setup_bundle(tmp, broken)
279
+ out = parse_bundle.extract_bundle(tmp, "MyPlanner")
280
+ self.assertEqual(len(out["topics"]), 1)
281
+ self.assertEqual(out["topics"][0]["name"], "Good")
282
+
283
+ def test_returns_skeleton_when_xml_is_malformed(self):
284
+ with TemporaryDirectory() as t:
285
+ tmp = Path(t)
286
+ self._setup_bundle(tmp, "<<<broken>>>")
287
+ out = parse_bundle.extract_bundle(tmp, "MyPlanner")
288
+ # Malformed → no fields populated, but skeleton shape preserved.
289
+ self.assertEqual(out["topics"], [])
290
+ self.assertEqual(out["plannerActions"], [])
291
+ self.assertEqual(out["plannerType"], None)
292
+
293
+
294
+ # -----------------------------------------------------------------------------
295
+ # atomic_write — tmp-file + os.replace
296
+ # -----------------------------------------------------------------------------
297
+
298
+
299
+ class AtomicWriteTests(unittest.TestCase):
300
+
301
+ def test_writes_content_to_target_path(self):
302
+ with TemporaryDirectory() as t:
303
+ target = Path(t) / "out.txt"
304
+ parse_bundle.atomic_write(target, "hello\n")
305
+ self.assertEqual(target.read_text(), "hello\n")
306
+
307
+ def test_overwrites_existing_file(self):
308
+ with TemporaryDirectory() as t:
309
+ target = Path(t) / "out.txt"
310
+ target.write_text("old")
311
+ parse_bundle.atomic_write(target, "new")
312
+ self.assertEqual(target.read_text(), "new")
313
+
314
+ def test_no_tmp_file_left_behind_after_success(self):
315
+ with TemporaryDirectory() as t:
316
+ target = Path(t) / "out.txt"
317
+ parse_bundle.atomic_write(target, "x")
318
+ # the .tmp sibling should NOT remain
319
+ tmp_sibling = target.with_suffix(target.suffix + ".tmp")
320
+ self.assertFalse(tmp_sibling.exists())
321
+
322
+
323
+ # -----------------------------------------------------------------------------
324
+ # main() — orchestration via env vars
325
+ # -----------------------------------------------------------------------------
326
+
327
+
328
+ class MainTests(unittest.TestCase):
329
+
330
+ def _setup_full_workdir(self, tmp: Path) -> None:
331
+ # Bot file
332
+ bots_dir = tmp / "sf_meta" / "wave1_bot" / "unpackaged" / "bots"
333
+ bots_dir.mkdir(parents=True)
334
+ (bots_dir / "MyAgent.bot").write_text(_bot_xml("v1", "MyPlanner"))
335
+ # Bundle file
336
+ bundle_dir = (
337
+ tmp / "sf_meta" / "wave1_bundle" / "unpackaged"
338
+ / "genAiPlannerBundles" / "MyPlanner"
339
+ )
340
+ bundle_dir.mkdir(parents=True)
341
+ (bundle_dir / "MyPlanner.genAiPlannerBundle").write_text(_bundle_xml())
342
+
343
+ def test_main_writes_bundle_parsed_json_and_returns_zero(self):
344
+ with TemporaryDirectory() as t:
345
+ tmp = Path(t)
346
+ self._setup_full_workdir(tmp)
347
+ old = dict(os.environ)
348
+ os.environ["WORK_DIR"] = str(tmp)
349
+ os.environ["AGENT_VERSION"] = "v1"
350
+ try:
351
+ rc = parse_bundle.main()
352
+ finally:
353
+ os.environ.clear()
354
+ os.environ.update(old)
355
+ self.assertEqual(rc, 0)
356
+ parsed = json.loads((tmp / "_bundle_parsed.json").read_text())
357
+ self.assertEqual(parsed["plannerName"], "MyPlanner")
358
+ self.assertEqual(parsed["plannerType"], "Atlas__Reasoning")
359
+ self.assertEqual(parsed["generation"], "nga")
360
+ # _agent_generation.txt is newline-terminated for shell sourcing
361
+ self.assertEqual(
362
+ (tmp / "_agent_generation.txt").read_text(), "nga\n"
363
+ )
364
+
365
+ def test_main_returns_one_when_env_missing(self):
366
+ # Both WORK_DIR and AGENT_VERSION required; remove WORK_DIR.
367
+ old = dict(os.environ)
368
+ os.environ.pop("WORK_DIR", None)
369
+ os.environ["AGENT_VERSION"] = "v1"
370
+ try:
371
+ rc = parse_bundle.main()
372
+ finally:
373
+ os.environ.clear()
374
+ os.environ.update(old)
375
+ self.assertEqual(rc, 1)
376
+
377
+ def test_main_emits_empty_skeleton_when_planner_unresolved(self):
378
+ # Bot file has no <conversationDefinitionPlanners> → planner_name == ""
379
+ with TemporaryDirectory() as t:
380
+ tmp = Path(t)
381
+ bots_dir = tmp / "sf_meta" / "wave1_bot" / "unpackaged" / "bots"
382
+ bots_dir.mkdir(parents=True)
383
+ (bots_dir / "MyAgent.bot").write_text(_bot_xml("v1", planner_name=None))
384
+ old = dict(os.environ)
385
+ os.environ["WORK_DIR"] = str(tmp)
386
+ os.environ["AGENT_VERSION"] = "v1"
387
+ try:
388
+ rc = parse_bundle.main()
389
+ finally:
390
+ os.environ.clear()
391
+ os.environ.update(old)
392
+ self.assertEqual(rc, 0)
393
+ parsed = json.loads((tmp / "_bundle_parsed.json").read_text())
394
+ self.assertIsNone(parsed["plannerName"])
395
+ self.assertEqual(parsed["topics"], [])
396
+ self.assertEqual((tmp / "_agent_generation.txt").read_text(), "unknown\n")
397
+
398
+
399
+ if __name__ == "__main__":
400
+ unittest.main()