@salesforce/afv-skills 1.14.0 → 1.16.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 (365) hide show
  1. package/package.json +1 -1
  2. package/skills/activating-datacloud/SKILL.md +0 -1
  3. package/skills/analyzing-omnistudio-dependencies/SKILL.md +0 -1
  4. package/skills/applying-slds/SKILL.md +322 -0
  5. package/skills/applying-slds/checklists.md +83 -0
  6. package/skills/applying-slds/examples.md +283 -0
  7. package/skills/applying-slds/guidance/README.md +83 -0
  8. package/skills/applying-slds/guidance/blueprints-index.md +213 -0
  9. package/skills/applying-slds/guidance/icons-guidance.md +186 -0
  10. package/skills/applying-slds/guidance/overviews/borders.md +236 -0
  11. package/skills/applying-slds/guidance/overviews/color.md +266 -0
  12. package/skills/applying-slds/guidance/overviews/display-density.md +366 -0
  13. package/skills/applying-slds/guidance/overviews/icons.md +240 -0
  14. package/skills/applying-slds/guidance/overviews/illustrations.md +235 -0
  15. package/skills/applying-slds/guidance/overviews/shadows.md +176 -0
  16. package/skills/applying-slds/guidance/overviews/spacing.md +216 -0
  17. package/skills/applying-slds/guidance/overviews/typography.md +323 -0
  18. package/skills/applying-slds/guidance/overviews/utilities.md +542 -0
  19. package/skills/applying-slds/guidance/slds-development-guide.md +288 -0
  20. package/skills/applying-slds/guidance/styling-hooks/borders.md +202 -0
  21. package/skills/applying-slds/guidance/styling-hooks/color/expressive-palette-hooks.md +153 -0
  22. package/skills/applying-slds/guidance/styling-hooks/color/index.md +171 -0
  23. package/skills/applying-slds/guidance/styling-hooks/color/semantic/accent-hooks.md +204 -0
  24. package/skills/applying-slds/guidance/styling-hooks/color/semantic/feedback-hooks.md +768 -0
  25. package/skills/applying-slds/guidance/styling-hooks/color/semantic/surface-hooks.md +337 -0
  26. package/skills/applying-slds/guidance/styling-hooks/color/system-hooks.md +132 -0
  27. package/skills/applying-slds/guidance/styling-hooks/index.md +327 -0
  28. package/skills/applying-slds/guidance/styling-hooks/shadows.md +238 -0
  29. package/skills/applying-slds/guidance/styling-hooks/spacing.md +254 -0
  30. package/skills/applying-slds/guidance/styling-hooks/typography.md +448 -0
  31. package/skills/applying-slds/guidance/utilities/alignment.md +119 -0
  32. package/skills/applying-slds/guidance/utilities/borders.md +131 -0
  33. package/skills/applying-slds/guidance/utilities/box.md +125 -0
  34. package/skills/applying-slds/guidance/utilities/color.md +165 -0
  35. package/skills/applying-slds/guidance/utilities/dark-mode.md +111 -0
  36. package/skills/applying-slds/guidance/utilities/description-list.md +168 -0
  37. package/skills/applying-slds/guidance/utilities/floats.md +117 -0
  38. package/skills/applying-slds/guidance/utilities/grid.md +264 -0
  39. package/skills/applying-slds/guidance/utilities/horizontal-list.md +110 -0
  40. package/skills/applying-slds/guidance/utilities/hyphenation.md +84 -0
  41. package/skills/applying-slds/guidance/utilities/index.md +205 -0
  42. package/skills/applying-slds/guidance/utilities/interactions.md +89 -0
  43. package/skills/applying-slds/guidance/utilities/layout.md +109 -0
  44. package/skills/applying-slds/guidance/utilities/line-clamp.md +131 -0
  45. package/skills/applying-slds/guidance/utilities/margin.md +155 -0
  46. package/skills/applying-slds/guidance/utilities/media-object.md +161 -0
  47. package/skills/applying-slds/guidance/utilities/name-value-list.md +152 -0
  48. package/skills/applying-slds/guidance/utilities/padding.md +155 -0
  49. package/skills/applying-slds/guidance/utilities/position.md +177 -0
  50. package/skills/applying-slds/guidance/utilities/print.md +114 -0
  51. package/skills/applying-slds/guidance/utilities/scrollable.md +126 -0
  52. package/skills/applying-slds/guidance/utilities/sizing.md +190 -0
  53. package/skills/applying-slds/guidance/utilities/themes.md +121 -0
  54. package/skills/applying-slds/guidance/utilities/truncate.md +127 -0
  55. package/skills/applying-slds/guidance/utilities/typography.md +166 -0
  56. package/skills/applying-slds/guidance/utilities/vertical-list.md +166 -0
  57. package/skills/applying-slds/guidance/utilities/visibility.md +228 -0
  58. package/skills/applying-slds/metadata/README.md +84 -0
  59. package/skills/applying-slds/metadata/blueprints/components/accordion.yaml +304 -0
  60. package/skills/applying-slds/metadata/blueprints/components/activity-timeline.yaml +92 -0
  61. package/skills/applying-slds/metadata/blueprints/components/alert.yaml +103 -0
  62. package/skills/applying-slds/metadata/blueprints/components/app-launcher.yaml +94 -0
  63. package/skills/applying-slds/metadata/blueprints/components/avatar-group.yaml +81 -0
  64. package/skills/applying-slds/metadata/blueprints/components/avatar.yaml +97 -0
  65. package/skills/applying-slds/metadata/blueprints/components/badges.yaml +102 -0
  66. package/skills/applying-slds/metadata/blueprints/components/brand-band.yaml +198 -0
  67. package/skills/applying-slds/metadata/blueprints/components/breadcrumbs.yaml +95 -0
  68. package/skills/applying-slds/metadata/blueprints/components/builder-header.yaml +192 -0
  69. package/skills/applying-slds/metadata/blueprints/components/button-groups.yaml +82 -0
  70. package/skills/applying-slds/metadata/blueprints/components/button-icons.yaml +295 -0
  71. package/skills/applying-slds/metadata/blueprints/components/buttons.yaml +230 -0
  72. package/skills/applying-slds/metadata/blueprints/components/cards.yaml +124 -0
  73. package/skills/applying-slds/metadata/blueprints/components/carousel.yaml +140 -0
  74. package/skills/applying-slds/metadata/blueprints/components/chat.yaml +179 -0
  75. package/skills/applying-slds/metadata/blueprints/components/checkbox-button-group.yaml +192 -0
  76. package/skills/applying-slds/metadata/blueprints/components/checkbox-button.yaml +204 -0
  77. package/skills/applying-slds/metadata/blueprints/components/checkbox-toggle.yaml +177 -0
  78. package/skills/applying-slds/metadata/blueprints/components/checkbox.yaml +108 -0
  79. package/skills/applying-slds/metadata/blueprints/components/color-picker.yaml +172 -0
  80. package/skills/applying-slds/metadata/blueprints/components/combobox.yaml +136 -0
  81. package/skills/applying-slds/metadata/blueprints/components/counter.yaml +147 -0
  82. package/skills/applying-slds/metadata/blueprints/components/data-tables.yaml +157 -0
  83. package/skills/applying-slds/metadata/blueprints/components/datepickers.yaml +130 -0
  84. package/skills/applying-slds/metadata/blueprints/components/datetime-picker.yaml +155 -0
  85. package/skills/applying-slds/metadata/blueprints/components/docked-composer.yaml +201 -0
  86. package/skills/applying-slds/metadata/blueprints/components/docked-form-footer.yaml +161 -0
  87. package/skills/applying-slds/metadata/blueprints/components/docked-utility-bar.yaml +175 -0
  88. package/skills/applying-slds/metadata/blueprints/components/drop-zone.yaml +115 -0
  89. package/skills/applying-slds/metadata/blueprints/components/dueling-picklist.yaml +196 -0
  90. package/skills/applying-slds/metadata/blueprints/components/dynamic-icons.yaml +128 -0
  91. package/skills/applying-slds/metadata/blueprints/components/dynamic-menu.yaml +141 -0
  92. package/skills/applying-slds/metadata/blueprints/components/expandable-section.yaml +115 -0
  93. package/skills/applying-slds/metadata/blueprints/components/expression.yaml +143 -0
  94. package/skills/applying-slds/metadata/blueprints/components/feeds.yaml +125 -0
  95. package/skills/applying-slds/metadata/blueprints/components/file-selector.yaml +154 -0
  96. package/skills/applying-slds/metadata/blueprints/components/files.yaml +119 -0
  97. package/skills/applying-slds/metadata/blueprints/components/form-element.yaml +145 -0
  98. package/skills/applying-slds/metadata/blueprints/components/global-header.yaml +120 -0
  99. package/skills/applying-slds/metadata/blueprints/components/global-navigation.yaml +100 -0
  100. package/skills/applying-slds/metadata/blueprints/components/icons.yaml +138 -0
  101. package/skills/applying-slds/metadata/blueprints/components/illustration.yaml +205 -0
  102. package/skills/applying-slds/metadata/blueprints/components/input.yaml +151 -0
  103. package/skills/applying-slds/metadata/blueprints/components/list-builder.yaml +127 -0
  104. package/skills/applying-slds/metadata/blueprints/components/lookups.yaml +132 -0
  105. package/skills/applying-slds/metadata/blueprints/components/map.yaml +118 -0
  106. package/skills/applying-slds/metadata/blueprints/components/menus.yaml +134 -0
  107. package/skills/applying-slds/metadata/blueprints/components/modals.yaml +152 -0
  108. package/skills/applying-slds/metadata/blueprints/components/notifications.yaml +88 -0
  109. package/skills/applying-slds/metadata/blueprints/components/page-headers.yaml +135 -0
  110. package/skills/applying-slds/metadata/blueprints/components/panels.yaml +149 -0
  111. package/skills/applying-slds/metadata/blueprints/components/path.yaml +154 -0
  112. package/skills/applying-slds/metadata/blueprints/components/picklist.yaml +125 -0
  113. package/skills/applying-slds/metadata/blueprints/components/pills.yaml +154 -0
  114. package/skills/applying-slds/metadata/blueprints/components/popovers.yaml +120 -0
  115. package/skills/applying-slds/metadata/blueprints/components/progress-bar.yaml +110 -0
  116. package/skills/applying-slds/metadata/blueprints/components/progress-indicator.yaml +133 -0
  117. package/skills/applying-slds/metadata/blueprints/components/progress-ring.yaml +102 -0
  118. package/skills/applying-slds/metadata/blueprints/components/prompt.yaml +126 -0
  119. package/skills/applying-slds/metadata/blueprints/components/publishers.yaml +178 -0
  120. package/skills/applying-slds/metadata/blueprints/components/radio-button-group.yaml +172 -0
  121. package/skills/applying-slds/metadata/blueprints/components/radio-group.yaml +112 -0
  122. package/skills/applying-slds/metadata/blueprints/components/rich-text-editor.yaml +135 -0
  123. package/skills/applying-slds/metadata/blueprints/components/scoped-notifications.yaml +188 -0
  124. package/skills/applying-slds/metadata/blueprints/components/scoped-tabs.yaml +97 -0
  125. package/skills/applying-slds/metadata/blueprints/components/select.yaml +127 -0
  126. package/skills/applying-slds/metadata/blueprints/components/setup-assistant.yaml +152 -0
  127. package/skills/applying-slds/metadata/blueprints/components/slider.yaml +111 -0
  128. package/skills/applying-slds/metadata/blueprints/components/spinners.yaml +135 -0
  129. package/skills/applying-slds/metadata/blueprints/components/split-view.yaml +112 -0
  130. package/skills/applying-slds/metadata/blueprints/components/summary-detail.yaml +103 -0
  131. package/skills/applying-slds/metadata/blueprints/components/tabs.yaml +138 -0
  132. package/skills/applying-slds/metadata/blueprints/components/textarea.yaml +116 -0
  133. package/skills/applying-slds/metadata/blueprints/components/tiles.yaml +108 -0
  134. package/skills/applying-slds/metadata/blueprints/components/timepicker.yaml +111 -0
  135. package/skills/applying-slds/metadata/blueprints/components/toast.yaml +154 -0
  136. package/skills/applying-slds/metadata/blueprints/components/tooltips.yaml +107 -0
  137. package/skills/applying-slds/metadata/blueprints/components/tree-grid.yaml +116 -0
  138. package/skills/applying-slds/metadata/blueprints/components/trees.yaml +116 -0
  139. package/skills/applying-slds/metadata/blueprints/components/trial-bar.yaml +112 -0
  140. package/skills/applying-slds/metadata/blueprints/components/vertical-navigation.yaml +130 -0
  141. package/skills/applying-slds/metadata/blueprints/components/vertical-tabs.yaml +140 -0
  142. package/skills/applying-slds/metadata/blueprints/components/visual-picker.yaml +150 -0
  143. package/skills/applying-slds/metadata/blueprints/components/welcome-mat.yaml +136 -0
  144. package/skills/applying-slds/metadata/hooks-index.json +6272 -0
  145. package/skills/applying-slds/metadata/icon-metadata.json +38466 -0
  146. package/skills/applying-slds/metadata/utilities-index.json +21912 -0
  147. package/skills/applying-slds/references/component-selection.md +112 -0
  148. package/skills/applying-slds/references/icons-decision-guide.md +124 -0
  149. package/skills/applying-slds/references/styling-decision-guide.md +228 -0
  150. package/skills/applying-slds/references/utilities-quick-ref.md +125 -0
  151. package/skills/applying-slds/scripts/search-blueprints.cjs +117 -0
  152. package/skills/applying-slds/scripts/search-hooks.cjs +139 -0
  153. package/skills/applying-slds/scripts/search-icons.cjs +174 -0
  154. package/skills/applying-slds/scripts/search-utilities.cjs +161 -0
  155. package/skills/building-mobile-apps/SKILL.md +0 -1
  156. package/skills/building-omnistudio-callable-apex/SKILL.md +0 -1
  157. package/skills/building-omnistudio-datamapper/SKILL.md +0 -1
  158. package/skills/building-omnistudio-flexcard/SKILL.md +0 -1
  159. package/skills/building-omnistudio-integration-procedure/SKILL.md +0 -1
  160. package/skills/building-omnistudio-omniscript/SKILL.md +0 -1
  161. package/skills/building-sf-integrations/SKILL.md +0 -1
  162. package/skills/configuring-connected-apps/SKILL.md +0 -1
  163. package/skills/connecting-datacloud/SKILL.md +0 -1
  164. package/skills/creating-b2b-commerce-store/SKILL.md +0 -1
  165. package/skills/debugging-apex-logs/SKILL.md +0 -1
  166. package/skills/deploying-metadata/SKILL.md +0 -1
  167. package/skills/deploying-omnistudio-datapacks/SKILL.md +0 -1
  168. package/skills/developing-agentforce/SKILL.md +0 -1
  169. package/skills/fetching-salesforce-docs/SKILL.md +0 -1
  170. package/skills/generating-custom-lightning-type/SKILL.md +17 -39
  171. package/skills/generating-custom-lightning-type/assets/primitive-types-and-constraints.md +41 -0
  172. package/skills/generating-custom-lightning-type/references/widget-rendition.md +124 -0
  173. package/skills/generating-lwc-components/SKILL.md +0 -1
  174. package/skills/generating-mermaid-diagrams/SKILL.md +0 -1
  175. package/skills/generating-visual-diagrams/SKILL.md +0 -1
  176. package/skills/handling-sf-data/SKILL.md +0 -1
  177. package/skills/harmonizing-datacloud/SKILL.md +0 -1
  178. package/skills/integrating-b2b-commerce-open-code-components/SKILL.md +0 -1
  179. package/skills/investigating-agentforce-architecture/README.md +156 -0
  180. package/skills/investigating-agentforce-architecture/SKILL.md +230 -0
  181. package/skills/investigating-agentforce-architecture/assets/cli/describe_sobject.yaml +16 -0
  182. package/skills/investigating-agentforce-architecture/assets/cli/describe_tooling_sobject.yaml +17 -0
  183. package/skills/investigating-agentforce-architecture/assets/cli/list_metadata_genaiprompttemplate.yaml +17 -0
  184. package/skills/investigating-agentforce-architecture/assets/cli/org_display.yaml +15 -0
  185. package/skills/investigating-agentforce-architecture/assets/cli/retrieve_genai_plugin.yaml +18 -0
  186. package/skills/investigating-agentforce-architecture/assets/cli/show_access_token.yaml +27 -0
  187. package/skills/investigating-agentforce-architecture/assets/mermaid/action_tree.mmd +20 -0
  188. package/skills/investigating-agentforce-architecture/assets/mermaid/data_flow.mmd +19 -0
  189. package/skills/investigating-agentforce-architecture/assets/mermaid/dependency_graph.mmd +19 -0
  190. package/skills/investigating-agentforce-architecture/assets/mermaid/invocation_sequence.mmd +20 -0
  191. package/skills/investigating-agentforce-architecture/assets/mermaid/planner_state.mmd +18 -0
  192. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_ids.soql +3 -0
  193. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_names.soql +3 -0
  194. package/skills/investigating-agentforce-architecture/assets/soql/bot_definition_details.soql +3 -0
  195. package/skills/investigating-agentforce-architecture/assets/soql/bot_version_lookup.soql +4 -0
  196. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_by_ids.soql +3 -0
  197. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_ids_by_names.soql +3 -0
  198. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_view_by_durable_ids.soql +4 -0
  199. package/skills/investigating-agentforce-architecture/assets/soql/flow_metadata_by_id.soql +3 -0
  200. package/skills/investigating-agentforce-architecture/assets/soql/functions_by_plugins.soql +5 -0
  201. package/skills/investigating-agentforce-architecture/assets/soql/planner_attrs_by_parent_ids.soql +3 -0
  202. package/skills/investigating-agentforce-architecture/assets/soql/planner_bundle_functions.soql +3 -0
  203. package/skills/investigating-agentforce-architecture/assets/soql/planner_definition_by_agent_chain.soql +3 -0
  204. package/skills/investigating-agentforce-architecture/assets/soql/plugin_functions_by_plugin_ids.soql +3 -0
  205. package/skills/investigating-agentforce-architecture/assets/soql/plugin_instructions_by_plugin_ids.soql +3 -0
  206. package/skills/investigating-agentforce-architecture/assets/soql/plugins_by_planner.soql +4 -0
  207. package/skills/investigating-agentforce-architecture/references/architecture_sections.md +243 -0
  208. package/skills/investigating-agentforce-architecture/references/contract.json +244 -0
  209. package/skills/investigating-agentforce-architecture/references/soql_fields.md +512 -0
  210. package/skills/investigating-agentforce-architecture/scripts/_shared/__init__.py +1 -0
  211. package/skills/investigating-agentforce-architecture/scripts/_shared/fs_guard.py +329 -0
  212. package/skills/investigating-agentforce-architecture/scripts/_shared/paths.py +110 -0
  213. package/skills/investigating-agentforce-architecture/scripts/_shared/runtime.py +59 -0
  214. package/skills/investigating-agentforce-architecture/scripts/_shared/sql.py +10 -0
  215. package/skills/investigating-agentforce-architecture/scripts/cache_check.py +234 -0
  216. package/skills/investigating-agentforce-architecture/scripts/config.py +131 -0
  217. package/skills/investigating-agentforce-architecture/scripts/fetch_soql.py +689 -0
  218. package/skills/investigating-agentforce-architecture/scripts/finalize.py +295 -0
  219. package/skills/investigating-agentforce-architecture/scripts/main.py +2835 -0
  220. package/skills/investigating-agentforce-architecture/scripts/metadata_listing.py +265 -0
  221. package/skills/investigating-agentforce-architecture/scripts/parallel_retrieve.py +69 -0
  222. package/skills/investigating-agentforce-architecture/scripts/parse_bundle.py +215 -0
  223. package/skills/investigating-agentforce-architecture/scripts/parse_wave.py +845 -0
  224. package/skills/investigating-agentforce-architecture/scripts/probe_channels.py +302 -0
  225. package/skills/investigating-agentforce-architecture/scripts/render_architecture.py +1043 -0
  226. package/skills/investigating-agentforce-architecture/scripts/resolve_bot.py +255 -0
  227. package/skills/investigating-agentforce-architecture/scripts/resolve_invocation_target.py +130 -0
  228. package/skills/investigating-agentforce-architecture/scripts/rest_client.py +763 -0
  229. package/skills/investigating-agentforce-architecture/scripts/retrieve_planner.py +13 -0
  230. package/skills/investigating-agentforce-architecture/scripts/sf_cli.py +242 -0
  231. package/skills/investigating-agentforce-architecture/scripts/soql_loader.py +253 -0
  232. package/skills/investigating-agentforce-architecture/scripts/summarize_tree.py +143 -0
  233. package/skills/investigating-agentforce-architecture/scripts/tests/__init__.py +0 -0
  234. package/skills/investigating-agentforce-architecture/scripts/tests/_bootstrap.py +23 -0
  235. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/__init__.py +0 -0
  236. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/genai_payloads.py +400 -0
  237. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check.py +307 -0
  238. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check_main.py +283 -0
  239. package/skills/investigating-agentforce-architecture/scripts/tests/test_config.py +115 -0
  240. package/skills/investigating-agentforce-architecture/scripts/tests/test_end_to_end_fixture.py +651 -0
  241. package/skills/investigating-agentforce-architecture/scripts/tests/test_finalize.py +278 -0
  242. package/skills/investigating-agentforce-architecture/scripts/tests/test_flow_children_inflation.py +582 -0
  243. package/skills/investigating-agentforce-architecture/scripts/tests/test_fs_guard.py +113 -0
  244. package/skills/investigating-agentforce-architecture/scripts/tests/test_iterative_wave_b.py +478 -0
  245. package/skills/investigating-agentforce-architecture/scripts/tests/test_main_pipeline.py +3359 -0
  246. package/skills/investigating-agentforce-architecture/scripts/tests/test_parallel_retrieve.py +131 -0
  247. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_bundle.py +400 -0
  248. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave.py +644 -0
  249. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py +224 -0
  250. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_helpers.py +380 -0
  251. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_main.py +397 -0
  252. package/skills/investigating-agentforce-architecture/scripts/tests/test_per_branch_visited.py +244 -0
  253. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_channels.py +359 -0
  254. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_cli_recipes.py +185 -0
  255. package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py +810 -0
  256. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_bot.py +203 -0
  257. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_creds.py +157 -0
  258. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_invocation_target.py +145 -0
  259. package/skills/investigating-agentforce-architecture/scripts/tests/test_rest_client.py +1253 -0
  260. package/skills/investigating-agentforce-architecture/scripts/tests/test_runtime_override.py +100 -0
  261. package/skills/investigating-agentforce-architecture/scripts/tests/test_sf_cli.py +261 -0
  262. package/skills/investigating-agentforce-architecture/scripts/tests/test_signature_stamping.py +466 -0
  263. package/skills/investigating-agentforce-architecture/scripts/tests/test_soql_loader.py +501 -0
  264. package/skills/investigating-agentforce-architecture/scripts/tests/test_summarize_tree.py +241 -0
  265. package/skills/investigating-agentforce-architecture/scripts/tests/test_write_emit_ctx.py +480 -0
  266. package/skills/investigating-agentforce-architecture/tools/emit_env.py +157 -0
  267. package/skills/investigating-agentforce-architecture/tools/emit_result.py +262 -0
  268. package/skills/investigating-agentforce-architecture/tools/sanitize.py +33 -0
  269. package/skills/investigating-agentforce-architecture/tools/write_emit_ctx.py +332 -0
  270. package/skills/investigating-agentforce-d360/README.md +123 -0
  271. package/skills/investigating-agentforce-d360/SKILL.md +163 -0
  272. package/skills/investigating-agentforce-d360/assets/dc/app_generation.sql +51 -0
  273. package/skills/investigating-agentforce-d360/assets/dc/content_category.sql +44 -0
  274. package/skills/investigating-agentforce-d360/assets/dc/content_quality.sql +41 -0
  275. package/skills/investigating-agentforce-d360/assets/dc/discover_sessions.sql +36 -0
  276. package/skills/investigating-agentforce-d360/assets/dc/feedback.sql +47 -0
  277. package/skills/investigating-agentforce-d360/assets/dc/feedback_details.sql +38 -0
  278. package/skills/investigating-agentforce-d360/assets/dc/gateway_records.sql +45 -0
  279. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_llm.sql +50 -0
  280. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_metadata.sql +44 -0
  281. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_tags.sql +42 -0
  282. package/skills/investigating-agentforce-d360/assets/dc/gateway_requests.sql +89 -0
  283. package/skills/investigating-agentforce-d360/assets/dc/gateway_responses.sql +43 -0
  284. package/skills/investigating-agentforce-d360/assets/dc/generations.sql +52 -0
  285. package/skills/investigating-agentforce-d360/assets/dc/interactions.sql +53 -0
  286. package/skills/investigating-agentforce-d360/assets/dc/messages.sql +53 -0
  287. package/skills/investigating-agentforce-d360/assets/dc/messaging_session.sql +37 -0
  288. package/skills/investigating-agentforce-d360/assets/dc/moment_interactions.sql +34 -0
  289. package/skills/investigating-agentforce-d360/assets/dc/moments.sql +39 -0
  290. package/skills/investigating-agentforce-d360/assets/dc/participants.sql +48 -0
  291. package/skills/investigating-agentforce-d360/assets/dc/sessions.sql +78 -0
  292. package/skills/investigating-agentforce-d360/assets/dc/steps.sql +64 -0
  293. package/skills/investigating-agentforce-d360/assets/dc/tag_associations.sql +46 -0
  294. package/skills/investigating-agentforce-d360/assets/dc/tag_definition_associations.sql +37 -0
  295. package/skills/investigating-agentforce-d360/assets/dc/tag_definitions.sql +50 -0
  296. package/skills/investigating-agentforce-d360/assets/dc/tags.sql +37 -0
  297. package/skills/investigating-agentforce-d360/assets/dc/telemetry_spans.sql +55 -0
  298. package/skills/investigating-agentforce-d360/references/artifacts.md +50 -0
  299. package/skills/investigating-agentforce-d360/references/dc_dmo_fields.md +823 -0
  300. package/skills/investigating-agentforce-d360/references/dc_pipeline_contract.md +608 -0
  301. package/skills/investigating-agentforce-d360/scripts/_shared/__init__.py +2 -0
  302. package/skills/investigating-agentforce-d360/scripts/_shared/cli_override.py +98 -0
  303. package/skills/investigating-agentforce-d360/scripts/_shared/fs_guard.py +334 -0
  304. package/skills/investigating-agentforce-d360/scripts/_shared/paths.py +155 -0
  305. package/skills/investigating-agentforce-d360/scripts/_shared/runtime.py +59 -0
  306. package/skills/investigating-agentforce-d360/scripts/_shared/sql.py +14 -0
  307. package/skills/investigating-agentforce-d360/scripts/assemble_dc.py +1624 -0
  308. package/skills/investigating-agentforce-d360/scripts/config.py +45 -0
  309. package/skills/investigating-agentforce-d360/scripts/dc.py +188 -0
  310. package/skills/investigating-agentforce-d360/scripts/discover_sessions.py +556 -0
  311. package/skills/investigating-agentforce-d360/scripts/fetch_dc.py +1045 -0
  312. package/skills/investigating-agentforce-d360/scripts/render_dc.py +1750 -0
  313. package/skills/investigating-agentforce-d360/scripts/resolve_session.py +264 -0
  314. package/skills/investigating-agentforce-d360/scripts/storage.py +92 -0
  315. package/skills/investigating-agentforce-d360/scripts/tests/__init__.py +0 -0
  316. package/skills/investigating-agentforce-d360/scripts/tests/_bootstrap.py +15 -0
  317. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/__init__.py +0 -0
  318. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/synthetic_session.py +424 -0
  319. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_bootstrap_and_mode.py +115 -0
  320. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct.py +220 -0
  321. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct_integration.py +158 -0
  322. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_helpers.py +287 -0
  323. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_integration.py +247 -0
  324. package/skills/investigating-agentforce-d360/scripts/tests/test_dc_and_resolve_session.py +433 -0
  325. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions.py +458 -0
  326. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions_grep_ci.py +193 -0
  327. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_helpers.py +266 -0
  328. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_identity.py +528 -0
  329. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_main.py +251 -0
  330. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall.py +229 -0
  331. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall_full.py +283 -0
  332. package/skills/investigating-agentforce-d360/scripts/tests/test_identity_coherence.py +327 -0
  333. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_branches.py +256 -0
  334. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_gateway_direct.py +130 -0
  335. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_helpers.py +291 -0
  336. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_integration.py +220 -0
  337. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_planner_llm_calls.py +284 -0
  338. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_show_prompts_gating.py +215 -0
  339. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_from_disk.py +100 -0
  340. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_session_main.py +149 -0
  341. package/skills/investigating-agentforce-d360/scripts/tests/test_runtime_override.py +104 -0
  342. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape.py +95 -0
  343. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape_dropped_by_stdm.py +85 -0
  344. package/skills/managing-managed-event-subscription/SKILL.md +152 -0
  345. package/skills/managing-managed-event-subscription/assets/managed-event-subscription-template.xml +20 -0
  346. package/skills/managing-managed-event-subscription/references/delete-guide.md +57 -0
  347. package/skills/managing-managed-event-subscription/references/topic-name-formats.md +26 -0
  348. package/skills/managing-managed-event-subscription/references/update-constraints.md +30 -0
  349. package/skills/modeling-omnistudio-epc-catalog/SKILL.md +0 -1
  350. package/skills/observing-agentforce/SKILL.md +0 -1
  351. package/skills/orchestrating-datacloud/SKILL.md +0 -1
  352. package/skills/preparing-datacloud/SKILL.md +0 -1
  353. package/skills/querying-soql/SKILL.md +0 -1
  354. package/skills/retrieving-datacloud/SKILL.md +0 -1
  355. package/skills/running-apex-tests/SKILL.md +0 -1
  356. package/skills/running-code-analyzer/SKILL.md +0 -1
  357. package/skills/segmenting-datacloud/SKILL.md +0 -1
  358. package/skills/testing-agentforce/SKILL.md +0 -1
  359. package/skills/uplifting-components-to-slds2/SKILL.md +3 -2
  360. package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +30 -9
  361. package/skills/uplifting-components-to-slds2/references/examples.md +24 -6
  362. package/skills/validating-slds/SKILL.md +262 -0
  363. package/skills/validating-slds/references/quality-checks.md +308 -0
  364. package/skills/validating-slds/references/report-format.md +302 -0
  365. package/skills/validating-slds/scripts/analyze-quality.cjs +521 -0
@@ -0,0 +1,810 @@
1
+ """Tests for render_architecture.render + load_mermaid.
2
+
3
+ P2.2-1: exercise every per-generation branch (classic ReAct, classic
4
+ Sequential, NGA ConcurrentMultiAgent, search/BYOP placeholder), the
5
+ _partial / _unresolved / cycle surfaces, and the node-cap behaviour.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import tempfile
11
+ import unittest
12
+ from pathlib import Path
13
+ from unittest import mock
14
+
15
+ from . import _bootstrap # noqa: F401 — sys.path setup
16
+
17
+ import render_architecture # type: ignore
18
+
19
+ # SKILL_ROOT is now file-relative (config.py uses
20
+ # Path(__file__).resolve().parent.parent), so config.MERMAID_DIR auto-
21
+ # resolves to the repo's assets/mermaid/ under test. We still construct
22
+ # _REPO_MERMAID_DIR explicitly for tests that compare paths or pass them
23
+ # as args; render_architecture's own MERMAID_DIR captures the right path
24
+ # at module import time.
25
+ _REPO_MERMAID_DIR = (
26
+ Path(__file__).resolve().parent.parent.parent / "assets" / "mermaid"
27
+ )
28
+
29
+
30
+ def _classic_react_tree() -> dict:
31
+ """MyAgent v5 shape — classic ReAct, 2 topics, a handful of
32
+ children to keep the fixture inline-readable."""
33
+ return {
34
+ "_schema_version": "3.0",
35
+ "agent": {
36
+ "api_name": "MyAgent",
37
+ "version": "v5",
38
+ "master_label": "Xero support AI",
39
+ "description": "External-facing service agent.",
40
+ "agent_type": "EinsteinAgentKind",
41
+ "type": "ExternalCopilot",
42
+ "agent_template": "SvcCopilotTmpl__EinsteinAgentKind",
43
+ "bot_source": "None",
44
+ "generation": "classic",
45
+ "planner_name": "MyAgent_v2_v3_v4_v5",
46
+ "planner_type": "AiCopilot__ReAct",
47
+ "bot_id": "0XxXx00000000FdKAI",
48
+ },
49
+ "root": {
50
+ "kind": "BOT_DEFINITION",
51
+ "api_name": "MyAgent",
52
+ "children": [
53
+ {
54
+ "kind": "TOPIC",
55
+ "api_name": "Customer_Q_A",
56
+ "children": [
57
+ {
58
+ "kind": "GEN_AI_FUNCTION",
59
+ "api_name": "Find_Articles",
60
+ "unwraps_to": {"kind": "FLOW",
61
+ "api_name": "Find_Public_Articles"},
62
+ "children": [
63
+ {
64
+ "kind": "FLOW",
65
+ "api_name": "Find_Public_Articles",
66
+ "children": [
67
+ {"kind": "APEX",
68
+ "api_name": "KnowledgeRetriever"},
69
+ ],
70
+ },
71
+ ],
72
+ },
73
+ ],
74
+ },
75
+ {
76
+ "kind": "TOPIC",
77
+ "api_name": "Escalation",
78
+ "children": [
79
+ {
80
+ "kind": "GEN_AI_FUNCTION",
81
+ "api_name": "Escalate",
82
+ "unwraps_to": {"kind": "APEX",
83
+ "api_name": "EscalateAction"},
84
+ "children": [
85
+ {"kind": "APEX", "api_name": "EscalateAction"},
86
+ ],
87
+ },
88
+ ],
89
+ },
90
+ ],
91
+ },
92
+ "node_count": 8,
93
+ "depth": 4,
94
+ "_partial": False,
95
+ "_pending_fetches": {"Flow": [], "ApexClass": [], "GenAiPromptTemplate": []},
96
+ "_unresolved": [],
97
+ "_kind_counts": {
98
+ "BOT_DEFINITION": 1,
99
+ "TOPIC": 2,
100
+ "GEN_AI_FUNCTION": 2,
101
+ "FLOW": 1,
102
+ "APEX": 2,
103
+ },
104
+ }
105
+
106
+
107
+ def _nga_tree() -> dict:
108
+ t = _classic_react_tree()
109
+ t["agent"]["generation"] = "nga"
110
+ t["agent"]["planner_name"] = "Atlas__ConcurrentMultiAgentOrchestration"
111
+ t["agent"]["planner_type"] = "Atlas__ConcurrentMultiAgentOrchestration"
112
+ return t
113
+
114
+
115
+ def _sequential_tree() -> dict:
116
+ """Zero-topic Sequential planner (classic)."""
117
+ return {
118
+ "_schema_version": "3.0",
119
+ "agent": {
120
+ "api_name": "KAMAgent",
121
+ "version": "v5",
122
+ "generation": "classic",
123
+ "planner_name": "KAM_Planner",
124
+ "planner_type": "AiCopilot__SequentialPlannerIntentClassifier",
125
+ },
126
+ "root": {
127
+ "kind": "BOT_DEFINITION",
128
+ "api_name": "KAMAgent",
129
+ "children": [
130
+ {
131
+ "kind": "GEN_AI_FUNCTION",
132
+ "api_name": "SalesPlay",
133
+ "children": [],
134
+ },
135
+ ],
136
+ },
137
+ "node_count": 2,
138
+ "depth": 1,
139
+ "_partial": False,
140
+ "_pending_fetches": {},
141
+ "_unresolved": [],
142
+ "_kind_counts": {"BOT_DEFINITION": 1, "GEN_AI_FUNCTION": 1},
143
+ }
144
+
145
+
146
+ def _write_tree(tmp: Path, tree: dict) -> Path:
147
+ p = tmp / "metadata_tree.json"
148
+ p.write_text(json.dumps(tree))
149
+ return p
150
+
151
+
152
+ class _RenderTestBase(unittest.TestCase):
153
+ """Shared setup: patch MERMAID_DIR at the renderer binding + tmp dir.
154
+
155
+ Every test that invokes `render_architecture.render` or
156
+ `render_architecture.load_mermaid` patches MERMAID_DIR to point at the
157
+ repo's `assets/mermaid/`. With file-relative SKILL_ROOT this is now
158
+ coincident with the natural resolution, but the explicit patch keeps
159
+ each TestCase deterministic and isolated from any sys.path drift.
160
+ """
161
+
162
+ def setUp(self) -> None:
163
+ self._patch = mock.patch.object(
164
+ render_architecture, "MERMAID_DIR", _REPO_MERMAID_DIR,
165
+ )
166
+ self._patch.start()
167
+ self.addCleanup(self._patch.stop)
168
+ self._tmp = tempfile.TemporaryDirectory()
169
+ self.addCleanup(self._tmp.cleanup)
170
+ self.tmp = Path(self._tmp.name)
171
+
172
+
173
+ class RenderClassicReActTests(_RenderTestBase):
174
+ def test_all_eight_sections_present(self):
175
+ tree_path = _write_tree(self.tmp, _classic_react_tree())
176
+ out = self.tmp / "architecture.md"
177
+ render_architecture.render(tree_path, out)
178
+ text = out.read_text()
179
+ # Section headers 2..8 all present; H1 covers section 1.
180
+ # Invocation sequence (formerly section 3) was removed from the
181
+ # default pipeline in 2026-05 — the heuristic was not trusted.
182
+ # Planner state machine (formerly section 6) was also removed in
183
+ # 2026-05. Both `_render_invocation_sequence` and
184
+ # `_render_planner_state` stay callable for a future re-enable;
185
+ # see `test_nga_invocation_sequence_has_orchestrator_lane` and
186
+ # `test_nga_state_diagram_has_par_and_block`.
187
+ self.assertIn("# Architecture", text)
188
+ for heading in (
189
+ "## 2. Anatomy summary",
190
+ "## 3. Action tree",
191
+ "## 4. Topic anatomy",
192
+ "## 5. Action catalog",
193
+ "## 6. Data flow / context propagation",
194
+ "## 7. Flow / Apex / Prompt catalogs",
195
+ "## 8. Unresolved refs + artifact pointers",
196
+ ):
197
+ self.assertIn(heading, text)
198
+ # Explicit regression guard: the retired sections must NOT appear.
199
+ self.assertNotIn("## 3. Invocation sequence", text)
200
+ self.assertNotIn("Invocation sequence", text)
201
+ self.assertNotIn("Planner state machine", text)
202
+
203
+ def test_two_mermaid_diagrams_rendered(self):
204
+ tree_path = _write_tree(self.tmp, _classic_react_tree())
205
+ out = self.tmp / "architecture.md"
206
+ render_architecture.render(tree_path, out)
207
+ text = out.read_text()
208
+ # each diagram-kind keyword must appear as a bare
209
+ # non-comment line inside exactly one fenced block. Keywords in
210
+ # prose inside `%%` comments are now harmless (Mermaid parses
211
+ # them as comments at render time), so we don't policework the
212
+ # comment contents — we only require a real keyword line.
213
+ # sequenceDiagram was removed from the default pipeline in 2026-05
214
+ # along with the Invocation sequence section; stateDiagram-v2
215
+ # was removed the same cycle along with Planner state machine.
216
+ import re
217
+ blocks = re.findall(r"```mermaid\n(.*?)\n```", text, re.DOTALL)
218
+ keywords = {"flowchart TB", "flowchart LR"}
219
+ seen_keywords: set[str] = set()
220
+ for b in blocks:
221
+ for ln in b.splitlines():
222
+ stripped = ln.strip()
223
+ if stripped.startswith("%%"):
224
+ continue
225
+ if stripped in keywords:
226
+ seen_keywords.add(stripped)
227
+ self.assertEqual(seen_keywords, keywords)
228
+ # Regression: sequenceDiagram and stateDiagram-v2 must NOT appear
229
+ # in the default render.
230
+ for b in blocks:
231
+ for ln in b.splitlines():
232
+ stripped = ln.strip()
233
+ if stripped.startswith("%%"):
234
+ continue
235
+ self.assertNotEqual(stripped, "sequenceDiagram")
236
+ self.assertNotEqual(stripped, "stateDiagram-v2")
237
+ # Every block must be fully substituted — no stray `{{PARAM}}`
238
+ # tokens left behind. removed the `{{...}}` from
239
+ # comment headers precisely to make this invariant hold.
240
+ for b in blocks:
241
+ self.assertNotIn("{{", b)
242
+ self.assertNotIn("}}", b)
243
+ # No dependency graph when _unresolved is empty.
244
+ self.assertNotIn("## Dependency graph", text)
245
+
246
+ def test_react_state_machine_not_in_default_render(self):
247
+ # Planner state machine was retired from the default pipeline in
248
+ # 2026-05. Thought/Action/Observation states are emitted by
249
+ # `_render_planner_state` which is still tested directly but not
250
+ # wired into `render`.
251
+ tree_path = _write_tree(self.tmp, _classic_react_tree())
252
+ out = self.tmp / "architecture.md"
253
+ render_architecture.render(tree_path, out)
254
+ text = out.read_text()
255
+ self.assertNotIn("Thought", text)
256
+ self.assertNotIn("Action --> Observation", text)
257
+
258
+
259
+ class RenderNgaTests(_RenderTestBase):
260
+ def test_nga_state_diagram_has_par_and_block(self):
261
+ # `_render_planner_state` was removed from the default render
262
+ # pipeline in 2026-05, but the function + mermaid template are
263
+ # retained so the feature can be re-enabled by uncommenting one
264
+ # line in `render`. This test exercises the function directly to
265
+ # keep the NGA par/and-block behaviour covered.
266
+ tree = _nga_tree()
267
+ rendered = render_architecture._render_planner_state(
268
+ tree["agent"], "nga",
269
+ dict(render_architecture.DEFAULT_MAX_MERMAID_NODES),
270
+ )
271
+ # par/and block marker is the `--` separator inside an `Orchestration`
272
+ # state nest. Also confirm the lanes differ from ReAct.
273
+ self.assertIn("state Orchestration", rendered)
274
+ self.assertIn("--", rendered)
275
+ self.assertIn("SubAgentA", rendered)
276
+ self.assertIn("SubAgentB", rendered)
277
+ # ReAct-specific states must NOT appear.
278
+ self.assertNotIn("Thought", rendered)
279
+
280
+ def test_nga_invocation_sequence_has_orchestrator_lane(self):
281
+ # `_render_invocation_sequence` was removed from the default
282
+ # render pipeline in 2026-05 (heuristic distrusted), but the
283
+ # function + mermaid template are retained so the feature can
284
+ # be re-enabled by uncommenting one line in `render`. This test
285
+ # exercises the function directly to keep the behaviour covered.
286
+ tree = _nga_tree()
287
+ walker = render_architecture._TreeWalker(tree)
288
+ walker.walk()
289
+ rendered = render_architecture._render_invocation_sequence(
290
+ tree, tree["agent"], walker,
291
+ dict(render_architecture.DEFAULT_MAX_MERMAID_NODES),
292
+ )
293
+ self.assertIn("participant Orchestrator", rendered)
294
+ self.assertIn("participant SubAgent", rendered)
295
+
296
+
297
+ class RenderSequentialTests(_RenderTestBase):
298
+ def test_sequential_zero_topic_produces_empty_topic_section(self):
299
+ tree_path = _write_tree(self.tmp, _sequential_tree())
300
+ out = self.tmp / "architecture.md"
301
+ render_architecture.render(tree_path, out)
302
+ text = out.read_text()
303
+ self.assertIn("_No topics defined", text)
304
+ # Sequential state machine uses Classify -> Execute, but the
305
+ # Planner state machine section was retired from the default
306
+ # pipeline in 2026-05 — exercise the helper directly instead.
307
+ rendered = render_architecture._render_planner_state(
308
+ _sequential_tree()["agent"], "classic",
309
+ dict(render_architecture.DEFAULT_MAX_MERMAID_NODES),
310
+ )
311
+ self.assertIn("Classify --> Execute", rendered)
312
+
313
+ def test_sequential_does_not_emit_react_states(self):
314
+ tree_path = _write_tree(self.tmp, _sequential_tree())
315
+ out = self.tmp / "architecture.md"
316
+ render_architecture.render(tree_path, out)
317
+ text = out.read_text()
318
+ self.assertNotIn("Thought", text)
319
+ self.assertNotIn("Orchestration", text)
320
+
321
+
322
+ class RenderPartialTreeTests(_RenderTestBase):
323
+ def test_partial_true_emits_health_callout(self):
324
+ tree = _classic_react_tree()
325
+ tree["_partial"] = True
326
+ tree["_partial_reason"] = "flow-metadata-timeout"
327
+ tree["_pending_fetches"] = {"FLOW": ["Foo", "Bar"], "APEX": []}
328
+ tree_path = _write_tree(self.tmp, tree)
329
+ out = self.tmp / "architecture.md"
330
+ render_architecture.render(tree_path, out)
331
+ text = out.read_text()
332
+ self.assertIn("**Health: PARTIAL.**", text)
333
+ self.assertIn("flow-metadata-timeout", text)
334
+ self.assertIn("Pending fetches: 2", text)
335
+
336
+ def test_missing_planner_name_emits_warn(self):
337
+ tree = _classic_react_tree()
338
+ tree["agent"]["planner_name"] = None
339
+ tree_path = _write_tree(self.tmp, tree)
340
+ out = self.tmp / "architecture.md"
341
+ render_architecture.render(tree_path, out)
342
+ text = out.read_text()
343
+ self.assertIn("`planner_name` missing", text)
344
+
345
+
346
+ class RenderUnresolvedAndCyclesTests(_RenderTestBase):
347
+ def test_unresolved_renders_dependency_graph(self):
348
+ tree = _classic_react_tree()
349
+ tree["_unresolved"] = [
350
+ {"kind": "FLOW", "api_name": "Missing_Flow",
351
+ "reason": "not-in-org"},
352
+ ]
353
+ tree_path = _write_tree(self.tmp, tree)
354
+ out = self.tmp / "architecture.md"
355
+ render_architecture.render(tree_path, out)
356
+ text = out.read_text()
357
+ self.assertIn("## Dependency graph", text)
358
+ self.assertIn("Missing_Flow", text)
359
+ # Section 8 renders the unresolved row in its table.
360
+ self.assertIn("not-in-org", text)
361
+
362
+ def test_cycle_annotation_renders_dotted_back_edge(self):
363
+ tree = _classic_react_tree()
364
+ # Inject a _cycle_back_to annotation on the Find_Public_Articles flow.
365
+ tree["root"]["children"][0]["children"][0]["children"][0][
366
+ "_cycle_back_to"] = "Find_Public_Articles"
367
+ tree_path = _write_tree(self.tmp, tree)
368
+ out = self.tmp / "architecture.md"
369
+ render_architecture.render(tree_path, out)
370
+ text = out.read_text()
371
+ # Dotted back-edge syntax `-.->` plus the `cycle_back_to:` label.
372
+ self.assertIn("-.->", text)
373
+ self.assertIn("cycle_back_to", text)
374
+
375
+
376
+ class RenderNodeCapTests(_RenderTestBase):
377
+ def _large_tree(self, n_actions: int) -> dict:
378
+ tree = _classic_react_tree()
379
+ # Overwrite the second topic with a fan-out of n_actions children.
380
+ big_topic = {
381
+ "kind": "TOPIC",
382
+ "api_name": "Mega_Topic",
383
+ "children": [
384
+ {
385
+ "kind": "GEN_AI_FUNCTION",
386
+ "api_name": f"Action_{i:03d}",
387
+ "children": [
388
+ {"kind": "APEX", "api_name": f"ApexClass_{i:03d}"},
389
+ ],
390
+ }
391
+ for i in range(n_actions)
392
+ ],
393
+ }
394
+ tree["root"]["children"] = [big_topic]
395
+ tree["_kind_counts"] = {
396
+ "BOT_DEFINITION": 1,
397
+ "TOPIC": 1,
398
+ "GEN_AI_FUNCTION": n_actions,
399
+ "APEX": n_actions,
400
+ }
401
+ return tree
402
+
403
+ def test_flowchart_cap_exceeded_emits_placeholder(self):
404
+ # 250 actions + 250 apex + 1 topic + 1 bot = 502 nodes > 200 cap.
405
+ tree = self._large_tree(250)
406
+ tree_path = _write_tree(self.tmp, tree)
407
+ out = self.tmp / "architecture.md"
408
+ render_architecture.render(tree_path, out)
409
+ text = out.read_text()
410
+ self.assertIn("[diagram truncated: flowchart", text)
411
+ self.assertIn("exceed cap of 200", text)
412
+ # Top-5 fan-out line present.
413
+ self.assertIn("Top 5 nodes by fan-out", text)
414
+ # Mega_Topic shows up as a top fan-out node.
415
+ self.assertIn("Mega_Topic", text)
416
+
417
+ def test_react_30_topics_does_not_false_trip_sequence_cap(self):
418
+ # pre-fix, msg_count was computed as
419
+ # `2 * len(topics) + len(actions) + 2`, which for 30 topics
420
+ # yields 62+ > cap=60. But ReAct only samples topics[:5], so
421
+ # the rendered sequence has ~14 messages — nowhere near the
422
+ # cap. Assert the diagram renders, no truncation placeholder.
423
+ # 2026-05: `_render_invocation_sequence` is no longer wired
424
+ # into `render`; invoke it directly so the cap math stays
425
+ # covered.
426
+ tree = _classic_react_tree()
427
+ topics = [
428
+ {
429
+ "kind": "TOPIC",
430
+ "api_name": f"Topic_{i:02d}",
431
+ "children": [
432
+ {"kind": "GEN_AI_FUNCTION",
433
+ "api_name": f"Action_{i:02d}"},
434
+ ],
435
+ }
436
+ for i in range(30)
437
+ ]
438
+ tree["root"]["children"] = topics
439
+ tree["_kind_counts"] = {
440
+ "BOT_DEFINITION": 1, "TOPIC": 30, "GEN_AI_FUNCTION": 30,
441
+ }
442
+ walker = render_architecture._TreeWalker(tree)
443
+ walker.walk()
444
+ rendered = render_architecture._render_invocation_sequence(
445
+ tree, tree["agent"], walker,
446
+ dict(render_architecture.DEFAULT_MAX_MERMAID_NODES),
447
+ )
448
+ self.assertNotIn("[diagram truncated: sequenceDiagram", rendered)
449
+ self.assertIn("sequenceDiagram", rendered)
450
+ # Sampling at :5 means exactly 5 topics show up in the messages.
451
+ # Topic_00..Topic_04 must appear; Topic_05 must not.
452
+ self.assertIn("Topic_04", rendered)
453
+ self.assertNotIn("Topic_05", rendered)
454
+
455
+ def test_sequence_cap_trips_on_actually_rendered_overflow(self):
456
+ # construct a scenario where the rendered message
457
+ # list truly exceeds the cap. We use a tiny cap (5) rather than
458
+ # fabricating 60+ NGA messages — the test's intent is "cap math
459
+ # is evaluated against the rendered list", not "cap=60 is
460
+ # specifically correct".
461
+ # 2026-05: invoke `_render_invocation_sequence` directly since
462
+ # the section was retired from the default pipeline.
463
+ tree = _classic_react_tree()
464
+ walker = render_architecture._TreeWalker(tree)
465
+ walker.walk()
466
+ caps = dict(render_architecture.DEFAULT_MAX_MERMAID_NODES)
467
+ caps["sequenceDiagram"] = 3
468
+ rendered = render_architecture._render_invocation_sequence(
469
+ tree, tree["agent"], walker, caps,
470
+ )
471
+ self.assertIn("[diagram truncated: sequenceDiagram", rendered)
472
+ self.assertIn("exceed cap of 3", rendered)
473
+
474
+
475
+ class RenderLoadMermaidTests(_RenderTestBase):
476
+ def test_nested_placeholder_in_value_logs_warning(self):
477
+ with self.assertLogs(render_architecture.logger, level="WARNING") as cm:
478
+ out = render_architecture.load_mermaid(
479
+ "invocation_sequence",
480
+ PARTICIPANTS="participant {{NESTED}}",
481
+ MESSAGES="User->>+Planner: x",
482
+ )
483
+ self.assertTrue(any("nested placeholder" in m for m in cm.output))
484
+ # Still renders — the nested token is left as-is (no second pass).
485
+ self.assertIn("{{NESTED}}", out)
486
+
487
+ def test_missing_template_raises_filenotfounderror_without_path(self):
488
+ with self.assertRaises(FileNotFoundError) as ctx:
489
+ render_architecture.load_mermaid("nonexistent_template_zzz")
490
+ msg = str(ctx.exception)
491
+ self.assertIn("nonexistent_template_zzz", msg)
492
+ # Hygiene: absolute SKILL_ROOT path must not bleed through.
493
+ self.assertNotIn("/assets/mermaid", msg)
494
+
495
+ def test_traversal_name_rejected_without_absolute_path_leak(self):
496
+ # The hygiene contract is that the absolute MERMAID_DIR path
497
+ # must not appear in the error text — echoing the caller's
498
+ # own input back (which here happens to contain `/etc/passwd`)
499
+ # is fine; that's information the caller already had.
500
+ with self.assertRaises(FileNotFoundError) as ctx:
501
+ render_architecture.load_mermaid("../../../etc/passwd")
502
+ msg = str(ctx.exception)
503
+ self.assertNotIn(str(_REPO_MERMAID_DIR), msg)
504
+ # Also must not leak the absolute install path. Asserting the user's
505
+ # home directory doesn't appear is a stricter, runtime-agnostic check
506
+ # than naming any specific install root (.claude / .vibe /
507
+ # plugin-specific layouts).
508
+ self.assertNotIn(str(Path.home()), msg)
509
+
510
+ def test_non_string_param_raises_typeerror(self):
511
+ with self.assertRaises(TypeError):
512
+ render_architecture.load_mermaid(
513
+ "invocation_sequence",
514
+ PARTICIPANTS=None, # type: ignore[arg-type]
515
+ MESSAGES="x",
516
+ )
517
+
518
+ def test_leading_comment_block_preserved_but_not_substituted(self):
519
+ # the `%%` header comments in the shipped templates
520
+ # are kept in rendered output (Mermaid ignores `%%` lines at
521
+ # render time). The prior _strip_leading_comment_block workaround
522
+ # was a misdiagnosis — the real bug was templates documenting
523
+ # placeholders as `{{NAME}}` inside `%%` comments, causing
524
+ # load_mermaid's str.replace to substitute them.
525
+ out = render_architecture.load_mermaid(
526
+ "action_tree", SUBGRAPHS="SG_INJECTED", EDGES="EDGES_INJECTED",
527
+ )
528
+ # Header comments survive.
529
+ self.assertTrue(out.startswith("%%"))
530
+ # `%%` lines must not have been corrupted by substitution.
531
+ for ln in out.splitlines():
532
+ if ln.startswith("%%"):
533
+ self.assertNotIn("SG_INJECTED", ln)
534
+ self.assertNotIn("EDGES_INJECTED", ln)
535
+ # Diagram-kind keyword still present on its own bare line.
536
+ self.assertIn("\nflowchart TB", "\n" + out)
537
+ # Placeholders in the body were substituted exactly once.
538
+ self.assertIn("SG_INJECTED", out)
539
+ self.assertIn("EDGES_INJECTED", out)
540
+
541
+
542
+ class RenderEmptyAndSearchTests(_RenderTestBase):
543
+ def test_empty_bot_definition_renders_without_crash(self):
544
+ tree = {
545
+ "_schema_version": "3.0",
546
+ "agent": {
547
+ "api_name": "Empty",
548
+ "version": "v1",
549
+ "generation": "classic",
550
+ "planner_type": "AiCopilot__ReAct",
551
+ "planner_name": "Empty_Planner",
552
+ },
553
+ "root": {"kind": "BOT_DEFINITION", "api_name": "Empty",
554
+ "children": []},
555
+ "node_count": 1,
556
+ "depth": 0,
557
+ "_partial": False,
558
+ "_unresolved": [],
559
+ "_kind_counts": {"BOT_DEFINITION": 1},
560
+ }
561
+ tree_path = _write_tree(self.tmp, tree)
562
+ out = self.tmp / "architecture.md"
563
+ render_architecture.render(tree_path, out)
564
+ text = out.read_text()
565
+ self.assertIn("_No topics defined", text)
566
+ self.assertIn("_No actions declared._", text)
567
+ self.assertIn("_No backing artifacts in tree._", text)
568
+
569
+ def test_search_generation_skips_state_diagram(self):
570
+ # Planner state machine was retired from the default render in
571
+ # 2026-05. Exercise `_render_planner_state` directly to keep the
572
+ # search-generation prose-placeholder branch covered.
573
+ tree = _classic_react_tree()
574
+ tree["agent"]["generation"] = "search"
575
+ tree["agent"]["planner_type"] = "custom_search_Apex"
576
+ rendered = render_architecture._render_planner_state(
577
+ tree["agent"], "search",
578
+ dict(render_architecture.DEFAULT_MAX_MERMAID_NODES),
579
+ )
580
+ self.assertIn("Custom planner", rendered)
581
+ self.assertNotIn("stateDiagram-v2", rendered)
582
+
583
+ def test_byop_generation_skips_state_diagram(self):
584
+ # Same 2026-05 retirement — exercise the helper directly.
585
+ tree = _classic_react_tree()
586
+ tree["agent"]["generation"] = "byop"
587
+ rendered = render_architecture._render_planner_state(
588
+ tree["agent"], "byop",
589
+ dict(render_architecture.DEFAULT_MAX_MERMAID_NODES),
590
+ )
591
+ self.assertIn("Custom planner", rendered)
592
+
593
+
594
+ class RenderPromptCatalogTests(_RenderTestBase):
595
+ """Section-7 'Prompt templates' sub-section shape.
596
+
597
+ Flows and Apex classes each get an H4 per entry with a fenced
598
+ signature block. Prompts were historically rendered as a bare bullet
599
+ list (just names) which left the reader with no indication of what
600
+ each prompt does. We now mirror the flow/apex shape: H4 heading,
601
+ optional `Type:` line, optional signature fence, `_Details not
602
+ captured._` fallback when the walker has no metadata on the node.
603
+ Wave B doesn't yet stamp prompt signatures — the renderer just has
604
+ to be ready for when it does."""
605
+
606
+ def _tree_with_prompts(self, prompt_nodes: list[dict]) -> dict:
607
+ """Wrap two prompt nodes under a TOPIC -> GEN_AI_FUNCTION so the
608
+ walker indexes them into `walker.prompts`."""
609
+ tree = _classic_react_tree()
610
+ tree["root"]["children"].append({
611
+ "kind": "TOPIC",
612
+ "api_name": "PromptTopic",
613
+ "children": [
614
+ {
615
+ "kind": "GEN_AI_FUNCTION",
616
+ "api_name": "UsePrompts",
617
+ "children": prompt_nodes,
618
+ },
619
+ ],
620
+ })
621
+ return tree
622
+
623
+ def test_prompt_h4_per_entry_with_type_and_fallback(self):
624
+ r"""Two prompts: one with `prompt_type` set, one without. Both
625
+ get H4 headings. The typed one shows ``Type: `flex```; the
626
+ bare one shows `_Details not captured._`."""
627
+ tree = self._tree_with_prompts([
628
+ {
629
+ "kind": "PROMPT_TEMPLATE",
630
+ "api_name": "DraftReply",
631
+ "prompt_type": "flex",
632
+ "children": [],
633
+ },
634
+ {
635
+ "kind": "PROMPT_TEMPLATE",
636
+ "api_name": "SummarizeThread",
637
+ "children": [],
638
+ },
639
+ ])
640
+ tree_path = _write_tree(self.tmp, tree)
641
+ out = self.tmp / "architecture.md"
642
+ render_architecture.render(tree_path, out)
643
+ text = out.read_text()
644
+
645
+ self.assertIn("### Prompt templates", text)
646
+ # Both prompts render as H4, not as bare bullets.
647
+ self.assertIn("#### `DraftReply`", text)
648
+ self.assertIn("#### `SummarizeThread`", text)
649
+ self.assertNotIn("- `DraftReply`", text)
650
+ self.assertNotIn("- `SummarizeThread`", text)
651
+ # Typed prompt surfaces its Type line.
652
+ self.assertIn("- Type: `flex`", text)
653
+ # Untyped prompt (no signature either) falls back honestly.
654
+ # Locate the SummarizeThread block and confirm its body.
655
+ import re
656
+ m = re.search(
657
+ r"#### `SummarizeThread`\n\n(.*?)(?:\n#### |\n### |\n## |\Z)",
658
+ text, re.DOTALL,
659
+ )
660
+ self.assertIsNotNone(m, "SummarizeThread prompt block not found")
661
+ self.assertIn("_Details not captured._", m.group(1))
662
+
663
+ def test_prompt_with_signature_renders_fenced_block(self):
664
+ """When a prompt node carries a `signature` (future Wave B
665
+ stamp), the renderer emits it inside a fenced code block, same
666
+ shape used for flows/apex."""
667
+ tree = self._tree_with_prompts([
668
+ {
669
+ "kind": "PROMPT_TEMPLATE",
670
+ "api_name": "ClassifyIntent",
671
+ "prompt_type": "genAiPromptTemplate",
672
+ "signature": "in: userUtterance: String | out: intent: String",
673
+ "children": [],
674
+ },
675
+ ])
676
+ tree_path = _write_tree(self.tmp, tree)
677
+ out = self.tmp / "architecture.md"
678
+ render_architecture.render(tree_path, out)
679
+ text = out.read_text()
680
+
681
+ self.assertIn("#### `ClassifyIntent`", text)
682
+ self.assertIn("- Type: `genAiPromptTemplate`", text)
683
+ # Fenced signature block present.
684
+ self.assertIn(
685
+ "```\nin: userUtterance: String | out: intent: String\n```",
686
+ text,
687
+ )
688
+ # No fallback when we have either type or sig.
689
+ import re
690
+ m = re.search(
691
+ r"#### `ClassifyIntent`\n\n(.*?)(?:\n#### |\n### |\n## |\Z)",
692
+ text, re.DOTALL,
693
+ )
694
+ self.assertIsNotNone(m)
695
+ self.assertNotIn("_Details not captured._", m.group(1))
696
+
697
+
698
+ class RenderPromptTemplateBodyTests(_RenderTestBase):
699
+ """Gap C (2026-05-05): prompt template bodies retrieved via
700
+ `retrieve_prompt_templates` are stamped onto PROMPT_TEMPLATE leaves
701
+ as `master_label`, `content`, `inputs`, `_body_available`. The
702
+ renderer emits:
703
+ - `_Label: <master_label>_` blurb
704
+ - `**Inputs**:` bulleted list
705
+ - fenced `text` code block for the content
706
+ - `_Body not retrieved._` when `_body_available` is False and no
707
+ other details are available
708
+ """
709
+
710
+ def _tree_with_prompts(self, prompt_nodes: list[dict]) -> dict:
711
+ tree = _classic_react_tree()
712
+ tree["root"]["children"].append({
713
+ "kind": "TOPIC",
714
+ "api_name": "PromptTopic",
715
+ "children": [
716
+ {
717
+ "kind": "GEN_AI_FUNCTION",
718
+ "api_name": "UsePrompts",
719
+ "children": prompt_nodes,
720
+ },
721
+ ],
722
+ })
723
+ return tree
724
+
725
+ def test_body_rendered_with_label_inputs_and_fenced_content(self):
726
+ tree = self._tree_with_prompts([
727
+ {
728
+ "kind": "PROMPT_TEMPLATE",
729
+ "api_name": "AGNT_US_Q_A_with_Site_Scraping",
730
+ "master_label": "AGNT - US Q&A with Site Scraping",
731
+ "content": "# ROLE & OBJECTIVE\nAnswer the customer question.",
732
+ "inputs": [
733
+ {"name": "Query", "dataType": "String"},
734
+ {"name": "Site", "dataType": "String"},
735
+ ],
736
+ "_body_available": True,
737
+ "children": [],
738
+ },
739
+ ])
740
+ tree_path = _write_tree(self.tmp, tree)
741
+ out = self.tmp / "architecture.md"
742
+ render_architecture.render(tree_path, out)
743
+ text = out.read_text()
744
+
745
+ self.assertIn(
746
+ "#### `AGNT_US_Q_A_with_Site_Scraping`", text,
747
+ )
748
+ self.assertIn("_Label: AGNT - US Q&A with Site Scraping_", text)
749
+ self.assertIn("**Inputs**:", text)
750
+ self.assertIn("- `Query`: `String`", text)
751
+ self.assertIn("- `Site`: `String`", text)
752
+ # Fenced content block uses `text` language hint.
753
+ self.assertIn(
754
+ "```text\n# ROLE & OBJECTIVE\nAnswer the customer question.\n```",
755
+ text,
756
+ )
757
+ self.assertNotIn("_Details not captured._", text)
758
+ self.assertNotIn("_Body not retrieved._", text)
759
+
760
+ def test_body_unavailable_falls_back_honestly(self):
761
+ tree = self._tree_with_prompts([
762
+ {
763
+ "kind": "PROMPT_TEMPLATE",
764
+ "api_name": "MissingTpl",
765
+ "_body_available": False,
766
+ "children": [],
767
+ },
768
+ ])
769
+ tree_path = _write_tree(self.tmp, tree)
770
+ out = self.tmp / "architecture.md"
771
+ render_architecture.render(tree_path, out)
772
+ text = out.read_text()
773
+
774
+ self.assertIn("#### `MissingTpl`", text)
775
+ import re
776
+ m = re.search(
777
+ r"#### `MissingTpl`\n\n(.*?)(?:\n#### |\n### |\n## |\Z)",
778
+ text, re.DOTALL,
779
+ )
780
+ self.assertIsNotNone(m)
781
+ block = m.group(1)
782
+ self.assertIn("_Body not retrieved._", block)
783
+ # No content fence; no Inputs block.
784
+ self.assertNotIn("```text", block)
785
+ self.assertNotIn("**Inputs**:", block)
786
+
787
+ def test_inputs_without_datatype_still_render(self):
788
+ """Older templates may omit <dataType> on <inputs>. Render the
789
+ name alone rather than crashing or dropping the input."""
790
+ tree = self._tree_with_prompts([
791
+ {
792
+ "kind": "PROMPT_TEMPLATE",
793
+ "api_name": "LegacyTpl",
794
+ "content": "body",
795
+ "inputs": [{"name": "Query"}],
796
+ "_body_available": True,
797
+ "children": [],
798
+ },
799
+ ])
800
+ tree_path = _write_tree(self.tmp, tree)
801
+ out = self.tmp / "architecture.md"
802
+ render_architecture.render(tree_path, out)
803
+ text = out.read_text()
804
+
805
+ self.assertIn("**Inputs**:", text)
806
+ self.assertIn("- `Query`", text)
807
+
808
+
809
+ if __name__ == "__main__":
810
+ unittest.main()