@salesforce/afv-skills 1.13.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- 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-ui-bundle-app/SKILL.md +33 -8
- package/skills/generating-custom-application/SKILL.md +1 -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-ui-bundle-custom-app/SKILL.md +93 -0
- package/skills/generating-ui-bundle-custom-app/docs/configure-metadata-custom-application.md +70 -0
- package/skills/generating-ui-bundle-metadata/SKILL.md +39 -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/reviewing-lwc-mobile-offline/SKILL.md +168 -0
- package/skills/reviewing-lwc-mobile-offline/references/grounding.md +7 -0
- package/skills/reviewing-lwc-mobile-offline/references/inline-graphql.md +43 -0
- package/skills/reviewing-lwc-mobile-offline/references/komaci-eslint.md +125 -0
- package/skills/reviewing-lwc-mobile-offline/references/lwc-if.md +78 -0
- package/skills/reviewing-lwc-mobile-offline/scripts/komaci.config.mjs +18 -0
- package/skills/reviewing-lwc-mobile-offline/scripts/package.json +10 -0
- package/skills/reviewing-lwc-mobile-offline/scripts/run-komaci.sh +69 -0
- 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/using-mobile-native-capabilities/SKILL.md +182 -0
- package/skills/using-mobile-native-capabilities/references/app-review.md +68 -0
- package/skills/using-mobile-native-capabilities/references/ar-space-capture.md +125 -0
- package/skills/using-mobile-native-capabilities/references/barcode-scanner.md +219 -0
- package/skills/using-mobile-native-capabilities/references/base-capability.md +22 -0
- package/skills/using-mobile-native-capabilities/references/biometrics.md +90 -0
- package/skills/using-mobile-native-capabilities/references/calendar.md +213 -0
- package/skills/using-mobile-native-capabilities/references/contacts.md +232 -0
- package/skills/using-mobile-native-capabilities/references/document-scanner.md +342 -0
- package/skills/using-mobile-native-capabilities/references/geofencing.md +123 -0
- package/skills/using-mobile-native-capabilities/references/location.md +158 -0
- package/skills/using-mobile-native-capabilities/references/mobile-capabilities.md +30 -0
- package/skills/using-mobile-native-capabilities/references/nfc.md +181 -0
- package/skills/using-mobile-native-capabilities/references/payments.md +95 -0
- 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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""Tests for ``assemble_dc._assemble_gateway_direct``.
|
|
2
|
+
|
|
3
|
+
Exercises the ``interactions_not_materialized_yet`` path: session row
|
|
4
|
+
resolved, gateway_requests populated, interactions/steps empty. Builds a
|
|
5
|
+
minimal rows-dict fixture and calls the private assembler directly — the
|
|
6
|
+
function is pure over (sid, rows, manifest, parse_warnings), so no disk
|
|
7
|
+
fixtures are needed.
|
|
8
|
+
|
|
9
|
+
Scope:
|
|
10
|
+
- ``_source == "gateway_direct"`` sentinel
|
|
11
|
+
- ``session.interactions == []`` (downstream consumers no-op)
|
|
12
|
+
- ``session.gateway_chain`` length == fixture's gateway_requests
|
|
13
|
+
- each gateway_chain entry has a populated ``response.timestamp``
|
|
14
|
+
- finish_reason is lifted out of the HTML-escaped parameters JSON
|
|
15
|
+
- model / provider / prompt_template_dev_name / token counts populated
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import unittest
|
|
20
|
+
|
|
21
|
+
from . import _bootstrap # noqa: F401 — sys.path setup
|
|
22
|
+
|
|
23
|
+
from assemble_dc import _assemble_gateway_direct # type: ignore
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_SID = "01912345-0000-7000-8000-000000000001"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _gw_request(req_id: str, ts: str, model: str, tokens: tuple) -> dict:
|
|
30
|
+
"""Shape mirrors GenAIGatewayRequest__dlm rows as fetched."""
|
|
31
|
+
prompt_tok, completion_tok, total_tok = tokens
|
|
32
|
+
return {
|
|
33
|
+
"gatewayRequestId__c": req_id,
|
|
34
|
+
"sf__Id": f"sf-{req_id}",
|
|
35
|
+
"sessionId__c": f'"{_SID}"',
|
|
36
|
+
"timestamp__c": ts,
|
|
37
|
+
"model__c": model,
|
|
38
|
+
"provider__c": "openai",
|
|
39
|
+
"promptTemplateDevName__c": "AiCopilot__ReactTopicPrompt",
|
|
40
|
+
"feature__c": "plannerservice",
|
|
41
|
+
"promptTokens__c": prompt_tok,
|
|
42
|
+
"completionTokens__c": completion_tok,
|
|
43
|
+
"totalTokens__c": total_tok,
|
|
44
|
+
"prompt__c": f"This is the prompt body for {req_id}.",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _gw_response(req_id: str, resp_id: str, ts: str, finish: str) -> dict:
|
|
49
|
+
"""Shape mirrors GenAIGatewayResponse__dlm rows as fetched.
|
|
50
|
+
|
|
51
|
+
parameters__c is HTML-escaped JSON with the finish_reason value
|
|
52
|
+
wrapped in escaped quotes — the exact on-wire shape the live fetcher
|
|
53
|
+
produces. Mirrors the fixture used elsewhere in the test suite.
|
|
54
|
+
"""
|
|
55
|
+
# Build the raw JSON first, then HTML-escape the quotes the same way
|
|
56
|
+
# the gateway serializer does.
|
|
57
|
+
raw = '{"finish_reason":"\\"' + finish + '\\""}'
|
|
58
|
+
escaped = raw.replace('"', """)
|
|
59
|
+
return {
|
|
60
|
+
"generationResponseId__c": resp_id,
|
|
61
|
+
"generationRequestId__c": req_id,
|
|
62
|
+
"parameters__c": escaped,
|
|
63
|
+
"timestamp__c": ts,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _tag(req_id: str, key: str, value: str) -> dict:
|
|
68
|
+
return {
|
|
69
|
+
"id__c": f"tag-{req_id}-{key}",
|
|
70
|
+
"parent__c": req_id,
|
|
71
|
+
"tag__c": key,
|
|
72
|
+
"tagValue__c": value,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _md(req_id: str, mtype: str, payload: str) -> dict:
|
|
77
|
+
return {
|
|
78
|
+
"id__c": f"md-{req_id}",
|
|
79
|
+
"parent__c": req_id,
|
|
80
|
+
"metadataType__c": mtype,
|
|
81
|
+
"metadata__c": payload,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _make_rows() -> dict:
|
|
86
|
+
session_row = {
|
|
87
|
+
"ssot__Id__c": _SID,
|
|
88
|
+
"ssot__StartTimestamp__c": "2026-05-03T10:00:00.000Z",
|
|
89
|
+
"ssot__EndTimestamp__c": None,
|
|
90
|
+
"ssot__AiAgentChannelType__c": "EmbeddedMessaging",
|
|
91
|
+
"ssot__AiAgentSessionEndType__c": None,
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
"sessions": [session_row],
|
|
95
|
+
"participants": [],
|
|
96
|
+
"messages": [],
|
|
97
|
+
"moments": [],
|
|
98
|
+
"interactions": [],
|
|
99
|
+
"steps": [],
|
|
100
|
+
"generations": [],
|
|
101
|
+
"gateway_requests": [
|
|
102
|
+
_gw_request("gw-1", "2026-05-03T10:00:01.000Z", "gpt-4o", (100, 50, 150)),
|
|
103
|
+
_gw_request("gw-2", "2026-05-03T10:00:05.000Z", "gpt-4o", (200, 80, 280)),
|
|
104
|
+
_gw_request("gw-3", "2026-05-03T10:00:10.000Z", "gpt-4o-mini", (50, 30, 80)),
|
|
105
|
+
],
|
|
106
|
+
"gateway_responses": [
|
|
107
|
+
_gw_response("gw-1", "resp-1", "2026-05-03T10:00:02.500Z", "stop"),
|
|
108
|
+
_gw_response("gw-2", "resp-2", "2026-05-03T10:00:06.000Z", "tool_calls"),
|
|
109
|
+
_gw_response("gw-3", "resp-3", "2026-05-03T10:00:11.000Z", "stop"),
|
|
110
|
+
],
|
|
111
|
+
"gateway_request_tags": [
|
|
112
|
+
_tag("gw-1", "bot_id", "0Xx000000000001"),
|
|
113
|
+
_tag("gw-2", "feature", "plannerservice"),
|
|
114
|
+
],
|
|
115
|
+
"gateway_request_metadata": [
|
|
116
|
+
_md("gw-2", "ToolCall", '{"tool":"apex"}'),
|
|
117
|
+
],
|
|
118
|
+
"gateway_request_llm": [],
|
|
119
|
+
"content_quality": [],
|
|
120
|
+
"content_category": [],
|
|
121
|
+
"feedback": [],
|
|
122
|
+
"feedback_details": [],
|
|
123
|
+
"gateway_records": [],
|
|
124
|
+
"tag_associations": [],
|
|
125
|
+
"tag_definitions": [],
|
|
126
|
+
"tag_definition_associations": [],
|
|
127
|
+
"tags": [],
|
|
128
|
+
"app_generation": [],
|
|
129
|
+
"telemetry_spans": [],
|
|
130
|
+
"moment_interactions": [],
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _make_manifest() -> dict:
|
|
135
|
+
return {
|
|
136
|
+
"session_id": _SID,
|
|
137
|
+
"org_alias": "test-org",
|
|
138
|
+
"instance_url": "https://example.my.salesforce.com",
|
|
139
|
+
"org_id_15": "00DXX0000000001",
|
|
140
|
+
"agent_api_name": "My_Agent",
|
|
141
|
+
"agent_version": "v1",
|
|
142
|
+
"session_shape": "interactions_not_materialized_yet",
|
|
143
|
+
"queries": [],
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class AssembleGatewayDirectTests(unittest.TestCase):
|
|
148
|
+
|
|
149
|
+
def setUp(self):
|
|
150
|
+
self.rows = _make_rows()
|
|
151
|
+
self.manifest = _make_manifest()
|
|
152
|
+
self.tree = _assemble_gateway_direct(
|
|
153
|
+
_SID, self.rows, self.manifest, parse_warnings=[])
|
|
154
|
+
|
|
155
|
+
def test_source_sentinel(self):
|
|
156
|
+
"""The render layer keys off this exact string."""
|
|
157
|
+
self.assertEqual(self.tree["_source"], "gateway_direct")
|
|
158
|
+
|
|
159
|
+
def test_interactions_is_explicit_empty_list(self):
|
|
160
|
+
"""Downstream consumers walk session.interactions — must exist + be empty."""
|
|
161
|
+
self.assertEqual(self.tree["session"]["interactions"], [])
|
|
162
|
+
|
|
163
|
+
def test_gateway_chain_length_matches_input(self):
|
|
164
|
+
self.assertEqual(len(self.tree["session"]["gateway_chain"]), 3)
|
|
165
|
+
|
|
166
|
+
def test_each_chain_entry_has_response_timestamp(self):
|
|
167
|
+
"""Join via generationRequestId__c must populate response.timestamp on each."""
|
|
168
|
+
for call in self.tree["session"]["gateway_chain"]:
|
|
169
|
+
with self.subTest(req_id=call.get("gateway_request_id")):
|
|
170
|
+
self.assertIsNotNone(call.get("response"))
|
|
171
|
+
self.assertIsNotNone(call["response"].get("timestamp"))
|
|
172
|
+
|
|
173
|
+
def test_finish_reason_parsed(self):
|
|
174
|
+
"""HTML-escaped, quote-wrapped finish_reason must decode to bare string."""
|
|
175
|
+
reasons = [c["response"]["finish_reason"]
|
|
176
|
+
for c in self.tree["session"]["gateway_chain"]]
|
|
177
|
+
self.assertEqual(reasons, ["stop", "tool_calls", "stop"])
|
|
178
|
+
|
|
179
|
+
def test_chain_sorted_by_timestamp(self):
|
|
180
|
+
"""Deterministic ordering — required for byte-identical output across runs."""
|
|
181
|
+
timestamps = [c["timestamp"] for c in self.tree["session"]["gateway_chain"]]
|
|
182
|
+
self.assertEqual(timestamps, sorted(timestamps))
|
|
183
|
+
|
|
184
|
+
def test_model_and_tokens_harvested(self):
|
|
185
|
+
first = self.tree["session"]["gateway_chain"][0]
|
|
186
|
+
self.assertEqual(first["model"], "gpt-4o")
|
|
187
|
+
self.assertEqual(first["provider"], "openai")
|
|
188
|
+
self.assertEqual(first["prompt_template_dev_name"],
|
|
189
|
+
"AiCopilot__ReactTopicPrompt")
|
|
190
|
+
self.assertEqual(first["prompt_tokens"], 100)
|
|
191
|
+
self.assertEqual(first["total_tokens"], 150)
|
|
192
|
+
|
|
193
|
+
def test_prompt_text_untruncated_on_disk(self):
|
|
194
|
+
"""Assembler stores the full prompt — the 64 KB cap is render-only."""
|
|
195
|
+
for call in self.tree["session"]["gateway_chain"]:
|
|
196
|
+
self.assertIn("This is the prompt body for",
|
|
197
|
+
call.get("prompt_text") or "")
|
|
198
|
+
|
|
199
|
+
def test_identity_block_present(self):
|
|
200
|
+
"""Top-level identity must carry (org_id_15, agent_api_name, agent_version)."""
|
|
201
|
+
self.assertEqual(self.tree["identity"]["org_id_15"], "00DXX0000000001")
|
|
202
|
+
self.assertEqual(self.tree["identity"]["agent_api_name"], "My_Agent")
|
|
203
|
+
self.assertEqual(self.tree["identity"]["agent_version"], "v1")
|
|
204
|
+
|
|
205
|
+
def test_stdm_lag_note_on_session(self):
|
|
206
|
+
note = self.tree["session"].get("_stdm_lag_note") or ""
|
|
207
|
+
self.assertIn("materialize on a separate cadence", note)
|
|
208
|
+
|
|
209
|
+
def test_counts_reflect_input_rows(self):
|
|
210
|
+
counts = self.tree["session"]["counts"]
|
|
211
|
+
self.assertEqual(counts["gateway_requests"], 3)
|
|
212
|
+
self.assertEqual(counts["gateway_responses"], 3)
|
|
213
|
+
self.assertEqual(counts["interactions_total"], 0)
|
|
214
|
+
self.assertEqual(counts["steps_total"], 0)
|
|
215
|
+
self.assertEqual(counts["session_shape"],
|
|
216
|
+
"interactions_not_materialized_yet")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
if __name__ == "__main__":
|
|
220
|
+
unittest.main()
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Integration tests for ``assemble_dc._assemble_gateway_direct``.
|
|
2
|
+
|
|
3
|
+
Drives the materialization-lag path: session row + gateway_requests
|
|
4
|
+
populated, but interactions / steps absent. Triggered by setting
|
|
5
|
+
``manifest.session_shape = "interactions_not_materialized_yet"``.
|
|
6
|
+
|
|
7
|
+
Complements ``test_assemble_dc_gateway_direct.py`` which calls the
|
|
8
|
+
private function with a hand-built rows dict; this file drives the
|
|
9
|
+
public ``assemble`` entry point through the disk-load path so manifest
|
|
10
|
+
+ rows + path resolution are all exercised.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import unittest
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from tempfile import TemporaryDirectory
|
|
18
|
+
from unittest import mock
|
|
19
|
+
|
|
20
|
+
from . import _bootstrap # noqa: F401 — sys.path setup
|
|
21
|
+
|
|
22
|
+
import assemble_dc # type: ignore
|
|
23
|
+
from config import paths # type: ignore
|
|
24
|
+
from .fixtures.synthetic_session import ( # type: ignore
|
|
25
|
+
IDS, write_to_disk, make_manifest,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _materialization_lag_mutator(sdir: Path) -> None:
|
|
30
|
+
"""Make the fixture look like a session whose STDM hierarchy hasn't
|
|
31
|
+
materialized yet: keep gateway_requests + gateway_responses + tags,
|
|
32
|
+
but wipe interactions / steps / messages and flip the manifest.
|
|
33
|
+
|
|
34
|
+
This is the live-DC failure mode that the gateway-direct path was
|
|
35
|
+
written to handle.
|
|
36
|
+
"""
|
|
37
|
+
# Wipe interaction/step/message rows (file with empty list)
|
|
38
|
+
for name in ("interactions", "steps", "messages"):
|
|
39
|
+
(sdir / f"dc.{name}.json").write_text("[]")
|
|
40
|
+
# Update manifest session_shape so assemble's branch fires.
|
|
41
|
+
manifest = make_manifest()
|
|
42
|
+
manifest["session_shape"] = "interactions_not_materialized_yet"
|
|
43
|
+
(sdir / "dc._session_manifest.json").write_text(
|
|
44
|
+
json.dumps(manifest, indent=2)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class _GatewayDirectHarness:
|
|
49
|
+
def __enter__(self):
|
|
50
|
+
self._tmp = TemporaryDirectory()
|
|
51
|
+
self._tmpdir = Path(self._tmp.name)
|
|
52
|
+
sdir = write_to_disk(self._tmpdir)
|
|
53
|
+
_materialization_lag_mutator(sdir)
|
|
54
|
+
self._patch = mock.patch.object(paths, "DATA_ROOT", self._tmpdir)
|
|
55
|
+
self._patch.start()
|
|
56
|
+
self.tree, self.sdir = assemble_dc.assemble(IDS.SID)
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
def __exit__(self, *exc):
|
|
60
|
+
self._patch.stop()
|
|
61
|
+
self._tmp.cleanup()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# -----------------------------------------------------------------------------
|
|
65
|
+
# Top-level shape on the gateway-direct branch
|
|
66
|
+
# -----------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class GatewayDirectShapeTests(unittest.TestCase):
|
|
70
|
+
|
|
71
|
+
def test_source_marker_set_to_gateway_direct(self):
|
|
72
|
+
with _GatewayDirectHarness() as h:
|
|
73
|
+
self.assertEqual(h.tree["_source"], "gateway_direct")
|
|
74
|
+
|
|
75
|
+
def test_interactions_is_empty_list(self):
|
|
76
|
+
with _GatewayDirectHarness() as h:
|
|
77
|
+
self.assertEqual(h.tree["session"]["interactions"], [])
|
|
78
|
+
|
|
79
|
+
def test_gateway_chain_populated_from_gateway_requests(self):
|
|
80
|
+
with _GatewayDirectHarness() as h:
|
|
81
|
+
chain = h.tree["session"]["gateway_chain"]
|
|
82
|
+
# Fixture has 2 gateway_requests; both make it into the chain.
|
|
83
|
+
self.assertEqual(len(chain), 2)
|
|
84
|
+
chain_ids = {gw["gateway_request_id"] for gw in chain}
|
|
85
|
+
self.assertEqual(chain_ids, {IDS.GW_REQ_DECLARED, IDS.GW_REQ_WINDOW})
|
|
86
|
+
|
|
87
|
+
def test_chain_sorted_by_timestamp(self):
|
|
88
|
+
# Earlier gateway request timestamp comes first.
|
|
89
|
+
with _GatewayDirectHarness() as h:
|
|
90
|
+
chain = h.tree["session"]["gateway_chain"]
|
|
91
|
+
timestamps = [gw.get("timestamp") for gw in chain]
|
|
92
|
+
self.assertEqual(timestamps, sorted(t or "" for t in timestamps))
|
|
93
|
+
|
|
94
|
+
def test_each_gateway_chain_entry_has_response(self):
|
|
95
|
+
# Only the declared request has a response in the fixture; the
|
|
96
|
+
# window-bound one has response=None. Both shapes are accepted.
|
|
97
|
+
with _GatewayDirectHarness() as h:
|
|
98
|
+
chain = h.tree["session"]["gateway_chain"]
|
|
99
|
+
declared = next(gw for gw in chain
|
|
100
|
+
if gw["gateway_request_id"] == IDS.GW_REQ_DECLARED)
|
|
101
|
+
self.assertIsNotNone(declared["response"])
|
|
102
|
+
|
|
103
|
+
def test_top_identity_block_resolved_from_manifest(self):
|
|
104
|
+
with _GatewayDirectHarness() as h:
|
|
105
|
+
ident = h.tree["identity"]
|
|
106
|
+
self.assertEqual(ident["org_id_15"], IDS.ORG_ID_15)
|
|
107
|
+
self.assertEqual(ident["agent_api_name"], IDS.AGENT_API)
|
|
108
|
+
self.assertEqual(ident["agent_version"], IDS.AGENT_VERSION)
|
|
109
|
+
|
|
110
|
+
def test_counts_block_records_session_shape_and_zero_interactions(self):
|
|
111
|
+
with _GatewayDirectHarness() as h:
|
|
112
|
+
counts = h.tree["session"]["counts"]
|
|
113
|
+
self.assertEqual(counts["session_shape"],
|
|
114
|
+
"interactions_not_materialized_yet")
|
|
115
|
+
self.assertEqual(counts["interactions_total"], 0)
|
|
116
|
+
self.assertEqual(counts["steps_total"], 0)
|
|
117
|
+
self.assertEqual(counts["gateway_requests"], 2)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# -----------------------------------------------------------------------------
|
|
121
|
+
# render_dc on the gateway-direct tree
|
|
122
|
+
# -----------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class GatewayDirectRenderTests(unittest.TestCase):
|
|
126
|
+
"""The gateway-direct render branch is short — covers identity,
|
|
127
|
+
lag banner, and gateway-chain table. Drives it end-to-end."""
|
|
128
|
+
|
|
129
|
+
def test_render_emits_lag_banner(self):
|
|
130
|
+
import render_dc # noqa: WPS433
|
|
131
|
+
with _GatewayDirectHarness() as h:
|
|
132
|
+
md = render_dc.render(h.tree, manifest=None, session_dir=h.sdir)
|
|
133
|
+
# _STDM_LAG_NOTE is referenced in the gateway-direct render path
|
|
134
|
+
# — must surface as visible text. We assert on the substring
|
|
135
|
+
# "STDM" since the exact phrasing may evolve.
|
|
136
|
+
self.assertIn("STDM", md)
|
|
137
|
+
|
|
138
|
+
def test_render_lists_both_gateway_requests(self):
|
|
139
|
+
import render_dc # noqa: WPS433
|
|
140
|
+
with _GatewayDirectHarness() as h:
|
|
141
|
+
md = render_dc.render(h.tree, manifest=None, session_dir=h.sdir)
|
|
142
|
+
# Gateway-direct render truncates IDs to 8-char prefixes — assert
|
|
143
|
+
# on the prefix substring + on the prompt-template name (verbatim
|
|
144
|
+
# in the per-call detail block).
|
|
145
|
+
self.assertIn(IDS.GW_REQ_DECLARED[:8], md)
|
|
146
|
+
self.assertIn(IDS.GW_REQ_WINDOW[:8], md)
|
|
147
|
+
self.assertIn("AiCopilot__ReactInitialPrompt", md)
|
|
148
|
+
self.assertIn("AiCopilot__PromptTemplateGenerationsInvocable", md)
|
|
149
|
+
|
|
150
|
+
def test_render_includes_session_id(self):
|
|
151
|
+
import render_dc # noqa: WPS433
|
|
152
|
+
with _GatewayDirectHarness() as h:
|
|
153
|
+
md = render_dc.render(h.tree, manifest=None, session_dir=h.sdir)
|
|
154
|
+
self.assertIn(IDS.SID, md)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
unittest.main()
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Tests for ``assemble_dc`` pure helpers — independent of disk fixtures.
|
|
2
|
+
|
|
3
|
+
Complements ``test_assemble_dc_gateway_direct.py`` (binding chain) by
|
|
4
|
+
hitting the small, mechanical helpers that bridge raw DC rows and the
|
|
5
|
+
assembled tree:
|
|
6
|
+
|
|
7
|
+
- ``_clean`` NOT_SET sentinel collapse
|
|
8
|
+
- ``_harvest_str`` unescape + quote-strip + UNSET_VALUE
|
|
9
|
+
- ``_ts`` ISO-8601 → datetime; NOT_SET / non-string → None
|
|
10
|
+
- ``_index_unique`` PK dedup with first-write-wins + collision record
|
|
11
|
+
- ``_groupby`` group rows by FK, dropping NOT_SET keys
|
|
12
|
+
- ``_extract_trace_id`` primary col with HTML-escaped fallback
|
|
13
|
+
- ``_tier`` ACTION < TOPIC < GUARDRAIL < other
|
|
14
|
+
- ``_window_contains`` half-open / closed timestamp interval semantics
|
|
15
|
+
- ``_load`` disk read with malformed-json tolerance
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import unittest
|
|
21
|
+
from datetime import datetime, timezone
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from tempfile import TemporaryDirectory
|
|
24
|
+
|
|
25
|
+
from . import _bootstrap # noqa: F401 — sys.path setup
|
|
26
|
+
|
|
27
|
+
import assemble_dc # type: ignore
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# -----------------------------------------------------------------------------
|
|
31
|
+
# _clean — NOT_SET → None
|
|
32
|
+
# -----------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CleanTests(unittest.TestCase):
|
|
36
|
+
|
|
37
|
+
def test_not_set_token_returns_none(self):
|
|
38
|
+
self.assertIsNone(assemble_dc._clean("NOT_SET"))
|
|
39
|
+
|
|
40
|
+
def test_empty_string_returns_none(self):
|
|
41
|
+
self.assertIsNone(assemble_dc._clean(""))
|
|
42
|
+
|
|
43
|
+
def test_actual_value_passes_through(self):
|
|
44
|
+
self.assertEqual(assemble_dc._clean("real value"), "real value")
|
|
45
|
+
self.assertEqual(assemble_dc._clean(42), 42)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# -----------------------------------------------------------------------------
|
|
49
|
+
# _harvest_str — unescape + quote-strip + UNSET_VALUE collapse
|
|
50
|
+
# -----------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class HarvestStrTests(unittest.TestCase):
|
|
54
|
+
|
|
55
|
+
def test_none_returns_none(self):
|
|
56
|
+
self.assertIsNone(assemble_dc._harvest_str(None))
|
|
57
|
+
|
|
58
|
+
def test_unescapes_html_entities(self):
|
|
59
|
+
self.assertEqual(
|
|
60
|
+
assemble_dc._harvest_str(""0Xx000""),
|
|
61
|
+
"0Xx000",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def test_strips_outer_double_quotes(self):
|
|
65
|
+
self.assertEqual(assemble_dc._harvest_str('"value"'), "value")
|
|
66
|
+
|
|
67
|
+
def test_unset_value_collapses_to_none(self):
|
|
68
|
+
self.assertIsNone(assemble_dc._harvest_str("UNSET_VALUE"))
|
|
69
|
+
|
|
70
|
+
def test_not_set_collapses_to_none(self):
|
|
71
|
+
self.assertIsNone(assemble_dc._harvest_str("NOT_SET"))
|
|
72
|
+
|
|
73
|
+
def test_empty_string_collapses_to_none(self):
|
|
74
|
+
self.assertIsNone(assemble_dc._harvest_str(""))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# -----------------------------------------------------------------------------
|
|
78
|
+
# _ts — ISO-8601 timestamp parsing
|
|
79
|
+
# -----------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TsTests(unittest.TestCase):
|
|
83
|
+
|
|
84
|
+
def test_parses_iso_z_timestamp(self):
|
|
85
|
+
out = assemble_dc._ts("2026-04-22T10:00:00Z")
|
|
86
|
+
self.assertEqual(out, datetime(2026, 4, 22, 10, 0, 0, tzinfo=timezone.utc))
|
|
87
|
+
|
|
88
|
+
def test_parses_iso_offset_timestamp(self):
|
|
89
|
+
out = assemble_dc._ts("2026-04-22T10:00:00+00:00")
|
|
90
|
+
self.assertEqual(out, datetime(2026, 4, 22, 10, 0, 0, tzinfo=timezone.utc))
|
|
91
|
+
|
|
92
|
+
def test_returns_none_for_non_string(self):
|
|
93
|
+
self.assertIsNone(assemble_dc._ts(None))
|
|
94
|
+
self.assertIsNone(assemble_dc._ts(12345))
|
|
95
|
+
|
|
96
|
+
def test_returns_none_for_not_set(self):
|
|
97
|
+
self.assertIsNone(assemble_dc._ts("NOT_SET"))
|
|
98
|
+
self.assertIsNone(assemble_dc._ts(""))
|
|
99
|
+
|
|
100
|
+
def test_returns_none_for_unparseable(self):
|
|
101
|
+
self.assertIsNone(assemble_dc._ts("not-a-timestamp"))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# -----------------------------------------------------------------------------
|
|
105
|
+
# _index_unique
|
|
106
|
+
# -----------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class IndexUniqueTests(unittest.TestCase):
|
|
110
|
+
|
|
111
|
+
def test_builds_dict_keyed_by_field(self):
|
|
112
|
+
rows = [{"id": "a", "v": 1}, {"id": "b", "v": 2}]
|
|
113
|
+
out = assemble_dc._index_unique(rows, "id", [], dmo_label="t")
|
|
114
|
+
self.assertEqual(set(out.keys()), {"a", "b"})
|
|
115
|
+
|
|
116
|
+
def test_first_write_wins_on_collision(self):
|
|
117
|
+
rows = [{"id": "a", "v": 1}, {"id": "a", "v": 2}]
|
|
118
|
+
collisions: list[dict] = []
|
|
119
|
+
out = assemble_dc._index_unique(rows, "id", collisions, dmo_label="dmo1")
|
|
120
|
+
self.assertEqual(out["a"]["v"], 1)
|
|
121
|
+
self.assertEqual(len(collisions), 1)
|
|
122
|
+
self.assertEqual(collisions[0]["dmo"], "dmo1")
|
|
123
|
+
self.assertEqual(collisions[0]["key"], "a")
|
|
124
|
+
|
|
125
|
+
def test_skips_NOT_SET_keys(self):
|
|
126
|
+
rows = [{"id": "NOT_SET", "v": 1}, {"id": "", "v": 2}, {"id": "a", "v": 3}]
|
|
127
|
+
out = assemble_dc._index_unique(rows, "id", [], dmo_label="t")
|
|
128
|
+
self.assertEqual(set(out.keys()), {"a"})
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# -----------------------------------------------------------------------------
|
|
132
|
+
# _groupby
|
|
133
|
+
# -----------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class GroupbyTests(unittest.TestCase):
|
|
137
|
+
|
|
138
|
+
def test_groups_rows_by_key(self):
|
|
139
|
+
rows = [{"k": "a", "v": 1}, {"k": "b", "v": 2}, {"k": "a", "v": 3}]
|
|
140
|
+
out = assemble_dc._groupby(rows, "k")
|
|
141
|
+
self.assertEqual([r["v"] for r in out["a"]], [1, 3])
|
|
142
|
+
self.assertEqual([r["v"] for r in out["b"]], [2])
|
|
143
|
+
|
|
144
|
+
def test_skips_NOT_SET_keys(self):
|
|
145
|
+
rows = [{"k": "a", "v": 1}, {"k": "NOT_SET", "v": 2}]
|
|
146
|
+
out = assemble_dc._groupby(rows, "k")
|
|
147
|
+
self.assertNotIn("NOT_SET", out)
|
|
148
|
+
self.assertIn("a", out)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# -----------------------------------------------------------------------------
|
|
152
|
+
# _extract_trace_id
|
|
153
|
+
# -----------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class ExtractTraceIdTests(unittest.TestCase):
|
|
157
|
+
|
|
158
|
+
def test_uses_primary_column_when_populated(self):
|
|
159
|
+
out = assemble_dc._extract_trace_id({"ssot__TelemetryTraceId__c": "abc"})
|
|
160
|
+
self.assertEqual(out, "abc")
|
|
161
|
+
|
|
162
|
+
def test_falls_back_to_html_escaped_attribute_text(self):
|
|
163
|
+
out = assemble_dc._extract_trace_id({
|
|
164
|
+
"ssot__TelemetryTraceId__c": "",
|
|
165
|
+
"ssot__AttributeText__c": '"internalTraceId":"deadbeef"',
|
|
166
|
+
})
|
|
167
|
+
self.assertEqual(out, "deadbeef")
|
|
168
|
+
|
|
169
|
+
def test_returns_none_when_no_trace_id_anywhere(self):
|
|
170
|
+
out = assemble_dc._extract_trace_id({
|
|
171
|
+
"ssot__TelemetryTraceId__c": "",
|
|
172
|
+
"ssot__AttributeText__c": "no trace here",
|
|
173
|
+
})
|
|
174
|
+
self.assertIsNone(out)
|
|
175
|
+
|
|
176
|
+
def test_returns_none_for_NOT_SET(self):
|
|
177
|
+
out = assemble_dc._extract_trace_id({
|
|
178
|
+
"ssot__TelemetryTraceId__c": "NOT_SET",
|
|
179
|
+
"ssot__AttributeText__c": "",
|
|
180
|
+
})
|
|
181
|
+
self.assertIsNone(out)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# -----------------------------------------------------------------------------
|
|
185
|
+
# _tier — ACTION < TOPIC < GUARDRAIL < other
|
|
186
|
+
# -----------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class TierTests(unittest.TestCase):
|
|
190
|
+
|
|
191
|
+
def test_action_step_is_lowest_tier(self):
|
|
192
|
+
self.assertLess(
|
|
193
|
+
assemble_dc._tier("ACTION_STEP"),
|
|
194
|
+
assemble_dc._tier("TOPIC_STEP"),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def test_topic_step_outranks_guardrails(self):
|
|
198
|
+
self.assertLess(
|
|
199
|
+
assemble_dc._tier("TOPIC_STEP"),
|
|
200
|
+
assemble_dc._tier("TRUST_GUARDRAILS_STEP"),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
def test_unknown_step_type_falls_to_end(self):
|
|
204
|
+
self.assertEqual(
|
|
205
|
+
assemble_dc._tier("MYSTERY"),
|
|
206
|
+
len(assemble_dc._TIER_ORDER),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# -----------------------------------------------------------------------------
|
|
211
|
+
# _window_contains
|
|
212
|
+
# -----------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class WindowContainsTests(unittest.TestCase):
|
|
216
|
+
|
|
217
|
+
def _ts(self, h, m, s):
|
|
218
|
+
return datetime(2026, 4, 22, h, m, s, tzinfo=timezone.utc)
|
|
219
|
+
|
|
220
|
+
def test_in_range_inclusive_of_start_and_end(self):
|
|
221
|
+
gw = self._ts(10, 0, 5)
|
|
222
|
+
self.assertTrue(
|
|
223
|
+
assemble_dc._window_contains(gw, self._ts(10, 0, 0), self._ts(10, 0, 10))
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def test_open_ended_when_end_is_none(self):
|
|
227
|
+
gw = self._ts(11, 0, 0)
|
|
228
|
+
self.assertTrue(
|
|
229
|
+
assemble_dc._window_contains(gw, self._ts(10, 0, 0), None)
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
def test_returns_false_when_start_is_none(self):
|
|
233
|
+
gw = self._ts(10, 0, 0)
|
|
234
|
+
self.assertFalse(
|
|
235
|
+
assemble_dc._window_contains(gw, None, self._ts(11, 0, 0))
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def test_returns_false_when_before_start(self):
|
|
239
|
+
gw = self._ts(9, 0, 0)
|
|
240
|
+
self.assertFalse(
|
|
241
|
+
assemble_dc._window_contains(gw, self._ts(10, 0, 0), self._ts(11, 0, 0))
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
def test_returns_false_when_after_end(self):
|
|
245
|
+
gw = self._ts(12, 0, 0)
|
|
246
|
+
self.assertFalse(
|
|
247
|
+
assemble_dc._window_contains(gw, self._ts(10, 0, 0), self._ts(11, 0, 0))
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# -----------------------------------------------------------------------------
|
|
252
|
+
# _load — disk read with tolerance
|
|
253
|
+
# -----------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class LoadTests(unittest.TestCase):
|
|
257
|
+
|
|
258
|
+
def test_returns_empty_list_when_file_missing(self):
|
|
259
|
+
with TemporaryDirectory() as t:
|
|
260
|
+
warnings: list[str] = []
|
|
261
|
+
self.assertEqual(
|
|
262
|
+
assemble_dc._load(Path(t), "missing", warnings), []
|
|
263
|
+
)
|
|
264
|
+
# Missing file is normal — no warning recorded.
|
|
265
|
+
self.assertEqual(warnings, [])
|
|
266
|
+
|
|
267
|
+
def test_returns_loaded_rows_when_file_valid(self):
|
|
268
|
+
with TemporaryDirectory() as t:
|
|
269
|
+
tmp = Path(t)
|
|
270
|
+
(tmp / "dc.sessions.json").write_text(json.dumps([{"a": 1}, {"a": 2}]))
|
|
271
|
+
warnings: list[str] = []
|
|
272
|
+
out = assemble_dc._load(tmp, "sessions", warnings)
|
|
273
|
+
self.assertEqual(out, [{"a": 1}, {"a": 2}])
|
|
274
|
+
self.assertEqual(warnings, [])
|
|
275
|
+
|
|
276
|
+
def test_returns_empty_list_and_records_warning_on_malformed_json(self):
|
|
277
|
+
with TemporaryDirectory() as t:
|
|
278
|
+
tmp = Path(t)
|
|
279
|
+
(tmp / "dc.broken.json").write_text("<<<not json>>>")
|
|
280
|
+
warnings: list[str] = []
|
|
281
|
+
out = assemble_dc._load(tmp, "broken", warnings)
|
|
282
|
+
self.assertEqual(out, [])
|
|
283
|
+
self.assertEqual(warnings, ["broken"])
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
if __name__ == "__main__":
|
|
287
|
+
unittest.main()
|