@salesforce/afv-skills 1.55.0 → 1.56.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-generate/SKILL.md +8 -0
- package/skills/agentforce-generate/references/actions-reference.md +2 -2
- package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
- package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
- package/skills/agentforce-observe/SKILL.md +13 -9
- package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
- package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
- package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
- package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
- package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
- package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
- package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
- package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
- package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
- package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
- package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
- package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
- package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
- package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
- package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
- package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
- package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
- package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
- package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
- package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
- package/skills/dx-org-analyze/SKILL.md +0 -1
- package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
- package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
- package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
- package/skills/dx-org-manage/SKILL.md +6 -50
- package/skills/dx-org-manage/examples/README.md +10 -16
- package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
- package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
- package/skills/dx-org-shape-manage/SKILL.md +4 -3
- package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
- package/skills/experience-accessibility-validate/SKILL.md +8 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
- package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
- package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
- package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
- package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
- package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
- package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
- package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
- package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
- package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
- package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
- package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
- package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
- package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
- package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
- package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
- package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
- package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
- package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
- package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
- package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
- package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
- package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
- package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
- package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
- package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
- package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
- package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
- package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
- package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
- package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
- package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
- package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
- package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
- package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
- package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
- package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
- package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
- package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
- package/skills/mobile-apps-create/SKILL.md +1 -1
- package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
- package/skills/platform-sandbox-configure/SKILL.md +108 -133
- package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
- package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
- package/skills/platform-widget-generate/SKILL.md +26 -18
- package/skills/platform-widget-generate/examples/formulas.json +191 -0
- package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
- package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
- package/skills/sales-call-scoring-configure/SKILL.md +318 -0
- package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
- package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
- package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
- package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
- package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
- package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
- package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
- package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
- package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
- package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
- package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
- package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
- package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
- package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
- package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
- package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
- package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
- package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
- package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
- package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
- package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
- package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
- package/skills/service-de-channel-activate/SKILL.md +6 -5
- package/skills/service-de-channel-create/SKILL.md +3 -3
- package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
- package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
- package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
- package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
- package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
- package/skills/service-helpagent-coordinate/SKILL.md +3 -2
- package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
- package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
- package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
- package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
- package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
- package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
- package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
- package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
- package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
- package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
- package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
- package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
- package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
- package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
- package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
- package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
- package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
- package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
- package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
- package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
- package/skills/dx-org-manage/references/cli_flags.md +0 -67
- package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
- package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
- package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
- package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
- package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
- /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
- /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Reference — The Sync Management App Sync-Config Install Backbone
|
|
2
|
+
|
|
3
|
+
Background for [Act 1](act1-setup-sync.md). The install *logic* here is the
|
|
4
|
+
backbone of the whole steel thread. The logic lives in `public` (namespace-private) Apex with no
|
|
5
|
+
`global`/REST entry point, so it isn't directly callable from a subscriber org. The package exposes
|
|
6
|
+
it headlessly through `SyncConfigInstallEndpoint` (`@RestResource /v1/syncconfig/install`, GET
|
|
7
|
+
discovery + POST install) — a thin `global` wrapper over `installBaseline` (see "The headless
|
|
8
|
+
install endpoint" below). Understanding the underlying logic precisely still matters because it's
|
|
9
|
+
the most important capability and everything in Act 1 either feeds this install or works around
|
|
10
|
+
what it does *not* do.
|
|
11
|
+
|
|
12
|
+
## The four sync objects (+ state)
|
|
13
|
+
|
|
14
|
+
Installed/populated by the backbone; all are **List custom settings** (each prefixed by the
|
|
15
|
+
runtime-resolved namespace — from the installed package or the org's own namespace, written `<ns>`
|
|
16
|
+
below):
|
|
17
|
+
|
|
18
|
+
| Object | Role |
|
|
19
|
+
|--------|------|
|
|
20
|
+
| `Sync_Config__c` | Root sync configuration (the installed baseline root — e.g. the Retail Execution baseline; there is no config literally named "Standard"). |
|
|
21
|
+
| `Sync_Tracked_Object_Config__c` | Which objects sync + per-object settings. |
|
|
22
|
+
| `Sync_Named_Fetch_Tree_Nodes__c` | Fetch-tree graph (parent/child fetch relationships). |
|
|
23
|
+
| `Sync_Named_Query__c` | Named queries the client runs to fetch data. |
|
|
24
|
+
| `Sync_Config_State__c` | Per-config **version** rows — how installed version is tracked. |
|
|
25
|
+
|
|
26
|
+
**List custom settings ⇒ no auto-defaults on DML, but also ⇒ reachable via generic `core`
|
|
27
|
+
sObject REST + SOQL.** Two consequences:
|
|
28
|
+
- A raw `POST /sobjects/<ns>Sync_Config__c` inserts a bare row; the schema `<defaultValue>`s do
|
|
29
|
+
**not** fire. This is exactly why the baseline CSVs (not schema defaults) carry every value —
|
|
30
|
+
installing the baseline yields a fully-populated config; a hand-rolled insert would not.
|
|
31
|
+
- Because they're custom settings, **generic `core` reads** (`GET /query?q=...`) reach them with
|
|
32
|
+
no package code — this is what makes A3.1 count/version *verification* doable **today**. But
|
|
33
|
+
**row-level *writes* are not all safe as raw `POST /sobjects/<ns>...`.** The
|
|
34
|
+
`Sync_Client_App_Profile_Mapping__c` assignment row (A2.3) carries the **overloaded**
|
|
35
|
+
`Business_Area_Name__c` field (an IOU Id under `Default_IOU`, a Business Area picklist value under
|
|
36
|
+
`Custom_User_Field`); a raw insert that doesn't derive it from the config's strategy silently
|
|
37
|
+
corrupts the row (and skips dedup + the unique-Name convention). That write must go through the
|
|
38
|
+
**`/v1/syncconfig/assignment` endpoint** (`SyncConfigAssignmentEndpoint`, A2.3), which wraps the
|
|
39
|
+
same service the VF page uses. What generic sObject REST also cannot do is run the baseline-ZIP
|
|
40
|
+
install orchestration (unzip, `$$NS` macro, ordered multi-object insert, state-row bookkeeping) —
|
|
41
|
+
that's the `public` logic that now has a `global` wrapper (`SyncConfigInstallEndpoint`, A1.3; see
|
|
42
|
+
below).
|
|
43
|
+
|
|
44
|
+
### Field dictionary — the API names the skill's own reads/writes touch
|
|
45
|
+
|
|
46
|
+
Casing matters and several fields are **not** named the obvious way — guessing costs a describe
|
|
47
|
+
round-trip per miss. These are the exact API names (verified against the package object metadata)
|
|
48
|
+
for the fields this skill queries or writes. All take the `<ns>` prefix at runtime; the names below
|
|
49
|
+
are the unprefixed `__c` suffixes.
|
|
50
|
+
|
|
51
|
+
| Object | Field(s) the skill uses | Purpose / gotcha |
|
|
52
|
+
|--------|-------------------------|------------------|
|
|
53
|
+
| `Sync_Config__c` | `ClientApp_ID__c`, `Business_Area_Resolution_Strategy__c`, `Business_Area_Mapping__c` | Config root. `ClientApp_ID__c` keys the config (the `clientAppId` the list/assignment endpoints return); `Business_Area_Resolution_Strategy__c` is `Default_IOU` vs `Custom_User_Field`. |
|
|
54
|
+
| `Sync_Tracked_Object_Config__c` | `Object_Api_Name__c`, `Fieldset_Name_List__c`, `List_Header_Fieldset__c`, `ClientApp_ID__c` | Per-tracked-object. **It's `Object_Api_Name__c`** — not `Tracked_Object_API_Name__c`. `Fieldset_Name_List__c` + `List_Header_Fieldset__c` hold the A1.4 field-set names (comma-split). |
|
|
55
|
+
| `Sync_Named_Fetch_Tree_Nodes__c` | `Object_Api_Name__c`, `Fetch_Tree_Name__c`, `Parent_Node_Id__c` | Fetch-tree graph; count-only for A3.x. |
|
|
56
|
+
| `Sync_Named_Query__c` | `Bindable_Name__c`, `SOQL_Statement__c`, `Type__c` | Named queries; count-only for A3.x. |
|
|
57
|
+
| `Sync_Config_State__c` | `Current_Version__c`, `Initial_Install_Version__c`, `Last_Upgrade_Date__c` | Installed version is **`Current_Version__c`** — there is **no** `Config_Id__c` on this object; rows are keyed by the custom-setting `Name`, not a config-id field. |
|
|
58
|
+
| `Sync_Client_App_Profile_Mapping__c` | `Client_App_ID__c`, `Client_App_Profile__c`, `Mapped_Record_Id__c`, `Business_Area_Name__c` | The A2.3 assignment row (write via the endpoint, never raw). `Business_Area_Name__c` is the overloaded IOU-Id/Business-Area field. |
|
|
59
|
+
|
|
60
|
+
**The client-app field is spelled two different ways across objects — do not conflate:**
|
|
61
|
+
`Sync_Config__c` and `Sync_Tracked_Object_Config__c` use **`ClientApp_ID__c`** (no underscore
|
|
62
|
+
between "Client" and "App"), while `Sync_Client_App_Profile_Mapping__c` uses
|
|
63
|
+
**`Client_App_ID__c`** (underscore between every word). Both end `_ID__c` (uppercase `ID`).
|
|
64
|
+
|
|
65
|
+
When in doubt, **describe the object before querying** rather than guessing a field name — but
|
|
66
|
+
these are the ones the flow actually needs.
|
|
67
|
+
|
|
68
|
+
## The logic: `SyncConfigInstall.installBaseline(String staticResourceName)`
|
|
69
|
+
|
|
70
|
+
- Reads a **`SyncConfigBaseline_*` static-resource ZIP** containing `manifest.json` + CSVs.
|
|
71
|
+
- The `manifest.json` declares per-object CSVs; a namespace macro (`$$NS`) is rewritten to the
|
|
72
|
+
live prefix at install; per-config `Sync_Config_State__c` version rows are written so the
|
|
73
|
+
installed version is recorded.
|
|
74
|
+
- `SyncConfigOrchestrator` drives the orchestration and exposes the state readers
|
|
75
|
+
(`buildStateMap()` / `readCurrentVersion()`, backed by the `private` helper
|
|
76
|
+
`installedVersionMap()`).
|
|
77
|
+
|
|
78
|
+
## Why the install needs a package endpoint (not a raw insert)
|
|
79
|
+
|
|
80
|
+
The underlying logic is `public` (namespace-private) — the reason A1.3 (the baseline install,
|
|
81
|
+
which *is* the sync-configuration creation) needs the endpoint rather than a raw insert:
|
|
82
|
+
|
|
83
|
+
- `SyncConfigInstall` is declared **`public virtual with sharing`**; `installBaseline` is
|
|
84
|
+
**`public static`** — no `global`/`webservice`/`@RestResource`/`@InvocableMethod` of its own.
|
|
85
|
+
- The one caller that "ran" it, `MobileAppController.installManagedBaseline()`, is a
|
|
86
|
+
**`public PageReference`** Visualforce action — also not `global`/REST/invocable.
|
|
87
|
+
- The state readers on `SyncConfigOrchestrator` — `buildStateMap()` / `readCurrentVersion()` — are
|
|
88
|
+
**`public static`, unannotated** (their helper `installedVersionMap()` is **`private static`**).
|
|
89
|
+
- In a managed package, **`public` = namespace-private**: a subscriber org can invoke only
|
|
90
|
+
`global` members via anonymous Apex or REST (a `private` member is even less reachable). So none
|
|
91
|
+
of the above is reachable headlessly.
|
|
92
|
+
|
|
93
|
+
### The headless install endpoint — `SyncConfigInstallEndpoint`
|
|
94
|
+
|
|
95
|
+
The endpoint is a thin `global @RestResource(urlMapping='/v1/syncconfig/install/*')` wrapper over
|
|
96
|
+
the existing `public` logic — **no logic changes to the install path itself.**
|
|
97
|
+
|
|
98
|
+
- **POST `installConfig`** — body `{"req":{"staticResourceName":"..."}}` → `installBaseline`;
|
|
99
|
+
returns an `InstallResponse` (`status` SUCCESS/FAILED, `configId`, `version`, `recordsInserted`,
|
|
100
|
+
`errorMessage`). **On `status == FAILED` (or a thrown call), report `errorMessage` and stop —
|
|
101
|
+
a `FAILED` body inside an HTTP 200 is a failure, not success, and there is no raw-sObject
|
|
102
|
+
recovery** (see A1.3). **Single argument — `staticResourceName`. There is no `parentConfigId` param:**
|
|
103
|
+
parent linkage is read from the baseline ZIP's `manifest.json` (`parentConfigId`), not passed by
|
|
104
|
+
the caller. The install still guards a missing parent (a child whose parent isn't installed
|
|
105
|
+
fails) — but that's derived from the manifest, not a method argument.
|
|
106
|
+
- **GET `discoverConfigs`** — lists installable baselines as `ConfigOption`s
|
|
107
|
+
(`configId`, `displayName`, `parentConfigId`, `staticResourceName`, `availability`,
|
|
108
|
+
`requiresLabel`), each classified `AVAILABLE` / `ALREADY_INSTALLED` / `REQUIRES_PARENT`. This is
|
|
109
|
+
the discovery step: call GET to find the `staticResourceName` to POST. Wraps
|
|
110
|
+
`SyncConfigOrchestrator.discoverBaselines()` (the same read the admin VF install dropdown uses) —
|
|
111
|
+
read-only, no side effects.
|
|
112
|
+
- **Access** is by Apex class access — granted through *a* permission set in the target org (named
|
|
113
|
+
e.g. `Sync_Admin`, but that name can be renamed or folded into another set across orgs/releases,
|
|
114
|
+
so never depend on it) — **not** a runtime CRUD gate: the four objects are List custom settings,
|
|
115
|
+
for which `SYNCSecurityUtil.isObjectPermissible()` always returns true, so a CRUD gate would be a
|
|
116
|
+
no-op. The skill does not verify a permission set by name; a denied call is reported as an access
|
|
117
|
+
boundary (with who grants it).
|
|
118
|
+
- Never substitute a raw sObject insert for the endpoint — that skips the ZIP orchestration, `$$NS`
|
|
119
|
+
macro, ordering, and state rows.
|
|
120
|
+
|
|
121
|
+
A3.1 verification is record counts via generic `core` SOQL — the intended method, which works
|
|
122
|
+
today — so **no `/v1/syncconfig/verify` surface is proposed**; the state readers being
|
|
123
|
+
`public`/namespace-private is moot. Two sibling `/v1/syncconfig/*` endpoints ship alongside
|
|
124
|
+
install: **`/configs`** (`SyncConfigListEndpoint`, GET — lists every deployed `Sync_Config__c` as
|
|
125
|
+
`{name, clientAppId, resolutionStrategy}`; closes A3.1's *list* half) and **`/assignment`**
|
|
126
|
+
(`SyncConfigAssignmentEndpoint`, GET/POST/DELETE — the safe A2.3 user→config binding surface). See
|
|
127
|
+
[transports-and-namespace.md](transports-and-namespace.md) for the endpoint table.
|
|
128
|
+
|
|
129
|
+
## The shipped baselines
|
|
130
|
+
|
|
131
|
+
Several baseline **editions** ship — covering Consumer Goods, Direct-Store-Delivery, and Retail
|
|
132
|
+
Execution — each as its own static resource. **Enumerate them from the discovery endpoint
|
|
133
|
+
(`discoverConfigs`) at runtime; never hardcode the names or assume a fixed set** — editions are
|
|
134
|
+
added across releases.
|
|
135
|
+
|
|
136
|
+
**Pick the baseline that matches the org's edition.** The steel thread is the **Retail Execution**
|
|
137
|
+
path (its exact static-resource name comes from the discovery endpoint at runtime — never hardcode
|
|
138
|
+
it). **Act 3 verification derives the expected counts at runtime from whichever baseline was
|
|
139
|
+
actually installed** (that baseline's own manifest/CSVs) and compares them to the installed record
|
|
140
|
+
counts — it must **not** assert against hardcoded literals, since a different baseline (or release)
|
|
141
|
+
declares different counts.
|
|
142
|
+
|
|
143
|
+
## What the backbone does NOT do (and who owns those pieces)
|
|
144
|
+
|
|
145
|
+
- It does **not** resolve the **namespace** or **enable Retail Execution** (A1.1/A1.2 — core). (There is no license read to do — installing/using the app is not license-gated.)
|
|
146
|
+
- It does **not** ship **field-set definitions** (A1.4 — the CSV assigns field-set *names*; the
|
|
147
|
+
`FieldSet` metadata they reference, chiefly `RetailMobilityRelevant` on standard objects, is
|
|
148
|
+
**provisioned by Retail Execution**, not by this install and not by a separate deploy in this
|
|
149
|
+
flow — a managed package can't own a FieldSet on a standard object). A1.4 therefore **verifies**
|
|
150
|
+
the referenced sets exist (read-only describe), rather than deploying them; a missing set is an
|
|
151
|
+
RE-provisioning issue, and because the sync query-field path throws on it, the verify matters.
|
|
152
|
+
- It does **not** write **IOU membership** (A2.2 — core/RE; the skill only *reads* it as a
|
|
153
|
+
`Default_IOU` precondition and never writes it). User→config *assignment* (A2.3) and
|
|
154
|
+
*listing* deployed configs (A3.1) are now handled by the sibling `/v1/syncconfig/assignment` and
|
|
155
|
+
`/v1/syncconfig/configs` endpoints, not by the install backbone. A3.1 verification is record
|
|
156
|
+
counts via `core` SOQL — no additional invokable action is needed.
|
|
157
|
+
|
|
158
|
+
Treat `installBaseline` as the trunk; every other Act-1 sub-act is a branch that either prepares
|
|
159
|
+
its input (right baseline, RE-enabled, namespace resolved) or fills something it leaves undone.
|
package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Reference — Transports & Namespace
|
|
2
|
+
|
|
3
|
+
Load-bearing background for [`consumer-goods-sync-management-configure`](../SKILL.md). Read this before
|
|
4
|
+
authoring any step: it defines the two transports every step must be tagged with, and the
|
|
5
|
+
namespace discipline that every Sync Management App sObject/REST reference depends on.
|
|
6
|
+
|
|
7
|
+
## The two transports (the honesty spine)
|
|
8
|
+
|
|
9
|
+
Every step in every Act is exactly one of these. Tag it explicitly; never let a step be
|
|
10
|
+
untagged.
|
|
11
|
+
|
|
12
|
+
| Tag | Meaning | Where the action comes from |
|
|
13
|
+
|-----|---------|------------------------------|
|
|
14
|
+
| **`core`** | A generic Salesforce / Headless-360 capability. | `execute_api` (`/query`, `/tooling/query`, `/sobjects/`, `/connect/`, `/headless/*`). |
|
|
15
|
+
| **`Sync Management App`** | An in-package action the agent can trigger **today**. | A **`global @RestResource /v1/*`** endpoint (the ONLY headless package surface — see below). |
|
|
16
|
+
|
|
17
|
+
Where neither transport can complete a required step, report what's missing and who provides it
|
|
18
|
+
(the CG Cloud platform / Retail Execution, or the org admin), then stop that step — never fake it.
|
|
19
|
+
|
|
20
|
+
> **Transport reality check (do not skip).** The only real headless `Sync Management App` surface
|
|
21
|
+
> is the set of **`global @RestResource /v1/*`** endpoints. This matters because the install/verify
|
|
22
|
+
> logic exists as **`public`** Apex — and in a managed package `public` is **namespace-private**:
|
|
23
|
+
> unreachable from a subscriber org's anonymous Apex *or* REST. Only `global` members are
|
|
24
|
+
> reachable. So "the class has a method that does it" is **not** sufficient to call a step
|
|
25
|
+
> `Sync Management App`; there must be a `global`/`webservice` entry point (a `/v1/*` route) that reaches it.
|
|
26
|
+
|
|
27
|
+
Every automatable step resolves to a generic core capability or an invokable Sync Management App
|
|
28
|
+
action. Where neither exists for a required step, the skill reports what's needed and who
|
|
29
|
+
provides it, then stops that step rather than faking it.
|
|
30
|
+
|
|
31
|
+
### How to decide the tag
|
|
32
|
+
|
|
33
|
+
1. **Is there a `global @RestResource /v1/*` endpoint that does it?** → `Sync Management App`.
|
|
34
|
+
(A `public` Apex method does **not** count — it's namespace-private, unreachable from a subscriber org.)
|
|
35
|
+
2. **Else, is it a generic Salesforce operation via `execute_api`
|
|
36
|
+
(SOQL, sObject REST, Connect, Tooling)?** → `core`. *Note: the
|
|
37
|
+
four sync objects are **custom settings**, so generic sObject REST + SOQL reach them as
|
|
38
|
+
`core` — this covers most **reads** without any package change.*
|
|
39
|
+
|
|
40
|
+
If neither applies, the step is not automatable here — state what's missing, who provides it
|
|
41
|
+
(Sync Management App package code, or CG Cloud core / Retail Execution / the platform), and stop
|
|
42
|
+
that step. Do not invent a Sync Management App action to paper over a core limitation, and do not
|
|
43
|
+
fake success.
|
|
44
|
+
|
|
45
|
+
### What isn't automatable here
|
|
46
|
+
|
|
47
|
+
A3.1 verification is record counts via `core` SOQL — the intended method, so there is no
|
|
48
|
+
`/v1/syncconfig/verify` endpoint. And A2.2 IOU-membership *creation* is a CG Cloud core / Retail Execution task done
|
|
49
|
+
outside this flow — the skill only **reads** IOU membership as a `Default_IOU` precondition and
|
|
50
|
+
reports-and-stops if absent; it never writes it.
|
|
51
|
+
|
|
52
|
+
## The real `Sync Management App` headless surface: `global @RestResource /v1/*`
|
|
53
|
+
|
|
54
|
+
The **only** package actions an agent can trigger from a subscriber org today. `global`
|
|
55
|
+
endpoints in `force-app/main/default/classes/*Endpoint.cls`:
|
|
56
|
+
|
|
57
|
+
| Route | Purpose |
|
|
58
|
+
|-------|---------|
|
|
59
|
+
| `/v1/managedpackage` | Check whether the package is installed (`isManagedPackageInstalled`) — **check only, does not install**. |
|
|
60
|
+
| `/v1/devicemetadata` | Device metadata (feature settings) — **read-only**. |
|
|
61
|
+
| `/v1/namedquery`, `/v1/namedfetchtree` | Read named queries / fetch trees for a client app. |
|
|
62
|
+
| `/v1/download`, `/v1/upload` | Device sync data transfer. |
|
|
63
|
+
| `/v1/batchsoql`, `/v1/batchdml` | Generic SOQL/DML, but **gated by `isClientAppIdAvailable()`** (needs a pre-existing `Sync_Config__c`). |
|
|
64
|
+
| `/v1/assignment`, `/v1/clientappmapper`, `/v1/clientregistration`, `/v1/bulksobjectdescribe`, `/v1/metadata` | Device-facing sync support. **`/v1/assignment` (`AssignmentEndpoint`) is NOT the A2.3 user→config binding surface** — despite the name, it's a device call that *deletes* `Sync_ID_Mapping__c` local-id rows (`@HttpPost cleanRequest(recordIdList, deviceId)`) — note `Sync_ID_Mapping__c` is a **regular custom object** (the device-owned local-to-server id map), **not** one of the config List custom settings. For A2.3 use **`/v1/syncconfig/assignment`** (row below). |
|
|
65
|
+
| `/v1/syncconfig/install` | `SyncConfigInstallEndpoint`. **POST** installs a baseline (`installConfig`, body `{"req":{"staticResourceName":"..."}}`) → `InstallResponse` (status/configId/version/recordsInserted). **GET** (`discoverConfigs`) lists installable baselines with availability (`AVAILABLE` / `ALREADY_INSTALLED` / `REQUIRES_PARENT`) so a caller can discover the `staticResourceName` before POSTing. Provides the A1.3 baseline install path. |
|
|
66
|
+
| `/v1/syncconfig/configs` | `SyncConfigListEndpoint`. **GET** (`listConfigs`) lists every **deployed** `Sync_Config__c` as `{name, clientAppId, resolutionStrategy}` — distinct from install's `discoverConfigs` (installable *baselines*), this is what's actually *deployed*. Lets an assignment caller pick a config by name; the `name` it returns is the value `/v1/syncconfig/assignment` expects as `configPath`, and `resolutionStrategy` tells the caller whether the assignment takes an IOU id (`Default_IOU`) or a Business Area value (`Custom_User_Field`). Provides the *list* half of A3.1's read/verify path. |
|
|
67
|
+
| `/v1/syncconfig/assignment` | `SyncConfigAssignmentEndpoint`. Binds a Sync Config to a User(`005`)/Role(`00E`)/Profile(`00e`) — the headless "Config Assignment page". **Do not confuse with the device-facing `/v1/assignment` (`AssignmentEndpoint`) above** — that one cleans `Sync_ID_Mapping__c` device rows and is *not* the user→config surface; A2.3 is **this** `/v1/syncconfig/assignment` route (`Sync_Client_App_Profile_Mapping__c`). **GET** `?configId=<name>` lists assignments (resolves target names); **POST** body `{"req":{configPath, mappedRecordId, businessArea, iouId, recordId}}` upserts; **DELETE** `?id=<name-or-id>` removes. **This is the safe A2.3 explicit-mapping surface — a raw sObject POST to `Sync_Client_App_Profile_Mapping__c` is UNSAFE** because `Business_Area_Name__c` is overloaded (IOU Id under `Default_IOU`, Business Area picklist value under `Custom_User_Field`); the endpoint derives it from the config's strategy server-side and also enforces cross-config dedup + the unique-Name convention. Provides the A2.3 explicit-mapping capability. |
|
|
68
|
+
|
|
69
|
+
> The three `/v1/syncconfig/*` routes cover install/discovery, listing deployed configs with
|
|
70
|
+
> strategy, and binding a Sync Config to a User/Role/Profile with server-side derivation of the
|
|
71
|
+
> overloaded `Business_Area_Name__c` field from the config's resolution strategy. Their
|
|
72
|
+
> package-release rollout is a separate workstream. What the skill must never do is *fake* these
|
|
73
|
+
> calls: for the install, do **not** hand-roll raw sObject inserts (skips the ZIP orchestration,
|
|
74
|
+
> `$$NS` macro, ordering, and state rows); for A2.3, do **not** substitute a raw
|
|
75
|
+
> `Sync_Client_App_Profile_Mapping__c` write (it corrupts the overloaded
|
|
76
|
+
> `Business_Area_Name__c`). A `core` SOQL list is an equivalent fallback for the A3.1 *read/verify*
|
|
77
|
+
> path.
|
|
78
|
+
|
|
79
|
+
### Building the callable URL — the namespace goes in the ROUTE, not just the object names
|
|
80
|
+
|
|
81
|
+
The table above lists each endpoint's `urlMapping` (`/v1/syncconfig/*`). That is **not** the
|
|
82
|
+
callable path. Apex REST routes are served under `/services/apexrest`, and **when the metadata
|
|
83
|
+
carries a namespace the namespace is injected as a path segment** — exactly as it is for object
|
|
84
|
+
API names. This bites the org-namespace source-deploy case the skill treats as first-class: a
|
|
85
|
+
first call to `/services/apexrest/v1/syncconfig/configs` returns **`NOT_FOUND`**; the reachable
|
|
86
|
+
URL is `/services/apexrest/<ns>/v1/syncconfig/configs`.
|
|
87
|
+
|
|
88
|
+
- **Rule:** `POST/GET/DELETE /services/apexrest/<ns>/<urlMapping>`, where `<ns>` is the resolved
|
|
89
|
+
namespace **as a bare segment** — and this applies to **both** namespace sources:
|
|
90
|
+
a managed-package install **and** an org-namespaced source deploy. Only a bare *unmanaged* deploy
|
|
91
|
+
(no namespace at all) omits the segment.
|
|
92
|
+
- **`<ns>` here is the bare namespace, NOT `ns_prefix`.** `ns_prefix` carries the trailing `__`
|
|
93
|
+
(`<ns>__`) and is what prefixes **object/field API names** (`<ns>__Sync_Config__c`).
|
|
94
|
+
The **URL segment is the namespace *without* the underscores** (`<ns>`). Strip the trailing
|
|
95
|
+
`__` from `ns_prefix` to build the route segment.
|
|
96
|
+
|
|
97
|
+
| Namespace source | Object API name | Callable route |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| Namespaced — **either** a managed-package install **or** an org-namespaced source deploy | `<ns>__Sync_Config__c` | `/services/apexrest/<ns>/v1/syncconfig/configs` |
|
|
100
|
+
| Bare unmanaged (no namespace) | `Sync_Config__c` | `/services/apexrest/v1/syncconfig/configs` |
|
|
101
|
+
|
|
102
|
+
Resolve `<ns>` once from A1.1's namespace gate (same value the object paths use, minus the `__`)
|
|
103
|
+
and build every `/v1/syncconfig/*` route with it. A `NOT_FOUND` on a `/v1/*` route in a namespaced
|
|
104
|
+
org almost always means the segment was omitted — not that the endpoint's release is missing (probe
|
|
105
|
+
that only after confirming the namespaced URL also 404s).
|
|
106
|
+
|
|
107
|
+
**A3.1's *list* half is now covered by `/v1/syncconfig/configs`, and its *verify* half is record
|
|
108
|
+
counts via generic `core` SOQL — the intended method, which works today.** No `/v1/syncconfig/verify`
|
|
109
|
+
endpoint is proposed: Act 3 verification adds no Sync Management App call, so `SyncConfigOrchestrator`'s
|
|
110
|
+
state readers (`buildStateMap()` / `readCurrentVersion()` are `public static`, their helper
|
|
111
|
+
`installedVersionMap()` `private static`) being namespace-private is moot.
|
|
112
|
+
|
|
113
|
+
## Namespace discipline
|
|
114
|
+
|
|
115
|
+
- **The namespace has two possible sources — a package install OR the org's own namespace.** When
|
|
116
|
+
the metadata is installed as a **managed package**, the prefix is the package's, which **varies by
|
|
117
|
+
how it was built** (a production install and a non-production/CI build can carry different
|
|
118
|
+
prefixes). When the metadata is **source-deployed into a DE/packaging/dev org**, it takes the
|
|
119
|
+
**org's own registered namespace** (`Organization.NamespacePrefix`) and there is **no**
|
|
120
|
+
`InstalledSubscriberPackage` row at all. Never assume a specific value, and never assume a
|
|
121
|
+
package install — an empty package list can still be a fully-namespaced org.
|
|
122
|
+
- **Resolve the prefix at runtime — never hardcode.** The package itself resolves via
|
|
123
|
+
`SYNC_NamespaceHandler` / `SYNCConstants.NAMESPACEPREFIX`. For agent-issued `core` SOQL /
|
|
124
|
+
sObject REST against package objects, **discover the live prefix** in this order: (1) match the
|
|
125
|
+
Sync Management App package in `InstalledSubscriberPackage` and read its `NamespacePrefix`
|
|
126
|
+
(Tooling); (2) if there's no such row, read the **org namespace** with `SELECT NamespacePrefix
|
|
127
|
+
FROM Organization` (plain `core`, no Tooling) and confirm by resolving `<orgNs>Sync_Config__c`;
|
|
128
|
+
(3) failing both, probe candidate prefixes (package's known namespace(s), the org namespace, bare
|
|
129
|
+
unmanaged) via one known object's describe. Then substitute the resolved value as `<ns>` in every
|
|
130
|
+
package-object reference. See [readiness-and-enablement.md](readiness-and-enablement.md) A1.1.0 for the
|
|
131
|
+
full gate.
|
|
132
|
+
- **Core objects carry no namespace.** `User`, `InternalOrgUnitUser`,
|
|
133
|
+
`InternalOrganizationUnit`, `Profile`, `UserRole`, etc. are core — do **not** prefix
|
|
134
|
+
them. Only the four sync objects + `Sync_Config_State__c` + `Sync_Client_App_Profile_Mapping__c`
|
|
135
|
+
and other package custom settings take `<ns>`.
|
|
136
|
+
- **Why it bites:** the four sync objects are **List custom settings**. Their schema
|
|
137
|
+
`<defaultValue>`s do **not** auto-apply on Apex/`sobjects` DML insert — the install path
|
|
138
|
+
populates them from baseline CSVs, not from schema defaults. See
|
|
139
|
+
[sync-management-app-install-backbone.md](sync-management-app-install-backbone.md).
|
|
140
|
+
|
|
141
|
+
## Dispatcher vs. direct REST (which `execute_api` path)
|
|
142
|
+
|
|
143
|
+
- **`@AuraEnabled` / Apex-controller actions** go through the H360 **dispatcher**
|
|
144
|
+
(`/headless/invoke/...` envelope).
|
|
145
|
+
- **Generic reads/writes bypass the dispatcher and hit Core directly** — `/query`,
|
|
146
|
+
`/tooling/query`, `/sobjects/`, `/connect/`. Nearly every
|
|
147
|
+
`core` step in this skill is a direct-REST read/write, not a dispatcher call.
|
|
148
|
+
|
|
149
|
+
## `sf` CLI cookbook — parse-safe invocations (the CLI-fallback path)
|
|
150
|
+
|
|
151
|
+
When `execute_api` is unavailable or unauthenticated, every `core` and `Sync Management App`
|
|
152
|
+
op runs through the `sf` CLI. The CLI is only sugar over the REST resources above, but two concrete
|
|
153
|
+
mechanics bite hard and cost retries — bake these in:
|
|
154
|
+
|
|
155
|
+
- **stdout hygiene — the linked-plugin banner pollutes output.** In this workspace the linked
|
|
156
|
+
ESM plugin prints `Warning: @ind-rcg/modeler-sfdx-cli-plugin is a linked ESM module …` on the
|
|
157
|
+
first line, so `sf … --json` piped to a JSON parser (`python3`, `jq`) fails intermittently with `KeyError: 'result'` /
|
|
158
|
+
a JSON parse error. **Always `2>/dev/null` before parsing**, and if a stray line still slips into
|
|
159
|
+
stdout, slice from the first `{`/`[` (e.g. `sed -n '/^[[{]/,$p'`) before piping to a parser.
|
|
160
|
+
- **`--body @file.json` needs the `@`.** `sf api request rest … --body @install.json` reads the
|
|
161
|
+
request body **from the file**; without the `@` the CLI sends the literal string `install.json`
|
|
162
|
+
as the body → `JSON_PARSER_ERROR` on the POST. Write the body to a temp file, pass it with `@`.
|
|
163
|
+
- **Batch the four-object COUNT() verify into ONE anonymous-Apex call — never loop `sf data
|
|
164
|
+
query` per object.** Each `sf` invocation cold-starts the CLI (~15–20s); a 4–5-object COUNT()
|
|
165
|
+
loop blows the 2-min tool timeout. Run **one** `sf apex run` whose anonymous block queries all
|
|
166
|
+
four sync objects (+ `Sync_Config_State__c`) and `System.debug`s a single JSON blob — one
|
|
167
|
+
cold-start, every count. **This is the default for the A1.3 landing check and the A3.1 count
|
|
168
|
+
verify** (form 6 below); a single `sf data query COUNT()` (form 1) is fine only for a genuine
|
|
169
|
+
one-off read.
|
|
170
|
+
- **Parsing `sf apex run` output — grep `USER_DEBUG` FIRST.** `sf apex run` echoes the anonymous
|
|
171
|
+
block back as `Execute Anonymous:` source lines, so a bare grep for your marker string matches the
|
|
172
|
+
**echoed source** as well as the real `USER_DEBUG|…` line → two hits, parse failure. Always filter
|
|
173
|
+
to `USER_DEBUG` before extracting the marker (the direct analogue of the linked-plugin banner
|
|
174
|
+
hazard above): `… | grep USER_DEBUG | grep -o 'SYNCCOUNTS=.*'`.
|
|
175
|
+
|
|
176
|
+
Copy-pasteable forms (substitute `<org>` alias and the resolved `<ns>`/`<ns_prefix>`):
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# 1. core SOQL query (objects take ns_prefix with trailing __)
|
|
180
|
+
sf data query --target-org <org> --json 2>/dev/null \
|
|
181
|
+
--query "SELECT COUNT() FROM <ns_prefix>Sync_Config__c"
|
|
182
|
+
|
|
183
|
+
# 2. core Tooling query (InstalledSubscriberPackage, FieldSet, RetailExecutionSettings, …)
|
|
184
|
+
sf data query --use-tooling-api --target-org <org> --json 2>/dev/null \
|
|
185
|
+
--query "SELECT SubscriberPackage.Name, SubscriberPackage.NamespacePrefix FROM InstalledSubscriberPackage"
|
|
186
|
+
|
|
187
|
+
# 3. Apex-REST GET — namespace is a ROUTE SEGMENT (bare ns, no __); see "Building the callable URL"
|
|
188
|
+
sf api request rest "/services/apexrest/<ns>/v1/syncconfig/configs" \
|
|
189
|
+
--target-org <org> 2>/dev/null
|
|
190
|
+
|
|
191
|
+
# 4. Apex-REST POST with a body FILE — note the @ prefix
|
|
192
|
+
printf '%s' '{"req":{"staticResourceName":"<name>"}}' > "$TMPDIR/install.json"
|
|
193
|
+
sf api request rest "/services/apexrest/<ns>/v1/syncconfig/install" \
|
|
194
|
+
--method POST --body @"$TMPDIR/install.json" \
|
|
195
|
+
--target-org <org> 2>/dev/null
|
|
196
|
+
|
|
197
|
+
# 5. Apex-REST DELETE (assignment removal)
|
|
198
|
+
sf api request rest "/services/apexrest/<ns>/v1/syncconfig/assignment?id=<name-or-id>" \
|
|
199
|
+
--method DELETE --target-org <org> 2>/dev/null
|
|
200
|
+
|
|
201
|
+
# 6. Batched four-object COUNT() verify — ONE cold-start, all counts as a JSON blob
|
|
202
|
+
# (DEFAULT for the A1.3 landing check and the A3.1 count verify; SOQL takes ns_prefix + trailing __)
|
|
203
|
+
cat > "$TMPDIR/counts.apex" <<'APEX'
|
|
204
|
+
Map<String,Integer> c = new Map<String,Integer>{
|
|
205
|
+
'Sync_Config__c' => [SELECT COUNT() FROM <ns_prefix>Sync_Config__c],
|
|
206
|
+
'Sync_Tracked_Object_Config__c' => [SELECT COUNT() FROM <ns_prefix>Sync_Tracked_Object_Config__c],
|
|
207
|
+
'Sync_Named_Fetch_Tree_Nodes__c' => [SELECT COUNT() FROM <ns_prefix>Sync_Named_Fetch_Tree_Nodes__c],
|
|
208
|
+
'Sync_Named_Query__c' => [SELECT COUNT() FROM <ns_prefix>Sync_Named_Query__c],
|
|
209
|
+
'Sync_Config_State__c' => [SELECT COUNT() FROM <ns_prefix>Sync_Config_State__c]};
|
|
210
|
+
System.debug('SYNCCOUNTS=' + JSON.serialize(c));
|
|
211
|
+
APEX
|
|
212
|
+
sf apex run --target-org <org> --file "$TMPDIR/counts.apex" 2>/dev/null \
|
|
213
|
+
| grep USER_DEBUG | grep -o 'SYNCCOUNTS=.*' # grep USER_DEBUG FIRST — the block is echoed too
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`sf data query` returns `{"result":{"records":[…],"totalSize":N}}`; a bare `SELECT COUNT()` puts the
|
|
217
|
+
count in `result.totalSize`. `sf api request rest` returns the endpoint's raw JSON body (no
|
|
218
|
+
`result` envelope). `sf apex run` returns the debug log — the counts ride a `USER_DEBUG` line, and
|
|
219
|
+
the block is echoed as `Execute Anonymous:` source, so **grep `USER_DEBUG` before extracting**. All
|
|
220
|
+
three still need the `2>/dev/null` banner strip before parsing.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Reference — Verify Steps & Smoke Tests
|
|
2
|
+
|
|
3
|
+
Background for [Act 3](act3-plan-verify.md) and for the `purpose: verify`
|
|
4
|
+
re-reads that every write step in Acts 1–2 should end with. These are the skill's verify
|
|
5
|
+
conventions — apply them consistently across every write step.
|
|
6
|
+
|
|
7
|
+
## End every write with an explicit verify step
|
|
8
|
+
|
|
9
|
+
- **Convention & template:** the standard verify-artifact format. A verify step
|
|
10
|
+
is a distinct step with `id: verify`, a read `api`, and an `assert:` predicate:
|
|
11
|
+
```yaml
|
|
12
|
+
- id: verify
|
|
13
|
+
description: "Confirm the write took"
|
|
14
|
+
api: "GET /query?q=SELECT COUNT() FROM <ns>Sync_Tracked_Object_Config__c"
|
|
15
|
+
assert: "count == <expected for the installed baseline>" # derived at runtime, never hardcoded
|
|
16
|
+
```
|
|
17
|
+
- **Pre-flight validate vs. post-write verify:** the skill also uses `purpose: validate`
|
|
18
|
+
(pre-flight, e.g. "does this user exist" in A2.1) — that's the front of a step. `verify` is the
|
|
19
|
+
back. Acts 1–2 writes should carry both: validate inputs before, assert results after.
|
|
20
|
+
- **The multi-object count verify is ONE batched call, not a loop.** The single-object `COUNT()`
|
|
21
|
+
template above is fine for a lone row (the assignment write). But the **four-object A1.3 landing
|
|
22
|
+
check and A3.1 count verify** must read every count in a **single anonymous-Apex call** that
|
|
23
|
+
`System.debug`s a JSON blob — **never** a `sf data query` per object: each `sf` cold-start is
|
|
24
|
+
~15–20s, so a 4–5-object loop blows the 2-min timeout. And when parsing that call's output, **grep
|
|
25
|
+
`USER_DEBUG` first** — `sf apex run` echoes the block as `Execute Anonymous:` source, so a bare
|
|
26
|
+
marker grep matches the source too. Batched form + parse rule: transports cookbook form 6 in
|
|
27
|
+
[transports-and-namespace.md](transports-and-namespace.md).
|
|
28
|
+
- **Read-back is the cheapest honesty mechanism.** For every `Sync Management App`/`core` write in this skill
|
|
29
|
+
(config install, mapping row), the verify is a plain SOQL `COUNT()` or a targeted `SELECT` of
|
|
30
|
+
the row just written. If the read-back doesn't match, report failure — never infer success from
|
|
31
|
+
a 200/201 alone. (A2.2's IOU membership is a **read-only precondition**, not a write — its
|
|
32
|
+
"verify" *is* the read itself: a matching `InternalOrgUnitUser` row proceeds; no row
|
|
33
|
+
reports-and-stops under `Default_IOU`.)
|
|
34
|
+
|
|
35
|
+
## Write the run report to disk — the deliverable is a file, not the chat
|
|
36
|
+
|
|
37
|
+
Reporting in the conversation is **not** the deliverable. This is a long, multi-Act, org-mutating
|
|
38
|
+
run: a stream can truncate, a turn can end early, or the agent can simply conclude in chat — and if
|
|
39
|
+
nothing was written, the run leaves **no artifact at all**. So persist a `report.md` to the working
|
|
40
|
+
directory and **treat writing it as a first-class step, not an afterthought**:
|
|
41
|
+
|
|
42
|
+
- **Create it before the first org write.** The mandatory pre-write plan (SKILL.md) is the report's
|
|
43
|
+
first section — write the file the moment the plan is formed, before enabling RE / installing /
|
|
44
|
+
assigning. Use `write_file` (or an equivalent explicit file write); do not rely on shell echoes or
|
|
45
|
+
on the chat transcript being captured.
|
|
46
|
+
- **Append as you go — one section per Act.** After each Act's end state, append what ran: the
|
|
47
|
+
sub-steps executed, each step's **transport tag** (`core` / `Sync Management App`), the
|
|
48
|
+
**read-back (`assert:`) result**, and any entries added to `blocked_steps` (step + boundary +
|
|
49
|
+
who owns the missing piece). Incremental appends mean a partial or interrupted run still yields a
|
|
50
|
+
faithful, scorable record of exactly how far it got.
|
|
51
|
+
- **Close with a single verdict.** End the file with the overall outcome: full setup reached
|
|
52
|
+
(Act 3 end state) — or the exact step that blocked, why, and who provides the missing piece.
|
|
53
|
+
A faithful report-and-stop is a complete deliverable; a silent chat-only summary is not.
|
|
54
|
+
|
|
55
|
+
The report mirrors the honesty spine already used throughout the skill — it just makes the record
|
|
56
|
+
durable. Keep it concise and factual; it is a run log, not a re-explanation of the skill.
|
|
57
|
+
|
|
58
|
+
## A1.4 field-set verify — compare on the bare name, case-insensitively
|
|
59
|
+
|
|
60
|
+
The A1.4 read-only describe (does each referenced field set exist per tracked object?) produces
|
|
61
|
+
false "missing" hits unless the comparison is both **case-insensitive** and **namespace-agnostic**:
|
|
62
|
+
|
|
63
|
+
- **`Schema.describeSObjects(...)[0].fieldSets.getMap()` keys are all-lowercase AND fully
|
|
64
|
+
namespace-qualified** (`<ns>__name`). So `getMap().containsKey('RetailMobilityRelevant')` — the
|
|
65
|
+
referenced name in its original case — returns **false even when the set exists** (the real key is
|
|
66
|
+
e.g. `<ns>__retailmobilityrelevant`). Never `containsKey` the raw referenced name.
|
|
67
|
+
- **Whether the set is prefixed depends on the ORG, not the `$$NS` macro.** The
|
|
68
|
+
`Fieldset_Name_List__c` / `List_Header_Fieldset__c` names use `$$NS`
|
|
69
|
+
(`$$NSMobilityRelevant` → `<ns>__MobilityRelevant` at install). But `RetailMobilityRelevant`
|
|
70
|
+
(no `$$NS`) is **not** always unprefixed: in a pure **subscriber** org RE provisions it as
|
|
71
|
+
**standard** metadata (`NamespacePrefix` null), whereas in a **source-deployed / namespaced** org
|
|
72
|
+
(the `Organization.NamespacePrefix` branch A1.1 handles) **every** set — including
|
|
73
|
+
`RetailMobilityRelevant` — carries the **org namespace** (`FieldSet.getNamespace()` = `<ns>`).
|
|
74
|
+
- **Correct comparison: strip any `<prefix>__` from both sides and compare lowercased.** In Apex,
|
|
75
|
+
build the present-set from `FieldSet.getName().toLowerCase()` (`getName()` is the bare local name;
|
|
76
|
+
`getNamespace()` is the prefix, separately) — not from raw `getMap()` keys. In Tooling, compare
|
|
77
|
+
`DeveloperName` case-insensitively and ignore `NamespacePrefix` (it varies by org).
|
|
78
|
+
- **Tooling `FROM FieldSet` requires an EntityDefinition filter** — `WHERE
|
|
79
|
+
EntityDefinition.QualifiedApiName = '<obj>'`. An unfiltered `SELECT … FROM FieldSet` returns
|
|
80
|
+
**zero rows**, which reads as "no field sets" and is a trap.
|
|
81
|
+
|
|
82
|
+
## A3.2 verification — record counts only (no device call, no new endpoint)
|
|
83
|
+
|
|
84
|
+
- **The method is a record-count check, full stop.** After install, compare the installed
|
|
85
|
+
per-object row counts (from A3.1's `core` `COUNT()` reads) to the **installed baseline's own
|
|
86
|
+
declared counts**. One pass / fail verdict per object. Act 3 issues **no** representative
|
|
87
|
+
device-facing `/v1/*` call as part of verification, and proposes **no** new Sync Management App endpoint
|
|
88
|
+
(`/v1/syncconfig/verify`) — the count comparison is the complete server-side check.
|
|
89
|
+
- **Derive the expected counts at runtime — never hardcode them.** The expected numbers vary by
|
|
90
|
+
which baseline was installed. Identify the installed baseline from the activated config (the
|
|
91
|
+
`staticResourceName`/version captured at install time in A1.3, carried in session state, or
|
|
92
|
+
the installed `configId`/`version` read back in A3.1), then compare against the baseline's own
|
|
93
|
+
declared counts — never a fixed set of numbers.
|
|
94
|
+
|
|
95
|
+
### How to get the expected counts — A3.2 is the per-object split; the aggregate was Act 1's landing check
|
|
96
|
+
|
|
97
|
+
- **The aggregate was already checked at install — Act 3 does not just repeat it.** At A1.3 the
|
|
98
|
+
install ran the cheap **aggregate** landing check: sum of the four `core` COUNT() reads ==
|
|
99
|
+
`InstallResponse.recordsInserted` (which equals the manifest's total row count), needing **no**
|
|
100
|
+
static-resource fetch. `recordsInserted` + `version` were captured into session state there; reuse
|
|
101
|
+
them, but do **not** treat re-running the aggregate as A3.2's verification — that would duplicate
|
|
102
|
+
Act 1. A3.2's job, and its value over the landing check, is the **per-object split** below (it
|
|
103
|
+
catches a right-total / wrong-distribution install the aggregate cannot).
|
|
104
|
+
- **Per-object split (A3.2's verification — the one thing that needs the manifest):** to verify each object's count
|
|
105
|
+
individually (e.g. `Sync_Config__c` = 1, `Sync_Tracked_Object_Config__c` = 39,
|
|
106
|
+
`Sync_Named_Fetch_Tree_Nodes__c` = 63, `Sync_Named_Query__c` = 77 — illustrative; read the real
|
|
107
|
+
split from the installed baseline), you need the baseline's per-object row counts. **Retrieve the
|
|
108
|
+
static resource in *source* format into an *in-project* scratch dir, then read `manifest.json`
|
|
109
|
+
directly — do NOT `unzip`, and do NOT stream the Body over REST.** Two field-verified constraints
|
|
110
|
+
drive the exact shape of this command:
|
|
111
|
+
- **`--output-dir` must resolve to a path INSIDE the sfdx project root.** `sf project retrieve
|
|
112
|
+
start --output-dir` rejects any dir outside the project with `OutputDirOutsideProjectError`, so
|
|
113
|
+
`mktemp -d` (which returns `/var/folders/…`, outside the project) **always errors**. Walk up
|
|
114
|
+
from the cwd to the dir containing `sfdx-project.json`, make the scratch dir *under* it, and
|
|
115
|
+
`rm -rf` it when done.
|
|
116
|
+
- **Source-format retrieve produces a folder, not a ZIP.** A retrieved static resource expands to
|
|
117
|
+
`staticresources/<name>/{manifest.json, *.csv}` — there is **no `<name>.resource` ZIP**, so any
|
|
118
|
+
`unzip` step can never run. `manifest.json` already carries the **per-object row count**, so read
|
|
119
|
+
it straight — you don't even need to count CSV rows.
|
|
120
|
+
```bash
|
|
121
|
+
# 1) find the sfdx project root — `retrieve --output-dir` MUST resolve to a path inside it
|
|
122
|
+
ROOT="$PWD"; while [ "$ROOT" != "/" ] && [ ! -f "$ROOT/sfdx-project.json" ]; do ROOT="$(dirname "$ROOT")"; done
|
|
123
|
+
WORK="$ROOT/.sync-verify-tmp" # in-project scratch dir — NOT mktemp -d (that path is outside the project → error)
|
|
124
|
+
trap 'rm -rf "$WORK"' EXIT # ALWAYS clean up, even on early return / failure
|
|
125
|
+
# 2) SOURCE-format retrieve (~45s here; the MDAPI --target-metadata-dir path hung >2min)
|
|
126
|
+
sf project retrieve start --metadata "StaticResource:<baselineName>" \
|
|
127
|
+
--target-org <org> --output-dir "$WORK"
|
|
128
|
+
# 3) expands to staticresources/<baselineName>/manifest.json (+ *.csv) — no ZIP, no unzip.
|
|
129
|
+
# manifest.json carries the per-object row counts; read them straight from it.
|
|
130
|
+
MANIFEST="$(find "$WORK" -path '*/staticresources/<baselineName>/manifest.json' | head -1)"
|
|
131
|
+
# parse each object's declared count from "$MANIFEST" and compare to the A3.1 COUNT() reads
|
|
132
|
+
```
|
|
133
|
+
**Don't fetch the ZIP over REST, and don't reach for the MDAPI path first:**
|
|
134
|
+
- `sf api request rest .../StaticResource/<id>/Body` **UTF-8-corrupts the binary** (a 7,391-byte
|
|
135
|
+
zip came back as ~12,918 bytes of mojibake) — never stream the Body.
|
|
136
|
+
- The MDAPI/SOAP retrieve (`sf project retrieve start --target-metadata-dir <dir>`) *does* sidestep
|
|
137
|
+
the in-project `--output-dir` constraint, but it **hung past 2 min** in the field, whereas the
|
|
138
|
+
source-format `--output-dir` retrieve above finished in **~45s**. Prefer source format.
|
|
139
|
+
- **Clean up the download — always.** The retrieved `staticresources/<name>/` folder (`manifest.json`
|
|
140
|
+
+ CSVs) is a **throwaway verification artefact**, not project source. Because it must live *inside*
|
|
141
|
+
the project root, delete it as soon as the counts are read — the `trap 'rm -rf "$WORK"' EXIT` above,
|
|
142
|
+
or `rm -rf` before every early return. Never leave it behind in the workspace, never commit it, and
|
|
143
|
+
add the scratch path to `.forceignore` so a stray run isn't picked up as source.
|
|
144
|
+
- **The hard boundary:** a green count check proves the **server side** installed the baseline's
|
|
145
|
+
artefacts. Say exactly what was verified — report it as a server-side count check.
|
|
146
|
+
|
|
147
|
+
## When there is no programmatic path
|
|
148
|
+
|
|
149
|
+
- **When there's no programmatic path, it's a platform ask.** If a feature is UI-only with no
|
|
150
|
+
API anywhere, name the team that owns it and move on — don't build around it.
|
|
151
|
+
- **Report genuine non-automatable capabilities to the admin, with the owner named.** For
|
|
152
|
+
example, A1.2's advanced-RE pilot GA gate (`orgHasAdvncdRetailExecutionPilot`) has no API
|
|
153
|
+
anywhere — a CG Cloud core / RE concern — so it's reported, not worked around. This is
|
|
154
|
+
different from A2.2: the absence of a default mobility IOU is a report-and-stop
|
|
155
|
+
**precondition**, not a missing capability (see below).
|
|
156
|
+
|
|
157
|
+
## Assignment write (A2.3) and the IOU precondition (A2.2)
|
|
158
|
+
|
|
159
|
+
- **A2.3's assignment write goes through the package endpoint, not a raw junction POST.** The
|
|
160
|
+
user→config binding is written via `POST /v1/syncconfig/assignment`
|
|
161
|
+
(`SyncConfigAssignmentEndpoint`), which derives the overloaded `Business_Area_Name__c`
|
|
162
|
+
(IOU Id vs Business Area value) from the config's strategy and enforces dedup + the unique-Name
|
|
163
|
+
convention — a raw `POST /sobjects/<ns>Sync_Client_App_Profile_Mapping__c` would silently corrupt
|
|
164
|
+
the row. So the generic junction-write / sObject-REST playbooks are **not** the
|
|
165
|
+
path for A2.3 — the endpoint is. Verify the write with a `GET /v1/syncconfig/assignment`
|
|
166
|
+
read-back, as with any other write.
|
|
167
|
+
- **A2.2 is NOT a junction write — it is a `core` read-only precondition (Default_IOU only).**
|
|
168
|
+
`InternalOrgUnitUser` is never written by this skill — it only **reads** the user's default
|
|
169
|
+
mobility IOU (`SELECT InternalOrganizationUnitId FROM InternalOrgUnitUser WHERE UserId = :userId
|
|
170
|
+
AND IsDfltMobIntrOrgUnit = true LIMIT 1`, mirroring `ClientAppMapperService.getDefaultIouId()`).
|
|
171
|
+
A row → carry the `iouId` to A2.3; no row → **report and stop** (the user needs a default
|
|
172
|
+
mobility IOU, a CG Cloud core / RE task done outside this flow); pure-MP org → `QueryException`
|
|
173
|
+
→ degrade to the MP path. Under `Custom_User_Field` the precondition does not apply — skip it.
|
|
174
|
+
Creating IOU membership is out of scope; do **not** probe a raw `POST /sobjects/InternalOrgUnitUser`
|
|
175
|
+
or report its absence as a missing capability — it is the report-and-stop precondition above.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dx-org-analyze
|
|
3
3
|
description: "Compare two Salesforce orgs side-by-side and produce a comparison report with drift score, or analyze a single org to produce an inventory covering metadata components, org permissions, system permissions, profiles, installed packages, licenses, and org limits. Use this skill when the user wants to compare orgs, diff orgs, audit configuration drift, find what changed between two environments, compare sandbox against production, or analyze a single org. Trigger phrases include: 'compare orgs', 'compare these 2 orgs', 'compare my orgs', 'diff orgs', 'org diff', 'what is different between these orgs', 'compare metadata', 'org comparison', 'audit org differences', 'compare sandbox to production', 'introspect org', 'org inventory', 'what is in my org', 'analyze org', 'org analysis', 'analyze my org', 'analyze my Salesforce org'. Do NOT use for retrieving metadata (use platform-metadata-retrieve), deploying metadata (use platform-metadata-deploy), or switching orgs (use dx-org-switch)."
|
|
4
|
-
allowed-tools: Bash Read
|
|
5
4
|
metadata:
|
|
6
5
|
version: "1.0"
|
|
7
6
|
minApiVersion: "62.0"
|
|
@@ -623,13 +623,13 @@ def collect_deep_data(org_alias, base_url, access_token):
|
|
|
623
623
|
queries = {
|
|
624
624
|
"apex_classes": (
|
|
625
625
|
"SELECT Name, ApiVersion, LengthWithoutComments, Status, Body FROM ApexClass WHERE NamespacePrefix = null",
|
|
626
|
-
lambda r: {"name": r
|
|
626
|
+
lambda r: {"name": r["Name"], "apiVersion": r.get("ApiVersion"),
|
|
627
627
|
"loc": r.get("LengthWithoutComments", 0), "status": r.get("Status"),
|
|
628
628
|
"bodyHash": hashlib.sha256(r.get("Body", "").encode()).hexdigest() if r.get("Body") else None}
|
|
629
629
|
),
|
|
630
630
|
"apex_triggers": (
|
|
631
631
|
"SELECT Name, ApiVersion, Status, Body FROM ApexTrigger WHERE NamespacePrefix = null",
|
|
632
|
-
lambda r: {"name": r
|
|
632
|
+
lambda r: {"name": r["Name"], "apiVersion": r.get("ApiVersion"),
|
|
633
633
|
"status": r.get("Status"),
|
|
634
634
|
"bodyHash": hashlib.sha256(r.get("Body", "").encode()).hexdigest() if r.get("Body") else None}
|
|
635
635
|
),
|
|
@@ -845,7 +845,7 @@ def render_markdown(a_data, b_data, meta_stats, perm_diff, val_diff, pkg_diff, l
|
|
|
845
845
|
items = val_diff["cats"].get(cat_name, [])
|
|
846
846
|
if not items:
|
|
847
847
|
continue
|
|
848
|
-
w(f"**{cat_name}** ({len(items)}
|
|
848
|
+
w(f"**{cat_name}** ({len(items)} differences):")
|
|
849
849
|
w()
|
|
850
850
|
w(f"| Setting | {label_a} | {label_b} |")
|
|
851
851
|
w("|---------|-----|-----|")
|
|
@@ -958,10 +958,8 @@ def render_markdown(a_data, b_data, meta_stats, perm_diff, val_diff, pkg_diff, l
|
|
|
958
958
|
if ld["diffs"]:
|
|
959
959
|
w(f"| License | {label_a} Total | {label_b} Total |")
|
|
960
960
|
w("|---------|-----|-----|")
|
|
961
|
-
def _fmt_lic(v):
|
|
962
|
-
return "-1 (unlimited)" if v == -1 else str(v)
|
|
963
961
|
for d in ld["diffs"]:
|
|
964
|
-
w(f"| {d['name']} | {
|
|
962
|
+
w(f"| {d['name']} | {d['a_total']} | {d['b_total']} |")
|
|
965
963
|
w()
|
|
966
964
|
if ld["a_only"]:
|
|
967
965
|
names = [l.get("Name") or l.get("DeveloperName") or l.get("NamespacePrefix", "?") for l in ld["a_only"]]
|