scc-universal 1.1.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 (271) hide show
  1. package/.claude-plugin/plugin.json +44 -0
  2. package/.cursor/agents/deep-researcher.md +142 -0
  3. package/.cursor/agents/doc-updater.md +219 -0
  4. package/.cursor/agents/eval-runner.md +335 -0
  5. package/.cursor/agents/learning-engine.md +210 -0
  6. package/.cursor/agents/loop-operator.md +245 -0
  7. package/.cursor/agents/refactor-cleaner.md +119 -0
  8. package/.cursor/agents/sf-admin-agent.md +127 -0
  9. package/.cursor/agents/sf-agentforce-agent.md +126 -0
  10. package/.cursor/agents/sf-apex-agent.md +117 -0
  11. package/.cursor/agents/sf-architect.md +426 -0
  12. package/.cursor/agents/sf-aura-reviewer.md +369 -0
  13. package/.cursor/agents/sf-bugfix-agent.md +101 -0
  14. package/.cursor/agents/sf-flow-agent.md +155 -0
  15. package/.cursor/agents/sf-integration-agent.md +141 -0
  16. package/.cursor/agents/sf-lwc-agent.md +123 -0
  17. package/.cursor/agents/sf-review-agent.md +357 -0
  18. package/.cursor/agents/sf-visualforce-reviewer.md +465 -0
  19. package/.cursor/hooks/adapter.js +81 -0
  20. package/.cursor/hooks/after-file-edit.js +26 -0
  21. package/.cursor/hooks/after-mcp-execution.js +12 -0
  22. package/.cursor/hooks/after-shell-execution.js +30 -0
  23. package/.cursor/hooks/after-tab-file-edit.js +12 -0
  24. package/.cursor/hooks/before-mcp-execution.js +11 -0
  25. package/.cursor/hooks/before-read-file.js +13 -0
  26. package/.cursor/hooks/before-shell-execution.js +29 -0
  27. package/.cursor/hooks/before-submit-prompt.js +23 -0
  28. package/.cursor/hooks/pre-compact.js +7 -0
  29. package/.cursor/hooks/session-end.js +10 -0
  30. package/.cursor/hooks/session-start.js +10 -0
  31. package/.cursor/hooks/stop.js +18 -0
  32. package/.cursor/hooks/subagent-start.js +10 -0
  33. package/.cursor/hooks/subagent-stop.js +10 -0
  34. package/.cursor/hooks.json +107 -0
  35. package/.cursor/skills/aside/SKILL.md +115 -0
  36. package/.cursor/skills/checkpoint/SKILL.md +50 -0
  37. package/.cursor/skills/configure-scc/SKILL.md +160 -0
  38. package/.cursor/skills/continuous-agent-loop/SKILL.md +260 -0
  39. package/.cursor/skills/mcp-server-patterns/SKILL.md +142 -0
  40. package/.cursor/skills/model-route/SKILL.md +81 -0
  41. package/.cursor/skills/prompt-optimizer/SKILL.md +366 -0
  42. package/.cursor/skills/refactor-clean/SKILL.md +133 -0
  43. package/.cursor/skills/resume-session/SKILL.md +111 -0
  44. package/.cursor/skills/save-session/SKILL.md +183 -0
  45. package/.cursor/skills/search-first/SKILL.md +140 -0
  46. package/.cursor/skills/security-scan/SKILL.md +142 -0
  47. package/.cursor/skills/sessions/SKILL.md +124 -0
  48. package/.cursor/skills/sf-agentforce-development/SKILL.md +449 -0
  49. package/.cursor/skills/sf-apex-async-patterns/SKILL.md +324 -0
  50. package/.cursor/skills/sf-apex-best-practices/SKILL.md +421 -0
  51. package/.cursor/skills/sf-apex-constraints/SKILL.md +79 -0
  52. package/.cursor/skills/sf-apex-cursor/SKILL.md +336 -0
  53. package/.cursor/skills/sf-apex-enterprise-patterns/SKILL.md +344 -0
  54. package/.cursor/skills/sf-apex-testing/SKILL.md +407 -0
  55. package/.cursor/skills/sf-api-design/SKILL.md +237 -0
  56. package/.cursor/skills/sf-approval-processes/SKILL.md +312 -0
  57. package/.cursor/skills/sf-aura-development/SKILL.md +260 -0
  58. package/.cursor/skills/sf-build-fix/SKILL.md +120 -0
  59. package/.cursor/skills/sf-data-modeling/SKILL.md +274 -0
  60. package/.cursor/skills/sf-debugging/SKILL.md +362 -0
  61. package/.cursor/skills/sf-deployment/SKILL.md +291 -0
  62. package/.cursor/skills/sf-deployment-constraints/SKILL.md +153 -0
  63. package/.cursor/skills/sf-devops-ci-cd/SKILL.md +322 -0
  64. package/.cursor/skills/sf-docs-lookup/SKILL.md +100 -0
  65. package/.cursor/skills/sf-e2e-testing/SKILL.md +321 -0
  66. package/.cursor/skills/sf-experience-cloud/SKILL.md +248 -0
  67. package/.cursor/skills/sf-flow-development/SKILL.md +376 -0
  68. package/.cursor/skills/sf-governor-limits/SKILL.md +319 -0
  69. package/.cursor/skills/sf-harness-audit/SKILL.md +139 -0
  70. package/.cursor/skills/sf-help/SKILL.md +156 -0
  71. package/.cursor/skills/sf-integration/SKILL.md +479 -0
  72. package/.cursor/skills/sf-lwc-constraints/SKILL.md +128 -0
  73. package/.cursor/skills/sf-lwc-development/SKILL.md +302 -0
  74. package/.cursor/skills/sf-lwc-testing/SKILL.md +387 -0
  75. package/.cursor/skills/sf-metadata-management/SKILL.md +285 -0
  76. package/.cursor/skills/sf-platform-events-cdc/SKILL.md +372 -0
  77. package/.cursor/skills/sf-quickstart/SKILL.md +170 -0
  78. package/.cursor/skills/sf-security/SKILL.md +330 -0
  79. package/.cursor/skills/sf-security-constraints/SKILL.md +125 -0
  80. package/.cursor/skills/sf-soql-constraints/SKILL.md +129 -0
  81. package/.cursor/skills/sf-soql-optimization/SKILL.md +353 -0
  82. package/.cursor/skills/sf-tdd-workflow/SKILL.md +332 -0
  83. package/.cursor/skills/sf-testing-constraints/SKILL.md +198 -0
  84. package/.cursor/skills/sf-trigger-constraints/SKILL.md +88 -0
  85. package/.cursor/skills/sf-trigger-frameworks/SKILL.md +343 -0
  86. package/.cursor/skills/sf-visualforce-development/SKILL.md +259 -0
  87. package/.cursor/skills/strategic-compact/SKILL.md +205 -0
  88. package/.cursor/skills/update-docs/SKILL.md +162 -0
  89. package/.cursor/skills/update-platform-docs/SKILL.md +86 -0
  90. package/.cursor-plugin/plugin.json +26 -0
  91. package/LICENSE +21 -0
  92. package/README.md +522 -0
  93. package/agents/deep-researcher.md +145 -0
  94. package/agents/doc-updater.md +222 -0
  95. package/agents/eval-runner.md +340 -0
  96. package/agents/learning-engine.md +211 -0
  97. package/agents/loop-operator.md +247 -0
  98. package/agents/refactor-cleaner.md +122 -0
  99. package/agents/sf-admin-agent.md +131 -0
  100. package/agents/sf-agentforce-agent.md +132 -0
  101. package/agents/sf-apex-agent.md +124 -0
  102. package/agents/sf-architect.md +435 -0
  103. package/agents/sf-aura-reviewer.md +372 -0
  104. package/agents/sf-bugfix-agent.md +105 -0
  105. package/agents/sf-flow-agent.md +159 -0
  106. package/agents/sf-integration-agent.md +146 -0
  107. package/agents/sf-lwc-agent.md +127 -0
  108. package/agents/sf-review-agent.md +366 -0
  109. package/agents/sf-visualforce-reviewer.md +468 -0
  110. package/assets/logo.svg +18 -0
  111. package/docs/ARCHITECTURE.md +133 -0
  112. package/docs/authoring-guide.md +373 -0
  113. package/docs/hook-development.md +578 -0
  114. package/docs/token-optimization.md +139 -0
  115. package/docs/workflow-examples.md +645 -0
  116. package/examples/agentforce-action/README.md +227 -0
  117. package/examples/apex-trigger-handler/README.md +114 -0
  118. package/examples/devops-pipeline/README.md +325 -0
  119. package/examples/flow-automation/README.md +188 -0
  120. package/examples/integration-pattern/README.md +416 -0
  121. package/examples/lwc-component/README.md +180 -0
  122. package/examples/platform-events/README.md +492 -0
  123. package/examples/scratch-org-setup/README.md +138 -0
  124. package/examples/security-audit/README.md +244 -0
  125. package/examples/visualforce-migration/README.md +314 -0
  126. package/hooks/hooks.json +338 -0
  127. package/hooks/memory-persistence/README.md +73 -0
  128. package/manifests/install-modules.json +217 -0
  129. package/manifests/install-profiles.json +17 -0
  130. package/mcp-configs/mcp-servers.json +19 -0
  131. package/package.json +89 -0
  132. package/schemas/hooks.schema.json +123 -0
  133. package/schemas/install-modules.schema.json +76 -0
  134. package/schemas/install-profiles.schema.json +28 -0
  135. package/schemas/install-state.schema.json +73 -0
  136. package/schemas/package-manager.schema.json +18 -0
  137. package/schemas/plugin.schema.json +112 -0
  138. package/schemas/scc-install-config.schema.json +29 -0
  139. package/schemas/state-store.schema.json +111 -0
  140. package/scripts/cli/install-apply.js +170 -0
  141. package/scripts/cli/uninstall.js +193 -0
  142. package/scripts/hooks/check-console-log.js +101 -0
  143. package/scripts/hooks/check-hook-enabled.js +17 -0
  144. package/scripts/hooks/check-platform-docs-age.js +48 -0
  145. package/scripts/hooks/cost-tracker.js +78 -0
  146. package/scripts/hooks/doc-file-warning.js +63 -0
  147. package/scripts/hooks/evaluate-session.js +98 -0
  148. package/scripts/hooks/governor-check.js +220 -0
  149. package/scripts/hooks/learning-observe.sh +206 -0
  150. package/scripts/hooks/mcp-health-check.js +588 -0
  151. package/scripts/hooks/post-bash-build-complete.js +34 -0
  152. package/scripts/hooks/post-bash-pr-created.js +43 -0
  153. package/scripts/hooks/post-edit-console-warn.js +61 -0
  154. package/scripts/hooks/post-edit-format.js +79 -0
  155. package/scripts/hooks/post-edit-typecheck.js +98 -0
  156. package/scripts/hooks/post-write.js +168 -0
  157. package/scripts/hooks/pre-bash-git-push-reminder.js +35 -0
  158. package/scripts/hooks/pre-bash-tmux-reminder.js +47 -0
  159. package/scripts/hooks/pre-compact.js +51 -0
  160. package/scripts/hooks/pre-tool-use.js +163 -0
  161. package/scripts/hooks/pre-write-doc-warn.js +9 -0
  162. package/scripts/hooks/quality-gate.js +251 -0
  163. package/scripts/hooks/run-with-flags-shell.sh +32 -0
  164. package/scripts/hooks/run-with-flags.js +135 -0
  165. package/scripts/hooks/session-end-marker.js +29 -0
  166. package/scripts/hooks/session-end.js +311 -0
  167. package/scripts/hooks/session-start.js +202 -0
  168. package/scripts/hooks/sfdx-scanner-check.js +142 -0
  169. package/scripts/hooks/sfdx-validate.js +119 -0
  170. package/scripts/hooks/stop-hook.js +170 -0
  171. package/scripts/hooks/suggest-compact.js +67 -0
  172. package/scripts/lib/agent-adapter.js +82 -0
  173. package/scripts/lib/apex-analysis.js +194 -0
  174. package/scripts/lib/hook-flags.js +74 -0
  175. package/scripts/lib/install-config.js +73 -0
  176. package/scripts/lib/install-executor.js +363 -0
  177. package/scripts/lib/install-state.js +121 -0
  178. package/scripts/lib/orchestration-session.js +299 -0
  179. package/scripts/lib/package-manager.js +124 -0
  180. package/scripts/lib/project-detect.js +228 -0
  181. package/scripts/lib/schema-validator.js +190 -0
  182. package/scripts/lib/skill-adapter.js +100 -0
  183. package/scripts/lib/state-store.js +376 -0
  184. package/scripts/lib/tmux-worktree-orchestrator.js +598 -0
  185. package/scripts/lib/utils.js +313 -0
  186. package/scripts/scc.js +164 -0
  187. package/skills/_reference/AGENTFORCE_PATTERNS.md +112 -0
  188. package/skills/_reference/APEX_CURSOR.md +159 -0
  189. package/skills/_reference/API_VERSIONS.md +78 -0
  190. package/skills/_reference/APPROVAL_PROCESSES.md +105 -0
  191. package/skills/_reference/ASYNC_PATTERNS.md +163 -0
  192. package/skills/_reference/AURA_COMPONENTS.md +146 -0
  193. package/skills/_reference/DATA_MIGRATION_PATTERNS.md +151 -0
  194. package/skills/_reference/DATA_MODELING.md +124 -0
  195. package/skills/_reference/DEBUGGING_TOOLS.md +140 -0
  196. package/skills/_reference/DEPLOYMENT_CHECKLIST.md +87 -0
  197. package/skills/_reference/DEPRECATIONS.md +79 -0
  198. package/skills/_reference/DOCKER_CI_PATTERNS.md +138 -0
  199. package/skills/_reference/ENTERPRISE_PATTERNS.md +122 -0
  200. package/skills/_reference/EXPERIENCE_CLOUD.md +143 -0
  201. package/skills/_reference/FLOW_PATTERNS.md +113 -0
  202. package/skills/_reference/GOVERNOR_LIMITS.md +77 -0
  203. package/skills/_reference/INTEGRATION_PATTERNS.md +105 -0
  204. package/skills/_reference/LWC_PATTERNS.md +79 -0
  205. package/skills/_reference/METADATA_TYPES.md +115 -0
  206. package/skills/_reference/NAMING_CONVENTIONS.md +84 -0
  207. package/skills/_reference/PACKAGE_DEVELOPMENT.md +150 -0
  208. package/skills/_reference/PLATFORM_EVENTS.md +121 -0
  209. package/skills/_reference/REPORTING_API.md +143 -0
  210. package/skills/_reference/SCRATCH_ORG_PATTERNS.md +126 -0
  211. package/skills/_reference/SECURITY_PATTERNS.md +127 -0
  212. package/skills/_reference/SHARING_MODEL.md +120 -0
  213. package/skills/_reference/SOQL_PATTERNS.md +119 -0
  214. package/skills/_reference/TESTING_STANDARDS.md +96 -0
  215. package/skills/_reference/TRIGGER_PATTERNS.md +114 -0
  216. package/skills/_reference/VISUALFORCE_PATTERNS.md +121 -0
  217. package/skills/aside/SKILL.md +118 -0
  218. package/skills/checkpoint/SKILL.md +53 -0
  219. package/skills/configure-scc/SKILL.md +163 -0
  220. package/skills/continuous-agent-loop/SKILL.md +264 -0
  221. package/skills/mcp-server-patterns/SKILL.md +146 -0
  222. package/skills/model-route/SKILL.md +84 -0
  223. package/skills/prompt-optimizer/SKILL.md +369 -0
  224. package/skills/refactor-clean/SKILL.md +136 -0
  225. package/skills/resume-session/SKILL.md +114 -0
  226. package/skills/save-session/SKILL.md +186 -0
  227. package/skills/search-first/SKILL.md +144 -0
  228. package/skills/security-scan/SKILL.md +146 -0
  229. package/skills/sessions/SKILL.md +127 -0
  230. package/skills/sf-agentforce-development/SKILL.md +450 -0
  231. package/skills/sf-apex-async-patterns/SKILL.md +326 -0
  232. package/skills/sf-apex-best-practices/SKILL.md +425 -0
  233. package/skills/sf-apex-constraints/SKILL.md +81 -0
  234. package/skills/sf-apex-cursor/SKILL.md +338 -0
  235. package/skills/sf-apex-enterprise-patterns/SKILL.md +348 -0
  236. package/skills/sf-apex-testing/SKILL.md +409 -0
  237. package/skills/sf-api-design/SKILL.md +238 -0
  238. package/skills/sf-approval-processes/SKILL.md +315 -0
  239. package/skills/sf-aura-development/SKILL.md +263 -0
  240. package/skills/sf-build-fix/SKILL.md +121 -0
  241. package/skills/sf-data-modeling/SKILL.md +278 -0
  242. package/skills/sf-debugging/SKILL.md +363 -0
  243. package/skills/sf-deployment/SKILL.md +295 -0
  244. package/skills/sf-deployment-constraints/SKILL.md +155 -0
  245. package/skills/sf-devops-ci-cd/SKILL.md +325 -0
  246. package/skills/sf-docs-lookup/SKILL.md +103 -0
  247. package/skills/sf-e2e-testing/SKILL.md +324 -0
  248. package/skills/sf-experience-cloud/SKILL.md +249 -0
  249. package/skills/sf-flow-development/SKILL.md +377 -0
  250. package/skills/sf-governor-limits/SKILL.md +323 -0
  251. package/skills/sf-harness-audit/SKILL.md +142 -0
  252. package/skills/sf-help/SKILL.md +159 -0
  253. package/skills/sf-integration/SKILL.md +483 -0
  254. package/skills/sf-lwc-constraints/SKILL.md +130 -0
  255. package/skills/sf-lwc-development/SKILL.md +303 -0
  256. package/skills/sf-lwc-testing/SKILL.md +388 -0
  257. package/skills/sf-metadata-management/SKILL.md +288 -0
  258. package/skills/sf-platform-events-cdc/SKILL.md +375 -0
  259. package/skills/sf-quickstart/SKILL.md +173 -0
  260. package/skills/sf-security/SKILL.md +334 -0
  261. package/skills/sf-security-constraints/SKILL.md +127 -0
  262. package/skills/sf-soql-constraints/SKILL.md +131 -0
  263. package/skills/sf-soql-optimization/SKILL.md +354 -0
  264. package/skills/sf-tdd-workflow/SKILL.md +336 -0
  265. package/skills/sf-testing-constraints/SKILL.md +200 -0
  266. package/skills/sf-trigger-constraints/SKILL.md +90 -0
  267. package/skills/sf-trigger-frameworks/SKILL.md +347 -0
  268. package/skills/sf-visualforce-development/SKILL.md +260 -0
  269. package/skills/strategic-compact/SKILL.md +208 -0
  270. package/skills/update-docs/SKILL.md +165 -0
  271. package/skills/update-platform-docs/SKILL.md +90 -0
@@ -0,0 +1,188 @@
1
+ # Flow Automation Example
2
+
3
+ Sample Record-Triggered Flow with best practices for review. API version 66.0 (Spring '26).
4
+
5
+ ## Structure
6
+
7
+ ```text
8
+ force-app/main/default/flows/
9
+ Account_Set_Rating.flow-meta.xml
10
+ ```
11
+
12
+ ## Flow Design: Account Rating Auto-Set
13
+
14
+ **Type:** Record-Triggered Flow (Before Save)
15
+ **Object:** Account
16
+ **When:** A record is created or updated
17
+ **Why Before Save:** Same-record field updates in a Before Save flow require no extra DML statement, making it more efficient than After Save for this use case.
18
+
19
+ ### Logic
20
+
21
+ 1. **Entry Criteria:** Annual Revenue is not null AND has changed (prevents recursion)
22
+ 2. **Decision:** Evaluate revenue ranges (ordered most-specific first)
23
+ - Revenue > 1,000,000 → Set Rating = "Hot"
24
+ - Revenue > 100,000 → Set Rating = "Warm"
25
+ - Revenue <= 100,000 → Set Rating = "Cold"
26
+ 3. **Assignment:** Set the Rating field on `$Record` (no Update element needed in Before Save)
27
+
28
+ ## Flow Metadata
29
+
30
+ ```xml
31
+ <!-- Account_Set_Rating.flow-meta.xml -->
32
+ <?xml version="1.0" encoding="UTF-8"?>
33
+ <Flow xmlns="http://soap.sforce.com/2006/04/metadata">
34
+ <apiVersion>66.0</apiVersion>
35
+ <description>Sets Account Rating based on Annual Revenue ranges.
36
+ Before-save flow — no DML needed for same-record updates.</description>
37
+ <interviewLabel>Account Set Rating {!$Flow.CurrentDateTime}</interviewLabel>
38
+ <label>Account Set Rating</label>
39
+ <processType>AutoLaunchedFlow</processType>
40
+ <status>Active</status>
41
+
42
+ <!-- Entry point: triggers on Account create/update -->
43
+ <start>
44
+ <locationX>50</locationX>
45
+ <locationY>0</locationY>
46
+ <connector>
47
+ <targetReference>Check_Revenue</targetReference>
48
+ </connector>
49
+ <filterFormula>
50
+ NOT(ISNULL({!$Record.AnnualRevenue}))
51
+ &amp;&amp; ISCHANGED({!$Record.AnnualRevenue})
52
+ </filterFormula>
53
+ <object>Account</object>
54
+ <recordTriggerType>CreateAndUpdate</recordTriggerType>
55
+ <triggerType>RecordBeforeSave</triggerType>
56
+ </start>
57
+
58
+ <!-- Decision: evaluate revenue ranges -->
59
+ <decisions>
60
+ <name>Check_Revenue</name>
61
+ <label>Check Revenue</label>
62
+ <locationX>182</locationX>
63
+ <locationY>158</locationY>
64
+ <defaultConnector>
65
+ <targetReference>Set_Rating_Cold</targetReference>
66
+ </defaultConnector>
67
+ <defaultConnectorLabel>Cold</defaultConnectorLabel>
68
+ <rules>
69
+ <name>Is_Hot</name>
70
+ <conditionLogic>and</conditionLogic>
71
+ <conditions>
72
+ <leftValueReference>$Record.AnnualRevenue</leftValueReference>
73
+ <operator>GreaterThan</operator>
74
+ <rightValue>
75
+ <numberValue>1000000</numberValue>
76
+ </rightValue>
77
+ </conditions>
78
+ <connector>
79
+ <targetReference>Set_Rating_Hot</targetReference>
80
+ </connector>
81
+ <label>Hot</label>
82
+ </rules>
83
+ <rules>
84
+ <name>Is_Warm</name>
85
+ <conditionLogic>and</conditionLogic>
86
+ <conditions>
87
+ <leftValueReference>$Record.AnnualRevenue</leftValueReference>
88
+ <operator>GreaterThan</operator>
89
+ <rightValue>
90
+ <numberValue>100000</numberValue>
91
+ </rightValue>
92
+ </conditions>
93
+ <connector>
94
+ <targetReference>Set_Rating_Warm</targetReference>
95
+ </connector>
96
+ <label>Warm</label>
97
+ </rules>
98
+ </decisions>
99
+
100
+ <!-- Assignment elements: set Rating on $Record (Before Save = no DML) -->
101
+ <assignments>
102
+ <name>Set_Rating_Hot</name>
103
+ <label>Set Rating Hot</label>
104
+ <locationX>50</locationX>
105
+ <locationY>334</locationY>
106
+ <assignmentItems>
107
+ <assignToReference>$Record.Rating</assignToReference>
108
+ <operator>Assign</operator>
109
+ <value>
110
+ <stringValue>Hot</stringValue>
111
+ </value>
112
+ </assignmentItems>
113
+ </assignments>
114
+ <assignments>
115
+ <name>Set_Rating_Warm</name>
116
+ <label>Set Rating Warm</label>
117
+ <locationX>182</locationX>
118
+ <locationY>334</locationY>
119
+ <assignmentItems>
120
+ <assignToReference>$Record.Rating</assignToReference>
121
+ <operator>Assign</operator>
122
+ <value>
123
+ <stringValue>Warm</stringValue>
124
+ </value>
125
+ </assignmentItems>
126
+ </assignments>
127
+ <assignments>
128
+ <name>Set_Rating_Cold</name>
129
+ <label>Set Rating Cold</label>
130
+ <locationX>314</locationX>
131
+ <locationY>334</locationY>
132
+ <assignmentItems>
133
+ <assignToReference>$Record.Rating</assignToReference>
134
+ <operator>Assign</operator>
135
+ <value>
136
+ <stringValue>Cold</stringValue>
137
+ </value>
138
+ </assignmentItems>
139
+ </assignments>
140
+ </Flow>
141
+ ```
142
+
143
+ ### Review Checklist
144
+
145
+ - [ ] Uses Record-Triggered Flow (not Process Builder or Workflow)
146
+ - [ ] Entry criteria prevents unnecessary executions
147
+ - [ ] Decision elements are ordered most-specific to least-specific
148
+ - [ ] No DML operations inside loops
149
+ - [ ] Bulkification-safe (handles 200+ records)
150
+ - [ ] Error handling with fault paths
151
+ - [ ] Description filled in for all elements
152
+ - [ ] Flow is versioned (not overwriting active version)
153
+
154
+ ## Anti-Patterns to Avoid
155
+
156
+ 1. **Loops with DML** — Never put Create/Update/Delete inside a Loop element
157
+ 2. **Missing fault paths** — Always add fault connectors to DML elements
158
+ 3. **Recursive triggers** — Use `$Record__Prior` to check if values actually changed
159
+ 4. **Too many flows per object** — Consolidate into fewer flows with decision elements
160
+ 5. **Hardcoded values** — Use Custom Metadata or Custom Labels instead
161
+
162
+ ## Testing
163
+
164
+ ```apex
165
+ @IsTest
166
+ static void shouldSetRatingToHotForHighRevenue() {
167
+ Account acc = new Account(Name = 'Test', AnnualRevenue = 2000000);
168
+ insert acc;
169
+
170
+ Account result = [SELECT Rating FROM Account WHERE Id = :acc.Id];
171
+ System.assertEquals('Hot', result.Rating, 'High revenue should set rating to Hot');
172
+ }
173
+
174
+ @IsTest
175
+ static void shouldSetRatingToColdForLowRevenue() {
176
+ Account acc = new Account(Name = 'Small Co', AnnualRevenue = 50000);
177
+ insert acc;
178
+
179
+ Account result = [SELECT Rating FROM Account WHERE Id = :acc.Id];
180
+ System.assertEquals('Cold', result.Rating, 'Low revenue should set rating to Cold');
181
+ }
182
+ ```
183
+
184
+ ## SCC Skills
185
+
186
+ - `sf-flow-development` -- Flow best practices and anti-patterns
187
+ - `sf-governor-limits` -- verify flow doesn't accumulate DML in loops
188
+ - `sf-apex-testing` -- write Apex tests that fire record-triggered flows
@@ -0,0 +1,416 @@
1
+ # External Integration with Named Credentials
2
+
3
+ Secure external API integration using Named Credentials, HTTP callout service, retry handling, async processing, and test mocking. Compatible with API version 66.0 (Spring '26).
4
+
5
+ ## When to Use This Pattern
6
+
7
+ - Calling external REST APIs from Salesforce (payment gateways, ERPs, shipping providers)
8
+ - Building callouts that need authentication managed by the platform
9
+ - Implementing retry logic for transient network failures
10
+ - Processing callouts asynchronously to avoid governor limits in synchronous contexts
11
+ - Writing testable integration code with `HttpCalloutMock`
12
+
13
+ ## Structure
14
+
15
+ ```text
16
+ force-app/main/default/
17
+ namedCredentials/
18
+ Payment_Gateway.namedCredential-meta.xml
19
+ externalCredentials/
20
+ Payment_Gateway_Credential.externalCredential-meta.xml
21
+ classes/
22
+ PaymentGatewayService.cls # HTTP callout service
23
+ PaymentGatewayService_Test.cls # Test with mock
24
+ PaymentGatewayQueueable.cls # Async callout wrapper
25
+ PaymentGatewayMock.cls # HttpCalloutMock implementation
26
+ ```
27
+
28
+ ## Named Credential Setup
29
+
30
+ ```xml
31
+ <!-- Payment_Gateway.namedCredential-meta.xml -->
32
+ <?xml version="1.0" encoding="UTF-8"?>
33
+ <NamedCredential xmlns="http://soap.sforce.com/2006/04/metadata">
34
+ <fullName>Payment_Gateway</fullName>
35
+ <label>Payment Gateway</label>
36
+ <type>SecuredEndpoint</type>
37
+ <url>https://api.paymentgateway.example.com/v2</url>
38
+ <externalCredential>Payment_Gateway_Credential</externalCredential>
39
+ <allowMergeFieldsInBody>false</allowMergeFieldsInBody>
40
+ <allowMergeFieldsInHeader>false</allowMergeFieldsInHeader>
41
+ <generateAuthorizationHeader>true</generateAuthorizationHeader>
42
+ </NamedCredential>
43
+ ```
44
+
45
+ ```xml
46
+ <!-- Payment_Gateway_Credential.externalCredential-meta.xml -->
47
+ <?xml version="1.0" encoding="UTF-8"?>
48
+ <ExternalCredential xmlns="http://soap.sforce.com/2006/04/metadata">
49
+ <fullName>Payment_Gateway_Credential</fullName>
50
+ <label>Payment Gateway Credential</label>
51
+ <authenticationProtocol>Custom</authenticationProtocol>
52
+ <externalCredentialParameters>
53
+ <parameterName>Authorization</parameterName>
54
+ <parameterType>AuthHeader</parameterType>
55
+ </externalCredentialParameters>
56
+ </ExternalCredential>
57
+ ```
58
+
59
+ ## HTTP Callout Service Class
60
+
61
+ ```apex
62
+ public with sharing class PaymentGatewayService {
63
+
64
+ private static final String NAMED_CREDENTIAL = 'callout:Payment_Gateway';
65
+ private static final Integer DEFAULT_TIMEOUT = 30000; // 30 seconds
66
+
67
+ /**
68
+ * Charges a payment method. Returns a PaymentResult with success/failure details.
69
+ */
70
+ public static PaymentResult chargePayment(String paymentMethodId, Decimal amount, String currency_x) {
71
+ Map<String, Object> requestBody = new Map<String, Object>{
72
+ 'payment_method' => paymentMethodId,
73
+ 'amount' => (amount * 100).intValue(), // Convert to cents
74
+ 'currency' => currency_x,
75
+ 'capture' => true
76
+ };
77
+
78
+ HttpResponse response = sendRequest('POST', '/charges', JSON.serialize(requestBody));
79
+ return parsePaymentResponse(response);
80
+ }
81
+
82
+ /**
83
+ * Retrieves the status of an existing charge.
84
+ */
85
+ public static PaymentResult getChargeStatus(String chargeId) {
86
+ HttpResponse response = sendRequest('GET', '/charges/' + chargeId, null);
87
+ return parsePaymentResponse(response);
88
+ }
89
+
90
+ /**
91
+ * Core HTTP request method. All callouts route through here.
92
+ */
93
+ private static HttpResponse sendRequest(String method, String path, String body) {
94
+ HttpRequest req = new HttpRequest();
95
+ req.setEndpoint(NAMED_CREDENTIAL + path);
96
+ req.setMethod(method);
97
+ req.setTimeout(DEFAULT_TIMEOUT);
98
+ req.setHeader('Content-Type', 'application/json');
99
+ req.setHeader('Accept', 'application/json');
100
+ req.setHeader('Idempotency-Key', generateIdempotencyKey());
101
+
102
+ if (body != null) {
103
+ req.setBody(body);
104
+ }
105
+
106
+ Http http = new Http();
107
+ return http.send(req);
108
+ }
109
+
110
+ private static PaymentResult parsePaymentResponse(HttpResponse response) {
111
+ PaymentResult result = new PaymentResult();
112
+ result.statusCode = response.getStatusCode();
113
+
114
+ if (response.getStatusCode() == 200 || response.getStatusCode() == 201) {
115
+ Map<String, Object> responseBody = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
116
+ result.success = true;
117
+ result.chargeId = (String) responseBody.get('id');
118
+ result.status = (String) responseBody.get('status');
119
+ } else {
120
+ result.success = false;
121
+ result.errorMessage = 'HTTP ' + response.getStatusCode() + ': ' + response.getBody();
122
+ }
123
+
124
+ return result;
125
+ }
126
+
127
+ private static String generateIdempotencyKey() {
128
+ Blob randomBytes = Crypto.generateAesKey(128);
129
+ return EncodingUtil.convertToHex(randomBytes);
130
+ }
131
+
132
+ public class PaymentResult {
133
+ @AuraEnabled public Boolean success;
134
+ @AuraEnabled public String chargeId;
135
+ @AuraEnabled public String status;
136
+ @AuraEnabled public String errorMessage;
137
+ @AuraEnabled public Integer statusCode;
138
+ }
139
+ }
140
+ ```
141
+
142
+ ## Retry and Error Handling
143
+
144
+ ```apex
145
+ public with sharing class CalloutRetryHelper {
146
+
147
+ private static final Integer MAX_RETRIES = 3;
148
+ private static final Set<Integer> RETRYABLE_STATUS_CODES = new Set<Integer>{
149
+ 408, 429, 500, 502, 503, 504
150
+ };
151
+
152
+ /**
153
+ * Executes an HTTP request with exponential backoff retry.
154
+ * Only retries on transient errors (5xx, 408, 429).
155
+ */
156
+ public static HttpResponse sendWithRetry(HttpRequest request) {
157
+ Integer attempts = 0;
158
+ HttpResponse response;
159
+ Http http = new Http();
160
+
161
+ while (attempts < MAX_RETRIES) {
162
+ attempts++;
163
+ try {
164
+ response = http.send(request);
165
+
166
+ if (!RETRYABLE_STATUS_CODES.contains(response.getStatusCode())) {
167
+ return response;
168
+ }
169
+
170
+ System.debug(LoggingLevel.WARN,
171
+ 'Retryable status ' + response.getStatusCode()
172
+ + ' on attempt ' + attempts + ' of ' + MAX_RETRIES
173
+ );
174
+ } catch (CalloutException e) {
175
+ System.debug(LoggingLevel.ERROR,
176
+ 'Callout exception on attempt ' + attempts + ': ' + e.getMessage()
177
+ );
178
+ if (attempts >= MAX_RETRIES) {
179
+ throw e;
180
+ }
181
+ }
182
+
183
+ // Note: Apex does not support Thread.sleep(). In synchronous context,
184
+ // retries happen immediately. For true backoff, use Queueable chaining.
185
+ }
186
+
187
+ return response;
188
+ }
189
+
190
+ /**
191
+ * Logs a failed callout to a custom object for monitoring and replay.
192
+ */
193
+ public static void logFailedCallout(String endpoint, String method, String body,
194
+ Integer statusCode, String errorMessage) {
195
+ Integration_Log__c log = new Integration_Log__c(
196
+ Endpoint__c = endpoint,
197
+ Method__c = method,
198
+ Request_Body__c = body != null ? body.left(131072) : null, // Long text area limit
199
+ Status_Code__c = statusCode,
200
+ Error_Message__c = errorMessage,
201
+ Timestamp__c = Datetime.now(),
202
+ Status__c = 'Failed'
203
+ );
204
+ insert log;
205
+ }
206
+ }
207
+ ```
208
+
209
+ ## Queueable for Async Callouts
210
+
211
+ ```apex
212
+ public class PaymentGatewayQueueable implements Queueable, Database.AllowsCallouts {
213
+
214
+ private final Id opportunityId;
215
+ private final String paymentMethodId;
216
+ private final Decimal amount;
217
+ private final String currency_x;
218
+
219
+ public PaymentGatewayQueueable(Id opportunityId, String paymentMethodId,
220
+ Decimal amount, String currency_x) {
221
+ this.opportunityId = opportunityId;
222
+ this.paymentMethodId = paymentMethodId;
223
+ this.amount = amount;
224
+ this.currency_x = currency_x;
225
+ }
226
+
227
+ public void execute(QueueableContext context) {
228
+ try {
229
+ PaymentGatewayService.PaymentResult result =
230
+ PaymentGatewayService.chargePayment(paymentMethodId, amount, currency_x);
231
+
232
+ Opportunity opp = new Opportunity(Id = opportunityId);
233
+ if (result.success) {
234
+ opp.Payment_Status__c = 'Charged';
235
+ opp.Payment_Reference__c = result.chargeId;
236
+ } else {
237
+ opp.Payment_Status__c = 'Failed';
238
+ opp.Payment_Error__c = result.errorMessage;
239
+
240
+ CalloutRetryHelper.logFailedCallout(
241
+ 'Payment_Gateway/charges', 'POST',
242
+ JSON.serialize(new Map<String, Object>{
243
+ 'payment_method' => paymentMethodId,
244
+ 'amount' => amount
245
+ }),
246
+ result.statusCode, result.errorMessage
247
+ );
248
+ }
249
+ update opp;
250
+ } catch (Exception e) {
251
+ CalloutRetryHelper.logFailedCallout(
252
+ 'Payment_Gateway/charges', 'POST', null, null, e.getMessage()
253
+ );
254
+ }
255
+ }
256
+ }
257
+ ```
258
+
259
+ ## Mock for Testing (HttpCalloutMock)
260
+
261
+ ```apex
262
+ @IsTest
263
+ public class PaymentGatewayMock implements HttpCalloutMock {
264
+
265
+ private final Integer statusCode;
266
+ private final String responseBody;
267
+
268
+ public PaymentGatewayMock(Integer statusCode, String responseBody) {
269
+ this.statusCode = statusCode;
270
+ this.responseBody = responseBody;
271
+ }
272
+
273
+ public HttpResponse respond(HttpRequest request) {
274
+ HttpResponse response = new HttpResponse();
275
+ response.setStatusCode(statusCode);
276
+ response.setBody(responseBody);
277
+ response.setHeader('Content-Type', 'application/json');
278
+ return response;
279
+ }
280
+
281
+ // Convenience factory methods for common scenarios
282
+ public static PaymentGatewayMock success() {
283
+ return new PaymentGatewayMock(200, JSON.serialize(new Map<String, Object>{
284
+ 'id' => 'ch_test_123456',
285
+ 'status' => 'succeeded',
286
+ 'amount' => 5000,
287
+ 'currency' => 'usd'
288
+ }));
289
+ }
290
+
291
+ public static PaymentGatewayMock failure() {
292
+ return new PaymentGatewayMock(402, JSON.serialize(new Map<String, Object>{
293
+ 'error' => new Map<String, Object>{
294
+ 'type' => 'card_error',
295
+ 'message' => 'Your card was declined'
296
+ }
297
+ }));
298
+ }
299
+
300
+ public static PaymentGatewayMock serverError() {
301
+ return new PaymentGatewayMock(500, '{"error": "Internal server error"}');
302
+ }
303
+ }
304
+ ```
305
+
306
+ ## Test Class
307
+
308
+ ```apex
309
+ @IsTest
310
+ private class PaymentGatewayService_Test {
311
+
312
+ @IsTest
313
+ static void testChargePayment_Success() {
314
+ Test.setMock(HttpCalloutMock.class, PaymentGatewayMock.success());
315
+
316
+ Test.startTest();
317
+ PaymentGatewayService.PaymentResult result =
318
+ PaymentGatewayService.chargePayment('pm_test_123', 50.00, 'usd');
319
+ Test.stopTest();
320
+
321
+ System.assertEquals(true, result.success);
322
+ System.assertEquals('ch_test_123456', result.chargeId);
323
+ System.assertEquals('succeeded', result.status);
324
+ }
325
+
326
+ @IsTest
327
+ static void testChargePayment_Failure() {
328
+ Test.setMock(HttpCalloutMock.class, PaymentGatewayMock.failure());
329
+
330
+ Test.startTest();
331
+ PaymentGatewayService.PaymentResult result =
332
+ PaymentGatewayService.chargePayment('pm_test_123', 50.00, 'usd');
333
+ Test.stopTest();
334
+
335
+ System.assertEquals(false, result.success);
336
+ System.assertEquals(402, result.statusCode);
337
+ System.assert(result.errorMessage.contains('402'));
338
+ }
339
+
340
+ @IsTest
341
+ static void testGetChargeStatus() {
342
+ Test.setMock(HttpCalloutMock.class, PaymentGatewayMock.success());
343
+
344
+ Test.startTest();
345
+ PaymentGatewayService.PaymentResult result =
346
+ PaymentGatewayService.getChargeStatus('ch_test_123456');
347
+ Test.stopTest();
348
+
349
+ System.assertEquals(true, result.success);
350
+ }
351
+
352
+ @IsTest
353
+ static void testQueueableCallout_Success() {
354
+ Opportunity opp = new Opportunity(
355
+ Name = 'Test Opp',
356
+ StageName = 'Closed Won',
357
+ CloseDate = Date.today()
358
+ );
359
+ insert opp;
360
+
361
+ Test.setMock(HttpCalloutMock.class, PaymentGatewayMock.success());
362
+
363
+ Test.startTest();
364
+ System.enqueueJob(
365
+ new PaymentGatewayQueueable(opp.Id, 'pm_test_123', 100.00, 'usd')
366
+ );
367
+ Test.stopTest();
368
+
369
+ Opportunity updated = [SELECT Payment_Status__c, Payment_Reference__c FROM Opportunity WHERE Id = :opp.Id];
370
+ System.assertEquals('Charged', updated.Payment_Status__c);
371
+ System.assertEquals('ch_test_123456', updated.Payment_Reference__c);
372
+ }
373
+
374
+ @IsTest
375
+ static void testRetryHelper_RetryableStatusCode() {
376
+ Test.setMock(HttpCalloutMock.class, PaymentGatewayMock.serverError());
377
+
378
+ HttpRequest req = new HttpRequest();
379
+ req.setEndpoint('callout:Payment_Gateway/charges');
380
+ req.setMethod('POST');
381
+ req.setBody('{}');
382
+
383
+ Test.startTest();
384
+ HttpResponse response = CalloutRetryHelper.sendWithRetry(req);
385
+ Test.stopTest();
386
+
387
+ // In test context, all retries return the same mock
388
+ System.assertEquals(500, response.getStatusCode());
389
+ }
390
+ }
391
+ ```
392
+
393
+ ## Key Principles
394
+
395
+ - Always use Named Credentials for external endpoints; never hardcode URLs or credentials in Apex
396
+ - Route all HTTP requests through a single method for consistent headers, timeouts, and logging
397
+ - Use idempotency keys for POST/PUT requests to prevent duplicate charges on retries
398
+ - Implement callouts in `Queueable` (with `Database.AllowsCallouts`) to avoid governor limits in triggers
399
+ - Create reusable `HttpCalloutMock` implementations with factory methods for common scenarios
400
+ - Log failed callouts to a custom object for monitoring, alerting, and manual replay
401
+
402
+ ## Common Pitfalls
403
+
404
+ - Hardcoding API keys or endpoints instead of using Named Credentials (security risk and deployment headache)
405
+ - Making callouts in trigger context without wrapping in a `Queueable` or `@future` method
406
+ - Not setting `Test.setMock` before making callouts in tests, which causes "uncommitted work pending" errors
407
+ - Forgetting `Database.AllowsCallouts` on the `Queueable` class, which throws a runtime exception
408
+ - Not handling non-JSON error responses (some APIs return HTML on 500 errors)
409
+ - Exceeding the 100-callout-per-transaction limit in batch jobs without tracking callout count
410
+
411
+ ## SCC Skills
412
+
413
+ - `/sf-security` -- verify Named Credential usage and no hardcoded secrets
414
+ - `/sf-apex-best-practices` -- review callout service code for best practices
415
+ - `/sf-tdd-workflow` -- write tests first using mock classes
416
+ - `/sf-governor-limits` -- check callout limits in async and batch contexts