@salesforce/afv-skills 1.46.0 → 1.48.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/agentforce-observe/SKILL.md +32 -4
- package/skills/agentforce-observe/references/ahm-alerts.md +719 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +275 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/README.md +32 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls +75 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls-meta.xml +5 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/interview-answers.json +13 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +10 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/create.json +20 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/update.json +16 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +273 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +236 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/reference-example-set-comment-value.md +132 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +211 -0
- package/skills/dx-devops-project-manage/SKILL.md +197 -0
- package/skills/dx-devops-project-manage/examples/common-workflows.md +197 -0
- package/skills/dx-devops-project-manage/references/cli-commands.md +295 -0
- package/skills/dx-devops-project-manage/scripts/create-project.sh +48 -0
- package/skills/dx-devops-project-manage/scripts/list-projects.sh +51 -0
- package/skills/dx-devops-project-manage/scripts/update-project.sh +96 -0
- package/skills/education-cloud-academic-calendar-generate/SKILL.md +225 -0
- package/skills/education-cloud-academic-calendar-generate/examples/quarter-calendar.json +47 -0
- package/skills/education-cloud-academic-calendar-generate/examples/sample-output.md +57 -0
- package/skills/education-cloud-academic-calendar-generate/examples/semester-calendar.json +54 -0
- package/skills/education-cloud-academic-calendar-generate/references/calendar-systems.md +127 -0
- package/skills/education-cloud-academic-calendar-generate/references/date-validation.md +222 -0
- package/skills/education-cloud-academic-calendar-generate/references/foundation_prerequisites.md +40 -0
- package/skills/education-cloud-academic-calendar-generate/scripts/validate_calendar_dates.py +143 -0
- package/skills/education-cloud-course-catalog-migrate/SKILL.md +321 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas-detail.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/large-catalog-handling.md +42 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/batch_courses.py +36 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_linked_courses.py +51 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_modality_variants.py +48 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/resolve_api_version.py +43 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/split_course_code.py +39 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/validate_completeness.py +54 -0
- package/skills/education-cloud-domain-configure/SKILL.md +234 -0
- package/skills/education-cloud-domain-configure/references/dependent-features.md +198 -0
- package/skills/education-cloud-domain-configure/references/domain-features/academic-operations.md +386 -0
- package/skills/education-cloud-domain-configure/references/domain-features/alumni-relations.md +155 -0
- package/skills/education-cloud-domain-configure/references/domain-features/cross-domain-dependencies.md +18 -0
- package/skills/education-cloud-domain-configure/references/domain-features/industriessettings-toggle-reference.md +43 -0
- package/skills/education-cloud-domain-configure/references/domain-features/mentoring.md +198 -0
- package/skills/education-cloud-domain-configure/references/domain-features/recruitment-admissions.md +315 -0
- package/skills/education-cloud-domain-configure/references/domain-features/student-management.md +155 -0
- package/skills/education-cloud-domain-configure/references/domain-features/student-success.md +249 -0
- package/skills/education-cloud-domain-configure/references/domain-features.md +27 -0
- package/skills/education-cloud-domain-configure/references/gotchas-extended.md +23 -0
- package/skills/education-cloud-domain-configure/references/toggle-write-path.md +67 -0
- package/skills/education-cloud-domain-configure/scripts/check_license_provisioned.py +62 -0
- package/skills/education-cloud-domain-configure/scripts/check_org_edition.py +44 -0
- package/skills/education-cloud-domain-configure/scripts/check_permission_assigned.py +39 -0
- package/skills/education-cloud-multi-campus-configure/SKILL.md +7 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +1 -2
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +177 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/agent-and-subagents.md +151 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/customer-narration.md +34 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/execution-model.md +54 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/flows.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/grounding.md +199 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/permissions.md +183 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/platform-enablement.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +158 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/routing.md +141 -0
- package/skills/experience-cms-brand-apply/SKILL.md +5 -5
- package/skills/experience-cms-brand-create/SKILL.md +2 -2
- package/skills/experience-cms-content-generate/SKILL.md +1 -0
- package/skills/experience-cms-content-render/SKILL.md +173 -0
- package/skills/experience-cms-content-render/assets/angular/DetailPage.component.ts +25 -0
- package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +133 -0
- package/skills/experience-cms-content-render/assets/angular/TypeList.component.ts +38 -0
- package/skills/experience-cms-content-render/assets/angular/TypeRenderer.component.ts +90 -0
- package/skills/experience-cms-content-render/assets/angular/cms-content.component.ts +248 -0
- package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +100 -0
- package/skills/experience-cms-content-render/assets/react/DetailPage.tsx +20 -0
- package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +129 -0
- package/skills/experience-cms-content-render/assets/react/TypeList.tsx +40 -0
- package/skills/experience-cms-content-render/assets/react/TypeRenderer.tsx +64 -0
- package/skills/experience-cms-content-render/assets/react/heuristicRenderer.tsx +310 -0
- package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +129 -0
- package/skills/experience-cms-content-render/assets/shared/cmsContentType.ts +49 -0
- package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +96 -0
- package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +55 -0
- package/skills/experience-cms-content-render/references/bulk-loading.md +60 -0
- package/skills/experience-cms-content-render/references/codegen-guardrails.md +111 -0
- package/skills/experience-cms-content-render/references/detail-pages.md +87 -0
- package/skills/experience-cms-content-render/references/embed-recipes.md +127 -0
- package/skills/experience-cms-content-render/references/failure-modes.md +96 -0
- package/skills/experience-cms-content-render/references/heuristic-render-rules.md +131 -0
- package/skills/experience-cms-content-render/references/init-scaffold.md +122 -0
- package/skills/experience-cms-content-render/references/interaction-model.md +173 -0
- package/skills/experience-cms-content-render/references/package-api.md +106 -0
- package/skills/experience-cms-content-render/references/schema-sync.md +114 -0
- package/skills/experience-cms-content-render/references/styling-scopes.md +65 -0
- package/skills/experience-cms-content-render/references/verify.md +49 -0
- package/skills/experience-cms-content-type-generate/SKILL.md +2 -2
- package/skills/experience-content-media-stock-image-search/SKILL.md +5 -4
- package/skills/experience-search-coordinate/SKILL.md +198 -0
- package/skills/experience-search-coordinate/assets/search-payload-template.json +25 -0
- package/skills/experience-search-coordinate/references/content-route.md +313 -0
- package/skills/experience-search-coordinate/references/content-type-discovery.md +57 -0
- package/skills/experience-search-coordinate/references/media-route.md +172 -0
- package/skills/experience-search-coordinate/references/scope-resolution.md +14 -0
- package/skills/experience-ui-bundle-localize/SKILL.md +1 -1
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +5 -3
- package/skills/experience-ui-bundle-project-generate/SKILL.md +18 -14
- package/skills/experience-ui-bundle-project-generate/references/angular-project-generate.md +22 -0
- package/skills/experience-ui-bundle-project-generate/references/react-project-generate.md +20 -0
- package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +58 -54
- package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/graphiti-cli.md +2 -2
- package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/rest-and-integration.md +2 -1
- package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +6 -0
- package/skills/experience-ui-bundle-site-generate/SKILL.md +59 -8
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-digital-experience.md +8 -3
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-language-settings.md +120 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +336 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +143 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +127 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-deploy-commands.md +116 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +111 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-overview.md +312 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +171 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-state-tracking.md +64 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-trigger-handlers.md +122 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-overview.md +335 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-user-provisioning-details.md +140 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +196 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +307 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +211 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/state-machine-and-changes.md +108 -0
- package/skills/life-sciences-kam-coordinate/SKILL.md +241 -0
- package/skills/life-sciences-kam-coordinate/references/orchestration-flow.md +152 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +79 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-deploy-commands.md +131 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-kam-config-records.md +85 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +112 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-overview.md +202 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +67 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-state-tracking.md +65 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-trigger-handlers.md +123 -0
- package/skills/life-sciences-kam-coordinate/references/stage-4-participant-role-and-sprint.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +337 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +248 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-ipad-validation-script.md +35 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-details.md +146 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-overview.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/state-machine-and-changes.md +114 -0
- package/skills/life-sciences-prerequisites-validate/SKILL.md +138 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-org-settings.md +190 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-user-and-package.md +211 -0
- package/skills/life-sciences-territory-configure/SKILL.md +217 -0
- package/skills/life-sciences-territory-configure/references/territory-metadata.md +262 -0
- package/skills/platform-dsar-policy-manage/SKILL.md +272 -0
- package/skills/platform-dsar-policy-manage/references/configure.md +106 -0
- package/skills/platform-dsar-policy-manage/references/export-and-history.md +123 -0
- package/skills/platform-dsar-policy-manage/references/gap-analysis-guide.md +150 -0
- package/skills/platform-dsar-policy-manage/references/gap-scan.md +129 -0
- package/skills/platform-dsar-policy-manage/references/headless-sor.md +59 -0
- package/skills/platform-dsar-policy-manage/references/report-format.md +59 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/__init__.py +0 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/test_validate_policy_tree.py +76 -0
- package/skills/platform-dsar-policy-manage/scripts/validate-policy-tree.py +130 -0
- package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +359 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/official-examples.md +69 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/scenarios.md +187 -0
- package/skills/service-agentforce-channel-configure/SKILL.md +33 -28
- package/skills/service-agentforce-human-escalation-configure/SKILL.md +205 -0
- package/skills/service-agentforce-human-escalation-configure/assets/escalation-thresholds.instructions.md +37 -0
- package/skills/service-agentforce-human-escalation-configure/assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml +173 -0
- package/skills/service-agentforce-human-escalation-configure/assets/package.xml +8 -0
- package/skills/service-agentforce-human-escalation-configure/assets/sfdx-project.json +5 -0
- package/skills/service-agentforce-human-escalation-configure/references/classifier-contracts.md +103 -0
- package/skills/service-agentforce-human-escalation-configure/references/escalation-mechanism.md +63 -0
- package/skills/service-agentforce-human-escalation-configure/references/failure-policy.md +44 -0
- package/skills/service-agentforce-human-escalation-configure/references/nga-escalation.md +57 -0
- package/skills/service-agentforce-human-escalation-configure/references/runtime-verification.md +72 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/classify-agent-active.mjs +49 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/classify-nga-escalation.mjs +166 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/extract-outbound-route.mjs +67 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/patch-escalation-surfaces.mjs +131 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/__init__.py +0 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +249 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +899 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/verify-and-configure.sh +705 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/verify-escalation-config.mjs +89 -0
- package/skills/service-de-channel-routing-configure/SKILL.md +13 -5
- package/skills/service-de-channel-routing-configure/scripts/tests/test_skill_contract.py +25 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +23 -10
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -48
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +6 -2
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +20 -27
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +47 -27
- package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +13 -9
- package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +104 -45
- package/skills/service-omni-agent-users-create/SKILL.md +96 -0
- package/skills/service-omni-agent-users-create/assets/create-users.apex.template +80 -0
- package/skills/service-omni-agent-users-create/references/apex-patterns.md +135 -0
- package/skills/service-omni-agent-users-create/references/apex-template-notes.md +40 -0
- package/skills/service-omni-agent-users-create/scripts/detect-and-create.sh +206 -0
- package/skills/service-omni-agent-users-create/scripts/detect-existing.sh +122 -0
- package/skills/service-omni-agent-users-create/scripts/run-create.sh +298 -0
- package/skills/service-omni-agent-users-create/scripts/tests/test_user_create_security.py +171 -0
- package/skills/service-omni-agent-work-sharing-configure/SKILL.md +78 -0
- package/skills/service-omni-agent-work-sharing-configure/references/api-notes.md +31 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/configure-and-report.sh +186 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/tests/_bootstrap.py +120 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/tests/test_agent_work_sharing_contracts.py +93 -0
- package/skills/service-omni-base-settings-configure/SKILL.md +77 -0
- package/skills/service-omni-base-settings-configure/assets/force-app/main/default/settings/OmniChannel.settings-meta.xml +8 -0
- package/skills/service-omni-base-settings-configure/assets/package.xml +8 -0
- package/skills/service-omni-base-settings-configure/assets/sfdx-project.json +12 -0
- package/skills/service-omni-base-settings-configure/references/api-notes.md +121 -0
- package/skills/service-omni-base-settings-configure/scripts/configure-and-report.sh +164 -0
- package/skills/service-omni-base-settings-configure/scripts/detect-existing.sh +69 -0
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +168 -0
- package/skills/service-omni-channel-setup-coordinate/examples/output-templates.md +161 -0
- package/skills/service-omni-channel-setup-coordinate/references/gap-catalog.md +99 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +1542 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/__init__.py +0 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/_bootstrap.py +223 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +804 -0
- package/skills/service-omni-command-center-analyze/SKILL.md +84 -0
- package/skills/service-omni-command-center-analyze/references/api-notes.md +54 -0
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +198 -0
- package/skills/service-omni-command-center-analyze/scripts/tests/_bootstrap.py +115 -0
- package/skills/service-omni-command-center-analyze/scripts/tests/test_command_center_analyze_contracts.py +165 -0
- package/skills/service-omni-permission-set-assign/SKILL.md +88 -0
- package/skills/service-omni-permission-set-assign/assets/package.xml +8 -0
- package/skills/service-omni-permission-set-assign/references/api-notes.md +143 -0
- package/skills/service-omni-permission-set-assign/scripts/verify-and-assign.sh +397 -0
- package/skills/service-omni-presence-status-deploy/SKILL.md +87 -0
- package/skills/service-omni-presence-status-deploy/assets/package.xml +12 -0
- package/skills/service-omni-presence-status-deploy/references/api-notes.md +115 -0
- package/skills/service-omni-presence-status-deploy/scripts/deploy-and-report.sh +422 -0
- package/skills/service-omni-presence-user-config-deploy/SKILL.md +80 -0
- package/skills/service-omni-presence-user-config-deploy/references/api-notes.md +60 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/deploy-and-report.sh +261 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/tests/_bootstrap.py +106 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/tests/test_presence_user_config_contracts.py +142 -0
- package/skills/service-omni-queue-deploy/SKILL.md +91 -0
- package/skills/service-omni-queue-deploy/references/api-notes.md +140 -0
- package/skills/service-omni-queue-deploy/scripts/verify-and-align.sh +470 -0
- package/skills/service-omni-queue-members-assign/SKILL.md +93 -0
- package/skills/service-omni-queue-members-assign/references/api-notes.md +144 -0
- package/skills/service-omni-queue-members-assign/scripts/tests/_bootstrap.py +67 -0
- package/skills/service-omni-queue-members-assign/scripts/tests/test_queue_members_contracts.py +73 -0
- package/skills/service-omni-queue-members-assign/scripts/verify-and-bind.sh +362 -0
- package/skills/service-omni-queue-routing-config-deploy/SKILL.md +101 -0
- package/skills/service-omni-queue-routing-config-deploy/references/api-notes.md +127 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/tests/_bootstrap.py +90 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/tests/test_queue_routing_config_contracts.py +70 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/upsert-and-report.sh +538 -0
- package/skills/service-omni-routing-flow-deploy/SKILL.md +111 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.SkillsBased.flow-meta.xml +94 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.flow-meta.xml +88 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Cases.flow-meta.xml +144 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.SkillsBased.flow-meta.xml +94 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.flow-meta.xml +88 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCalls.flow-meta.xml +144 -0
- package/skills/service-omni-routing-flow-deploy/assets/package.xml +11 -0
- package/skills/service-omni-routing-flow-deploy/assets/sfdx-project.json +11 -0
- package/skills/service-omni-routing-flow-deploy/references/api-notes.md +265 -0
- package/skills/service-omni-routing-flow-deploy/scripts/deploy-and-report.sh +684 -0
- package/skills/service-omni-service-channel-configure/SKILL.md +83 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Cases.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Incidents.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/MessagingSessions.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/VoiceCalls.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/package.xml +11 -0
- package/skills/service-omni-service-channel-configure/assets/sfdx-project.json +12 -0
- package/skills/service-omni-service-channel-configure/references/api-notes.md +84 -0
- package/skills/service-omni-service-channel-configure/scripts/deploy-and-report.sh +383 -0
- package/skills/service-omni-sidebar-configure/SKILL.md +77 -0
- package/skills/service-omni-sidebar-configure/references/api-notes.md +51 -0
- package/skills/service-omni-sidebar-configure/scripts/enable-and-report.sh +315 -0
- package/skills/service-omni-sidebar-configure/scripts/tests/_bootstrap.py +142 -0
- package/skills/service-omni-sidebar-configure/scripts/tests/test_sidebar_contracts.py +139 -0
- package/skills/service-omni-skills-based-routing-configure/SKILL.md +81 -0
- package/skills/service-omni-skills-based-routing-configure/references/api-notes.md +50 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/configure-and-report.sh +270 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/tests/_bootstrap.py +120 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/tests/test_sbr_contracts.py +129 -0
- package/skills/service-omni-supervisor-config-deploy/SKILL.md +97 -0
- package/skills/service-omni-supervisor-config-deploy/assets/force-app/main/default/omniSupervisorConfigs/Omni_Supervisor.omniSupervisorConfig-meta.xml +9 -0
- package/skills/service-omni-supervisor-config-deploy/assets/package.xml +8 -0
- package/skills/service-omni-supervisor-config-deploy/assets/sfdx-project.json +12 -0
- package/skills/service-omni-supervisor-config-deploy/references/api-notes.md +205 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/deploy-and-report.sh +586 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/tests/_bootstrap.py +153 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/tests/test_supervisor_config_contracts.py +116 -0
- package/skills/service-omni-supervisor-permset-assign/SKILL.md +88 -0
- package/skills/service-omni-supervisor-permset-assign/references/api-notes.md +48 -0
- package/skills/service-omni-supervisor-permset-assign/scripts/verify-and-assign.sh +316 -0
- package/skills/service-omni-supervisor-surface-deploy/SKILL.md +85 -0
- package/skills/service-omni-supervisor-surface-deploy/references/api-notes.md +47 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/deploy-and-report.sh +216 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/tests/_bootstrap.py +95 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/tests/test_supervisor_surface_contracts.py +154 -0
- package/skills/service-omni-supervisor-users-create/SKILL.md +92 -0
- package/skills/service-omni-supervisor-users-create/assets/create-supervisors.apex.template +72 -0
- package/skills/service-omni-supervisor-users-create/references/apex-patterns.md +73 -0
- package/skills/service-omni-supervisor-users-create/references/apex-template-notes.md +41 -0
- package/skills/service-omni-supervisor-users-create/scripts/detect-and-create.sh +197 -0
- package/skills/service-omni-supervisor-users-create/scripts/detect-existing.sh +112 -0
- package/skills/service-omni-supervisor-users-create/scripts/run-create.sh +296 -0
- package/skills/service-omni-work-skill-routing-configure/SKILL.md +80 -0
- package/skills/service-omni-work-skill-routing-configure/references/api-notes.md +67 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/configure-and-report.sh +262 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/tests/_bootstrap.py +121 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/tests/test_work_skill_routing_contracts.py +161 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Coverage gap analysis — mechanics (Workflow D)
|
|
2
|
+
|
|
3
|
+
The mechanics for **Workflow D**. The judgment (read-only, candidates-not-verdicts, depth-1,
|
|
4
|
+
disposition-to-admin) and the user-facing script live in `references/gap-analysis-guide.md`; this
|
|
5
|
+
file is the *how*. Everything here is **read-only** — Workflow D never writes metadata.
|
|
6
|
+
|
|
7
|
+
> **This entire file is INTERNAL — never narrated to the user.** The describe commands, the ~5-object
|
|
8
|
+
> bound, and every note about truncation / event-message limits / token cost / "scored 0" steer *how
|
|
9
|
+
> you work*; they are not status updates. Do the work quietly and present only the four user-facing
|
|
10
|
+
> blocks from `gap-analysis-guide.md`. See its *Internal vs. user-facing* section.
|
|
11
|
+
|
|
12
|
+
## Contents
|
|
13
|
+
|
|
14
|
+
- [1. Read current coverage](#1-read-current-coverage)
|
|
15
|
+
- [2. Enumerate one-hop candidate objects](#2-enumerate-one-hop-candidate-objects)
|
|
16
|
+
- [3. Flag candidate fields (the rubric)](#3-flag-candidate-fields-the-rubric)
|
|
17
|
+
- [4. Diff against the current tree](#4-diff-against-the-current-tree)
|
|
18
|
+
- [5. The depth gate](#5-the-depth-gate)
|
|
19
|
+
- [6. The transparency report shape](#6-the-transparency-report-shape)
|
|
20
|
+
|
|
21
|
+
## 1. Read current coverage
|
|
22
|
+
|
|
23
|
+
Load the policy's existing roots and paths so gaps are reported *relative to* them. On a plain `sf`
|
|
24
|
+
surface the tree is Metadata API:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
sf project retrieve start --metadata DsarPolicy:<PolicyDeveloperName> --target-org <alias>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
On an MCP / SOR surface, read it with the `DsarPolicyManager` `list`/`describe` operation instead.
|
|
31
|
+
Record the set of `{object → [fields]}` already covered — that set is subtracted in step 4.
|
|
32
|
+
|
|
33
|
+
## 2. Enumerate one-hop candidate objects
|
|
34
|
+
|
|
35
|
+
For **each root** object in the policy, describe it once and read two things from the result:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# A full object describe is ~100KB+ of JSON. Do NOT read it raw into the turn — the accumulated
|
|
39
|
+
# describe payloads overflow the agent's event-message size limit and the run TRUNCATES before it
|
|
40
|
+
# writes the report. Always project to just the fields you reason over, so only a few KB enters context:
|
|
41
|
+
sf sobject describe --sobject <RootObject> --target-org <alias> --json \
|
|
42
|
+
| python3 -c 'import json,sys; d=json.load(sys.stdin)["result"]; \
|
|
43
|
+
print(json.dumps({"fields":[{"name":f["name"],"type":f["type"],"label":f["label"]} for f in d["fields"]], \
|
|
44
|
+
"children":[c["childSObject"] for c in d["childRelationships"]][:40]}, indent=1))'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- **`fields[]`** — the root's own fields (candidate personal-data fields live here).
|
|
48
|
+
- **one relationship hop out** — the objects directly related to the root:
|
|
49
|
+
- `childRelationships[]` → child objects (e.g. Contact → Cases, Contact → ContactPointEmails);
|
|
50
|
+
- reference/lookup `fields[]` (`type: "reference"`) → parent objects the root points to.
|
|
51
|
+
|
|
52
|
+
**Never read a raw `--json` describe into the turn.** Project every describe (root and one-hop)
|
|
53
|
+
through the field-name/type/label filter above. This is the single most important guard against the
|
|
54
|
+
`Truncated event message received` failure — the describe JSON, not the number of objects, is what
|
|
55
|
+
overruns the stream.
|
|
56
|
+
|
|
57
|
+
Describe each one-hop object once to read *its* `fields[]`. **Stop there.** Do not describe the
|
|
58
|
+
objects *those* relate to — that is level 2, gated behind explicit confirmation (step 5). The
|
|
59
|
+
one-hop rule is what keeps the scan bounded and cheap; it is also the tree-cap discipline (skill
|
|
60
|
+
call #3) applied to discovery.
|
|
61
|
+
|
|
62
|
+
**Hard bound — describe at most ~5 one-hop objects, then stop and report.** A standard root such as
|
|
63
|
+
`Contact` exposes dozens of `childRelationships[]`; describing all of them floods the context and
|
|
64
|
+
the run can truncate before it ever writes the report (a failed audit, not a thorough one). Pick the
|
|
65
|
+
**most privacy-relevant** related objects (contact points, cases/activities carrying supplied
|
|
66
|
+
contact data, the parent account) — describe those few, and **name in the report the related objects
|
|
67
|
+
you did *not* describe** so the omission is transparent, offering them under the depth gate (step 5).
|
|
68
|
+
The root's own describe plus a couple of one-hop objects is enough for a correct, complete audit;
|
|
69
|
+
**describing more than ~5 objects adds no value and risks truncation.**
|
|
70
|
+
|
|
71
|
+
**Policy/type unreadable (the accepted environment path) → stop at the root describe.** If
|
|
72
|
+
`DsarPolicy` can't be read (Metadata/Tooling/Connect all 404 — the feature isn't enabled on this
|
|
73
|
+
org), do **not** fan out to one-hop objects at all: a single **projected** `Contact` describe is
|
|
74
|
+
enough to demonstrate the depth-1 candidate reasoning. Flag the candidate fields on the root, state
|
|
75
|
+
that the policy/type couldn't be read and that you therefore reasoned from the root alone, name the
|
|
76
|
+
one-hop objects you *would* scan next (without describing them), and write the report. Casting wider
|
|
77
|
+
here only adds truncation risk for no benefit — the gap-analysis *method* is what matters, not breadth.
|
|
78
|
+
|
|
79
|
+
**Write incrementally; never let discovery starve the report.** The report file is the deliverable —
|
|
80
|
+
an audit that describes many objects but never writes is a failed, incomplete audit — strictly worse
|
|
81
|
+
than one that scans fewer objects and writes. Create `${outputDir}` and draft the report early, fill it in
|
|
82
|
+
as you describe, and once you have the root + a couple of one-hop objects **write it** — then offer
|
|
83
|
+
more depth, rather than gathering everything first and writing last.
|
|
84
|
+
|
|
85
|
+
## 3. Flag candidate fields (the rubric)
|
|
86
|
+
|
|
87
|
+
On the root and each one-hop object, mark fields that **may** hold the subject's personal data.
|
|
88
|
+
Every flag carries a **reason**; use the strongest available signal and name it:
|
|
89
|
+
|
|
90
|
+
| Signal (strongest first) | How to read it | Reason string |
|
|
91
|
+
|--------------------------|----------------|---------------|
|
|
92
|
+
| Field compliance metadata | describe field's `complianceGroup` / `securityClassification` (when populated) | `marked <value> in field metadata` |
|
|
93
|
+
| Field type | `type` = `email` / `phone` / `address`; `date` used as birthdate | `<type> field, common personal data` |
|
|
94
|
+
| Name / label semantics | tokens: Name, First/Last, Email, Phone, Mobile, SSN, Passport, DOB/Birth, Address/Street/City/Postal, IP, DeviceId, TaxId | `name/label suggests <category>` |
|
|
95
|
+
| Duplicate of a covered value | same value already mapped on another object | `duplicate of a covered field; admin picks canonical source` |
|
|
96
|
+
|
|
97
|
+
**These are candidates, not classifications.** A field the rubric misses can still be personal
|
|
98
|
+
data; a flagged field may not be. The admin confirms (skill call #2). When `complianceGroup` /
|
|
99
|
+
`securityClassification` come back empty on the org, say so — the scan then rests on type and name
|
|
100
|
+
semantics only, which is weaker; state that limitation in the report.
|
|
101
|
+
|
|
102
|
+
## 4. Diff against the current tree
|
|
103
|
+
|
|
104
|
+
Candidate = flagged in step 3 **and not** in the covered set from step 1. Group the result by
|
|
105
|
+
object. Also list, per scanned object, the fields **already** covered — the report shows covered vs
|
|
106
|
+
newly-surfaced side by side so the admin sees what the policy already handles.
|
|
107
|
+
|
|
108
|
+
## 5. The depth gate
|
|
109
|
+
|
|
110
|
+
After presenting the one-hop findings, offer to go deeper — but only expand on **explicit
|
|
111
|
+
confirmation**, and warn first:
|
|
112
|
+
|
|
113
|
+
> Going beyond one level produces a large volume of output and uses considerably more tokens.
|
|
114
|
+
|
|
115
|
+
If the admin confirms, repeat steps 2–4 for exactly the depth they approved (usually one more hop),
|
|
116
|
+
then stop and re-offer. Never recurse unprompted; never "just keep going down the tree."
|
|
117
|
+
|
|
118
|
+
## 6. The transparency report shape
|
|
119
|
+
|
|
120
|
+
The report must make the method and its limits visible (skill call: transparency). Include:
|
|
121
|
+
|
|
122
|
+
- **Objects scanned** — the roots and the one-hop objects, by name (so the admin sees the actual
|
|
123
|
+
surface examined, not just conclusions).
|
|
124
|
+
- **Candidate fields** — a table of `object | field | reason | already in policy?`.
|
|
125
|
+
- **Covered vs surfaced** — what the policy already maps, alongside the new candidates.
|
|
126
|
+
- **Search limit** — an explicit line that only one level was scanned, and the offer to go deeper.
|
|
127
|
+
- **Disposition prompt** — which candidates to add; adding routes to Workflow A (authored INACTIVE,
|
|
128
|
+
no auto-reactivation). Report even when nothing new was found ("no uncovered candidates at one
|
|
129
|
+
level").
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Find the right SOR when a routing tool is in reach (discover → describe → dispatch)
|
|
2
|
+
|
|
3
|
+
**Detect by tool shape, not by host name.** Check the tools available to you this run for a generic
|
|
4
|
+
capability-routing tool exposing **`discover` / `describe` / `dispatch`** verbs. The name may carry a
|
|
5
|
+
host prefix (`mcp__<host>__discover`, a bare `discover`, etc.) — **match the verb shape**. Such tools
|
|
6
|
+
are provided by SOR-routing surfaces (project-codey / Headless 360, Vibes / `vibes-cli`, Tool Factory
|
|
7
|
+
today; treat these as examples — the names change, the verb shape is the stable signal).
|
|
8
|
+
|
|
9
|
+
When one is in reach, the entry move for any DsarPolicy task is to **locate the SOR that covers the
|
|
10
|
+
capability**, then let it route the call. You supply judgment (which SOR, which operation, is the
|
|
11
|
+
request bounded / consented / an export-not-erasure); the SOR supplies the plumbing.
|
|
12
|
+
|
|
13
|
+
## 1. Discover — find the SOR, don't guess its name
|
|
14
|
+
|
|
15
|
+
Search the **capability**, not a guessed identifier:
|
|
16
|
+
|
|
17
|
+
- "manage DSAR / data-subject portability policies"
|
|
18
|
+
- "execute a DSAR policy against a data subject"
|
|
19
|
+
- "DSAR policy run history"
|
|
20
|
+
|
|
21
|
+
The SOR that owns this steel thread is **`DsarPolicyManager`** (owning team **Privacy Center**,
|
|
22
|
+
complexity SIMPLE). Its `agent_description` names DSAR policies + Right-to-Portability and the full
|
|
23
|
+
lifecycle:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
getAccessInfo → list policies → save (create / full-tree update) → activate
|
|
27
|
+
→ execute against a dataSubjectId → deactivate → delete
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`isActive` is the load-bearing gate: **execute** needs ACTIVE; **edit / delete** need INACTIVE.
|
|
31
|
+
|
|
32
|
+
If several SORs surface, pick the one whose `agent_description` names **DSAR + Right-to-Portability**.
|
|
33
|
+
**Reject look-alikes:** data mask, generic consent, or anything framed as subject *erasure* — RTP
|
|
34
|
+
is a portability export, it deletes nothing. If none matches, say so and stop; do not fabricate a
|
|
35
|
+
SOR.
|
|
36
|
+
|
|
37
|
+
## 2. Describe — the contract comes from describe, not memory
|
|
38
|
+
|
|
39
|
+
`describe` the specific operation before calling it. The exact inputs — `policyName` (must match
|
|
40
|
+
`[a-zA-Z]+[a-zA-Z0-9_]*`), `description`, `isActive`, `relatedTrees` (UI-serialized node keys), and
|
|
41
|
+
`dataSubjectId` for execute — come from the operation's schema, not recall.
|
|
42
|
+
|
|
43
|
+
## 3. Dispatch — the SOR routes to the backend
|
|
44
|
+
|
|
45
|
+
`dispatch` the operation. The SOR picks the backend (the DsarPolicy config tree, the RTP execute
|
|
46
|
+
endpoint, the `DsarPolicyLog` read); you never hand-assemble the route.
|
|
47
|
+
|
|
48
|
+
## No routing tool in reach?
|
|
49
|
+
|
|
50
|
+
If no discover/describe/dispatch tool is available this run, fall back to the metadata / Connect /
|
|
51
|
+
SOQL routes in the SKILL.md object-model table. The judgment in the skill is identical either way —
|
|
52
|
+
the skill never changes *what* is correct, only *which tool* executes it.
|
|
53
|
+
|
|
54
|
+
## Related
|
|
55
|
+
|
|
56
|
+
- `../SKILL.md` — the object-model table (the plain-`sf` equivalents) and the three workflows
|
|
57
|
+
- The routing tool's own `discover` verb — "which SORs cover `<capability>`?" is answered by the
|
|
58
|
+
discover/describe verbs described at the top of this file (project-codey / Headless 360), not by a
|
|
59
|
+
separate skill.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Report format — per-workflow report contracts
|
|
2
|
+
|
|
3
|
+
Write the report to `${outputDir}/report.md`, reporting only **the workflow you ran**. The
|
|
4
|
+
length/concision rules (say each load-bearing point once, well under ~150 lines, no exhaustive
|
|
5
|
+
per-object dumps) live in SKILL.md's `## Output` section — they are not repeated here. **This file
|
|
6
|
+
adds only what SKILL.md does not: the per-workflow section contracts below.** The expected-content
|
|
7
|
+
lists are a ceiling, not a checklist to pad toward.
|
|
8
|
+
|
|
9
|
+
## Workflow A (configure)
|
|
10
|
+
|
|
11
|
+
**Bounded request:** (1) the classification strategy — subject, root(s), per-relationship
|
|
12
|
+
follow/stop and why; (2) the tree — roots, paths, fields, with node count vs 200, max depth vs 10,
|
|
13
|
+
max children vs 10; (3) lifecycle — authored INACTIVE, must be activated to run, ACTIVE edits need
|
|
14
|
+
deactivate→edit→then **explicit user-confirmed** reactivation (never auto-reactivated); (4) the
|
|
15
|
+
guardrail — authoring/editing/deleting the policy never deletes the subject's records; (5) deploy
|
|
16
|
+
outcome — deployed, or the raw error + prerequisite.
|
|
17
|
+
|
|
18
|
+
**Unbounded request:** instead state the caps, name the specific cap the request would exceed, and
|
|
19
|
+
offer the bounded alternative (split/prune) — never a silently truncated "complete" map.
|
|
20
|
+
|
|
21
|
+
**Under-specified request:** instead enumerate the classification decisions the admin must make
|
|
22
|
+
(roots; per-relationship follow/stop; fields) and mark any proposal disposition-pending — never an
|
|
23
|
+
authoritative map of guessed personal data.
|
|
24
|
+
|
|
25
|
+
## Workflow B (export)
|
|
26
|
+
|
|
27
|
+
(1) the **resolved subject** — identifier (email/name/id) → root-entity Id + type — and the
|
|
28
|
+
**policy chosen** (noting if it was ambiguous and you asked); (2) consent confirmed, or the run
|
|
29
|
+
withheld as ambiguous; (3) that this was an **export, not a deletion**; (4) the **poll ordering**
|
|
30
|
+
made explicit — a couple of polls, the file retrieved only after the run reaches a terminal state,
|
|
31
|
+
an early not-ready response is expected, and if still non-terminal you **asked** the user before
|
|
32
|
+
continuing (never looped); stated even on the accepted preflight-error path; (5) outcome from the
|
|
33
|
+
**envelope / run status** in plain terms — running / completed / errored — not the HTTP code; (6)
|
|
34
|
+
file location on success (from the `dsr` getfile route, after terminal); (7) any missing
|
|
35
|
+
permission/feature named with the raw error. A non-terminal run is a valid stopping point — report
|
|
36
|
+
it as *still running* and defer to the user, don't diagnose the async backend.
|
|
37
|
+
|
|
38
|
+
**Preflight-error path (feature/policy/subject absent — the run never started):** keep it short.
|
|
39
|
+
Lead with the blocker(s) and the prerequisite to unblock each; state export-not-deletion **once**;
|
|
40
|
+
state the poll-then-download ordering **once** (a single short paragraph — this is item 4, not a
|
|
41
|
+
recurring theme). Do **not** add a separate essay re-explaining "grab it right away," and do **not**
|
|
42
|
+
enumerate every subject-search query — one representative command and the outcome is enough. Two
|
|
43
|
+
blockers → two short entries, not two full-screen tables.
|
|
44
|
+
|
|
45
|
+
## Workflow C (history)
|
|
46
|
+
|
|
47
|
+
The prior runs from the `DsarPolicyLog` SOQL read (or an explicit "no prior runs"), and that **no
|
|
48
|
+
new run was started**.
|
|
49
|
+
|
|
50
|
+
## Workflow D (coverage gap analysis)
|
|
51
|
+
|
|
52
|
+
(1) the **method** stated once up front — from the policy root(s), one relationship hop out,
|
|
53
|
+
candidate fields flagged with a reason each, admin disposes; (2) the **objects scanned** by name
|
|
54
|
+
(roots + the one-hop objects); (3) a **candidate table** — `object | field | reason | in policy?`;
|
|
55
|
+
(4) **covered vs newly surfaced**; (5) the **one-level search limit** stated explicitly, plus the
|
|
56
|
+
offer to go deeper only on confirmation (with the token-cost warning); (6) a **disposition prompt** —
|
|
57
|
+
which to add (adding routes to Workflow A: authored INACTIVE, no auto-reactivation) — and that the
|
|
58
|
+
audit deletes nothing. "No uncovered candidates at one level" is a valid result. Keep the candidate
|
|
59
|
+
table to the roots + one hop; don't append further-hop objects to look thorough.
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Unit tests for validate-policy-tree.py.
|
|
2
|
+
|
|
3
|
+
Pure, deterministic cap/name checks on a DsarPolicy tree JSON — unit tested here
|
|
4
|
+
rather than via evals (evals score the LLM-authored artifact against a gold; they
|
|
5
|
+
do not assert the validator's fail-closed behavior).
|
|
6
|
+
|
|
7
|
+
The filename has hyphens, so it is loaded by path via importlib rather than a
|
|
8
|
+
normal import.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import importlib.util
|
|
13
|
+
import pathlib
|
|
14
|
+
import unittest
|
|
15
|
+
|
|
16
|
+
_SCRIPT = pathlib.Path(__file__).resolve().parents[1] / "validate-policy-tree.py"
|
|
17
|
+
_spec = importlib.util.spec_from_file_location("validate_policy_tree", _SCRIPT)
|
|
18
|
+
vpt = importlib.util.module_from_spec(_spec)
|
|
19
|
+
_spec.loader.exec_module(vpt)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _root(name="ContactRoot", **over):
|
|
23
|
+
node = {"developerName": name, "object": "Contact", "fields": ["Email"], "children": []}
|
|
24
|
+
node.update(over)
|
|
25
|
+
return node
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _tree(dev="CustomerPortability", roots=None):
|
|
29
|
+
return {"developerName": dev, "roots": roots if roots is not None else [_root()]}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class NameValidationTests(unittest.TestCase):
|
|
33
|
+
def test_plain_name_ok(self):
|
|
34
|
+
vpt.check_name("ContactRoot", "where") # does not raise
|
|
35
|
+
|
|
36
|
+
def test_trailing_newline_rejected(self):
|
|
37
|
+
# Regression: ^...$ with .match() accepted "Foo\n" because $ matches
|
|
38
|
+
# before a final newline. \A...\Z must reject it.
|
|
39
|
+
with self.assertRaises(vpt.TreeError):
|
|
40
|
+
vpt.check_name("ContactRoot\n", "where")
|
|
41
|
+
|
|
42
|
+
def test_embedded_newline_rejected(self):
|
|
43
|
+
with self.assertRaises(vpt.TreeError):
|
|
44
|
+
vpt.check_name("Contact\nRoot", "where")
|
|
45
|
+
|
|
46
|
+
def test_leading_digit_rejected(self):
|
|
47
|
+
with self.assertRaises(vpt.TreeError):
|
|
48
|
+
vpt.check_name("1Contact", "where")
|
|
49
|
+
|
|
50
|
+
def test_empty_or_nonstring_rejected(self):
|
|
51
|
+
with self.assertRaises(vpt.TreeError):
|
|
52
|
+
vpt.check_name("", "where")
|
|
53
|
+
with self.assertRaises(vpt.TreeError):
|
|
54
|
+
vpt.check_name(None, "where")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TreeValidationTests(unittest.TestCase):
|
|
58
|
+
def test_minimal_valid_tree(self):
|
|
59
|
+
self.assertEqual(vpt.validate(_tree()), 1)
|
|
60
|
+
|
|
61
|
+
def test_trailing_newline_devname_fails_end_to_end(self):
|
|
62
|
+
with self.assertRaises(vpt.TreeError):
|
|
63
|
+
vpt.validate(_tree(roots=[_root(name="ContactRoot\n")]))
|
|
64
|
+
|
|
65
|
+
def test_too_many_children_rejected(self):
|
|
66
|
+
kids = [_root(name=f"Child{i}") for i in range(vpt.MAX_CHILDREN + 1)]
|
|
67
|
+
with self.assertRaises(vpt.TreeError):
|
|
68
|
+
vpt.validate(_tree(roots=[_root(children=kids)]))
|
|
69
|
+
|
|
70
|
+
def test_empty_roots_rejected(self):
|
|
71
|
+
with self.assertRaises(vpt.TreeError):
|
|
72
|
+
vpt.validate(_tree(roots=[]))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
unittest.main()
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate a DsarPolicy tree against the hard caps before authoring metadata.
|
|
3
|
+
|
|
4
|
+
Checks (exits non-zero on the FIRST violation, printing it):
|
|
5
|
+
- children per path <= 10
|
|
6
|
+
- tree depth <= 10 (a root path is depth 1)
|
|
7
|
+
- total nodes <= 200 (roots + every descendant path)
|
|
8
|
+
- every developerName matches [a-zA-Z]+[a-zA-Z0-9_]*
|
|
9
|
+
|
|
10
|
+
Input: a JSON file describing the tree. Shape (see references/configure.md):
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
"developerName": "CustomerPortability",
|
|
14
|
+
"roots": [
|
|
15
|
+
{
|
|
16
|
+
"developerName": "ContactRoot",
|
|
17
|
+
"object": "Contact",
|
|
18
|
+
"fields": ["FirstName", "Email"],
|
|
19
|
+
"children": [
|
|
20
|
+
{ "developerName": "ContactCases", "relationship": "Cases",
|
|
21
|
+
"object": "Case", "fields": ["Subject"], "children": [] }
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Usage: python3 validate-policy-tree.py <tree.json>
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import json
|
|
31
|
+
import re
|
|
32
|
+
import sys
|
|
33
|
+
|
|
34
|
+
MAX_CHILDREN = 10
|
|
35
|
+
MAX_DEPTH = 10
|
|
36
|
+
MAX_NODES = 200
|
|
37
|
+
# \A ... \Z (not ^ ... $): $ also matches just before a trailing "\n", so a
|
|
38
|
+
# developerName like "Foo\n" would slip through .match(). \Z anchors the true
|
|
39
|
+
# end of the string and rejects any trailing newline.
|
|
40
|
+
NAME_RE = re.compile(r"\A[a-zA-Z]+[a-zA-Z0-9_]*\Z")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TreeError(Exception):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def check_name(name, where):
|
|
48
|
+
if not isinstance(name, str) or not name:
|
|
49
|
+
raise TreeError(f"{where}: developerName is missing or not a string")
|
|
50
|
+
if not NAME_RE.match(name):
|
|
51
|
+
raise TreeError(
|
|
52
|
+
f"{where}: developerName {name!r} does not match [a-zA-Z]+[a-zA-Z0-9_]*"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def walk(node, depth, path):
|
|
57
|
+
"""Return the node count for this subtree; raise on the first violation."""
|
|
58
|
+
where = f"{path}"
|
|
59
|
+
check_name(node.get("developerName"), where)
|
|
60
|
+
|
|
61
|
+
if depth > MAX_DEPTH:
|
|
62
|
+
raise TreeError(
|
|
63
|
+
f"{where}: depth {depth} exceeds the {MAX_DEPTH}-level cap"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
children = node.get("children") or []
|
|
67
|
+
if not isinstance(children, list):
|
|
68
|
+
raise TreeError(f"{where}: 'children' must be a list")
|
|
69
|
+
if len(children) > MAX_CHILDREN:
|
|
70
|
+
raise TreeError(
|
|
71
|
+
f"{where}: {len(children)} children exceeds the {MAX_CHILDREN}-per-path cap"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
count = 1
|
|
75
|
+
for i, child in enumerate(children):
|
|
76
|
+
child_name = child.get("developerName", f"[{i}]") if isinstance(child, dict) else f"[{i}]"
|
|
77
|
+
if not isinstance(child, dict):
|
|
78
|
+
raise TreeError(f"{where} > child {i}: not an object")
|
|
79
|
+
count += walk(child, depth + 1, f"{path} > {child_name}")
|
|
80
|
+
return count
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def validate(tree):
|
|
84
|
+
if not isinstance(tree, dict):
|
|
85
|
+
raise TreeError("top-level JSON must be an object")
|
|
86
|
+
check_name(tree.get("developerName"), "DsarPolicy")
|
|
87
|
+
|
|
88
|
+
roots = tree.get("roots")
|
|
89
|
+
if not isinstance(roots, list) or not roots:
|
|
90
|
+
raise TreeError("'roots' must be a non-empty list of root paths")
|
|
91
|
+
|
|
92
|
+
total = 0
|
|
93
|
+
for i, root in enumerate(roots):
|
|
94
|
+
if not isinstance(root, dict):
|
|
95
|
+
raise TreeError(f"roots[{i}]: not an object")
|
|
96
|
+
root_name = root.get("developerName", f"[{i}]")
|
|
97
|
+
total += walk(root, 1, f"{root_name}")
|
|
98
|
+
|
|
99
|
+
if total > MAX_NODES:
|
|
100
|
+
raise TreeError(f"total nodes {total} exceeds the {MAX_NODES}-node cap")
|
|
101
|
+
|
|
102
|
+
return total
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main(argv):
|
|
106
|
+
if len(argv) != 2:
|
|
107
|
+
print(f"usage: {argv[0]} <tree.json>", file=sys.stderr)
|
|
108
|
+
return 2
|
|
109
|
+
try:
|
|
110
|
+
with open(argv[1], "r", encoding="utf-8") as fh:
|
|
111
|
+
tree = json.load(fh)
|
|
112
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
113
|
+
print(f"error: cannot read/parse {argv[1]}: {exc}", file=sys.stderr)
|
|
114
|
+
return 2
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
total = validate(tree)
|
|
118
|
+
except TreeError as exc:
|
|
119
|
+
print(f"INVALID: {exc}", file=sys.stderr)
|
|
120
|
+
return 1
|
|
121
|
+
|
|
122
|
+
print(
|
|
123
|
+
f"OK: tree valid — {total} node(s), within caps "
|
|
124
|
+
f"(<= {MAX_CHILDREN} children/path, depth <= {MAX_DEPTH}, <= {MAX_NODES} nodes)"
|
|
125
|
+
)
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
sys.exit(main(sys.argv))
|