TPE Data Kit Deploy
Deploys the TPM Accruals Data Kit and its DMO mappings into a Salesforce org. Standalone and self-contained — runs on its own, or as a delegated step invoked by consumer-goods-tpe-dashboard-configure (Step 7), which passes through its own already-collected username and dry-run flag rather than this skill re-deriving them.
Inputs to collect first
Ask before starting, unless already supplied by a calling skill. Do not guess.
- TPM System Admin username — already authenticated via
sf CLI. Verify via the sibling
script, never sf org display --json directly — that command's raw output includes
accessToken (a live credential), which this script strips before printing:node ./scripts/sf-rest.js org-status --target-org <username>
Prints {"username","alias","connectedStatus","orgId"}. Non-zero exit or
connectedStatus != "Connected" ⇒ stop, ask the user to log in themselves.
- Dry-run? — offer by default. In dry-run, Phase 1 (preflight) and Phase 2's read/diff steps still run; Phase 2's
FieldPermissions writes and Phases 3-5 (deploy) are skipped and recorded as pending — not run (dry-run).
- TPM Data Connector installed and active — cannot be verified programmatically. Ask the user, spelling out the exact navigation steps rather than just naming the setting: "Can you confirm the TPM Data Connector is Active? 1) Go to Setup → Data Cloud Setup. 2) In the left nav, under EXTERNAL INTEGRATIONS, click Other Connectors. 3) Find 'TPM Data Connector' in the list and check its status." Wait for an explicit yes. If missing or inactive, stop — tell the user we cannot proceed without the TPM Data Connector being active, and explain the two different paths depending on org type:
- Production orgs — it's typically auto-installed within ~24h of Processing Service pairing, provided the org has a Data Cloud license. If both prerequisites are met, ask the user to wait and retry; if either isn't met (no pairing yet, or no Data Cloud license), that's the real blocker to resolve first.
- Sandbox orgs — auto-install commonly does not happen; it typically needs to be activated manually under EXTERNAL INTEGRATIONS → Other Connectors. Tell the user this is expected in sandboxes and ask them to activate it there, then confirm before continuing.
Re-run this check once the user confirms it's Active.
- Salesforce CRM Home org connection — cannot be verified programmatically. Ask the user, spelling out the exact navigation steps: "Can you confirm the Salesforce CRM Home org connection is Active? 1) Go to Setup → Data Cloud Setup. 2) In the left nav, under SALESFORCE INTEGRATIONS, click Salesforce CRM. 3) Check that the Home org connection status is Active." Wait for an explicit yes. If missing/inactive, stop — tell the user we cannot proceed without this connection being active (Phase 2's field-permission grants require the
Data Cloud Salesforce Connector permission set, which won't exist until this connection is configured) — ask them to activate/configure it first, then re-run this check.
- Permission set(s) for the TPM Admin persona + GenieAdmin permission set confirmed assigned — when invoked as a delegated step from
consumer-goods-tpe-dashboard-configure, it passes through both results already confirmed in its own earlier phases; reuse them, don't re-ask or re-query. When invoked standalone, confirm each directly:
- Permission set(s) for the TPM Admin persona — "TPM Admin persona" is a role, not an actual permission set name; the org-specific permission set backing that persona (name/assignment) cannot be verified programmatically. Ask the user to confirm: "Is the permission set(s) required for the TPM Admin persona assigned to <username>?" Wait for an explicit yes.
- GenieAdmin — resolve its Label first (never show the API
Name to the user), then check the assignment:sf data query --target-org <username> --query "SELECT Label FROM PermissionSet WHERE Name = 'GenieAdmin'"
sf data query --target-org <username> \
--query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '<username>' AND PermissionSet.Name = 'GenieAdmin'"
Missing ⇒ tell the user the Label from the first query (e.g. "User <username> is missing the <Label> permission set. Assign it now?") and ask permission to assign before continuing:sf data create record --sobject PermissionSetAssignment \
--values "AssigneeId=<userId> PermissionSetId=<psId>" --target-org <username>
If declined, skip and note it in the report.
- Namespace (
NS) — when invoked as a delegated step, consumer-goods-tpe-dashboard-configure passes through its already-detected NS (raw namespace string); reuse it, don't re-detect. When invoked standalone, detect it once yourself, before Phase 1, with the sibling script:node ./scripts/detect-namespace.js --target-org <username>
Prints {"NS","NS_SEGMENT","NS_APEX","NS_FIELD"} as one line of JSON. Store all four and reuse everywhere below.
Phase 1 — Preflight
- Baseline for later verification — capture the latest
DataKitDeploymentLog Id now, before any deploy work, to avoid a CreatedDate clock-skew false-negative in Phase 5:sf data query --target-org <username> \
--query "SELECT Id FROM DataKitDeploymentLog ORDER BY CreatedDate DESC LIMIT 1" --json
Store as LAST_ID_BEFORE_DEPLOY (empty if zero rows).
- Download & unzip
CGCloudAddons — this skill fetches it itself rather than asking the user to do it manually.node ./scripts/download-static-resource.js \
--target-org <username> --name CGCloudAddons
Capture STATIC_RESOURCE_PATH=<dir> from stdout as <addons_root> — every path below is relative to it (e.g. <addons_root>/TPM/Accruals/TPM Accruals Data Kit). Zero StaticResource rows ⇒ block: the package version installed in this org doesn't ship CGCloudAddons. Any other script failure ⇒ block and relay its stderr verbatim.
Phase 2 — Field permissions for CRM streams (derived from the downloaded package)
Two sibling scripts drive this phase — never hand-roll the parsing or the grant calls; see each script's own docstring for exactly what it does and why:
- Extract the
{sobject, fields[]} list from the downloaded package (read-only, always safe to run):node ./scripts/extract-crm-field-permissions.js \
--dir "<addons_root>/TPM/Accruals/TPM Accruals Data Kit/force-app/main/default/dataSourceObjects" \
> /tmp/crm-field-permissions.json
Zero entries in the output ⇒ block and report — shouldn't happen if Phase 1 succeeded.
- Get the diff to update, silently (the script's own
--dry-run — read-only: permission set, sobject/describe, existing FieldPermissions/ObjectPermissions rows) — don't show the user anything yet, this is just to find out whether there's anything to do:node ./scripts/update-field-permissions.js \
--target-org <username> --input /tmp/crm-field-permissions.json --dry-run
{"error":"permission_set_not_found",...} ⇒ stop this phase, mark it blocked, and tell the user: "Could not find the 'Data Cloud Salesforce Connector' permission set — a likely explanation is that Salesforce CRM hasn't been configured yet (Setup → Data Cloud → Salesforce CRM). Please configure that first, then re-run this phase."
- Only if
wouldGrant or objectWouldGrant is non-empty, show the user exactly that diff (not the full extracted list) and get an explicit go-ahead before writing anything. If both are empty, everything's already granted — skip straight to step 5, no confirmation needed.
- On confirmation, run it for real — add-only at both field and object level (see script docstring for exact semantics):
node ./scripts/update-field-permissions.js \
--target-org <username> --input /tmp/crm-field-permissions.json
Exit code 3 (or a non-empty failed[]/objectFailed[]) ⇒ don't guess a workaround — prompt the user to do it manually via Setup → Permission Sets → Data Cloud Salesforce Connector → Object Settings, using exactly the failed[]/objectFailed[] list, and note each in the report.
- Record pass/blocked/pending, and the touched-field/object outcome (
granted/alreadyGranted/notFlsEligible/failed, objectGranted/objectAlreadyGranted/objectFailed), for the final report. Dry-run mode (never proceed to step 4 when in dry run mode as it updates permissions, step1 and step2 can be run as they are read only)
objectGranted/objectAlreadyGranted entries with permissionsViewAllRecords: false are expected for a small, fixed set of sobjects (currently just Product2) whose license blocks that flag — this is a known platform restriction the script applies silently; don't flag it to the user or treat it as something needing manual Setup action.
Phase 3 — Prepare and deploy data kit metadata
- In
<addons_root>/TPM/Accruals/TPM Accruals Data Kit, find-and-replace every instance of the placeholder __SF_ORG_ID__ with the org's 15-digit Id. Get the org Id via the following sibling scriptnode ./scripts/sf-rest.js org-status --target-org <username>
Take the orgId field from the printed JSON and truncate it to 15 chars (this call is cheap and idempotent — always re-run it here rather than relying on the Inputs step 1 result still being in context).
- Deploy from that folder:
sf project deploy start --target-org <username>
Phase 4 — Deploy DMO mappings and run the deployment script
- DMO mappings — maps the data kit's DLOs to Data 360 DMOs. From
<addons_root>/TPM/Accruals/DmoMappings:sf project deploy start --target-org <username>
- Deployment script — from
<addons_root>/TPM/Accruals/Accruals Data Kit Deployment Scripts:SF_ORG_ALIAS=<username> node deploy-tpm-tpe.js
This script POSTs to /services/data/v67.0/actions/custom/flow/sfdatakit__DeployDataKitComponents and returns an async Flow interview guid immediately; that response alone doesn't mean success — always confirm via the DataKitDeploymentLog check in Phase 5. This script runs from inside the downloaded/unzipped static resource.
Phase 5 — Verify
sf data query --target-org <username> \
--query "SELECT Id, DeploymentStatus, ComponentName, DeploymentError, CreatedDate FROM DataKitDeploymentLog WHERE Id > '<LAST_ID_BEFORE_DEPLOY>' ORDER BY CreatedDate DESC"
Every row for this run must have DeploymentStatus = 'Successful' and a blank DeploymentError. Never claim success while any row is Failure or has a non-empty error.
Alternative UI check: Data Cloud Setup → Data Kits → TPM Accruals → Local Deployment History → status Successful.
Known gotchas
FlowDefinition is not SOQL-queryable — don't probe it for readiness.
- An
sfdatakit__DeployDataKitComponents ApexClass probe can return 0 rows even when the framework is live — prefer probing DataKitDeploymentLog directly (Phase 5), never the Apex class.
Rules
- Never claim success while any
DataKitDeploymentLog row for this run is Failure or has a non-empty DeploymentError.
- Only ever read the
CGCloudAddons StaticResource (via download-static-resource.js) — never deploy or modify it; the org's own sf-authenticated access token drives the download, never a hardcoded credential.
- Never probe
FlowDefinition or the sfdatakit__DeployDataKitComponents Apex class for readiness — probe DataKitDeploymentLog instead.
- Never consult the org-shipped
README.md (or any other in-package documentation bundled inside CGCloudAddons) for deployment guidance — follow this skill's phases exactly as written. If a script name or path documented here doesn't match what's actually in the downloaded package, that's a bug in this skill to fix (update the skill itself), not a cue to switch to package docs at runtime.
- If a script or folder is missing from the downloaded folder, do not guess or change the scripts. e.g.: never change the script
deploy-tpm-tpe.js.
- Never run
update-field-permissions.js without --dry-run first. If its wouldGrant/objectWouldGrant come back non-empty, never run for real without showing the user that exact diff and getting an explicit go-ahead — same as any other write in this skill. If both are empty, no confirmation is needed; proceed straight to reporting.
update-field-permissions.js only ever adds — a new FieldPermissions/ObjectPermissions row where none existed, or flipping an existing row's off flag (PermissionsRead, or PermissionsViewAllRecords at the object level) to true. Never call it in a way that deletes a row, weakens an existing grant, or touches PermissionsCreate/Edit/Delete/ModifyAllRecords.
- Never guess a workaround when the
sfdc_a360_sfcrm_data_extract ("Data Cloud Salesforce Connector") permission set is missing or a field/object ends up in update-field-permissions.js's failed[]/objectFailed[] — stop and prompt the user to grant field-level or object-level security manually via Setup (see Phase 2).
Report
Produce a final report (pass/blocked/pending) covering: CRM stream field permissions (per-object field list from extract-crm-field-permissions.js, and each field's granted/alreadyGranted/notFlsEligible/failed outcome from update-field-permissions.js — flag any notFlsEligible entry with onObject: false separately, since that's a package/org data mismatch, not a normal skip), CRM stream object-level permissions (each sobject's objectGranted/objectAlreadyGranted/objectFailed outcome from the same script — a permissionsViewAllRecords: false on Product2 specifically is expected and not worth calling out), preflight (connector status), metadata deploy, DMO mapping deploy, deployment script run, and the DataKitDeploymentLog verification result (row count, any failures). This is exactly what consumer-goods-tpe-dashboard-configure Step 7 relays as its own result when this skill is invoked via delegation.
When invoked via delegation (the calling skill used the Skill tool to reach this file, rather than the user directly): this report is an intermediate result, not the end of the task. Return it to the calling skill and let it continue with its own next phase — do not present this report to the user as the final answer and stop. Only surface this report directly to the user when this skill was invoked standalone.
1---2name: consumer-goods-tpe-datakit-deploy3description: Deploy the TPM Accruals Data Kit components specifically required for Trade Promotion Effectiveness (TPE) dashboards. Use when a customer or admin wants to install or deploy the TPM data kit for TPE dashboards, or as a delegated step from consumer-goods-tpe-dashboard-configure. Do not use this skill if the customer wants to deploy the TPM Accruals Data Kit for the Accruals feature.4---5
6# TPE Data Kit Deploy
7
8Deploys the TPM Accruals Data Kit and its DMO mappings into a Salesforce org. Standalone and self-contained — runs on its own, or as a delegated step invoked by `consumer-goods-tpe-dashboard-configure` (Step 7), which passes through its own already-collected username and dry-run flag rather than this skill re-deriving them.
9
10## Inputs to collect first
11
12Ask before starting, unless already supplied by a calling skill. Do not guess.
13
141. **TPM System Admin username** — already authenticated via `sf` CLI. Verify via the sibling
15 script, never `sf org display --json` directly — that command's raw output includes
16 `accessToken` (a live credential), which this script strips before printing:
17 ```bash
18 node ./scripts/sf-rest.js org-status --target-org <username>
19 ```
20 Prints `{"username","alias","connectedStatus","orgId"}`. Non-zero exit or
21 `connectedStatus != "Connected"` ⇒ stop, ask the user to log in themselves.
222. **Dry-run?** — offer by default. In dry-run, Phase 1 (preflight) and Phase 2's read/diff steps still run; Phase 2's `FieldPermissions` writes and Phases 3-5 (deploy) are skipped and recorded as `pending — not run (dry-run)`.
233. **TPM Data Connector installed and active** — cannot be verified programmatically. Ask the user, spelling out the exact navigation steps rather than just naming the setting: *"Can you confirm the TPM Data Connector is Active? 1) Go to Setup → Data Cloud Setup. 2) In the left nav, under EXTERNAL INTEGRATIONS, click Other Connectors. 3) Find 'TPM Data Connector' in the list and check its status."* Wait for an explicit yes. If missing or inactive, **stop** — tell the user we cannot proceed without the TPM Data Connector being active, and explain the two different paths depending on org type:
24 - **Production orgs** — it's typically auto-installed within ~24h of Processing Service pairing, provided the org has a Data Cloud license. If both prerequisites are met, ask the user to wait and retry; if either isn't met (no pairing yet, or no Data Cloud license), that's the real blocker to resolve first.
25 - **Sandbox orgs** — auto-install commonly does not happen; it typically needs to be activated manually under EXTERNAL INTEGRATIONS → Other Connectors. Tell the user this is expected in sandboxes and ask them to activate it there, then confirm before continuing.
26 Re-run this check once the user confirms it's Active.
274. **Salesforce CRM Home org connection** — cannot be verified programmatically. Ask the user, spelling out the exact navigation steps: *"Can you confirm the Salesforce CRM Home org connection is Active? 1) Go to Setup → Data Cloud Setup. 2) In the left nav, under SALESFORCE INTEGRATIONS, click Salesforce CRM. 3) Check that the Home org connection status is Active."* Wait for an explicit yes. If missing/inactive, **stop** — tell the user we cannot proceed without this connection being active (Phase 2's field-permission grants require the `Data Cloud Salesforce Connector` permission set, which won't exist until this connection is configured) — ask them to activate/configure it first, then re-run this check.
285. **Permission set(s) for the TPM Admin persona + GenieAdmin permission set confirmed assigned** — when invoked as a delegated step from `consumer-goods-tpe-dashboard-configure`, it passes through both results already confirmed in its own earlier phases; reuse them, don't re-ask or re-query. When invoked standalone, confirm each directly:
29 - **Permission set(s) for the TPM Admin persona** — "TPM Admin persona" is a role, not an actual permission set name; the org-specific permission set backing that persona (name/assignment) cannot be verified programmatically. Ask the user to confirm: *"Is the permission set(s) required for the TPM Admin persona assigned to <username>?"* Wait for an explicit yes.
30 - **GenieAdmin** — resolve its Label first (never show the API `Name` to the user), then check the assignment:
31 ```bash
32 sf data query --target-org <username> --query "SELECT Label FROM PermissionSet WHERE Name = 'GenieAdmin'"
33 sf data query --target-org <username> \
34 --query "SELECT PermissionSet.Name FROM PermissionSetAssignment WHERE Assignee.Username = '<username>' AND PermissionSet.Name = 'GenieAdmin'"
35 ```
36 Missing ⇒ tell the user the Label from the first query (e.g. *"User <username> is missing the <Label> permission set. Assign it now?"*) and ask permission to assign before continuing:
37 ```bash
38 sf data create record --sobject PermissionSetAssignment \
39 --values "AssigneeId=<userId> PermissionSetId=<psId>" --target-org <username>
40 ```
41 If declined, skip and note it in the report.
426. **Namespace (`NS`)** — when invoked as a delegated step, `consumer-goods-tpe-dashboard-configure` passes through its already-detected `NS` (raw namespace string); reuse it, don't re-detect. When invoked standalone, detect it once yourself, before Phase 1, with the sibling script:
43 ```bash
44 node ./scripts/detect-namespace.js --target-org <username>
45 ```
46 Prints `{"NS","NS_SEGMENT","NS_APEX","NS_FIELD"}` as one line of JSON. Store all four and reuse everywhere below.
47
48
49## Phase 1 — Preflight
50
512. **Baseline for later verification** — capture the latest `DataKitDeploymentLog` Id now, before any deploy work, to avoid a `CreatedDate` clock-skew false-negative in Phase 5:
52 ```bash
53 sf data query --target-org <username> \
54 --query "SELECT Id FROM DataKitDeploymentLog ORDER BY CreatedDate DESC LIMIT 1" --json
55 ```
56 Store as `LAST_ID_BEFORE_DEPLOY` (empty if zero rows).
573. **Download & unzip `CGCloudAddons`** — this skill fetches it itself rather than asking the user to do it manually.
58 ```bash
59 node ./scripts/download-static-resource.js \
60 --target-org <username> --name CGCloudAddons
61 ```
62 Capture `STATIC_RESOURCE_PATH=<dir>` from stdout as `<addons_root>` — every path below is relative to it (e.g. `<addons_root>/TPM/Accruals/TPM Accruals Data Kit`). Zero `StaticResource` rows ⇒ **block**: the package version installed in this org doesn't ship `CGCloudAddons`. Any other script failure ⇒ block and relay its stderr verbatim.
63
64## Phase 2 — Field permissions for CRM streams (derived from the downloaded package)
65
66Two sibling scripts drive this phase — never hand-roll the parsing or the grant calls; see each script's own docstring for exactly what it does and why:
67
681. **Extract** the `{sobject, fields[]}` list from the downloaded package (read-only, always safe to run):
69 ```bash
70 node ./scripts/extract-crm-field-permissions.js \
71 --dir "<addons_root>/TPM/Accruals/TPM Accruals Data Kit/force-app/main/default/dataSourceObjects" \
72 > /tmp/crm-field-permissions.json
73 ```
74 Zero entries in the output ⇒ block and report — shouldn't happen if Phase 1 succeeded.
752. **Get the diff to update, silently** (the script's own `--dry-run` — read-only: permission set, `sobject/describe`, existing `FieldPermissions`/`ObjectPermissions` rows) — don't show the user anything yet, this is just to find out whether there's anything to do:
76 ```bash
77 node ./scripts/update-field-permissions.js \
78 --target-org <username> --input /tmp/crm-field-permissions.json --dry-run
79 ```
80 `{"error":"permission_set_not_found",...}` ⇒ stop this phase, mark it blocked, and tell the user: *"Could not find the 'Data Cloud Salesforce Connector' permission set — a likely explanation is that Salesforce CRM hasn't been configured yet (Setup → Data Cloud → Salesforce CRM). Please configure that first, then re-run this phase."*
813. **Only if `wouldGrant` or `objectWouldGrant` is non-empty**, show the user exactly that diff (not the full extracted list) and get an explicit go-ahead before writing anything. If both are empty, everything's already granted — skip straight to step 5, no confirmation needed.
824. **On confirmation, run it for real** — add-only at both field and object level (see script docstring for exact semantics):
83 ```bash
84 node ./scripts/update-field-permissions.js \
85 --target-org <username> --input /tmp/crm-field-permissions.json
86 ```
87 Exit code `3` (or a non-empty `failed[]`/`objectFailed[]`) ⇒ don't guess a workaround — prompt the user to do it manually via Setup → Permission Sets → Data Cloud Salesforce Connector → Object Settings, using exactly the `failed[]`/`objectFailed[]` list, and note each in the report.
885. Record pass/blocked/pending, and the touched-field/object outcome (`granted`/`alreadyGranted`/`notFlsEligible`/`failed`, `objectGranted`/`objectAlreadyGranted`/`objectFailed`), for the final report. Dry-run mode (never proceed to step 4 when in dry run mode as it updates permissions, step1 and step2 can be run as they are read only)
896. `objectGranted`/`objectAlreadyGranted` entries with `permissionsViewAllRecords: false` are expected for a small, fixed set of sobjects (currently just Product2) whose license blocks that flag — this is a known platform restriction the script applies silently; don't flag it to the user or treat it as something needing manual Setup action.
90
91## Phase 3 — Prepare and deploy data kit metadata
92
931. In `<addons_root>/TPM/Accruals/TPM Accruals Data Kit`, find-and-replace every instance of the placeholder `__SF_ORG_ID__` with the org's 15-digit Id. Get the org Id via the following sibling script
94 ```bash
95 node ./scripts/sf-rest.js org-status --target-org <username>
96 ```
97 Take the `orgId` field from the printed JSON and truncate it to 15 chars (this call is cheap and idempotent — always re-run it here rather than relying on the Inputs step 1 result still being in context).
982. Deploy from that folder:
99 ```bash
100 sf project deploy start --target-org <username>
101 ```
102
103## Phase 4 — Deploy DMO mappings and run the deployment script
104
1051. **DMO mappings** — maps the data kit's DLOs to Data 360 DMOs. From `<addons_root>/TPM/Accruals/DmoMappings`:
106 ```bash
107 sf project deploy start --target-org <username>
108 ```
1092. **Deployment script** — from `<addons_root>/TPM/Accruals/Accruals Data Kit Deployment Scripts`:
110 ```bash
111 SF_ORG_ALIAS=<username> node deploy-tpm-tpe.js
112 ```
113 This script POSTs to `/services/data/v67.0/actions/custom/flow/sfdatakit__DeployDataKitComponents` and returns an async Flow interview guid immediately; that response alone doesn't mean success — always confirm via the `DataKitDeploymentLog` check in Phase 5. This script runs from inside the downloaded/unzipped static resource.
114
115## Phase 5 — Verify
116
117```bash
118sf data query --target-org <username> \
119 --query "SELECT Id, DeploymentStatus, ComponentName, DeploymentError, CreatedDate FROM DataKitDeploymentLog WHERE Id > '<LAST_ID_BEFORE_DEPLOY>' ORDER BY CreatedDate DESC"
120```
121
122Every row for this run must have `DeploymentStatus = 'Successful'` and a blank `DeploymentError`. **Never claim success while any row is `Failure` or has a non-empty error.**
123
124Alternative UI check: Data Cloud Setup → Data Kits → TPM Accruals → Local Deployment History → status `Successful`.
125
126## Known gotchas
127
128- `FlowDefinition` is **not** SOQL-queryable — don't probe it for readiness.
129- An `sfdatakit__DeployDataKitComponents` ApexClass probe can return 0 rows even when the framework is live — prefer probing `DataKitDeploymentLog` directly (Phase 5), never the Apex class.
130
131## Rules
132
133- Never claim success while any `DataKitDeploymentLog` row for this run is `Failure` or has a non-empty `DeploymentError`.
134- Only ever read the `CGCloudAddons` `StaticResource` (via `download-static-resource.js`) — never deploy or modify it; the org's own `sf`-authenticated access token drives the download, never a hardcoded credential.
135- Never probe `FlowDefinition` or the `sfdatakit__DeployDataKitComponents` Apex class for readiness — probe `DataKitDeploymentLog` instead.
136- Never consult the org-shipped `README.md` (or any other in-package documentation bundled inside `CGCloudAddons`) for deployment guidance — follow this skill's phases exactly as written. If a script name or path documented here doesn't match what's actually in the downloaded package, that's a bug in this skill to fix (update the skill itself), not a cue to switch to package docs at runtime.
137- If a script or folder is missing from the downloaded folder, do not guess or change the scripts. e.g.: never change the script `deploy-tpm-tpe.js`.
138- Never run `update-field-permissions.js` without `--dry-run` first. If its `wouldGrant`/`objectWouldGrant` come back non-empty, never run for real without showing the user that exact diff and getting an explicit go-ahead — same as any other write in this skill. If both are empty, no confirmation is needed; proceed straight to reporting.
139- `update-field-permissions.js` only ever adds — a new `FieldPermissions`/`ObjectPermissions` row where none existed, or flipping an existing row's off flag (`PermissionsRead`, or `PermissionsViewAllRecords` at the object level) to `true`. Never call it in a way that deletes a row, weakens an existing grant, or touches `PermissionsCreate`/`Edit`/`Delete`/`ModifyAllRecords`.
140- Never guess a workaround when the `sfdc_a360_sfcrm_data_extract` ("Data Cloud Salesforce Connector") permission set is missing or a field/object ends up in `update-field-permissions.js`'s `failed[]`/`objectFailed[]` — stop and prompt the user to grant field-level or object-level security manually via Setup (see Phase 2).
141
142## Report
143
144Produce a final report (pass/blocked/pending) covering: CRM stream field permissions (per-object field list from `extract-crm-field-permissions.js`, and each field's `granted`/`alreadyGranted`/`notFlsEligible`/`failed` outcome from `update-field-permissions.js` — flag any `notFlsEligible` entry with `onObject: false` separately, since that's a package/org data mismatch, not a normal skip), CRM stream object-level permissions (each sobject's `objectGranted`/`objectAlreadyGranted`/`objectFailed` outcome from the same script — a `permissionsViewAllRecords: false` on Product2 specifically is expected and not worth calling out), preflight (connector status), metadata deploy, DMO mapping deploy, deployment script run, and the `DataKitDeploymentLog` verification result (row count, any failures). This is exactly what `consumer-goods-tpe-dashboard-configure` Step 7 relays as its own result when this skill is invoked via delegation.
145
146**When invoked via delegation** (the calling skill used the `Skill` tool to reach this file, rather than the user directly): this report is an intermediate result, not the end of the task. Return it to the calling skill and let it continue with its own next phase — do not present this report to the user as the final answer and stop. Only surface this report directly to the user when this skill was invoked standalone.