@salesforce/afv-skills 1.62.0 → 1.64.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.
Files changed (138) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-offline-experimentation-run/SKILL.md +253 -0
  3. package/skills/agentforce-offline-experimentation-run/scripts/build_report.py +796 -0
  4. package/skills/agentforce-offline-experimentation-run/scripts/resolve-token.sh +111 -0
  5. package/skills/agentforce-offline-experimentation-run/scripts/sfap_replay.py +1053 -0
  6. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +21 -11
  7. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +10 -0
  8. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
  9. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +44 -1
  10. package/skills/automation-sandbox-post-copy-configure/SKILL.md +132 -124
  11. package/skills/automation-sandbox-post-copy-configure/references/flow_activation_path.md +151 -0
  12. package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +55 -9
  13. package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +54 -107
  14. package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +1 -1
  15. package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +82 -5
  16. package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +35 -2
  17. package/skills/consumer-goods-promotion-bo-api-deploy/references/preflight-and-seed-queries.md +149 -0
  18. package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +17 -1
  19. package/skills/dx-devops-conflict-resolve/SKILL.md +1 -5
  20. package/skills/dx-devops-pipeline-manage/SKILL.md +1 -5
  21. package/skills/dx-devops-project-manage/SKILL.md +1 -5
  22. package/skills/dx-devops-promote/SKILL.md +1 -5
  23. package/skills/dx-devops-request-status/SKILL.md +1 -5
  24. package/skills/dx-lifecycle-center/SKILL.md +120 -0
  25. package/skills/dx-lifecycle-center/examples/workflows.md +175 -0
  26. package/skills/dx-lifecycle-center/references/connect-api.md +123 -0
  27. package/skills/dx-lifecycle-center/references/connect-org.md +184 -0
  28. package/skills/dx-lifecycle-center/references/deploy.md +153 -0
  29. package/skills/dx-lifecycle-center/references/error-handling.md +21 -0
  30. package/skills/dx-lifecycle-center/references/hub-org.md +37 -0
  31. package/skills/dx-lifecycle-center/references/list-orgs.md +99 -0
  32. package/skills/dx-lifecycle-center/references/request-body.md +75 -0
  33. package/skills/dx-lifecycle-center/references/troubleshooting.md +31 -0
  34. package/skills/dx-lifecycle-center/scripts/poll-deploy-status.sh +200 -0
  35. package/skills/dx-lifecycle-center/scripts/resolve-environment.sh +120 -0
  36. package/skills/dx-org-devhub-configure/SKILL.md +1 -3
  37. package/skills/dx-org-shape-manage/SKILL.md +1 -5
  38. package/skills/dx-org-snapshot-manage/SKILL.md +1 -3
  39. package/skills/education-cloud-academic-calendar-generate/SKILL.md +1 -3
  40. package/skills/education-cloud-course-catalog-migrate/SKILL.md +1 -3
  41. package/skills/education-cloud-domain-configure/SKILL.md +1 -3
  42. package/skills/education-cloud-multi-campus-configure/SKILL.md +1 -3
  43. package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +1 -5
  44. package/skills/experience-ui-bundle-2gp-deploy/SKILL.md +1 -3
  45. package/skills/experience-ui-bundle-features-generate/SKILL.md +1 -5
  46. package/skills/experience-ui-bundle-mfa-configure/SKILL.md +1 -3
  47. package/skills/field-service-data-capture-migrate/SKILL.md +1 -3
  48. package/skills/insurance-brokerage-agency-billing-configure/SKILL.md +1 -3
  49. package/skills/marketing-message-test/SKILL.md +1 -3
  50. package/skills/nonprofit-fundraising-configure/SKILL.md +240 -0
  51. package/skills/nonprofit-fundraising-configure/references/gotchas.md +26 -0
  52. package/skills/nonprofit-fundraising-configure/references/mcp-invocation.md +678 -0
  53. package/skills/nonprofit-fundraising-configure/references/permission-targeting.md +35 -0
  54. package/skills/nonprofit-volunteer-management-model-get/SKILL.md +166 -0
  55. package/skills/nonprofit-volunteer-management-model-get/references/entities.md +315 -0
  56. package/skills/nonprofit-volunteer-management-model-get/references/relationships.md +175 -0
  57. package/skills/nonprofit-volunteer-management-model-get/references/themes.md +143 -0
  58. package/skills/nonprofit-volunteer-management-records-create/SKILL.md +121 -0
  59. package/skills/nonprofit-volunteer-management-records-create/references/field-guide.md +284 -0
  60. package/skills/nonprofit-volunteer-management-records-create/references/mcp-invocation.md +248 -0
  61. package/skills/nonprofit-volunteer-management-records-create/references/review-format.md +140 -0
  62. package/skills/nonprofit-volunteer-management-records-create/scripts/expand-series.py +632 -0
  63. package/skills/nonprofit-volunteer-management-records-create/scripts/render-review-table.py +419 -0
  64. package/skills/nonprofit-volunteer-management-records-create/scripts/tests/test_expand_series.py +690 -0
  65. package/skills/nonprofit-volunteer-management-records-create/scripts/tests/test_render_review_table.py +421 -0
  66. package/skills/nonprofit-volunteer-management-structure-generate/SKILL.md +119 -0
  67. package/skills/nonprofit-volunteer-management-structure-generate/references/configuration-decisions.md +159 -0
  68. package/skills/nonprofit-volunteer-management-structure-generate/references/discovery-interview.md +137 -0
  69. package/skills/nonprofit-volunteer-management-structure-generate/references/edge-cases.md +48 -0
  70. package/skills/nonprofit-volunteer-management-structure-generate/references/mcp-invocation.md +75 -0
  71. package/skills/nonprofit-volunteer-management-structure-generate/references/plan-output.md +141 -0
  72. package/skills/platform-custom-field-generate/SKILL.md +1 -0
  73. package/skills/platform-custom-field-generate/references/advanced-picklists.md +5 -5
  74. package/skills/platform-custom-metadata-type-generate/SKILL.md +3 -0
  75. package/skills/platform-custom-object-generate/SKILL.md +4 -0
  76. package/skills/platform-custom-setting-generate/SKILL.md +3 -0
  77. package/skills/platform-datamask-run/SKILL.md +1 -5
  78. package/skills/platform-permission-set-generate/SKILL.md +3 -0
  79. package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +1 -3
  80. package/skills/platform-sandbox-configure/SKILL.md +1 -3
  81. package/skills/platform-value-set-generate/SKILL.md +3 -0
  82. package/skills/sales-call-scoring-configure/SKILL.md +1 -5
  83. package/skills/service-agentforce-channel-configure/SKILL.md +1 -3
  84. package/skills/service-agentforce-human-escalation-configure/SKILL.md +1 -3
  85. package/skills/service-catalog-template-coordinate/SKILL.md +1 -3
  86. package/skills/service-concierge-portal-generate/SKILL.md +1 -3
  87. package/skills/service-email-to-case-configure/SKILL.md +1 -5
  88. package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/SKILL.md +1 -3
  89. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +1 -3
  90. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +1 -3
  91. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +1 -3
  92. package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +1 -3
  93. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -5
  94. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +1 -3
  95. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +1 -3
  96. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +1 -3
  97. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +1 -3
  98. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +1 -3
  99. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +1 -0
  100. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +1 -0
  101. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +1 -3
  102. package/skills/service-itsm-agentic-setup-uel-user-create/SKILL.md +1 -9
  103. package/skills/service-itsm-incident-mgmt-configure/SKILL.md +1 -5
  104. package/skills/service-itsm-incident-priority-configure/SKILL.md +1 -3
  105. package/skills/service-itsm-slack-itservice-configure/SKILL.md +1 -3
  106. package/skills/service-itsm-swarming-configure/SKILL.md +1 -3
  107. package/skills/service-itsm-teams-configure/SKILL.md +1 -3
  108. package/skills/service-itsm-teams-debug/SKILL.md +1 -3
  109. package/skills/service-itsm-teams-employee-agent-configure/SKILL.md +1 -3
  110. package/skills/service-itsm-teams-itdesk-configure/SKILL.md +1 -3
  111. package/skills/service-itsm-teams-itservice-configure/SKILL.md +1 -3
  112. package/skills/service-native-voice-recording-transcription-configure/SKILL.md +1 -3
  113. package/skills/service-omni-agent-users-create/SKILL.md +1 -3
  114. package/skills/service-omni-agent-work-sharing-configure/SKILL.md +1 -3
  115. package/skills/service-omni-attribute-routing-configure/SKILL.md +1 -3
  116. package/skills/service-omni-base-settings-configure/SKILL.md +1 -3
  117. package/skills/service-omni-channel-inventory-analyze/SKILL.md +1 -3
  118. package/skills/service-omni-channel-limits-analyze/SKILL.md +1 -3
  119. package/skills/service-omni-channel-setup-coordinate/SKILL.md +1 -3
  120. package/skills/service-omni-command-center-analyze/SKILL.md +1 -3
  121. package/skills/service-omni-command-center-configure/SKILL.md +1 -3
  122. package/skills/service-omni-permission-set-assign/SKILL.md +1 -3
  123. package/skills/service-omni-presence-status-deploy/SKILL.md +1 -3
  124. package/skills/service-omni-presence-user-config-deploy/SKILL.md +1 -3
  125. package/skills/service-omni-queue-deploy/SKILL.md +1 -3
  126. package/skills/service-omni-queue-members-assign/SKILL.md +1 -3
  127. package/skills/service-omni-queue-routing-config-deploy/SKILL.md +1 -3
  128. package/skills/service-omni-routing-flow-deploy/SKILL.md +1 -3
  129. package/skills/service-omni-service-channel-configure/SKILL.md +1 -3
  130. package/skills/service-omni-sidebar-configure/SKILL.md +1 -3
  131. package/skills/service-omni-skills-based-routing-configure/SKILL.md +1 -3
  132. package/skills/service-omni-supervisor-config-deploy/SKILL.md +1 -3
  133. package/skills/service-omni-supervisor-permset-assign/SKILL.md +1 -3
  134. package/skills/service-omni-supervisor-surface-deploy/SKILL.md +1 -3
  135. package/skills/service-omni-supervisor-users-create/SKILL.md +1 -3
  136. package/skills/service-omni-work-skill-routing-configure/SKILL.md +1 -3
  137. package/skills/field-service-setup-orchestrator-get/SKILL.md +0 -47
  138. package/skills/tableau-next-asset-create/README.md +0 -65
@@ -0,0 +1,151 @@
1
+ # Step G — Flow activation / deactivation (`Flows`)
2
+
3
+ `Flows` entries in the post-copy config mutate
4
+ `FlowDefinition.Metadata.activeVersionNumber` via the same
5
+ compound-`Metadata` PATCH transport as Steps D-1/D-3, but Step D-2's
6
+ mutation-key derivation does **not** apply — the mutation key is
7
+ always the literal `activeVersionNumber`. Follow G-1 → G-4 below
8
+ **instead of** Step D for `Flows` entries. Steps A + B still apply
9
+ (`FlowDefinition` is the pinned sobject; Step B confirms the
10
+ `Metadata` compound field exists on this org).
11
+
12
+ ## Fields shape
13
+
14
+ Every `Flows` entry carries these keys under `Fields`:
15
+
16
+ | Key | Meaning |
17
+ |-----|---------|
18
+ | `FlowApiName` | `DeveloperName` of the parent `FlowDefinition`. Required. |
19
+ | `Action` | `"Activate"` or `"Deactivate"`. Required. |
20
+ | `VersionNumber` | Integer. Required for `Activate` only when pinning a specific saved version; omit to activate the latest saved version. Ignored for `Deactivate`. |
21
+
22
+ Reject `FlowApiName` that doesn't match `^[A-Za-z_][A-Za-z0-9_]*$`
23
+ before any SOQL — the queries below interpolate it unescaped; a
24
+ malformed name is a SOQL-injection risk. Fail-closed on mismatch
25
+ (`API_NOT_IDENTIFIED`).
26
+
27
+ ## G-1 — Look up the FlowDefinition + current active version
28
+
29
+ ```bash
30
+ FLOW_API_NAME='<Fields.FlowApiName>'
31
+ SOQL="SELECT Id, DeveloperName, ActiveVersion.VersionNumber FROM FlowDefinition WHERE DeveloperName = '$FLOW_API_NAME'"
32
+ sf data query --use-tooling-api --json --query "$SOQL" \
33
+ > /tmp/entry-<Slug>-flow-lookup.json
34
+ ID=$(jq -r '.result.records[0].Id // empty' /tmp/entry-<Slug>-flow-lookup.json)
35
+ CURRENT_ACTIVE=$(jq -r '.result.records[0].ActiveVersion.VersionNumber // empty' /tmp/entry-<Slug>-flow-lookup.json)
36
+ ```
37
+
38
+ - Zero rows → `NOT_FOUND` (add Follow-ups bullet; skip PATCH).
39
+ - `CURRENT_ACTIVE` is the currently-active version number, or empty
40
+ if the flow is inactive. `DeveloperName` is unique, so multiple rows
41
+ should not occur; guard with `AMBIGUOUS` if they do.
42
+
43
+ ## G-2 — Resolve target `activeVersionNumber` + apply pre-flight
44
+
45
+ - If `Fields.Action == "Deactivate"`:
46
+ - Target = `null`.
47
+ - If `CURRENT_ACTIVE` is empty → record `ALREADY_INACTIVE`, skip
48
+ PATCH, add a Follow-ups bullet.
49
+ - If `Fields.Action == "Activate"`:
50
+ - If `Fields.VersionNumber` is present → target = that integer.
51
+ - Otherwise → query the latest saved version:
52
+ ```bash
53
+ SOQL="SELECT VersionNumber FROM Flow WHERE DefinitionId = '$ID' ORDER BY VersionNumber DESC LIMIT 1"
54
+ sf data query --use-tooling-api --json --query "$SOQL" \
55
+ > /tmp/entry-<Slug>-flow-latest.json
56
+ TARGET=$(jq -r '.result.records[0].VersionNumber // empty' /tmp/entry-<Slug>-flow-latest.json)
57
+ ```
58
+ Empty result → `NOT_FOUND` (flow has no saved versions).
59
+ - If `CURRENT_ACTIVE == TARGET` → record `ALREADY_ACTIVE`, skip
60
+ PATCH, add a Follow-ups bullet.
61
+
62
+ ## G-3 — GET current Metadata, mutate, PATCH back
63
+
64
+ Same compound-`Metadata` transport as Step D-3, with the mutation
65
+ fixed to `activeVersionNumber`. Do NOT run
66
+ `scripts/map-metadata-key.mjs` — the mutation key is always the
67
+ literal `activeVersionNumber`. Preserve every other key from the GET
68
+ below; overlay only `activeVersionNumber`. Never substitute
69
+ `ActiveVersion`, `activeVersionId`, `Status`, or `IsActive` — they
70
+ look like candidate keys but all fail with 400 or silent no-op.
71
+
72
+ The GET returns the complete Tooling API record (`Id`, `attributes`,
73
+ `Metadata`, …). Narrow with `?fields=Metadata` and extract the
74
+ `Metadata` object with `jq '.Metadata'` **before** the Step D-3
75
+ merge — the merge wraps its input as `{"Metadata": …}`, so if the
76
+ raw record is piped in, non-metadata keys end up inside `Metadata`
77
+ and the PATCH is rejected.
78
+
79
+ ```bash
80
+ sf api request rest \
81
+ "/services/data/v<apiVersion>/tooling/sobjects/FlowDefinition/$ID?fields=Metadata" \
82
+ | jq '.Metadata' > /tmp/entry-$ID-meta.json
83
+
84
+ # Build /tmp/entry-$ID-mutation.json with {"activeVersionNumber": <TARGET or null>},
85
+ # merge with /tmp/entry-$ID-meta.json using `jq --slurpfile`, PATCH the
86
+ # merged result. Full recipe: references/api_endpoints.md §Step D.
87
+ ```
88
+
89
+ Dry-run: resolve the plan via G-1 + G-2; skip the G-3 GET + PATCH;
90
+ record `DRY_RUN`. The plan prints the fixed mutation key
91
+ (`activeVersionNumber`) and the resolved target without reading or
92
+ mutating the current Metadata payload.
93
+
94
+ ## G-4 — Verify the change landed
95
+
96
+ Re-run the G-1 SOQL and confirm `ActiveVersion.VersionNumber` matches
97
+ the target (or is empty after Deactivate). Mismatch → `FAILED_VERIFY`.
98
+
99
+ ## Summary shape for a Flows entry
100
+
101
+ The generic `Planned request:` block from SKILL.md's canonical output
102
+ shape does **not** apply to a Flows entry — the Flows path dispatches
103
+ across four distinct sub-steps (SOQL lookup, target resolution, GET+
104
+ PATCH, verify), so the summary must record evidence that each
105
+ sub-step ran, not just the final PATCH body. Replace the
106
+ `Planned request:` block with the narrative below, keeping the
107
+ single-row phase table and the Totals / Follow-ups sections unchanged.
108
+
109
+ For each Flows entry's phase block, emit (in this order):
110
+
111
+ 1. A one-line route header:
112
+ ```text
113
+ Route: **Step G — Flows (compound-`Metadata` PATCH with fixed `activeVersionNumber` mutation)**. Mutation key is fixed, NOT derived from the describe payload — `scripts/map-metadata-key.mjs` is skipped for Flows.
114
+ ```
115
+
116
+ 2. `Pre-flight lookup (G-1):` — the G-1 SOQL and the single-row result
117
+ (`ActiveVersion.VersionNumber = <value or null>`), ending with the
118
+ decision (`Not already active → proceed to G-2` /
119
+ `ALREADY_INACTIVE short-circuit` / `AMBIGUOUS` / `NOT_FOUND`).
120
+
121
+ 3. `Target resolution (G-2):` — the branch taken (`Action = Activate`
122
+ with `Fields.VersionNumber` present / absent → latest-version SOQL;
123
+ `Action = Deactivate` → target `null`), the resolved target
124
+ `activeVersionNumber`, and a one-line comparison vs the pre-flight
125
+ state justifying no short-circuit. For `Activate` without
126
+ `Fields.VersionNumber`, include the latest-version SOQL verbatim
127
+ and its returned `VersionNumber = <N>`.
128
+
129
+ 4. `Request executed (G-3):` — the GET and PATCH as **two separate
130
+ bullets** (not a Method/Path/Body sub-structure), each with the
131
+ full Tooling path, then the PATCH body as
132
+ `{ "Metadata": { …retrieved keys preserved…, "activeVersionNumber": <target> } }`,
133
+ then the `204 No Content` response line. On dry-run, rename this
134
+ section to `Planned request (G-3, dry-run):`, drop the GET bullet,
135
+ render the PATCH body as the fixed-key-only
136
+ `{ "Metadata": { "activeVersionNumber": <target> } }` (no retrieved
137
+ keys — the GET is skipped), omit the response line, row Outcome is
138
+ `DRY_RUN`, and skip section 6 below.
139
+
140
+ 5. The single-row phase table (unchanged from SKILL.md's canonical
141
+ shape; `Describe` column may be `—` for Flows since Step B's
142
+ describe is prod-parity only).
143
+
144
+ 6. `Read-back verification (G-4):` — the re-run G-1 SOQL and the
145
+ observed `ActiveVersion.VersionNumber` (or `ActiveVersion = null`
146
+ after Deactivate), confirming the mutation landed.
147
+
148
+ Short-circuit cases (`ALREADY_ACTIVE` / `ALREADY_INACTIVE` /
149
+ `NOT_FOUND` / `AMBIGUOUS` / `API_NOT_IDENTIFIED`) emit G-1 (and G-2
150
+ if the short-circuit is only visible after target resolution) and
151
+ stop — skip G-3, G-4, and the row's `HTTP` column renders `—`.
@@ -131,15 +131,61 @@ Skip F-4 entirely in dry-run (nothing to verify).
131
131
 
132
132
  ## Summary shape for a Step F entry
133
133
 
134
- Reuse the phase table from the canonical output shape:
135
-
136
- - `Sobject` column reads `CronTrigger`.
137
- - `Describe` column reads `—` (no describe probe on this route).
138
- - "Planned request" / "Request executed" is the anonymous Apex snippet
139
- path (`/tmp/entry-<Slug>.apex`) and its contents inline, not a PATCH
140
- path/body.
141
- - `HTTP` column reads `—` (no direct HTTP status; `sf apex run` bundles
142
- compile+execute into one response).
134
+ The generic `Planned request:` block from SKILL.md's canonical output
135
+ shape does **not** apply to a ScheduledApex entry — the Step F path
136
+ dispatches across four distinct sub-steps (pre-flight SOQL, snippet
137
+ build, `sf apex run`, verify SOQL), so the summary must record
138
+ evidence that each sub-step ran, not just the final `sf apex run`.
139
+ Replace the `Planned request:` block with the labeled narrative
140
+ below, keeping the single-row phase table and the Totals /
141
+ Follow-ups sections unchanged.
142
+
143
+ For each ScheduledApex entry's phase block, emit (in this order):
144
+
145
+ 1. A one-line route header:
146
+ ```text
147
+ Route: **Step F — Scheduled Apex (anonymous Apex)**. No describe probe on this route; `CronTrigger` is read-only.
148
+ ```
149
+
150
+ 2. `Pre-flight AMBIGUOUS check:` — the F-1 SOQL and the single-row
151
+ `totalSize` result (never a bulk count claim), ending with the
152
+ decision (`totalSize = 0 → proceed to F-2` /
153
+ `AMBIGUOUS short-circuit — N existing CronTrigger rows`). Emit the
154
+ SOQL verbatim, with `JobName` single-quoted as it appeared on the
155
+ wire (post-SOQL-escape); do not paraphrase.
156
+
157
+ 3. `Request executed:` — the F-2/F-3 artifacts as **three separate
158
+ bullets** (not Method/Path/Body sub-structure):
159
+ - `Snippet path:` — the `/tmp/entry-<Slug>.apex` path F-2 wrote.
160
+ - `Snippet body:` — the exact one-line
161
+ `System.schedule('<JobName>', '<CronExpression>', new <ApexClassName>());`
162
+ string F-2 produced, with the same single-quote-escaping F-2
163
+ applied (not the pre-escape input).
164
+ - `Command:` — the exact
165
+ `sf apex run --file /tmp/entry-<Slug>.apex --json` invocation
166
+ F-3 ran.
167
+ On dry-run, rename this section to `Planned request (F-2, dry-run):`,
168
+ keep only the `Snippet path:` and `Snippet body:` bullets (omit
169
+ `Command:` — F-3 is skipped), row Outcome is `DRY_RUN`, and skip
170
+ section 5 below.
171
+
172
+ 4. The single-row phase table (unchanged from SKILL.md's canonical
173
+ shape):
174
+ - `Sobject` column reads `CronTrigger`.
175
+ - `Describe` column reads `—` (no describe probe on this route).
176
+ - `HTTP` column reads `—` (no direct HTTP status; `sf apex run`
177
+ bundles compile + execute into one response).
178
+
179
+ 5. `Read-back verification:` — the F-4 SOQL verbatim and the
180
+ observed row (`N row(s) with CronExpression = "<value>"`),
181
+ confirming the schedule landed. On `0 rows → FAILED_VERIFY`
182
+ emit the zero-row result; on `CronExpression mismatch →
183
+ FAILED_VERIFY` emit both the expected and observed values.
184
+
185
+ Short-circuit cases (`AMBIGUOUS` / `API_NOT_IDENTIFIED` /
186
+ `FAILED` at F-2 or F-3 / `FAILED_VERIFY`) emit F-1 (and F-2/F-3 if
187
+ the failure is visible later) and stop — skip F-4 and the row's
188
+ `HTTP` column renders `—`.
143
189
 
144
190
  ## Dry-run recap
145
191
 
@@ -12,9 +12,7 @@ metadata:
12
12
  semver: ">=20.17.0"
13
13
  - tool: ["sf"]
14
14
  semver: ">=2.0.0"
15
- accessCheck:
16
- - type: license
17
- value: "CGCRetailAndTPMMgmtPsl"
15
+ accessCheck: "License.CGCRetailAndTPMMgmtPsl"
18
16
  ---
19
17
 
20
18
  # Deploy the Promotion BO API
@@ -32,16 +30,13 @@ three REST endpoints:
32
30
  - `POST /services/apexrest/<prefix>/promotions/ingest`
33
31
  - `GET /services/apexrest/<prefix>/promotions/status?importId=...`
34
32
 
35
- It is meant for **headless delivery**: point it at an org, answer the interview,
36
- and the skill installs the step and verifies it. The Promotion BO API ships in
37
- the CGCloud **managed package** (namespace `cgcloud` released, `cgcloud_dev`
38
- dev/beta). Every Apex identifier, object/field API name, and REST URL is derived
39
- from the detected package prefix at runtime — this skill never hard-codes it.
33
+ It is meant for **headless delivery**: point it at an org, answer the interview, the skill installs and verifies the step. The Promotion BO API ships in the CGCloud **managed package** (`cgcloud` released, `cgcloud_dev` dev/beta). Every Apex identifier, object/field API name, and REST URL is derived from the detected package prefix at runtime.
40
34
 
41
35
  `derive` is out of scope here (a follow-up skill covers it). Detailed runbooks
42
36
  for the write and smoke phases live under `references/`:
43
37
 
44
- - `references/conventions-and-payload-rules.md` — sales-org partitioning, schema-first contract derivation, and the eight cross-workflow payload rules R1–R8.
38
+ - `references/preflight-and-seed-queries.md` — Phase 1/2/3 SOQL (TPM entitlement, permset gate, framework-state verification, static-resource retrieve recipe, batch-chain polling).
39
+ - `references/conventions-and-payload-rules.md` — sales-org partitioning, schema-first contract derivation, and the nine cross-workflow payload rules R1–R9.
45
40
  - `references/generate-and-wire.md` — Phase 5a (generate + deploy the class) and Phase 5b (register + wire the step), with the full `register-step.apex`.
46
41
  - `references/smoke-and-verify.md` — Phase 6 (contract derivation, payload materialization, create/update/copy legs + verification).
47
42
  - `references/reference-example-set-comment-value.md` — the shipped `SetCommentValue` preset (assets at `assets/set-comment-value/`).
@@ -83,50 +78,19 @@ Every Apex call is `${PREFIX_DOT}ClassName.method(...)`; every SOQL object/field
83
78
  Run these and stop on the first failure.
84
79
 
85
80
  1. `sf --version`; `node --version` (only if running shipped scripts).
86
- 2. `sf org display --target-org <alias> --json` — confirm reachable, capture `instanceUrl` + org id (for display/logging only). Every REST call in later phases goes through `sf api request rest`, which uses the CLI's stored session — this skill never extracts the access token.
87
- 3. Validate the sales org locally: length 4, uppercase, non-blank (mirrors `TPMSetupData.validateSalesOrg`). Fail fast instead of surfacing an Apex stack trace.
88
- 4. **TPM entitlement (Permission Set License)** — the TPM app is licensed through the **`CGCRetailAndTPMMgmtPsl`** Permission Set License ("CGC Retail and Trade Promotion Management"); there is **no** `TPM App` UserLicense. Check the running user's PSL assignment:
89
- ```sql
90
- SELECT Id FROM PermissionSetLicenseAssign
91
- WHERE Assignee.Username = '<running-user-name>'
92
- AND PermissionSetLicense.DeveloperName = 'CGCRetailAndTPMMgmtPsl'
93
- ```
94
- On scratch/dev-hub the PSL may be legitimately unassigned; detect env with `SELECT OrganizationType, IsSandbox FROM Organization`:
95
- - `OrganizationType = 'Developer Edition'` OR `IsSandbox = true` and empty → **warn** ("permset gate below is the real check"), continue.
96
- - Any other org shape and empty → **stop** ("TPM Permission Set License `CGCRetailAndTPMMgmtPsl` not assigned to the running user").
97
- 5. **Running-user permission set (TPM Admin persona)** — the setup needs a permission set granting the *TPM Master Data Admin* persona. The shipped permset is `TPM_Master_Data_Admin` (packaged; `PermissionSet.Name` is the bare name regardless of namespace), but **customers frequently clone it** under their own name — so treat a miss as "ask", not "fail". Take `<running-user-name>` from `sf org display ... .result.username`:
98
- ```sql
99
- SELECT PermissionSet.Name, PermissionSet.Label FROM PermissionSetAssignment
100
- WHERE Assignee.Username = '<running-user-name>'
101
- AND (PermissionSet.Name = 'TPM_Master_Data_Admin'
102
- OR PermissionSet.Label LIKE '%TPM%Master Data Admin%')
103
- ```
104
- Match → proceed. No match → **do not hard-fail on the name**: show `sf org assign permset --name TPM_Master_Data_Admin --target-org <alias>` as the default option, but also ask the user to confirm whether they already hold an equivalent (possibly cloned/renamed) permission set for the TPM admin persona. Pause, wait for a cloned-permset confirmation or the assignment, then re-query. Proceed only once admin access is confirmed.
81
+ 2. `sf org display --target-org <alias> --json` — confirm reachable; capture `instanceUrl` + org id (display/logging only). REST calls use `sf api request rest` on the CLI's stored session — the skill never extracts the access token.
82
+ 3. Validate sales org locally: length 4, uppercase, non-blank (mirrors `TPMSetupData.validateSalesOrg`). Fail fast.
83
+ 4. **TPM entitlement (Permission Set License)** — the `CGCRetailAndTPMMgmtPsl` PSL assignment on the running user. SOQL + warn/stop routing in `references/preflight-and-seed-queries.md` Step 4.
84
+ 5. **Running-user permission set (TPM Admin persona)** — the running user must hold `TPM_Master_Data_Admin` (or a clone matching `%TPM%Master Data Admin%`); if missing, offer `sf org assign permset --name TPM_Master_Data_Admin --target-org <alias>` and wait. SOQL in `references/preflight-and-seed-queries.md` Step 5. Proceed only once admin access is confirmed.
105
85
  6. Namespace discovery (above).
86
+ 7. **Running user's `${PREFIX_UNDER}Sales_Org__c` equals `<salesOrg>`.** Blank throws `SOU0003`; mismatch surfaces as "not found" (create/update) or `TransformationError` (copy). SOQL in `references/preflight-and-seed-queries.md` Step 7. Blank or `!= '<salesOrg>'` → stop; fix on the user record only. See `references/conventions-and-payload-rules.md` R6; category access is preflighted in Phase 6 per R9.
106
87
 
107
88
  ## Phase 2 — Verify BO API framework state
108
89
 
109
- Read-only; tells you whether Phase 3 needs to run. All three counts MUST be
110
- scoped to `<salesOrg>` — the framework rows are sales-org-partitioned.
111
-
112
- ```bash
113
- # 1. Workflow rows for this sales org (expect 4: create/update/copy/derive)
114
- sf data query --target-org <alias> --json --query "
115
- SELECT Name FROM ${PREFIX_UNDER}BO_API_Workflow__c
116
- WHERE ${PREFIX_UNDER}BO_API__r.Name = 'Promotion'
117
- AND ${PREFIX_UNDER}BO_API__r.${PREFIX_UNDER}Sales_Org__c = '<salesOrg>'"
118
-
119
- # 2. Step rows for this sales org
120
- sf data query --target-org <alias> --json --query "
121
- SELECT COUNT(Id) FROM ${PREFIX_UNDER}BO_API_Workflow_Step__c
122
- WHERE ${PREFIX_UNDER}Sales_Org__c = '<salesOrg>'"
123
-
124
- # 3. Junction rows whose parent workflow belongs to a Promotion BO API for this sales org
125
- sf data query --target-org <alias> --json --query "
126
- SELECT COUNT(Id) FROM ${PREFIX_UNDER}BO_API_Workflow_Workflow_Step__c
127
- WHERE ${PREFIX_UNDER}BO_API_Workflow__r.${PREFIX_UNDER}BO_API__r.Name = 'Promotion'
128
- AND ${PREFIX_UNDER}BO_API_Workflow__r.${PREFIX_UNDER}BO_API__r.${PREFIX_UNDER}Sales_Org__c = '<salesOrg>'"
129
- ```
90
+ Read-only; tells you whether Phase 3 needs to run. Run the three
91
+ sales-org-scoped SOQL queries in `references/preflight-and-seed-queries.md`
92
+ ("Phase 2 — Verify BO API framework state"): workflow rows, step row count,
93
+ junction row count — all scoped to `<salesOrg>`.
130
94
 
131
95
  - Zero workflow rows → fresh for this sales org, Phase 3 will seed.
132
96
  - Full state (4 workflow rows `create/update/copy/derive`; >0 step + junction rows) → Phase 3 is a no-op for this sales org (still run for idempotency; 3a shows zero net-new).
@@ -138,27 +102,10 @@ The workflows/steps/junctions/entities/input-structures ship as CSVs inside the
138
102
  packaged `TPMSetupData` static resource; `TPMSetupData.setupBOApi` reads and
139
103
  upserts them. Before running it, show the user what will land.
140
104
 
141
- 1. Locate the resource:
142
- ```bash
143
- sf data query --target-org <alias> --json --query "
144
- SELECT Id, Name, NamespacePrefix, SystemModStamp, BodyLength
145
- FROM StaticResource WHERE Name = 'TPMSetupData'"
146
- ```
147
- Zero rows → stop ("TPMSetupData static resource not found — is the CGCloud package installed?"). Multiple rows → prefer the one whose `NamespacePrefix` matches Phase 1; if prefixes disagree, stop and ask.
148
- 2. **Retrieve + expand the resource cross-platform via SFDX** — use `sf project retrieve`, which unpacks a zip StaticResource into a folder on every OS (no `curl`, no `unzip` — `unzip` isn't present on Windows by default). Retrieve into a **dedicated `setup-data/` subproject** so the read-only managed resource never mixes into the Phase-5a deploy tree (`./.promotion-bo-api-deploy/force-app`, which is what gets deployed back):
149
- ```bash
150
- mkdir -p ./.promotion-bo-api-deploy/setup-data/force-app/main/default
151
- printf '{ "packageDirectories": [{ "path": "force-app", "default": true }], "sourceApiVersion": "60.0" }' \
152
- > ./.promotion-bo-api-deploy/setup-data/sfdx-project.json
153
- ( cd ./.promotion-bo-api-deploy/setup-data && sf project retrieve start \
154
- --metadata "StaticResource:${PREFIX_UNDER}TPMSetupData" --target-org <alias> --json )
155
- ```
156
- Non-success → stop, print `result` errors.
157
- 3. SFDX expands the zip to `./.promotion-bo-api-deploy/setup-data/force-app/main/default/staticresources/${PREFIX_UNDER}TPMSetupData/BOApi/`. `ls` it. Expected: `0_BO_API__c.csv`, `0_BO_API_Entity__c.csv`, `0_BO_API_Output_Entity__c.csv`, `0_BO_API_Workflow__c.csv`, `0_BO_API_Workflow_Entity__c.csv`, `0_BO_API_Workflow_Step__c.csv`, `0_BO_API_Workflow_Workflow_Step__c.csv`, `0_BO_API_Step_Input_Structure__c.csv`, `import.json`. Any missing → stop, print the delta.
158
- 4. Confirm each target sObject exists: `sf sobject describe --sobject '${PREFIX_UNDER}BO_API_Workflow__c' --target-org <alias> > /dev/null` (etc.). Any describe failure → stop (package partially installed).
159
- 5. Show a summary (row counts per object; total ~232 rows across 8 objects; "upsert on Unique_Key__c — re-running is idempotent"), plus the first 3 rows of `0_BO_API_Workflow__c.csv` and `0_BO_API_Workflow_Step__c.csv` with `{{NS}}`/`{{SALES_ORG}}` substituted.
160
- 6. Wait for confirmation unless `--yes`. "no"/blank → abort clean, point at `./.promotion-bo-api-deploy/setup-data/force-app/main/default/staticresources/${PREFIX_UNDER}TPMSetupData/BOApi/`.
161
- 7. On `--dry-run` → stop here; do not run 3b.
105
+ 1. Locate + retrieve the `TPMSetupData` static resource into a dedicated `setup-data/` subproject (keeps the read-only managed resource out of the Phase-5a deploy tree at `./.promotion-bo-api-deploy/force-app`), then confirm each target sObject exists. SOQL + the full retrieve recipe + 8-CSV manifest + describe loop are in `references/preflight-and-seed-queries.md` ("Phase 3a — Retrieve the packaged seed"). Any step failing → stop with the printed delta/error.
106
+ 2. Show row counts per object (total ~232 rows across 8 objects; "upsert on `Unique_Key__c` — idempotent") + first 3 rows of `0_BO_API_Workflow__c.csv` and `0_BO_API_Workflow_Step__c.csv` with `{{NS}}`/`{{SALES_ORG}}` substituted.
107
+ 3. Wait for confirmation unless `--yes`. "no"/blank → abort clean, point at the retrieved BOApi/ directory.
108
+ 4. On `--dry-run` → stop here; do not run 3b.
162
109
 
163
110
  ## Phase 3b — Apply the default seed
164
111
 
@@ -166,17 +113,8 @@ Only after 3a confirmation.
166
113
 
167
114
  1. `PHASE3_START=$(date -u +%FT%TZ)`.
168
115
  2. Run metadata-wizard setup via anon Apex: `${PREFIX_DOT}TPMSetupData.setupMetadataWizard('<salesOrg>');` → `sf apex run --target-org <alias> --file ./.promotion-bo-api-deploy/setup-metadata-wizard.apex`.
169
- 3. Wait for the whole batch chain. `GenericDemoSetupDataBatch` self-chains via `finish()`; polling a single job id misses children. Poll by class:
170
- ```bash
171
- sf data query --target-org <alias> --json --query "
172
- SELECT COUNT() FROM AsyncApexJob
173
- WHERE ApexClass.Name = 'GenericDemoSetupDataBatch'
174
- AND CreatedDate >= ${PHASE3_START}
175
- AND Status NOT IN ('Completed','Failed','Aborted')"
176
- ```
177
- Sleep 5s between polls; break when count is zero for **two consecutive** polls (covers the gap between a parent's `finish()` and the child's `AsyncApexJob` row appearing).
178
- 4. Final check — every child succeeded (`SELECT Id, Status, NumberOfErrors, ExtendedStatus FROM AsyncApexJob WHERE ApexClass.Name = 'GenericDemoSetupDataBatch' AND CreatedDate >= ${PHASE3_START}`). Any `Failed`/`Aborted`/`NumberOfErrors > 0` → stop, print `ExtendedStatus`.
179
- 5. Repeat 2–4 for `${PREFIX_DOT}TPMSetupData.setupBOApi('<salesOrg>');`.
116
+ 3. Wait for the batch chain. `GenericDemoSetupDataBatch` self-chains via `finish()`, so poll by class with the two-consecutive-zero break pattern + run the final `NumberOfErrors`/`ExtendedStatus` check. SOQL in `references/preflight-and-seed-queries.md` ("Phase 3b — Poll the batch chain"). Any `Failed`/`Aborted`/`NumberOfErrors > 0` → stop.
117
+ 4. Repeat 2–3 for `${PREFIX_DOT}TPMSetupData.setupBOApi('<salesOrg>');`.
180
118
 
181
119
  ## Phase 4 — Verify the BO API framework is on
182
120
 
@@ -189,17 +127,16 @@ component failed.
189
127
  ## Phase 5 — Interview: what does the user want to customize?
190
128
 
191
129
  The framework is now on. The customization is **user-supplied** — do not skip
192
- the interview; do not invent an answer. Print:
130
+ the interview or invent an answer. Print:
193
131
 
194
132
  ```text
195
133
  The Promotion BO API framework is installed. To add a custom step, tell me:
196
- - Which BO API entity does it target? (e.g. Promotion or Tactic — I'll list the exact entities registered in your org)
134
+ - Which BO API entity does it target? (e.g. Promotion/Tactic — I'll list registered entities)
197
135
  - Which workflows should it fire in? (create, update, copy)
198
136
  - What does it read from the ingest input?
199
137
  - What does it write on the SObject or elsewhere?
200
- - Any preconditions or side effects I should know about?
201
- If you want a worked example, say "use the SetCommentValue reference" —
202
- it copies the tactic input Comment onto Tactic.Comment__c.
138
+ - Any preconditions or side effects?
139
+ Say "use the SetCommentValue reference" for a worked example.
203
140
  ```
204
141
 
205
142
  ### 5.1 — Answers to collect
@@ -207,11 +144,12 @@ it copies the tactic input Comment onto Tactic.Comment__c.
207
144
  | Field | Required | Notes |
208
145
  |---|---|---|
209
146
  | `stepName` | yes | PascalCase Apex class name + BO API Workflow Step `Name`. Unique across `ApexClass`. |
210
- | `entity` | yes | BO API entity `Name` — MUST match a row queried live from `${PREFIX_UNDER}BO_API_Entity__c` (shared, not sales-org-partitioned); do not assume a fixed list. In a current org these include `Promotion`, `Tactic`, `ProductFilter`, `ManualInputs`, `CustomState` (plus the `TPM_Promotion.*` structures). Query the org and offer the actual rows. |
147
+ | `entity` | yes | BO API entity `Name` — MUST match a row queried live from `${PREFIX_UNDER}BO_API_Entity__c` (shared, not sales-org-partitioned). Typical rows include `Promotion`, `Tactic`, `ProductFilter`, `ManualInputs`, `CustomState`. Query the org — don't assume a fixed list. |
211
148
  | `workflows` | yes | Non-empty subset of `{create, update, copy}`. `derive` is out of scope. |
212
149
  | `actionName` | yes | Symbolic action string the class receives in `call(String action, ...)`. Convention: lowerCamelCase of `stepName`. |
213
150
  | `inputPaths` | yes (≥1) | JSON paths to read from `currentInput`, e.g. `["Comment"]`. |
214
151
  | `inputPathType` | yes | JSON type of the input paths — `String`, `Array`, `Boolean`, `Number`, `Object`. Drives the `RecordType` on each SIS row; a wrong value causes `TransformationError: Expected <Type>` at ingest. Different types → run Phase 5b once per type group. |
152
+ | `inputPathRequired` | optional | Whether `inputPaths` must appear on the ingest payload. Defaults to `false`. Drives `BO_API_Step_Input_Structure__c.Required__c` (platform default is `true` — skill always writes explicitly). See `references/generate-and-wire.md`. |
215
153
  | `outputWrites` | yes (≥1) | `{field, source}` pairs; `field` is the target API name **without** the namespace prefix (skill adds `${PREFIX_UNDER}`), `source` references an `inputPaths` value / literal / computed expression. |
216
154
  | `description` | yes | One-sentence step description → `${PREFIX_UNDER}Description__c` + class docstring. |
217
155
  | `preconditions` | optional | Free-text guards before writing. |
@@ -219,33 +157,38 @@ it copies the tactic input Comment onto Tactic.Comment__c.
219
157
 
220
158
  ### 5.2 — Interview flow
221
159
 
222
- 1. **Preset shortcut.** If `--preset set-comment-value` or the user says "use the SetCommentValue reference", load the answers from `references/reference-example-set-comment-value.md` (`assets/set-comment-value/interview-answers.json`) and skip the interactive interview. `--interview-file <path>` loads answers from a JSON file mirroring the 5.1 table (wins over `--preset`).
223
- 2. **Ask the questions** one at a time (or as a block); do not proceed until every required field has a concrete value.
160
+ 1. **Preset shortcut.** `--preset set-comment-value` (or "use the SetCommentValue reference") loads answers from `references/reference-example-set-comment-value.md` (`assets/set-comment-value/interview-answers.json`) and skips the interactive interview. `--interview-file <path>` loads a JSON mirroring the 5.1 table (wins over `--preset`).
161
+ 2. **Ask the questions** one at a time (or as a block); do not proceed until every required field has a value.
224
162
  3. **Show the entity's writeable fields** after the user names the entity:
225
163
  ```bash
226
164
  sf sobject describe --target-org <alias> --sobject '${PREFIX_UNDER}<Entity>__c' --json \
227
165
  | jq -r '.fields[] | select(.updateable == true and .createable == true) | .name'
228
166
  ```
229
167
  Any `outputWrites` field must appear here; otherwise stop and ask for a different one (FLS is caller-scoped).
230
- 4. **Confirm the plan** (recap Class/Entity/Workflows/Reads/Writes/Sort/Description → "Deploy + wire? [y/N]"). `n`/blank → save answers to `./.promotion-bo-api-deploy/interview.json` and exit. `y` → Phase 5a.
168
+ 4. **Confirm the plan** (recap Class/Entity/Workflows/Reads/Writes/Sort/Description → "Deploy + wire? [y/N]"). If `workflows` includes `copy`, flag that per-child input values come from the source promotion — `outputWrites` still fires but `<inputPaths>` values are ignored (only top-level fields like `Slogan` propagate). `n`/blank → save answers to `./.promotion-bo-api-deploy/interview.json` and exit. `y` → Phase 5a.
231
169
 
232
170
  ## Phases 5a / 5b — Generate + deploy, register + wire
233
171
 
234
- Runbook in `references/generate-and-wire.md`. In short: guard the class name for
235
- idempotency; confirm target-field writeability; generate a namespace-agnostic
236
- `Callable` from the interview (reads `currentInput`/`currentOutput`, derives the
237
- prefix from `getSObjectType()` at runtime); show + confirm; `sf project deploy
238
- start`. Then, in one anon-Apex transaction, upsert one `BO_API_Workflow_Step__c`
239
- (bare class name in `Classname__c`), one `BO_API_Workflow_Workflow_Step__c`
240
- junction per chosen workflow (never assume all three; `derive` dropped), and the
241
- `BO_API_Step_Input_Structure__c` rows (RecordType per `inputPathType`). Both
172
+ Runbook in `references/generate-and-wire.md`. In short: guard the class name
173
+ for idempotency; confirm target-field writeability; clean any stale
174
+ `.cls`/`.cls-meta.xml` from
175
+ `./.promotion-bo-api-deploy/force-app/main/default/classes/` (leftovers cause a
176
+ silent MDAPI full-batch rollback); generate a namespace-agnostic `Callable`
177
+ from the interview — class **and** `call` method must be `global` (packaged
178
+ `CallableManager` resolves via `Type.forName` across the namespace boundary;
179
+ `public` fails at runtime on managed installs — see runbook); show + confirm;
180
+ `sf project deploy start`. Then, in one anon-Apex transaction, upsert one
181
+ `BO_API_Workflow_Step__c` (bare class name in `Classname__c`), one
182
+ `BO_API_Workflow_Workflow_Step__c` junction per chosen workflow (`derive`
183
+ dropped), and the `BO_API_Step_Input_Structure__c` rows (RecordType per
184
+ `inputPathType`, `Required__c` set explicitly from `inputPathRequired`). Both
242
185
  upserts key on `Unique_Key__c` (idempotent).
243
186
 
244
187
  ## Phase 6 — Smoke test through the REST endpoints
245
188
 
246
189
  Runbook in `references/smoke-and-verify.md`. Derive the payload contract per
247
190
  invoked workflow from `BO_API_Step_Input_Structure__c` (schema-first), apply
248
- rules R1–R8 from `references/conventions-and-payload-rules.md`, resolve every
191
+ rules R1–R9 from `references/conventions-and-payload-rules.md`, resolve every
249
192
  reference in-sales-org, materialize `./out/smoke/{create,update,copy}.json`, then
250
193
  run `initialize → ingest → poll status` for each chosen workflow. Verify via a
251
194
  direct `BO_API_Transaction_Log__c` query (any row `!= 'Calculated'` → fail) plus
@@ -254,12 +197,11 @@ copy legs run only if the interview included them.
254
197
 
255
198
  ## Phase 7 — Report
256
199
 
257
- Short status: org, sales org, dry-run flag; namespace prefix; BO API seed rows
258
- before/after (Phases 2 & 4); interview (preset name or resolved `stepName`,
259
- `entity`, `workflows`, `inputPaths`, `outputWrites`); class deploy result +
260
- workflow-step id + junction ids (1–3); smoke import ids per invoked workflow, all
261
- `Calculated` with matching write assertions — or exactly which failed (SOQL,
262
- expected, actual); manual follow-ups (`none` if clean).
200
+ Short status: org, sales org, dry-run flag; namespace prefix; seed rows
201
+ before/after (Phases 2 & 4); interview summary; deploy result + workflow-step
202
+ id + junction ids; smoke import ids per invoked workflow — all `Calculated`
203
+ with matching assertions, or exactly which failed (SOQL, expected, actual);
204
+ manual follow-ups (`none` if clean).
263
205
 
264
206
  ## Rules
265
207
 
@@ -269,7 +211,12 @@ expected, actual); manual follow-ups (`none` if clean).
269
211
  - Never hard-code the namespace prefix — compose every Apex/SOQL/REST reference from the Phase-1 detection.
270
212
  - Never invent Apex method signatures — use what the packaged `TPMSetupData` / `BO_API_*` objects and the shipping REST endpoints declare.
271
213
  - The seeded BO API metadata is packaged content — preview it and get user confirmation before invoking `TPMSetupData.setupBOApi`.
272
- - Never invent an interview answer. Absent a user and any `--preset`/`--interview-file`, stop and print the questions.
273
- - Never wire a workflow the interview did not include. `create`/`update`/`copy` are individually opt-in; `derive` is always excluded.
214
+ - Never invent an interview answer. Absent a user and `--preset`/`--interview-file`, stop and print the questions.
215
+ - Never wire a workflow the interview did not include. `create`/`update`/`copy` are opt-in; `derive` is always excluded.
274
216
  - Never compose a smoke payload from a hard-coded template — derive accepted paths per workflow from `BO_API_Step_Input_Structure__c` and validate user input against that contract first.
275
217
  - Never resolve a reference value without the sales-org filter. A record under a different sales org is not valid for this invocation.
218
+ - Never generate a `Callable` step class with `public` visibility — declare the class and the `call` method `global`. `public` works only on same-namespace dev orgs and fails on managed installs with `Type "<stepName>" does not exist`; the transaction log shows a generic `Error`. See `references/generate-and-wire.md`.
219
+ - Never re-use `./.promotion-bo-api-deploy/force-app/main/default/classes/` across step deploys — remove the prior step's `.cls`/`.cls-meta.xml` first. A stale file triggers a silent MDAPI full-batch rollback while the CLI still reports `"created": true`.
220
+ - Never insert a `BO_API_Step_Input_Structure__c` row without setting `Required__c` explicitly. The platform defaults it to `true`; the skill defaults `false` and only opts in when `inputPathRequired` is set. Omitting the field silently makes every optional path mandatory.
221
+ - Never proceed past Phase 1 without confirming the running user's `${PREFIX_UNDER}Sales_Org__c` equals `<salesOrg>` — blank throws `SOU0003`; mismatch surfaces as "not found" (create/update) or `TransformationError` (copy). Fix on the user record only. See `references/conventions-and-payload-rules.md` R6.
222
+ - Never ingest a payload whose `ProductFilter.Criteria.<level>` references categories the running user lacks `Product_Category_Share__c` `Access_Level__c='ReadWrite'` on — Phase 6 preflight per `references/conventions-and-payload-rules.md` R9. Do not silently insert share rows.
@@ -4,7 +4,7 @@
4
4
  "salesOrg": "0001",
5
5
  "promotions": [
6
6
  {
7
- "SourceId": "REPLACED-AT-RUNTIME-PROMOTION-ID"
7
+ "Id": "REPLACED-AT-RUNTIME-PROMOTION-ID"
8
8
  }
9
9
  ]
10
10
  }