@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.
- package/package.json +1 -1
- package/skills/activating-datacloud/SKILL.md +0 -1
- package/skills/analyzing-omnistudio-dependencies/SKILL.md +0 -1
- package/skills/applying-slds/SKILL.md +322 -0
- package/skills/applying-slds/checklists.md +83 -0
- package/skills/applying-slds/examples.md +283 -0
- package/skills/applying-slds/guidance/README.md +83 -0
- package/skills/applying-slds/guidance/blueprints-index.md +213 -0
- package/skills/applying-slds/guidance/icons-guidance.md +186 -0
- package/skills/applying-slds/guidance/overviews/borders.md +236 -0
- package/skills/applying-slds/guidance/overviews/color.md +266 -0
- package/skills/applying-slds/guidance/overviews/display-density.md +366 -0
- package/skills/applying-slds/guidance/overviews/icons.md +240 -0
- package/skills/applying-slds/guidance/overviews/illustrations.md +235 -0
- package/skills/applying-slds/guidance/overviews/shadows.md +176 -0
- package/skills/applying-slds/guidance/overviews/spacing.md +216 -0
- package/skills/applying-slds/guidance/overviews/typography.md +323 -0
- package/skills/applying-slds/guidance/overviews/utilities.md +542 -0
- package/skills/applying-slds/guidance/slds-development-guide.md +288 -0
- package/skills/applying-slds/guidance/styling-hooks/borders.md +202 -0
- package/skills/applying-slds/guidance/styling-hooks/color/expressive-palette-hooks.md +153 -0
- package/skills/applying-slds/guidance/styling-hooks/color/index.md +171 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/accent-hooks.md +204 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/feedback-hooks.md +768 -0
- package/skills/applying-slds/guidance/styling-hooks/color/semantic/surface-hooks.md +337 -0
- package/skills/applying-slds/guidance/styling-hooks/color/system-hooks.md +132 -0
- package/skills/applying-slds/guidance/styling-hooks/index.md +327 -0
- package/skills/applying-slds/guidance/styling-hooks/shadows.md +238 -0
- package/skills/applying-slds/guidance/styling-hooks/spacing.md +254 -0
- package/skills/applying-slds/guidance/styling-hooks/typography.md +448 -0
- package/skills/applying-slds/guidance/utilities/alignment.md +119 -0
- package/skills/applying-slds/guidance/utilities/borders.md +131 -0
- package/skills/applying-slds/guidance/utilities/box.md +125 -0
- package/skills/applying-slds/guidance/utilities/color.md +165 -0
- package/skills/applying-slds/guidance/utilities/dark-mode.md +111 -0
- package/skills/applying-slds/guidance/utilities/description-list.md +168 -0
- package/skills/applying-slds/guidance/utilities/floats.md +117 -0
- package/skills/applying-slds/guidance/utilities/grid.md +264 -0
- package/skills/applying-slds/guidance/utilities/horizontal-list.md +110 -0
- package/skills/applying-slds/guidance/utilities/hyphenation.md +84 -0
- package/skills/applying-slds/guidance/utilities/index.md +205 -0
- package/skills/applying-slds/guidance/utilities/interactions.md +89 -0
- package/skills/applying-slds/guidance/utilities/layout.md +109 -0
- package/skills/applying-slds/guidance/utilities/line-clamp.md +131 -0
- package/skills/applying-slds/guidance/utilities/margin.md +155 -0
- package/skills/applying-slds/guidance/utilities/media-object.md +161 -0
- package/skills/applying-slds/guidance/utilities/name-value-list.md +152 -0
- package/skills/applying-slds/guidance/utilities/padding.md +155 -0
- package/skills/applying-slds/guidance/utilities/position.md +177 -0
- package/skills/applying-slds/guidance/utilities/print.md +114 -0
- package/skills/applying-slds/guidance/utilities/scrollable.md +126 -0
- package/skills/applying-slds/guidance/utilities/sizing.md +190 -0
- package/skills/applying-slds/guidance/utilities/themes.md +121 -0
- package/skills/applying-slds/guidance/utilities/truncate.md +127 -0
- package/skills/applying-slds/guidance/utilities/typography.md +166 -0
- package/skills/applying-slds/guidance/utilities/vertical-list.md +166 -0
- package/skills/applying-slds/guidance/utilities/visibility.md +228 -0
- package/skills/applying-slds/metadata/README.md +84 -0
- package/skills/applying-slds/metadata/blueprints/components/accordion.yaml +304 -0
- package/skills/applying-slds/metadata/blueprints/components/activity-timeline.yaml +92 -0
- package/skills/applying-slds/metadata/blueprints/components/alert.yaml +103 -0
- package/skills/applying-slds/metadata/blueprints/components/app-launcher.yaml +94 -0
- package/skills/applying-slds/metadata/blueprints/components/avatar-group.yaml +81 -0
- package/skills/applying-slds/metadata/blueprints/components/avatar.yaml +97 -0
- package/skills/applying-slds/metadata/blueprints/components/badges.yaml +102 -0
- package/skills/applying-slds/metadata/blueprints/components/brand-band.yaml +198 -0
- package/skills/applying-slds/metadata/blueprints/components/breadcrumbs.yaml +95 -0
- package/skills/applying-slds/metadata/blueprints/components/builder-header.yaml +192 -0
- package/skills/applying-slds/metadata/blueprints/components/button-groups.yaml +82 -0
- package/skills/applying-slds/metadata/blueprints/components/button-icons.yaml +295 -0
- package/skills/applying-slds/metadata/blueprints/components/buttons.yaml +230 -0
- package/skills/applying-slds/metadata/blueprints/components/cards.yaml +124 -0
- package/skills/applying-slds/metadata/blueprints/components/carousel.yaml +140 -0
- package/skills/applying-slds/metadata/blueprints/components/chat.yaml +179 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-button-group.yaml +192 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-button.yaml +204 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox-toggle.yaml +177 -0
- package/skills/applying-slds/metadata/blueprints/components/checkbox.yaml +108 -0
- package/skills/applying-slds/metadata/blueprints/components/color-picker.yaml +172 -0
- package/skills/applying-slds/metadata/blueprints/components/combobox.yaml +136 -0
- package/skills/applying-slds/metadata/blueprints/components/counter.yaml +147 -0
- package/skills/applying-slds/metadata/blueprints/components/data-tables.yaml +157 -0
- package/skills/applying-slds/metadata/blueprints/components/datepickers.yaml +130 -0
- package/skills/applying-slds/metadata/blueprints/components/datetime-picker.yaml +155 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-composer.yaml +201 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-form-footer.yaml +161 -0
- package/skills/applying-slds/metadata/blueprints/components/docked-utility-bar.yaml +175 -0
- package/skills/applying-slds/metadata/blueprints/components/drop-zone.yaml +115 -0
- package/skills/applying-slds/metadata/blueprints/components/dueling-picklist.yaml +196 -0
- package/skills/applying-slds/metadata/blueprints/components/dynamic-icons.yaml +128 -0
- package/skills/applying-slds/metadata/blueprints/components/dynamic-menu.yaml +141 -0
- package/skills/applying-slds/metadata/blueprints/components/expandable-section.yaml +115 -0
- package/skills/applying-slds/metadata/blueprints/components/expression.yaml +143 -0
- package/skills/applying-slds/metadata/blueprints/components/feeds.yaml +125 -0
- package/skills/applying-slds/metadata/blueprints/components/file-selector.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/files.yaml +119 -0
- package/skills/applying-slds/metadata/blueprints/components/form-element.yaml +145 -0
- package/skills/applying-slds/metadata/blueprints/components/global-header.yaml +120 -0
- package/skills/applying-slds/metadata/blueprints/components/global-navigation.yaml +100 -0
- package/skills/applying-slds/metadata/blueprints/components/icons.yaml +138 -0
- package/skills/applying-slds/metadata/blueprints/components/illustration.yaml +205 -0
- package/skills/applying-slds/metadata/blueprints/components/input.yaml +151 -0
- package/skills/applying-slds/metadata/blueprints/components/list-builder.yaml +127 -0
- package/skills/applying-slds/metadata/blueprints/components/lookups.yaml +132 -0
- package/skills/applying-slds/metadata/blueprints/components/map.yaml +118 -0
- package/skills/applying-slds/metadata/blueprints/components/menus.yaml +134 -0
- package/skills/applying-slds/metadata/blueprints/components/modals.yaml +152 -0
- package/skills/applying-slds/metadata/blueprints/components/notifications.yaml +88 -0
- package/skills/applying-slds/metadata/blueprints/components/page-headers.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/panels.yaml +149 -0
- package/skills/applying-slds/metadata/blueprints/components/path.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/picklist.yaml +125 -0
- package/skills/applying-slds/metadata/blueprints/components/pills.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/popovers.yaml +120 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-bar.yaml +110 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-indicator.yaml +133 -0
- package/skills/applying-slds/metadata/blueprints/components/progress-ring.yaml +102 -0
- package/skills/applying-slds/metadata/blueprints/components/prompt.yaml +126 -0
- package/skills/applying-slds/metadata/blueprints/components/publishers.yaml +178 -0
- package/skills/applying-slds/metadata/blueprints/components/radio-button-group.yaml +172 -0
- package/skills/applying-slds/metadata/blueprints/components/radio-group.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/rich-text-editor.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/scoped-notifications.yaml +188 -0
- package/skills/applying-slds/metadata/blueprints/components/scoped-tabs.yaml +97 -0
- package/skills/applying-slds/metadata/blueprints/components/select.yaml +127 -0
- package/skills/applying-slds/metadata/blueprints/components/setup-assistant.yaml +152 -0
- package/skills/applying-slds/metadata/blueprints/components/slider.yaml +111 -0
- package/skills/applying-slds/metadata/blueprints/components/spinners.yaml +135 -0
- package/skills/applying-slds/metadata/blueprints/components/split-view.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/summary-detail.yaml +103 -0
- package/skills/applying-slds/metadata/blueprints/components/tabs.yaml +138 -0
- package/skills/applying-slds/metadata/blueprints/components/textarea.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/tiles.yaml +108 -0
- package/skills/applying-slds/metadata/blueprints/components/timepicker.yaml +111 -0
- package/skills/applying-slds/metadata/blueprints/components/toast.yaml +154 -0
- package/skills/applying-slds/metadata/blueprints/components/tooltips.yaml +107 -0
- package/skills/applying-slds/metadata/blueprints/components/tree-grid.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/trees.yaml +116 -0
- package/skills/applying-slds/metadata/blueprints/components/trial-bar.yaml +112 -0
- package/skills/applying-slds/metadata/blueprints/components/vertical-navigation.yaml +130 -0
- package/skills/applying-slds/metadata/blueprints/components/vertical-tabs.yaml +140 -0
- package/skills/applying-slds/metadata/blueprints/components/visual-picker.yaml +150 -0
- package/skills/applying-slds/metadata/blueprints/components/welcome-mat.yaml +136 -0
- package/skills/applying-slds/metadata/hooks-index.json +6272 -0
- package/skills/applying-slds/metadata/icon-metadata.json +38466 -0
- package/skills/applying-slds/metadata/utilities-index.json +21912 -0
- package/skills/applying-slds/references/component-selection.md +112 -0
- package/skills/applying-slds/references/icons-decision-guide.md +124 -0
- package/skills/applying-slds/references/styling-decision-guide.md +228 -0
- package/skills/applying-slds/references/utilities-quick-ref.md +125 -0
- package/skills/applying-slds/scripts/search-blueprints.cjs +117 -0
- package/skills/applying-slds/scripts/search-hooks.cjs +139 -0
- package/skills/applying-slds/scripts/search-icons.cjs +174 -0
- package/skills/applying-slds/scripts/search-utilities.cjs +161 -0
- package/skills/building-mobile-apps/SKILL.md +0 -1
- package/skills/building-omnistudio-callable-apex/SKILL.md +0 -1
- package/skills/building-omnistudio-datamapper/SKILL.md +0 -1
- package/skills/building-omnistudio-flexcard/SKILL.md +0 -1
- package/skills/building-omnistudio-integration-procedure/SKILL.md +0 -1
- package/skills/building-omnistudio-omniscript/SKILL.md +0 -1
- package/skills/building-sf-integrations/SKILL.md +0 -1
- package/skills/configuring-connected-apps/SKILL.md +0 -1
- package/skills/connecting-datacloud/SKILL.md +0 -1
- package/skills/creating-b2b-commerce-store/SKILL.md +0 -1
- package/skills/debugging-apex-logs/SKILL.md +0 -1
- package/skills/deploying-metadata/SKILL.md +0 -1
- package/skills/deploying-omnistudio-datapacks/SKILL.md +0 -1
- package/skills/developing-agentforce/SKILL.md +0 -1
- package/skills/fetching-salesforce-docs/SKILL.md +0 -1
- package/skills/generating-custom-lightning-type/SKILL.md +17 -39
- package/skills/generating-custom-lightning-type/assets/primitive-types-and-constraints.md +41 -0
- package/skills/generating-custom-lightning-type/references/widget-rendition.md +124 -0
- package/skills/generating-lwc-components/SKILL.md +0 -1
- package/skills/generating-mermaid-diagrams/SKILL.md +0 -1
- package/skills/generating-visual-diagrams/SKILL.md +0 -1
- package/skills/handling-sf-data/SKILL.md +0 -1
- package/skills/harmonizing-datacloud/SKILL.md +0 -1
- package/skills/integrating-b2b-commerce-open-code-components/SKILL.md +0 -1
- package/skills/investigating-agentforce-architecture/README.md +156 -0
- package/skills/investigating-agentforce-architecture/SKILL.md +230 -0
- package/skills/investigating-agentforce-architecture/assets/cli/describe_sobject.yaml +16 -0
- package/skills/investigating-agentforce-architecture/assets/cli/describe_tooling_sobject.yaml +17 -0
- package/skills/investigating-agentforce-architecture/assets/cli/list_metadata_genaiprompttemplate.yaml +17 -0
- package/skills/investigating-agentforce-architecture/assets/cli/org_display.yaml +15 -0
- package/skills/investigating-agentforce-architecture/assets/cli/retrieve_genai_plugin.yaml +18 -0
- package/skills/investigating-agentforce-architecture/assets/cli/show_access_token.yaml +27 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/action_tree.mmd +20 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/data_flow.mmd +19 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/dependency_graph.mmd +19 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/invocation_sequence.mmd +20 -0
- package/skills/investigating-agentforce-architecture/assets/mermaid/planner_state.mmd +18 -0
- package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_names.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/bot_definition_details.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/bot_version_lookup.soql +4 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_by_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_ids_by_names.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_view_by_durable_ids.soql +4 -0
- package/skills/investigating-agentforce-architecture/assets/soql/flow_metadata_by_id.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/functions_by_plugins.soql +5 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_attrs_by_parent_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_bundle_functions.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/planner_definition_by_agent_chain.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugin_functions_by_plugin_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugin_instructions_by_plugin_ids.soql +3 -0
- package/skills/investigating-agentforce-architecture/assets/soql/plugins_by_planner.soql +4 -0
- package/skills/investigating-agentforce-architecture/references/architecture_sections.md +243 -0
- package/skills/investigating-agentforce-architecture/references/contract.json +244 -0
- package/skills/investigating-agentforce-architecture/references/soql_fields.md +512 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/__init__.py +1 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/fs_guard.py +329 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/paths.py +110 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/runtime.py +59 -0
- package/skills/investigating-agentforce-architecture/scripts/_shared/sql.py +10 -0
- package/skills/investigating-agentforce-architecture/scripts/cache_check.py +234 -0
- package/skills/investigating-agentforce-architecture/scripts/config.py +131 -0
- package/skills/investigating-agentforce-architecture/scripts/fetch_soql.py +689 -0
- package/skills/investigating-agentforce-architecture/scripts/finalize.py +295 -0
- package/skills/investigating-agentforce-architecture/scripts/main.py +2835 -0
- package/skills/investigating-agentforce-architecture/scripts/metadata_listing.py +265 -0
- package/skills/investigating-agentforce-architecture/scripts/parallel_retrieve.py +69 -0
- package/skills/investigating-agentforce-architecture/scripts/parse_bundle.py +215 -0
- package/skills/investigating-agentforce-architecture/scripts/parse_wave.py +845 -0
- package/skills/investigating-agentforce-architecture/scripts/probe_channels.py +302 -0
- package/skills/investigating-agentforce-architecture/scripts/render_architecture.py +1043 -0
- package/skills/investigating-agentforce-architecture/scripts/resolve_bot.py +255 -0
- package/skills/investigating-agentforce-architecture/scripts/resolve_invocation_target.py +130 -0
- package/skills/investigating-agentforce-architecture/scripts/rest_client.py +763 -0
- package/skills/investigating-agentforce-architecture/scripts/retrieve_planner.py +13 -0
- package/skills/investigating-agentforce-architecture/scripts/sf_cli.py +242 -0
- package/skills/investigating-agentforce-architecture/scripts/soql_loader.py +253 -0
- package/skills/investigating-agentforce-architecture/scripts/summarize_tree.py +143 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/__init__.py +0 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/_bootstrap.py +23 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/__init__.py +0 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/genai_payloads.py +400 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check.py +307 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check_main.py +283 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_config.py +115 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_end_to_end_fixture.py +651 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_finalize.py +278 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_flow_children_inflation.py +582 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_fs_guard.py +113 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_iterative_wave_b.py +478 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_main_pipeline.py +3359 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parallel_retrieve.py +131 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_bundle.py +400 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave.py +644 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py +224 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_helpers.py +380 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_main.py +397 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_per_branch_visited.py +244 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_channels.py +359 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_cli_recipes.py +185 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py +810 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_bot.py +203 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_creds.py +157 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_invocation_target.py +145 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_rest_client.py +1253 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_runtime_override.py +100 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_sf_cli.py +261 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_signature_stamping.py +466 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_soql_loader.py +501 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_summarize_tree.py +241 -0
- package/skills/investigating-agentforce-architecture/scripts/tests/test_write_emit_ctx.py +480 -0
- package/skills/investigating-agentforce-architecture/tools/emit_env.py +157 -0
- package/skills/investigating-agentforce-architecture/tools/emit_result.py +262 -0
- package/skills/investigating-agentforce-architecture/tools/sanitize.py +33 -0
- package/skills/investigating-agentforce-architecture/tools/write_emit_ctx.py +332 -0
- package/skills/investigating-agentforce-d360/README.md +123 -0
- package/skills/investigating-agentforce-d360/SKILL.md +163 -0
- package/skills/investigating-agentforce-d360/assets/dc/app_generation.sql +51 -0
- package/skills/investigating-agentforce-d360/assets/dc/content_category.sql +44 -0
- package/skills/investigating-agentforce-d360/assets/dc/content_quality.sql +41 -0
- package/skills/investigating-agentforce-d360/assets/dc/discover_sessions.sql +36 -0
- package/skills/investigating-agentforce-d360/assets/dc/feedback.sql +47 -0
- package/skills/investigating-agentforce-d360/assets/dc/feedback_details.sql +38 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_records.sql +45 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_llm.sql +50 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_metadata.sql +44 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_request_tags.sql +42 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_requests.sql +89 -0
- package/skills/investigating-agentforce-d360/assets/dc/gateway_responses.sql +43 -0
- package/skills/investigating-agentforce-d360/assets/dc/generations.sql +52 -0
- package/skills/investigating-agentforce-d360/assets/dc/interactions.sql +53 -0
- package/skills/investigating-agentforce-d360/assets/dc/messages.sql +53 -0
- package/skills/investigating-agentforce-d360/assets/dc/messaging_session.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/moment_interactions.sql +34 -0
- package/skills/investigating-agentforce-d360/assets/dc/moments.sql +39 -0
- package/skills/investigating-agentforce-d360/assets/dc/participants.sql +48 -0
- package/skills/investigating-agentforce-d360/assets/dc/sessions.sql +78 -0
- package/skills/investigating-agentforce-d360/assets/dc/steps.sql +64 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_associations.sql +46 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_definition_associations.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/tag_definitions.sql +50 -0
- package/skills/investigating-agentforce-d360/assets/dc/tags.sql +37 -0
- package/skills/investigating-agentforce-d360/assets/dc/telemetry_spans.sql +55 -0
- package/skills/investigating-agentforce-d360/references/artifacts.md +50 -0
- package/skills/investigating-agentforce-d360/references/dc_dmo_fields.md +823 -0
- package/skills/investigating-agentforce-d360/references/dc_pipeline_contract.md +608 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/__init__.py +2 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/cli_override.py +98 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/fs_guard.py +334 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/paths.py +155 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/runtime.py +59 -0
- package/skills/investigating-agentforce-d360/scripts/_shared/sql.py +14 -0
- package/skills/investigating-agentforce-d360/scripts/assemble_dc.py +1624 -0
- package/skills/investigating-agentforce-d360/scripts/config.py +45 -0
- package/skills/investigating-agentforce-d360/scripts/dc.py +188 -0
- package/skills/investigating-agentforce-d360/scripts/discover_sessions.py +556 -0
- package/skills/investigating-agentforce-d360/scripts/fetch_dc.py +1045 -0
- package/skills/investigating-agentforce-d360/scripts/render_dc.py +1750 -0
- package/skills/investigating-agentforce-d360/scripts/resolve_session.py +264 -0
- package/skills/investigating-agentforce-d360/scripts/storage.py +92 -0
- package/skills/investigating-agentforce-d360/scripts/tests/__init__.py +0 -0
- package/skills/investigating-agentforce-d360/scripts/tests/_bootstrap.py +15 -0
- package/skills/investigating-agentforce-d360/scripts/tests/fixtures/__init__.py +0 -0
- package/skills/investigating-agentforce-d360/scripts/tests/fixtures/synthetic_session.py +424 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_bootstrap_and_mode.py +115 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct.py +220 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct_integration.py +158 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_helpers.py +287 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_integration.py +247 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_dc_and_resolve_session.py +433 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions.py +458 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions_grep_ci.py +193 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_helpers.py +266 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_identity.py +528 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_main.py +251 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall.py +229 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall_full.py +283 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_identity_coherence.py +327 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_branches.py +256 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_gateway_direct.py +130 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_helpers.py +291 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_integration.py +220 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_planner_llm_calls.py +284 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_show_prompts_gating.py +215 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_from_disk.py +100 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_session_main.py +149 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_runtime_override.py +104 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape.py +95 -0
- package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape_dropped_by_stdm.py +85 -0
- package/skills/managing-managed-event-subscription/SKILL.md +152 -0
- package/skills/managing-managed-event-subscription/assets/managed-event-subscription-template.xml +20 -0
- package/skills/managing-managed-event-subscription/references/delete-guide.md +57 -0
- package/skills/managing-managed-event-subscription/references/topic-name-formats.md +26 -0
- package/skills/managing-managed-event-subscription/references/update-constraints.md +30 -0
- package/skills/modeling-omnistudio-epc-catalog/SKILL.md +0 -1
- package/skills/observing-agentforce/SKILL.md +0 -1
- package/skills/orchestrating-datacloud/SKILL.md +0 -1
- package/skills/preparing-datacloud/SKILL.md +0 -1
- package/skills/querying-soql/SKILL.md +0 -1
- package/skills/retrieving-datacloud/SKILL.md +0 -1
- package/skills/running-apex-tests/SKILL.md +0 -1
- package/skills/running-code-analyzer/SKILL.md +0 -1
- package/skills/segmenting-datacloud/SKILL.md +0 -1
- package/skills/testing-agentforce/SKILL.md +0 -1
- package/skills/uplifting-components-to-slds2/SKILL.md +3 -2
- package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +30 -9
- package/skills/uplifting-components-to-slds2/references/examples.md +24 -6
- package/skills/validating-slds/SKILL.md +262 -0
- package/skills/validating-slds/references/quality-checks.md +308 -0
- package/skills/validating-slds/references/report-format.md +302 -0
- package/skills/validating-slds/scripts/analyze-quality.cjs +521 -0
package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py
ADDED
|
@@ -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()
|