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