@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,307 @@
1
+ """cache_check.py schema-version mismatch → delete-on-read + MISS.
2
+
3
+ Covers:
4
+ * manifest.schema_version == config.SCHEMA_VERSION → HIT
5
+ * manifest.schema_version != config.SCHEMA_VERSION → MISS +
6
+ CACHE_INVALIDATED_REASON=schema-version-mismatch + cache dir deleted
7
+ * missing manifest → MISS (existing behavior preserved)
8
+ * expired TTL + correct schema → MISS + dir NOT deleted
9
+ * _safe_rmtree_under_cache_root refuses paths outside CACHE_ROOT
10
+ * module import raises if CACHE_ROOT is a symlink
11
+
12
+ The script reads env vars + writes eval-able K=V lines to stdout +
13
+ exits 0 on both HIT and MISS. We drive it via subprocess so we exercise
14
+ the real stdout stream and exit code.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import datetime as dt
19
+ import importlib
20
+ import json
21
+ import os
22
+ import pathlib
23
+ import shutil
24
+ import subprocess
25
+ import sys
26
+ import tempfile
27
+ import unittest
28
+
29
+ from . import _bootstrap # noqa: F401
30
+
31
+ import config # type: ignore
32
+ import cache_check # type: ignore
33
+
34
+
35
+ SCRIPTS_DIR = pathlib.Path(__file__).resolve().parent.parent
36
+ CACHE_CHECK = SCRIPTS_DIR / "cache_check.py"
37
+
38
+
39
+ def _parse_kv(stdout: str) -> dict[str, str]:
40
+ """Parse the shlex-quoted K=V lines cache_check emits.
41
+
42
+ cache_check writes one `K=V` per line, V shlex-quoted. For our tests
43
+ we don't need full shell parsing — we just strip one surrounding
44
+ single-quote layer if present.
45
+ """
46
+ out: dict[str, str] = {}
47
+ for line in stdout.splitlines():
48
+ if "=" not in line:
49
+ continue
50
+ k, _, v = line.partition("=")
51
+ if v.startswith("'") and v.endswith("'") and len(v) >= 2:
52
+ v = v[1:-1]
53
+ out[k] = v
54
+ return out
55
+
56
+
57
+ class CacheCheckSchemaVersionTests(unittest.TestCase):
58
+ """strict schema-version match, delete-on-mismatch."""
59
+
60
+ def setUp(self) -> None:
61
+ # Isolated tempdir mirroring CACHE_ROOT so we can safely rmtree
62
+ # anything under it. We override config.CACHE_ROOT for the
63
+ # duration of each test — the script reads config.CACHE_ROOT at
64
+ # rmtree-validation time, and we drive the script via subprocess
65
+ # with CACHE_ROOT propagated through env only via a wrapper
66
+ # below (subprocess-style overrides).
67
+ self.tmp = tempfile.TemporaryDirectory()
68
+ self.root = pathlib.Path(self.tmp.name).resolve()
69
+ self.cache_root = self.root / "cache"
70
+ self.data_root = self.root / "data"
71
+ self.work_dir = self.root / "work"
72
+ self.cache_root.mkdir()
73
+ self.data_root.mkdir()
74
+ self.work_dir.mkdir()
75
+ # Agent-keyed sub-path under cache_root.
76
+ self.agent = "TestAgent_v1"
77
+ self.version = "v1"
78
+ self.cache_dir = self.cache_root / "00D000000000ABC" / f"{self.agent}__{self.version}"
79
+ self.data_dir = self.data_root / "00D000000000ABC" / f"{self.agent}__{self.version}"
80
+ self.cache_dir.mkdir(parents=True)
81
+ # Data file manifest points at — must exist for the HIT path.
82
+ self.data_file = self.cache_dir / "metadata_tree.json"
83
+ self.data_file.write_text(json.dumps({"root": {"kind": "Bot"}}))
84
+ # summary.md dropped from the output contract — no
85
+ # longer written by cache seeding.
86
+
87
+ def tearDown(self) -> None:
88
+ self.tmp.cleanup()
89
+
90
+ def _write_manifest(self, schema_version: str, age_days: int = 0) -> None:
91
+ built = dt.datetime.now(dt.timezone.utc) - dt.timedelta(days=age_days)
92
+ manifest = {
93
+ "schema_version": schema_version,
94
+ "data_path": str(self.data_file),
95
+ "built_at_utc": built.isoformat().replace("+00:00", "Z"),
96
+ "ttl_days": 7,
97
+ "node_count": 1,
98
+ "depth": 1,
99
+ "agent": {
100
+ "generation": "Einstein",
101
+ "bot_id": "0Xx000000000000",
102
+ "master_label": "TestAgent",
103
+ "planner_name": "p1",
104
+ },
105
+ "kind_counts": {"Bot": 1},
106
+ "partial": False,
107
+ "unresolved_count": 0,
108
+ }
109
+ (self.cache_dir / "manifest.json").write_text(json.dumps(manifest))
110
+
111
+ def _run_with_patched_cache_root(self, extra_env: dict | None = None) -> subprocess.CompletedProcess:
112
+ """Patch config.CACHE_ROOT via a small wrapper — simpler than the
113
+ HOME-redirect dance and keeps the test local to the module under
114
+ test. We write a tiny entry-point that monkeypatches config
115
+ before invoking cache_check.main, then runs it.
116
+ """
117
+ wrapper = self.root / "wrapper.py"
118
+ wrapper.write_text(
119
+ "import sys; sys.path.insert(0, %r); sys.path.insert(0, %r)\n"
120
+ "import config\n"
121
+ "config.CACHE_ROOT = __import__('pathlib').Path(%r)\n"
122
+ "import cache_check\n"
123
+ "sys.exit(cache_check.main())\n"
124
+ % (str(SCRIPTS_DIR), str(SCRIPTS_DIR.parent / 'tools'), str(self.cache_root))
125
+ )
126
+ env = os.environ.copy()
127
+ env.update({
128
+ "CACHE_DIR": str(self.cache_dir),
129
+ "DATA_DIR": str(self.data_dir),
130
+ "WORK_DIR": str(self.work_dir),
131
+ "AGENT_API_NAME": self.agent,
132
+ "AGENT_VERSION": self.version,
133
+ })
134
+ if extra_env:
135
+ env.update(extra_env)
136
+ return subprocess.run(
137
+ [sys.executable, str(wrapper)],
138
+ env=env,
139
+ capture_output=True,
140
+ text=True,
141
+ timeout=10,
142
+ )
143
+
144
+ def test_matching_schema_version_hits(self):
145
+ self._write_manifest(config.SCHEMA_VERSION)
146
+ cp = self._run_with_patched_cache_root()
147
+ self.assertEqual(cp.returncode, 0, cp.stderr)
148
+ kv = _parse_kv(cp.stdout)
149
+ self.assertEqual(kv.get("CACHE_HIT"), "true", cp.stdout)
150
+ self.assertNotIn("CACHE_INVALIDATED_REASON", kv)
151
+ # Cache dir survives.
152
+ self.assertTrue(self.cache_dir.exists())
153
+
154
+ def test_legacy_schema_version_misses_and_deletes(self):
155
+ """schema_version=2.4 (legacy) != 3.0 (current) → MISS + rmtree."""
156
+ self._write_manifest("2.4")
157
+ self.assertTrue(self.cache_dir.exists())
158
+ cp = self._run_with_patched_cache_root()
159
+ self.assertEqual(cp.returncode, 0, cp.stderr)
160
+ kv = _parse_kv(cp.stdout)
161
+ self.assertEqual(kv.get("CACHE_HIT"), "false")
162
+ self.assertEqual(kv.get("CACHE_INVALIDATED_REASON"), "schema-version-mismatch")
163
+ # Cache dir deleted by the safety-gated rmtree.
164
+ self.assertFalse(self.cache_dir.exists(),
165
+ "cache_dir must be removed on schema-version mismatch")
166
+
167
+ def test_unexpected_future_schema_version_misses_and_deletes(self):
168
+ """Any value != current SCHEMA_VERSION invalidates (not just legacy)."""
169
+ self._write_manifest("99.99")
170
+ cp = self._run_with_patched_cache_root()
171
+ kv = _parse_kv(cp.stdout)
172
+ self.assertEqual(kv.get("CACHE_HIT"), "false")
173
+ self.assertEqual(kv.get("CACHE_INVALIDATED_REASON"), "schema-version-mismatch")
174
+ self.assertFalse(self.cache_dir.exists())
175
+
176
+ def test_missing_manifest_misses_no_deletion(self):
177
+ """Existing behavior: no manifest → MISS, NO rmtree, no reason emit."""
178
+ # Don't write any manifest.
179
+ cp = self._run_with_patched_cache_root()
180
+ self.assertEqual(cp.returncode, 0, cp.stderr)
181
+ kv = _parse_kv(cp.stdout)
182
+ self.assertEqual(kv.get("CACHE_HIT"), "false")
183
+ # Missing manifest is a different reason class — no invalidation
184
+ # reason emitted here (cache is simply cold, not corrupt).
185
+ self.assertNotIn("CACHE_INVALIDATED_REASON", kv)
186
+ self.assertTrue(self.cache_dir.exists(),
187
+ "missing manifest must NOT trigger rmtree")
188
+
189
+ def test_expired_ttl_with_matching_schema_misses_no_deletion(self):
190
+ """TTL-expired cache with CURRENT schema → MISS but dir stays intact."""
191
+ self._write_manifest(config.SCHEMA_VERSION, age_days=30) # 30 > 7
192
+ cp = self._run_with_patched_cache_root()
193
+ kv = _parse_kv(cp.stdout)
194
+ self.assertEqual(kv.get("CACHE_HIT"), "false")
195
+ # No schema-mismatch reason — it's a stale cache, not a corrupt one.
196
+ self.assertNotIn("CACHE_INVALIDATED_REASON", kv)
197
+ self.assertTrue(self.cache_dir.exists(),
198
+ "expired TTL must NOT rmtree — just mark stale")
199
+
200
+
201
+ class SafeRmtreeUnderCacheRootTests(unittest.TestCase):
202
+ """defensive: `_safe_rmtree_under_cache_root` refuses out-of-root paths."""
203
+
204
+ def setUp(self) -> None:
205
+ self.tmp = tempfile.TemporaryDirectory()
206
+ self.root = pathlib.Path(self.tmp.name).resolve()
207
+ self.cache_root = self.root / "cache"
208
+ self.cache_root.mkdir()
209
+ # In-scope victim dir.
210
+ self.in_scope = self.cache_root / "agent_v1"
211
+ self.in_scope.mkdir()
212
+ (self.in_scope / "some.txt").write_text("x")
213
+ # Out-of-scope victim dir (sibling to cache_root).
214
+ self.out_of_scope = self.root / "other"
215
+ self.out_of_scope.mkdir()
216
+ (self.out_of_scope / "important.txt").write_text("do not delete")
217
+
218
+ # Monkeypatch config.CACHE_ROOT for the duration of each test.
219
+ self._orig_root = config.CACHE_ROOT
220
+ config.CACHE_ROOT = self.cache_root
221
+
222
+ def tearDown(self) -> None:
223
+ config.CACHE_ROOT = self._orig_root
224
+ self.tmp.cleanup()
225
+
226
+ def test_rmtree_under_cache_root_succeeds(self):
227
+ ok = cache_check._safe_rmtree_under_cache_root(self.in_scope)
228
+ self.assertTrue(ok)
229
+ self.assertFalse(self.in_scope.exists())
230
+
231
+ def test_rmtree_outside_cache_root_refused(self):
232
+ ok = cache_check._safe_rmtree_under_cache_root(self.out_of_scope)
233
+ self.assertFalse(ok, "out-of-root path must be refused")
234
+ # File still present.
235
+ self.assertTrue((self.out_of_scope / "important.txt").exists())
236
+
237
+ def test_nonexistent_under_root_is_noop_success(self):
238
+ """A caller asking to delete a path that doesn't exist under the
239
+ root gets a silent success — matches rmtree's semantics for
240
+ already-gone targets and avoids spurious error branches on the
241
+ miss() path."""
242
+ ghost = self.cache_root / "never_existed"
243
+ ok = cache_check._safe_rmtree_under_cache_root(ghost)
244
+ self.assertTrue(ok)
245
+
246
+
247
+ class SymlinkedCacheRootRejectedAtImportTests(unittest.TestCase):
248
+ """module import raises if CACHE_ROOT is a symlink.
249
+
250
+ A symlinked CACHE_ROOT would defeat the rmtree safety gate — both
251
+ sides of `target.resolve().is_relative_to(root.resolve())` would
252
+ collapse to the same symlink target, so the gate would approve
253
+ deletion of arbitrary paths the attacker aimed the symlink at.
254
+ The defence is a fail-fast guard at cache_check import time.
255
+ """
256
+
257
+ def setUp(self) -> None:
258
+ self.tmp = tempfile.TemporaryDirectory()
259
+ self.root = pathlib.Path(self.tmp.name).resolve()
260
+ self.real_target = self.root / "evil_target"
261
+ self.real_target.mkdir()
262
+ self.symlinked_root = self.root / "symlinked_cache_root"
263
+ self.symlinked_root.symlink_to(self.real_target)
264
+ self._orig_root = config.CACHE_ROOT
265
+
266
+ def tearDown(self) -> None:
267
+ # Restore and reload so the rest of the suite sees a clean module.
268
+ config.CACHE_ROOT = self._orig_root
269
+ importlib.reload(cache_check)
270
+ self.tmp.cleanup()
271
+
272
+ def test_symlinked_cache_root_raises_on_import(self):
273
+ """Reloading cache_check with a symlinked CACHE_ROOT must raise
274
+ RuntimeError — proof that the guard actually fires and the fix
275
+ is load-bearing."""
276
+ config.CACHE_ROOT = self.symlinked_root
277
+ with self.assertRaises(RuntimeError) as ctx:
278
+ importlib.reload(cache_check)
279
+ msg = str(ctx.exception)
280
+ # Message must name the offending path so operators can fix it
281
+ # without spelunking the source. Don't over-specify wording —
282
+ # future maintainers may clarify the phrasing.
283
+ self.assertIn(str(self.symlinked_root), msg)
284
+ self.assertIn("symlink", msg.lower())
285
+
286
+ def test_real_directory_cache_root_passes(self):
287
+ """Sanity: the guard is not over-broad — a real directory (the
288
+ common case) reloads cleanly."""
289
+ real_dir = self.root / "real_cache_root"
290
+ real_dir.mkdir()
291
+ config.CACHE_ROOT = real_dir
292
+ # No raise.
293
+ importlib.reload(cache_check)
294
+
295
+ def test_nonexistent_cache_root_passes(self):
296
+ """Pristine install: CACHE_ROOT hasn't been created yet.
297
+ Path.is_symlink() returns False on non-existent paths, so import
298
+ must succeed — the directory will be materialized on first write."""
299
+ ghost = self.root / "does_not_exist_yet"
300
+ self.assertFalse(ghost.exists())
301
+ config.CACHE_ROOT = ghost
302
+ # No raise.
303
+ importlib.reload(cache_check)
304
+
305
+
306
+ if __name__ == "__main__":
307
+ unittest.main()
@@ -0,0 +1,283 @@
1
+ """In-process tests for ``cache_check.main``.
2
+
3
+ The existing ``test_cache_check.py`` drives main via subprocess (so its
4
+ Python branches don't show up in coverage). This file calls main()
5
+ directly, patching ``os.environ`` + ``config.CACHE_ROOT`` so all the
6
+ hit/miss decision branches register.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import datetime as dt
11
+ import io
12
+ import json
13
+ import os
14
+ import unittest
15
+ from pathlib import Path
16
+ from tempfile import TemporaryDirectory
17
+ from unittest import mock
18
+
19
+ from . import _bootstrap # noqa: F401 — sys.path setup
20
+
21
+ import cache_check # type: ignore
22
+ import config # type: ignore
23
+
24
+
25
+ def _build_valid_manifest(*, data_path: str,
26
+ built_at_utc: str | None = None,
27
+ schema_version: str = "3.1",
28
+ node_count: int = 5,
29
+ depth: int = 2,
30
+ ttl_days: int = 7,
31
+ partial: bool = False,
32
+ unresolved_count: int = 0,
33
+ generation: str = "nga",
34
+ bot_id: str = "0Xx000000000Demo",
35
+ master_label: str = "Demo Agent",
36
+ version_auto_picked: bool = False,
37
+ planner_name: str = "DemoPlanner",
38
+ kind_counts: dict | None = None) -> dict:
39
+ if built_at_utc is None:
40
+ built_at_utc = (
41
+ dt.datetime.now(dt.timezone.utc)
42
+ .isoformat().replace("+00:00", "Z")
43
+ )
44
+ return {
45
+ "schema_version": schema_version,
46
+ "built_at_utc": built_at_utc,
47
+ "node_count": node_count,
48
+ "depth": depth,
49
+ "ttl_days": ttl_days,
50
+ "data_path": data_path,
51
+ "partial": partial,
52
+ "unresolved_count": unresolved_count,
53
+ "agent": {
54
+ "generation": generation,
55
+ "bot_id": bot_id,
56
+ "master_label": master_label,
57
+ "_version_auto_picked": version_auto_picked,
58
+ "planner_name": planner_name,
59
+ },
60
+ "kind_counts": kind_counts or {"TOPIC": 2, "STANDARD_ACTION": 3},
61
+ }
62
+
63
+
64
+ class _CacheHarness:
65
+ """Patch CACHE_ROOT + os.environ + capture stdout."""
66
+
67
+ def __init__(self, *, env_overrides: dict | None = None,
68
+ manifest: dict | None = None,
69
+ plant_data_file: bool = True):
70
+ self.env_overrides = env_overrides or {}
71
+ self.manifest = manifest
72
+ self.plant_data_file = plant_data_file
73
+
74
+ def __enter__(self):
75
+ self._tmp = TemporaryDirectory()
76
+ self.tmpdir = Path(self._tmp.name)
77
+ # Build a 4-deep CACHE_ROOT so cache_dir lives strictly inside it
78
+ # (the rmtree guard refuses paths NOT under CACHE_ROOT).
79
+ self.cache_root = self.tmpdir / "cache" / "investigating-agentforce-architecture"
80
+ self.cache_root.mkdir(parents=True)
81
+ self.cache_dir = self.cache_root / "ALPHA0000000000" / "MyAgent__v1"
82
+ self.cache_dir.mkdir(parents=True)
83
+ self.data_dir = self.tmpdir / "data" / "ALPHA0000000000" / "MyAgent__v1"
84
+ self.work_dir = self.tmpdir / "work"
85
+ self.work_dir.mkdir(parents=True)
86
+
87
+ # Plant the authoritative tree file UNDER cache_dir (where
88
+ # finalize.py originally wrote it). cache_check copies it INTO
89
+ # data_dir on hit, so source and dest must be different files.
90
+ self.tree_path = self.cache_dir / "MyAgent_v1_metadata_tree.json"
91
+ if self.plant_data_file:
92
+ self.tree_path.write_text(json.dumps({
93
+ "agent": {"api_name": "MyAgent", "version": "v1"},
94
+ "session": {"_schema_version": 1},
95
+ }))
96
+
97
+ # Plant the manifest under cache_dir
98
+ if self.manifest is not None:
99
+ (self.cache_dir / "manifest.json").write_text(
100
+ json.dumps(self.manifest)
101
+ )
102
+ elif self.manifest is None and self.plant_data_file:
103
+ # Default valid manifest pointing at the planted tree
104
+ (self.cache_dir / "manifest.json").write_text(json.dumps(
105
+ _build_valid_manifest(data_path=str(self.tree_path))
106
+ ))
107
+
108
+ self._env = {
109
+ "CACHE_DIR": str(self.cache_dir),
110
+ "DATA_DIR": str(self.data_dir),
111
+ "WORK_DIR": str(self.work_dir),
112
+ "AGENT_API_NAME": "MyAgent",
113
+ "AGENT_VERSION": "v1",
114
+ }
115
+ self._env.update(self.env_overrides)
116
+
117
+ self._saved_env = dict(os.environ)
118
+ os.environ.update(self._env)
119
+
120
+ self._patches = [
121
+ mock.patch.object(config, "CACHE_ROOT", self.cache_root),
122
+ ]
123
+ for p in self._patches:
124
+ p.start()
125
+ return self
126
+
127
+ def __exit__(self, *exc):
128
+ for p in self._patches:
129
+ p.stop()
130
+ # Restore env
131
+ for k in self._env:
132
+ os.environ.pop(k, None)
133
+ os.environ.update(self._saved_env)
134
+ self._tmp.cleanup()
135
+
136
+
137
+ def _run_main_capture() -> tuple[int, str]:
138
+ """Call main() with stdout captured. Translates SystemExit into rc."""
139
+ buf = io.StringIO()
140
+ rc: int
141
+ try:
142
+ with mock.patch("sys.stdout", buf):
143
+ rc = cache_check.main()
144
+ except SystemExit as e:
145
+ rc = e.code if isinstance(e.code, int) else 0
146
+ return rc, buf.getvalue()
147
+
148
+
149
+ # -----------------------------------------------------------------------------
150
+ # Hit path
151
+ # -----------------------------------------------------------------------------
152
+
153
+
154
+ class CacheHitTests(unittest.TestCase):
155
+
156
+ def test_hit_emits_cache_hit_true_with_full_export_block(self):
157
+ with _CacheHarness():
158
+ rc, out = _run_main_capture()
159
+ self.assertEqual(rc, 0)
160
+ self.assertIn("CACHE_HIT=true", out)
161
+ # Full hit block has these signature fields
162
+ self.assertIn("CACHED_AT_UTC=", out)
163
+ self.assertIn("NODE_COUNT=", out)
164
+ self.assertIn("DEPTH=", out)
165
+ self.assertIn("AGENT_GENERATION=", out)
166
+ self.assertIn("BOT_ID=", out)
167
+ self.assertIn("PLANNER_NAME=", out)
168
+
169
+ def test_hit_copies_tree_into_data_dir(self):
170
+ with _CacheHarness() as h:
171
+ rc, _ = _run_main_capture()
172
+ self.assertEqual(rc, 0)
173
+ dst = h.data_dir / "MyAgent_v1_metadata_tree.json"
174
+ # File search across resolved tree to dodge /private/var ↔ /var.
175
+ written = list(h.tmpdir.rglob("MyAgent_v1_metadata_tree.json"))
176
+ self.assertGreaterEqual(len(written), 1)
177
+
178
+ def test_hit_stages_declared_action_tree_in_work_dir(self):
179
+ with _CacheHarness() as h:
180
+ _run_main_capture()
181
+ staged = list(h.tmpdir.rglob("declared_action_tree.json"))
182
+ self.assertGreaterEqual(len(staged), 1)
183
+
184
+ def test_hit_emits_kind_counts_with_kc_prefix(self):
185
+ with _CacheHarness():
186
+ _, out = _run_main_capture()
187
+ self.assertIn("KC_TOPIC=", out)
188
+ self.assertIn("KC_STANDARD_ACTION=", out)
189
+
190
+
191
+ # -----------------------------------------------------------------------------
192
+ # Miss paths (each one exits 0 with CACHE_HIT=false)
193
+ # -----------------------------------------------------------------------------
194
+
195
+
196
+ class CacheMissTests(unittest.TestCase):
197
+
198
+ def test_miss_when_force_refresh_true(self):
199
+ with _CacheHarness(env_overrides={"FORCE_REFRESH": "true"}):
200
+ rc, out = _run_main_capture()
201
+ self.assertEqual(rc, 0)
202
+ self.assertIn("CACHE_HIT=false", out)
203
+
204
+ def test_miss_when_manifest_missing(self):
205
+ # Build harness without auto-planted manifest
206
+ with _CacheHarness(manifest={}, plant_data_file=False) as h:
207
+ (h.cache_dir / "manifest.json").unlink()
208
+ rc, out = _run_main_capture()
209
+ self.assertEqual(rc, 0)
210
+ self.assertIn("CACHE_HIT=false", out)
211
+
212
+ def test_miss_when_manifest_malformed(self):
213
+ with _CacheHarness(manifest={}) as h:
214
+ (h.cache_dir / "manifest.json").write_text("<<<not json>>>")
215
+ rc, out = _run_main_capture()
216
+ self.assertEqual(rc, 0)
217
+ self.assertIn("CACHE_HIT=false", out)
218
+
219
+ def test_miss_with_invalidated_reason_on_schema_mismatch(self):
220
+ # Schema version != config.SCHEMA_VERSION → rmtree + miss
221
+ with _CacheHarness(manifest=_build_valid_manifest(
222
+ data_path="/tmp/x", schema_version="2.0",
223
+ )) as h:
224
+ rc, out = _run_main_capture()
225
+ self.assertEqual(rc, 0)
226
+ self.assertIn("CACHE_HIT=false", out)
227
+ self.assertIn("CACHE_INVALIDATED_REASON=schema-version-mismatch", out)
228
+ # Cache dir got rmtree'd
229
+ self.assertFalse(h.cache_dir.exists())
230
+
231
+ def test_miss_when_data_path_missing(self):
232
+ with _CacheHarness(manifest=_build_valid_manifest(
233
+ data_path="/nope/not-a-real-path.json",
234
+ )):
235
+ rc, out = _run_main_capture()
236
+ self.assertEqual(rc, 0)
237
+ self.assertIn("CACHE_HIT=false", out)
238
+
239
+ def test_miss_when_built_at_unparseable(self):
240
+ with _CacheHarness() as h:
241
+ (h.cache_dir / "manifest.json").write_text(json.dumps(
242
+ _build_valid_manifest(
243
+ data_path=str(h.tree_path),
244
+ built_at_utc="not-a-timestamp",
245
+ ),
246
+ ))
247
+ rc, out = _run_main_capture()
248
+ self.assertEqual(rc, 0)
249
+ self.assertIn("CACHE_HIT=false", out)
250
+
251
+ def test_miss_when_ttl_expired(self):
252
+ # built_at_utc is far in the past + ttl_days=1 → age > ttl → miss
253
+ ancient = (dt.datetime.now(dt.timezone.utc)
254
+ - dt.timedelta(days=30)).isoformat().replace("+00:00", "Z")
255
+ with _CacheHarness(manifest=None) as h:
256
+ (h.cache_dir / "manifest.json").write_text(json.dumps(
257
+ _build_valid_manifest(
258
+ data_path=str(h.tree_path),
259
+ built_at_utc=ancient,
260
+ ttl_days=1,
261
+ ),
262
+ ))
263
+ rc, out = _run_main_capture()
264
+ self.assertEqual(rc, 0)
265
+ self.assertIn("CACHE_HIT=false", out)
266
+
267
+ def test_miss_when_required_env_missing(self):
268
+ with _CacheHarness() as h:
269
+ os.environ.pop("CACHE_DIR", None)
270
+ buf = io.StringIO()
271
+ rc: int
272
+ try:
273
+ with mock.patch("sys.stdout", buf):
274
+ with mock.patch("sys.stderr", io.StringIO()):
275
+ rc = cache_check.main()
276
+ except SystemExit as e:
277
+ rc = e.code if isinstance(e.code, int) else 0
278
+ self.assertEqual(rc, 0)
279
+ self.assertIn("CACHE_HIT=false", buf.getvalue())
280
+
281
+
282
+ if __name__ == "__main__":
283
+ unittest.main()