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,425 @@
1
+ ---
2
+ name: sf-apex-best-practices
3
+ description: >-
4
+ Use when writing production-ready Salesforce Apex classes — organization,
5
+ error handling, collection patterns. Do NOT use for test classes or triggers.
6
+ origin: SCC
7
+ user-invocable: false
8
+ allowed-tools: Read, Grep, Glob, Edit, Write, Bash
9
+ ---
10
+
11
+ # Apex Best Practices
12
+
13
+ Procedures for writing production-ready Apex. Constraint rules (never/always lists) live in `sf-apex-constraints`. This skill covers the _how_ — class organization, error handling patterns, null safety techniques, and collection usage.
14
+
15
+ Reference files:
16
+
17
+ @../_reference/GOVERNOR_LIMITS.md
18
+ @../_reference/NAMING_CONVENTIONS.md
19
+ @../_reference/SECURITY_PATTERNS.md
20
+
21
+ ---
22
+
23
+ ## When to Use
24
+
25
+ - When writing new Apex classes, triggers, or test classes for a Salesforce org
26
+ - When reviewing existing Apex code for structure or error handling issues
27
+ - When onboarding new developers to Salesforce Apex coding standards
28
+ - When refactoring legacy Apex code to improve readability and maintainability
29
+
30
+ ---
31
+
32
+ ## Class Organization
33
+
34
+ Organize class members in this order:
35
+
36
+ 1. Constants (`static final`)
37
+ 2. Static variables
38
+ 3. Instance variables (fields)
39
+ 4. Constructors
40
+ 5. Public methods
41
+ 6. Private/Protected methods
42
+ 7. Inner classes
43
+
44
+ ```apex
45
+ public with sharing class OrderProcessor {
46
+
47
+ // 1. Constants
48
+ private static final String STATUS_PENDING = 'Pending';
49
+ private static final String STATUS_PROCESSING = 'Processing';
50
+ private static final String STATUS_COMPLETE = 'Complete';
51
+ private static final Integer MAX_LINE_ITEMS = 500;
52
+
53
+ // 2. Static variables
54
+ private static Boolean isProcessing = false;
55
+
56
+ // 3. Instance variables
57
+ private List<Order__c> orders;
58
+ private Map<Id, Account> accountMap;
59
+ private OrderValidator validator;
60
+
61
+ // 4. Constructor
62
+ public OrderProcessor(List<Order__c> orders) {
63
+ this.orders = orders;
64
+ this.accountMap = new Map<Id, Account>();
65
+ this.validator = new OrderValidator();
66
+ }
67
+
68
+ // 5. Public methods
69
+ public List<ProcessResult> processAll() {
70
+ List<ProcessResult> results = new List<ProcessResult>();
71
+ loadRelatedAccounts();
72
+ for (Order__c order : orders) {
73
+ results.add(processSingleOrder(order));
74
+ }
75
+ return results;
76
+ }
77
+
78
+ // 6. Private methods
79
+ private void loadRelatedAccounts() {
80
+ Set<Id> accountIds = new Set<Id>();
81
+ for (Order__c order : orders) {
82
+ if (order.AccountId != null) {
83
+ accountIds.add(order.AccountId);
84
+ }
85
+ }
86
+ for (Account acc : [SELECT Id, Name, CreditLimit__c FROM Account WHERE Id IN :accountIds]) {
87
+ accountMap.put(acc.Id, acc);
88
+ }
89
+ }
90
+
91
+ private ProcessResult processSingleOrder(Order__c order) {
92
+ if (!validator.isValid(order)) {
93
+ return new ProcessResult(order.Id, false, validator.getLastError());
94
+ }
95
+ order.Status__c = STATUS_PROCESSING;
96
+ return new ProcessResult(order.Id, true, null);
97
+ }
98
+
99
+ // 7. Inner classes
100
+ public class ProcessResult {
101
+ public Id orderId { get; private set; }
102
+ public Boolean success { get; private set; }
103
+ public String message { get; private set; }
104
+
105
+ public ProcessResult(Id orderId, Boolean success, String message) {
106
+ this.orderId = orderId;
107
+ this.success = success;
108
+ this.message = message;
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Error Handling
117
+
118
+ ### Custom Exception Classes
119
+
120
+ Create domain-specific exception classes instead of using generic exceptions. This lets callers catch specifically what they care about.
121
+
122
+ ```apex
123
+ // Define exceptions in their own files or as inner classes
124
+ public class AccountServiceException extends Exception {}
125
+ public class OrderValidationException extends Exception {}
126
+ public class IntegrationCalloutException extends Exception {}
127
+
128
+ // Inner exception (acceptable for tight coupling)
129
+ public class AccountService {
130
+ public class AccountNotFoundException extends Exception {}
131
+ public class DuplicateAccountException extends Exception {}
132
+ }
133
+ ```
134
+
135
+ ### Catch Scope
136
+
137
+ Catch the most specific exception type available. Catching `Exception` hides programming errors.
138
+
139
+ ```apex
140
+ // Correct — catch what you expect, let others propagate
141
+ try {
142
+ processAccount(account);
143
+ } catch (DmlException e) {
144
+ throw new AccountServiceException('Failed to save account: ' + e.getDmlMessage(0), e);
145
+ } catch (CalloutException e) {
146
+ throw new IntegrationCalloutException('External service unavailable: ' + e.getMessage(), e);
147
+ }
148
+ ```
149
+
150
+ ### Database.SaveResult Checking
151
+
152
+ When using partial-success DML, check every result.
153
+
154
+ ```apex
155
+ List<Database.SaveResult> results = Database.insert(accounts, false);
156
+ List<String> errors = new List<String>();
157
+
158
+ for (Integer i = 0; i < results.size(); i++) {
159
+ Database.SaveResult result = results[i];
160
+ if (!result.isSuccess()) {
161
+ for (Database.Error err : result.getErrors()) {
162
+ errors.add(
163
+ 'Record ' + accounts[i].Name + ': ' +
164
+ err.getStatusCode() + ' - ' + err.getMessage()
165
+ );
166
+ }
167
+ }
168
+ }
169
+
170
+ if (!errors.isEmpty()) {
171
+ throw new AccountServiceException(
172
+ 'Partial DML failure. Errors:\n' + String.join(errors, '\n')
173
+ );
174
+ }
175
+ ```
176
+
177
+ ### Meaningful Error Messages
178
+
179
+ Include context in exception messages — what was being done, what record was involved, what the actual error was.
180
+
181
+ ```apex
182
+ throw new AccountServiceException(
183
+ String.format(
184
+ 'Failed to update Account {0} (Id: {1}) during credit limit recalculation. ' +
185
+ 'DML error: {2}',
186
+ new List<Object>{ account.Name, account.Id, dmlError.getMessage() }
187
+ )
188
+ );
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Single Responsibility Principle
194
+
195
+ Each class should have one reason to change. Split classes by responsibility, not by object type.
196
+
197
+ ```apex
198
+ // Correct — each class has one job
199
+ public class AccountService { public void createAccount() {} }
200
+ public class AccountNotificationService { public void sendWelcomeEmail() {} }
201
+ public class OpportunityService { public void createFromAccount() {} }
202
+ public class ERPSyncService { public void syncAccount() {} }
203
+ public class AccountDocumentService { public void generateOnboardingPDF() {} }
204
+ ```
205
+
206
+ ### Method Length
207
+
208
+ Methods longer than ~50 lines are doing too much. Extract private helper methods.
209
+
210
+ ```apex
211
+ // Correct — orchestrator calling focused helpers
212
+ public void processNewCustomer(Account account) {
213
+ validateNewCustomer(account);
214
+ enrichFromExternalData(account);
215
+ Account inserted = insertAccount(account);
216
+ createDefaultOpportunity(inserted);
217
+ sendWelcomeNotification(inserted);
218
+ }
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Access Modifiers
224
+
225
+ Start with `private`. Promote to `protected`, then `public`, only when necessary. Use `global` only for managed package APIs.
226
+
227
+ ```apex
228
+ public with sharing class DiscountCalculator {
229
+
230
+ // Private — internal state
231
+ private Decimal baseRate;
232
+ private Map<String, Decimal> tierRates;
233
+
234
+ // Private — internal logic
235
+ private Decimal lookupTierRate(String tier) {
236
+ return tierRates.containsKey(tier) ? tierRates.get(tier) : baseRate;
237
+ }
238
+
239
+ // Protected — available to subclasses for extension
240
+ protected Decimal applyMinimumDiscount(Decimal calculated) {
241
+ return Math.max(calculated, 0.05);
242
+ }
243
+
244
+ // Public — the contract
245
+ public Decimal calculateDiscount(String customerTier, Decimal orderAmount) {
246
+ Decimal rate = lookupTierRate(customerTier);
247
+ return applyMinimumDiscount(rate * orderAmount);
248
+ }
249
+ }
250
+ ```
251
+
252
+ ---
253
+
254
+ ## Null Safety
255
+
256
+ Check for null before dereferencing. Salesforce returns null (not empty collections) for uninitialized parent relationship fields. Child relationship sub-queries return empty lists, not null.
257
+
258
+ ```apex
259
+ // Preferred — null-safe navigation operator (?.)
260
+ String city = account?.BillingAddress?.City;
261
+ String ownerEmail = contact?.Account?.Owner?.Email;
262
+
263
+ // Null-safe Map retrieval with null coalescing (requires minimum API version — see @../_reference/API_VERSIONS.md)
264
+ String value = myMap.get('key')?.toLowerCase() ?? '';
265
+ ```
266
+
267
+ > **Note:** The `?.` operator prevents NullPointerException when the object reference is null. It does NOT prevent SObjectException when accessing fields not included in the SOQL query. Always ensure queried fields are in the SELECT clause.
268
+
269
+ ---
270
+
271
+ ## Collection Patterns
272
+
273
+ ### Choosing List vs Set vs Map
274
+
275
+ ```apex
276
+ // List — ordered, allows duplicates, use for DML and output
277
+ List<Account> accountsToInsert = new List<Account>();
278
+
279
+ // Set — unordered, no duplicates, use for Id lookup sets and deduplication
280
+ Set<Id> processedIds = new Set<Id>();
281
+
282
+ // Map — key-value lookup, use for joining data across queries
283
+ Map<Id, Account> accountById = new Map<Id, Account>(
284
+ [SELECT Id, Name FROM Account WHERE Id IN :accountIds]
285
+ );
286
+ ```
287
+
288
+ ### Build Maps Inline from Queries
289
+
290
+ ```apex
291
+ // Idiomatic Apex — Map constructor from query
292
+ Map<Id, Account> accountMap = new Map<Id, Account>(
293
+ [SELECT Id, Name, OwnerId FROM Account WHERE Id IN :accountIds]
294
+ );
295
+ Account acc = accountMap.get(someId);
296
+ ```
297
+
298
+ > **Note:** In Apex, `new List<String>(n)` creates a list pre-filled with n nulls (unlike Java). Use `new List<String>()` for an empty list.
299
+
300
+ ---
301
+
302
+ ## Comments
303
+
304
+ ### Javadoc-Style for Public Methods
305
+
306
+ Document the contract, not the implementation.
307
+
308
+ ```apex
309
+ /**
310
+ * Calculates the renewal opportunity amount based on the original contract value
311
+ * and the customer's tier-based renewal discount.
312
+ *
313
+ * @param contract The original contract record. Must not be null. Must have
314
+ * Amount__c and Customer_Tier__c populated.
315
+ * @param renewalDate The target renewal date. Used to determine active pricing tiers.
316
+ * @return The calculated renewal amount. Never negative. Returns 0 if contract
317
+ * amount is null.
318
+ * @throws RenewalCalculationException If no pricing tier is found for the contract's
319
+ * customer tier value.
320
+ */
321
+ public Decimal calculateRenewalAmount(Contract__c contract, Date renewalDate) {
322
+ // implementation
323
+ }
324
+ ```
325
+
326
+ ### Inline Comments
327
+
328
+ Only when logic is not obvious. Explain _why_, not _what_.
329
+
330
+ ```apex
331
+ // Salesforce does not enforce uniqueness on Name by default; we enforce it
332
+ // here because duplicate account names break downstream ERP sync.
333
+ if (existingAccountNames.contains(acc.Name)) {
334
+ acc.addError('An account with this name already exists. Use a unique trading name.');
335
+ }
336
+ ```
337
+
338
+ ---
339
+
340
+ ## Complete Well-Structured Class Example
341
+
342
+ ```apex
343
+ /**
344
+ * Service class for credit limit management operations on Account records.
345
+ * Enforces sharing rules; operates within the running user's data visibility.
346
+ */
347
+ public with sharing class CreditLimitService {
348
+
349
+ private static final Decimal DEFAULT_CREDIT_LIMIT = 10000.00;
350
+ private static final Decimal PREMIUM_CREDIT_LIMIT = 100000.00;
351
+ private static final String TIER_PREMIUM = 'Premium';
352
+ private static final String TIER_STANDARD = 'Standard';
353
+
354
+ private final List<Account> accounts;
355
+
356
+ public CreditLimitService(List<Account> accounts) {
357
+ if (accounts == null || accounts.isEmpty()) {
358
+ throw new CreditLimitException('Account list must not be null or empty.');
359
+ }
360
+ this.accounts = accounts;
361
+ }
362
+
363
+ public Map<Id, Decimal> recalculateLimits() {
364
+ Map<Id, Decimal> results = new Map<Id, Decimal>();
365
+ for (Account acc : accounts) {
366
+ results.put(acc.Id, calculateLimitForAccount(acc));
367
+ }
368
+ return results;
369
+ }
370
+
371
+ public List<String> saveLimits(Map<Id, Decimal> limitsByAccountId) {
372
+ List<Account> toUpdate = buildUpdateRecords(limitsByAccountId);
373
+ return executeDmlWithErrorCollection(toUpdate);
374
+ }
375
+
376
+ private Decimal calculateLimitForAccount(Account acc) {
377
+ if (acc.Customer_Tier__c == TIER_PREMIUM) return PREMIUM_CREDIT_LIMIT;
378
+ if (acc.Customer_Tier__c == TIER_STANDARD) return calculateStandardLimit(acc);
379
+ return DEFAULT_CREDIT_LIMIT;
380
+ }
381
+
382
+ private Decimal calculateStandardLimit(Account acc) {
383
+ if (acc.AnnualRevenue == null || acc.AnnualRevenue <= 0) return DEFAULT_CREDIT_LIMIT;
384
+ return Math.min(acc.AnnualRevenue * 0.05, 50000.00);
385
+ }
386
+
387
+ private List<Account> buildUpdateRecords(Map<Id, Decimal> limitsByAccountId) {
388
+ List<Account> records = new List<Account>();
389
+ for (Id accId : limitsByAccountId.keySet()) {
390
+ records.add(new Account(
391
+ Id = accId,
392
+ CreditLimit__c = limitsByAccountId.get(accId),
393
+ Last_Credit_Review_Date__c = Date.today()
394
+ ));
395
+ }
396
+ return records;
397
+ }
398
+
399
+ private List<String> executeDmlWithErrorCollection(List<Account> records) {
400
+ List<Database.SaveResult> results = Database.update(records, false);
401
+ List<String> errors = new List<String>();
402
+ for (Integer i = 0; i < results.size(); i++) {
403
+ if (!results[i].isSuccess()) {
404
+ for (Database.Error err : results[i].getErrors()) {
405
+ errors.add('Account Id ' + records[i].Id + ': ' +
406
+ err.getStatusCode() + ' — ' + err.getMessage());
407
+ }
408
+ }
409
+ }
410
+ return errors;
411
+ }
412
+
413
+ public class CreditLimitException extends Exception {}
414
+ }
415
+ ```
416
+
417
+ ---
418
+
419
+ ## Related
420
+
421
+ - **Agent**: `sf-review-agent` — For interactive, in-depth guidance
422
+
423
+ ### Guardrails
424
+
425
+ - `sf-apex-constraints` — Enforces governor limits, naming rules, security requirements, and bulkification rules that apply to all Apex code
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: sf-apex-constraints
3
+ description: "Enforce Apex governor limits, naming, bulkification, and security rules. Use when writing or reviewing ANY Apex class, trigger, or batch job. Do NOT use for LWC, Flow, or Visualforce."
4
+ origin: SCC
5
+ user-invocable: false
6
+ allowed-tools: Read, Grep, Glob
7
+ ---
8
+
9
+ # Apex Constraints
10
+
11
+ ## When to Use
12
+
13
+ This skill auto-activates when writing, reviewing, or modifying any Apex class, trigger, or batch job. It enforces governor limits, naming conventions, bulkification rules, and security requirements for all Apex artifacts.
14
+
15
+ Hard rules that every Apex class, trigger, and batch job must satisfy. Violations here cause governor failures, security review rejections, or production incidents. Reference files contain the full data; this skill contains only the enforcement rules.
16
+
17
+ ## Core Rules
18
+
19
+ @../_reference/GOVERNOR_LIMITS.md
20
+ @../_reference/NAMING_CONVENTIONS.md
21
+ @../_reference/SECURITY_PATTERNS.md
22
+ @../_reference/DEPRECATIONS.md
23
+
24
+ ## Apex-Specific Rules
25
+
26
+ ### Never Do
27
+
28
+ - **SOQL inside a loop** — exceeds the per-transaction SOQL query limit (see @../_reference/GOVERNOR_LIMITS.md); query once outside the loop, store in Map
29
+ - **DML inside a loop** — exceeds the per-transaction DML limit (see @../_reference/GOVERNOR_LIMITS.md); collect records in a List, single DML after loop
30
+ - **Catch generic `Exception`** — masks programming bugs (NullPointerException, TypeException); catch specific types only
31
+ - **Omit sharing keyword** — classes without a sharing keyword default to `without sharing`; always declare `with sharing`, `without sharing`, or `inherited sharing` explicitly
32
+ - **Use `without sharing` on user-facing classes** — bypasses record-level security; must be `with sharing`
33
+ - **Hardcode Record IDs** — IDs differ per org/sandbox; use SOQL, Custom Metadata, or Custom Settings
34
+ - **Hardcode credentials or endpoint URLs** — use Named Credentials / External Credentials
35
+ - **Use `global` access modifier** — locks managed package API surface; use `public` unless building a package API
36
+ - **Leave `System.debug` in production code** — fills debug logs, can expose sensitive data
37
+ - **Use string concatenation in dynamic SOQL** — SOQL injection risk; use bind variables or `Database.queryWithBinds()`
38
+ - **Ignore `Database.SaveResult`** — partial-success DML silently drops failures; always inspect every result
39
+ - **Use `element.innerHTML = userInput`** — XSS vulnerability; use `textContent` or sanitized components
40
+ - **Write methods longer than 50 lines** — extract private helper methods for testability
41
+ - **Use `List<sObject>` as a parameter type** — loses type information; use concrete types like `List<Account>`
42
+ - **Use Hungarian notation** (`strName`, `lstAccounts`) — use descriptive camelCase names instead
43
+
44
+ ### Always Do
45
+
46
+ - **Declare `with sharing` by default** — only use `without sharing` with a documented justification
47
+ - **Enforce CRUD/FLS** — use `WITH USER_MODE` for SOQL, `AccessLevel.USER_MODE` for DML on user-facing operations
48
+ - **Bulkify all triggers** — test with 200 records (standard trigger batch size); no per-record SOQL/DML
49
+ - **One trigger per object** — delegate all logic to a handler class (`{Object}TriggerHandler`)
50
+ - **Use PascalCase for classes**, camelCase for methods/variables, UPPER_SNAKE_CASE for constants
51
+ - **Suffix classes by role** — `Service`, `Selector`, `TriggerHandler`, `Batch`, `Job`, `Scheduler`, `Controller`, `Test`, `Exception`
52
+ - **Suffix test classes with `Test`** (not prefix) — `AccountServiceTest`, not `TestAccountService`
53
+ - **Name test methods** as `test{Method}_{scenario}_{expectedResult}`
54
+ - **Create domain-specific exception classes** — not generic `Exception` throws
55
+ - **Check limits programmatically** before expensive operations — use `Limits.getQueries()`, `Limits.getCpuTime()`, etc.
56
+ - **Use Map/Set for lookups** — O(1) vs O(n) nested loops; prevents CPU time exhaustion
57
+ - **Use `String.join()` for string building** — not concatenation in loops (heap + CPU cost)
58
+ - **Null-check before dereferencing** — use `?.` (null-safe navigation) for parent relationship fields
59
+ - **Offload to async** when processing >200 records or when CPU exceeds 8,000ms threshold
60
+ - **Organize class members** in order: constants, static variables, instance variables, constructors, public methods, private methods, inner classes
61
+
62
+ ## Anti-Pattern Reference
63
+
64
+ | Anti-Pattern | Problem | Correct Pattern |
65
+ |---|---|---|
66
+ | SOQL in loop | Exceeds per-transaction SOQL limit (see @../_reference/GOVERNOR_LIMITS.md) | Query once, store in Map |
67
+ | DML in loop | Exceeds per-transaction DML limit (see @../_reference/GOVERNOR_LIMITS.md) | Collect records, single DML after loop |
68
+ | Nested loops for matching | CPU time exhaustion (O(n^2)) | Map/Set lookup (O(1)) |
69
+ | String concat in loop | Heap growth + CPU waste | `List<String>` + `String.join()` |
70
+ | SELECT * (all fields) | Heap exhaustion | SELECT only required fields |
71
+ | No sharing keyword | Silent `without sharing` default | Explicit `with sharing` declaration |
72
+ | Missing CRUD/FLS check | Security review failure | `WITH USER_MODE` / `AccessLevel.USER_MODE` |
73
+ | Dynamic SOQL via concat | SOQL injection | Bind variables / `queryWithBinds()` |
74
+ | Catching `Exception` | Masks real bugs | Catch specific exception types |
75
+ | Ignoring SaveResult | Silent data loss | Inspect every `Database.SaveResult` |
76
+ | Hardcoded IDs | Breaks across orgs | SOQL / Custom Metadata lookup |
77
+
78
+ ## Related
79
+
80
+ - **Pattern skill**: `sf-apex-best-practices` — implementation examples for the rules above (class organization, error handling, collection patterns)
81
+ - Reference files: @../_reference/GOVERNOR_LIMITS.md, @../_reference/NAMING_CONVENTIONS.md, @../_reference/SECURITY_PATTERNS.md