sf-forcekit 1.0.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.
- package/License +21 -0
- package/README.md +106 -0
- package/bin/cli.js +85 -0
- package/package.json +44 -0
- package/templates/ai-dir/README.md +155 -0
- package/templates/ai-dir/agentforce.md +213 -0
- package/templates/ai-dir/architecture.md +123 -0
- package/templates/ai-dir/commands.md +276 -0
- package/templates/ai-dir/context-snapshots/TEMPLATE.md +64 -0
- package/templates/ai-dir/conventions.md +242 -0
- package/templates/ai-dir/current-state.md +113 -0
- package/templates/ai-dir/debugging-notes.md +165 -0
- package/templates/ai-dir/deployment.md +161 -0
- package/templates/ai-dir/integrations.md +199 -0
- package/templates/ai-dir/inventory.md +209 -0
- package/templates/ai-dir/known-issues.md +124 -0
- package/templates/ai-dir/org-context.md +110 -0
- package/templates/ai-dir/performance.md +312 -0
- package/templates/ai-dir/prompts/agentforce.md +163 -0
- package/templates/ai-dir/prompts/apex.md +165 -0
- package/templates/ai-dir/prompts/flows.md +125 -0
- package/templates/ai-dir/prompts/lwc.md +230 -0
- package/templates/ai-dir/prompts/security.md +181 -0
- package/templates/ai-dir/prompts/testing.md +269 -0
- package/templates/ai-dir/rules.md +238 -0
- package/templates/ai-dir/scripts/update_state.py +1406 -0
- package/templates/ai-dir/source-of-truth.md +180 -0
- package/templates/ai-dir/templates/Selector.cls +113 -0
- package/templates/ai-dir/templates/Service.cls +132 -0
- package/templates/ai-dir/templates/TestClass.cls +143 -0
- package/templates/ai-dir/templates/TriggerHandler.cls +67 -0
- package/templates/ai-dir/testing-strategy.md +342 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
> System design, data model, and integration map for this Salesforce project.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
<!-- Brief description of the application's purpose and business domain -->
|
|
8
|
+
|
|
9
|
+
**Domain:** [e.g., Sales Cloud, Service Cloud, Experience Cloud, Custom Platform]
|
|
10
|
+
**Primary Users:** [e.g., Sales Reps, Service Agents, Partners, Community Users]
|
|
11
|
+
**Org Strategy:** [e.g., Single Org, Multi-Org, Sandbox Strategy]
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Data Model
|
|
16
|
+
|
|
17
|
+
### Core Objects
|
|
18
|
+
|
|
19
|
+
<!-- List custom objects and their relationships. Use a table or ERD. -->
|
|
20
|
+
|
|
21
|
+
| Object API Name | Label | Type | Description |
|
|
22
|
+
|----------------|-------|------|-------------|
|
|
23
|
+
| `Account` | Account | Standard | — |
|
|
24
|
+
| `Contact` | Contact | Standard | — |
|
|
25
|
+
| <!-- Add rows --> | | | |
|
|
26
|
+
|
|
27
|
+
### Key Relationships
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Account (1) ──── (N) Contact
|
|
31
|
+
Account (1) ──── (N) Opportunity
|
|
32
|
+
<!-- Add your object relationships -->
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Record Types & Business Processes
|
|
36
|
+
|
|
37
|
+
| Object | Record Type | Sales/Support Process | Description |
|
|
38
|
+
|--------|------------|----------------------|-------------|
|
|
39
|
+
| <!-- Add rows --> | | | |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Architecture Layers
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
┌─────────────────────────────────────────────────┐
|
|
47
|
+
│ UI Layer │
|
|
48
|
+
│ LWC Components │ Aura (Legacy) │ Flows │
|
|
49
|
+
├─────────────────────────────────────────────────┤
|
|
50
|
+
│ Controller Layer │
|
|
51
|
+
│ LWC Controllers │ Aura Controllers │
|
|
52
|
+
├─────────────────────────────────────────────────┤
|
|
53
|
+
│ Service Layer │
|
|
54
|
+
│ *Service.cls — All business logic lives here │
|
|
55
|
+
├─────────────────────────────────────────────────┤
|
|
56
|
+
│ Domain / Trigger Layer │
|
|
57
|
+
│ One Trigger per Object → TriggerHandler.cls │
|
|
58
|
+
├─────────────────────────────────────────────────┤
|
|
59
|
+
│ Selector Layer │
|
|
60
|
+
│ *Selector.cls — All SOQL queries │
|
|
61
|
+
├─────────────────────────────────────────────────┤
|
|
62
|
+
│ Platform Services │
|
|
63
|
+
│ Platform Events │ Queueable │ Batch │ Schedule │
|
|
64
|
+
├─────────────────────────────────────────────────┤
|
|
65
|
+
│ Integration Layer │
|
|
66
|
+
│ Named Credentials │ REST/SOAP Callouts │
|
|
67
|
+
└─────────────────────────────────────────────────┘
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Integration Map
|
|
73
|
+
|
|
74
|
+
<!-- List all external system integrations -->
|
|
75
|
+
|
|
76
|
+
| System | Direction | Protocol | Auth Method | Named Credential | Frequency |
|
|
77
|
+
|--------|-----------|----------|-------------|-----------------|-----------|
|
|
78
|
+
| <!-- e.g., ERP --> | Outbound | REST | OAuth 2.0 | `ERP_API` | Real-time |
|
|
79
|
+
| <!-- Add rows --> | | | | | |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Automation Inventory
|
|
84
|
+
|
|
85
|
+
### Triggers
|
|
86
|
+
|
|
87
|
+
| Object | Trigger Name | Handler Class | Context |
|
|
88
|
+
|--------|-------------|---------------|---------|
|
|
89
|
+
| <!-- Add rows --> | | | Before Insert, After Update, etc. |
|
|
90
|
+
|
|
91
|
+
### Flows
|
|
92
|
+
|
|
93
|
+
| Flow Name | Type | Object | Purpose |
|
|
94
|
+
|-----------|------|--------|---------|
|
|
95
|
+
| <!-- Add rows --> | Record-Triggered (Before/After) | | |
|
|
96
|
+
|
|
97
|
+
### Scheduled / Batch Jobs
|
|
98
|
+
|
|
99
|
+
| Class Name | Type | Schedule | Purpose |
|
|
100
|
+
|-----------|------|----------|---------|
|
|
101
|
+
| <!-- Add rows --> | Schedulable / Batchable | | |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Security Model
|
|
106
|
+
|
|
107
|
+
### Profiles & Permission Sets
|
|
108
|
+
|
|
109
|
+
| Name | Type | Purpose |
|
|
110
|
+
|------|------|---------|
|
|
111
|
+
| <!-- Add rows --> | Profile / Perm Set / Perm Set Group | |
|
|
112
|
+
|
|
113
|
+
### Sharing Model
|
|
114
|
+
|
|
115
|
+
| Object | OWD | Sharing Rules | Apex Managed Sharing? |
|
|
116
|
+
|--------|-----|--------------|----------------------|
|
|
117
|
+
| <!-- Add rows --> | Private / Public Read / Public Read-Write | | Yes / No |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Notes
|
|
122
|
+
|
|
123
|
+
<!-- Any additional architectural decisions, constraints, or context -->
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# CLI Commands Cookbook
|
|
2
|
+
|
|
3
|
+
> Every `sf` CLI command you'll need, organized by workflow.
|
|
4
|
+
> Agents: Use these exact commands. Don't guess flags — run `--help` if unsure.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Quick Reference — Most Used
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Deploy all
|
|
12
|
+
sf project deploy start --target-org <target_org> --source-dir force-app
|
|
13
|
+
|
|
14
|
+
# Deploy one class
|
|
15
|
+
sf project deploy start --target-org <target_org> --metadata "ApexClass:AccountService"
|
|
16
|
+
|
|
17
|
+
# Run all tests
|
|
18
|
+
sf apex run test --target-org <target_org> --code-coverage --result-format human
|
|
19
|
+
|
|
20
|
+
# Run one test class
|
|
21
|
+
sf apex run test --target-org <target_org> --tests AccountServiceTest --code-coverage --result-format human
|
|
22
|
+
|
|
23
|
+
# Open org in browser
|
|
24
|
+
sf org open --target-org <target_org>
|
|
25
|
+
|
|
26
|
+
# SOQL query
|
|
27
|
+
sf data query --query "SELECT Id, Name FROM Account LIMIT 5" --target-org <target_org>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Metadata Verification
|
|
33
|
+
|
|
34
|
+
> Used by `source-of-truth.md` verification workflow. Always prefer these over guessing.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Does this object exist?
|
|
38
|
+
sf data query --query "SELECT QualifiedApiName FROM EntityDefinition WHERE QualifiedApiName = 'MyObject__c'" \
|
|
39
|
+
--target-org <target_org> --use-tooling-api
|
|
40
|
+
|
|
41
|
+
# Does this field exist on an object?
|
|
42
|
+
sf data query --query "SELECT QualifiedApiName, DataType FROM FieldDefinition WHERE EntityDefinition.QualifiedApiName = 'Account' AND QualifiedApiName = 'MyField__c'" \
|
|
43
|
+
--target-org <target_org> --use-tooling-api
|
|
44
|
+
|
|
45
|
+
# List all custom fields on an object
|
|
46
|
+
sf data query --query "SELECT QualifiedApiName, DataType FROM FieldDefinition WHERE EntityDefinition.QualifiedApiName = 'Account'" \
|
|
47
|
+
--target-org <target_org> --use-tooling-api
|
|
48
|
+
|
|
49
|
+
# Does this Apex class exist?
|
|
50
|
+
sf data query --query "SELECT Name, Status FROM ApexClass WHERE Name = 'AccountService'" \
|
|
51
|
+
--target-org <target_org> --use-tooling-api
|
|
52
|
+
|
|
53
|
+
# List active flows
|
|
54
|
+
sf data query --query "SELECT DeveloperName, ProcessType, Status FROM FlowDefinitionView WHERE IsActive = true" \
|
|
55
|
+
--target-org <target_org> --use-tooling-api
|
|
56
|
+
|
|
57
|
+
# Custom Labels
|
|
58
|
+
sf data query --query "SELECT Name, Value FROM ExternalString" \
|
|
59
|
+
--target-org <target_org> --use-tooling-api
|
|
60
|
+
|
|
61
|
+
# Named Credentials
|
|
62
|
+
sf data query --query "SELECT DeveloperName, Endpoint FROM NamedCredential" \
|
|
63
|
+
--target-org <target_org> --use-tooling-api
|
|
64
|
+
|
|
65
|
+
# Custom Metadata Types
|
|
66
|
+
sf data query --query "SELECT QualifiedApiName FROM EntityDefinition WHERE QualifiedApiName LIKE '%__mdt'" \
|
|
67
|
+
--target-org <target_org> --use-tooling-api
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Deploy & Retrieve
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Validate only (dry run — no actual deploy)
|
|
76
|
+
sf project deploy start --target-org <target_org> --source-dir force-app --dry-run
|
|
77
|
+
|
|
78
|
+
# Deploy with tests (required for Production)
|
|
79
|
+
sf project deploy start --target-org <target_org> --source-dir force-app \
|
|
80
|
+
--test-level RunLocalTests
|
|
81
|
+
|
|
82
|
+
# Deploy specific tests
|
|
83
|
+
sf project deploy start --target-org <target_org> --source-dir force-app \
|
|
84
|
+
--test-level RunSpecifiedTests --tests "AccountServiceTest,CaseServiceTest"
|
|
85
|
+
|
|
86
|
+
# Check last deploy status
|
|
87
|
+
sf project deploy report --target-org <target_org>
|
|
88
|
+
|
|
89
|
+
# Retrieve by metadata type
|
|
90
|
+
sf project retrieve start --target-org <target_org> --metadata "ApexClass:AccountService"
|
|
91
|
+
|
|
92
|
+
# Retrieve by manifest
|
|
93
|
+
sf project retrieve start --target-org <target_org> --manifest manifest/package.xml
|
|
94
|
+
|
|
95
|
+
# Destructive deploy (remove metadata from org)
|
|
96
|
+
sf project deploy start --target-org <target_org> \
|
|
97
|
+
--manifest manifest/package.xml \
|
|
98
|
+
--post-destructive-changes manifest/destructiveChanges.xml
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Testing
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Run all local tests with coverage
|
|
107
|
+
sf apex run test --target-org <target_org> \
|
|
108
|
+
--code-coverage --result-format human --test-level RunLocalTests
|
|
109
|
+
|
|
110
|
+
# Run specific test class
|
|
111
|
+
sf apex run test --target-org <target_org> \
|
|
112
|
+
--tests AccountServiceTest --code-coverage --result-format human
|
|
113
|
+
|
|
114
|
+
# Run specific test method
|
|
115
|
+
sf apex run test --target-org <target_org> \
|
|
116
|
+
--tests "AccountServiceTest.testBulkInsert" --code-coverage --result-format human
|
|
117
|
+
|
|
118
|
+
# Run tests with JSON output (for parsing)
|
|
119
|
+
sf apex run test --target-org <target_org> \
|
|
120
|
+
--code-coverage --result-format json --output-dir test-results --json
|
|
121
|
+
|
|
122
|
+
# Run anonymous Apex script
|
|
123
|
+
sf apex run --target-org <target_org> --file scripts/apex/debug-script.apex
|
|
124
|
+
|
|
125
|
+
# Run inline anonymous Apex
|
|
126
|
+
echo "System.debug(UserInfo.getUserName());" | sf apex run --target-org <target_org>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Debugging
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Tail debug logs in real-time
|
|
135
|
+
sf apex tail log --target-org <target_org>
|
|
136
|
+
|
|
137
|
+
# List recent debug logs
|
|
138
|
+
sf apex log list --target-org <target_org>
|
|
139
|
+
|
|
140
|
+
# View a specific log
|
|
141
|
+
sf apex log get --target-org <target_org> --log-id <logId>
|
|
142
|
+
|
|
143
|
+
# View org limits (API calls, storage, etc.)
|
|
144
|
+
sf limits api display --target-org <target_org>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Data Operations
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# SOQL query
|
|
153
|
+
sf data query --query "SELECT Id, Name FROM Account WHERE CreatedDate = TODAY" \
|
|
154
|
+
--target-org <target_org>
|
|
155
|
+
|
|
156
|
+
# Tooling API query
|
|
157
|
+
sf data query --query "SELECT Id, Name, ApiVersion FROM ApexClass ORDER BY Name" \
|
|
158
|
+
--target-org <target_org> --use-tooling-api
|
|
159
|
+
|
|
160
|
+
# Insert record
|
|
161
|
+
sf data create record --target-org <target_org> \
|
|
162
|
+
--sobject Account --values "Name='Test Account' Industry='Technology'"
|
|
163
|
+
|
|
164
|
+
# Update record
|
|
165
|
+
sf data update record --target-org <target_org> \
|
|
166
|
+
--sobject Account --record-id 001XXXXXXXXXXXX --values "Industry='Finance'"
|
|
167
|
+
|
|
168
|
+
# Delete record
|
|
169
|
+
sf data delete record --target-org <target_org> \
|
|
170
|
+
--sobject Account --record-id 001XXXXXXXXXXXX
|
|
171
|
+
|
|
172
|
+
# Bulk import CSV
|
|
173
|
+
sf data import bulk --target-org <target_org> \
|
|
174
|
+
--sobject Account --file data/accounts.csv
|
|
175
|
+
|
|
176
|
+
# Bulk export
|
|
177
|
+
sf data query --query "SELECT Id, Name FROM Account" \
|
|
178
|
+
--target-org <target_org> --result-format csv > data/accounts-export.csv
|
|
179
|
+
|
|
180
|
+
# Count records
|
|
181
|
+
sf data query --query "SELECT COUNT() FROM Account" --target-org <target_org>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Org Management
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Login to org (web browser)
|
|
190
|
+
sf org login web --alias <alias>
|
|
191
|
+
|
|
192
|
+
# Login with auth URL (CI/CD)
|
|
193
|
+
sf org login sfdx-url --sfdx-url-file authfile.txt --alias <alias>
|
|
194
|
+
|
|
195
|
+
# List authenticated orgs
|
|
196
|
+
sf org list
|
|
197
|
+
|
|
198
|
+
# Open org in browser
|
|
199
|
+
sf org open --target-org <target_org>
|
|
200
|
+
|
|
201
|
+
# Open specific page
|
|
202
|
+
sf org open --target-org <target_org> --path "/lightning/setup/ApexClasses/home"
|
|
203
|
+
|
|
204
|
+
# Display org info
|
|
205
|
+
sf org display --target-org <target_org>
|
|
206
|
+
|
|
207
|
+
# Set default org
|
|
208
|
+
sf config set target-org=<alias>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Helper Script Commands
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Scan project and update inventory.md
|
|
217
|
+
python3 .ai/scripts/update_state.py scan
|
|
218
|
+
|
|
219
|
+
# Sync org details + limits into org-context.local.md
|
|
220
|
+
python3 .ai/scripts/update_state.py sync-org
|
|
221
|
+
|
|
222
|
+
# Pre-cache schema (objects + fields) for offline verification
|
|
223
|
+
python3 .ai/scripts/update_state.py cache-schema
|
|
224
|
+
|
|
225
|
+
# Static analysis / lint check
|
|
226
|
+
python3 .ai/scripts/update_state.py check
|
|
227
|
+
python3 .ai/scripts/update_state.py check --files "force-app/main/default/classes/AccountService.cls"
|
|
228
|
+
|
|
229
|
+
# Verify metadata exists in org
|
|
230
|
+
python3 .ai/scripts/update_state.py verify --type object --name MyObject__c
|
|
231
|
+
python3 .ai/scripts/update_state.py verify --type field --name MyField__c --object Account
|
|
232
|
+
python3 .ai/scripts/update_state.py verify --type class --name AccountService
|
|
233
|
+
|
|
234
|
+
# Session management
|
|
235
|
+
python3 .ai/scripts/update_state.py session-start --agent "Claude" --goal "Implement X"
|
|
236
|
+
python3 .ai/scripts/update_state.py task-start "Task description"
|
|
237
|
+
python3 .ai/scripts/update_state.py task-complete "Task description" --files "file1.cls,file2.cls"
|
|
238
|
+
python3 .ai/scripts/update_state.py session-end --summary "Summary" --files "files"
|
|
239
|
+
|
|
240
|
+
# Clean for open-source distribution
|
|
241
|
+
python3 .ai/scripts/update_state.py clean
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Common Patterns
|
|
247
|
+
|
|
248
|
+
### Deploy → Test → Verify Cycle
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# 1. Deploy
|
|
252
|
+
sf project deploy start --target-org <target_org> --source-dir force-app
|
|
253
|
+
|
|
254
|
+
# 2. Run affected tests
|
|
255
|
+
sf apex run test --target-org <target_org> --tests "AccountServiceTest" --code-coverage --result-format human
|
|
256
|
+
|
|
257
|
+
# 3. Verify coverage
|
|
258
|
+
sf apex run test --target-org <target_org> --code-coverage --result-format human --test-level RunLocalTests
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Retrieve Unknown Metadata From Org
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# When you need to see what's in the org but not in local source
|
|
265
|
+
sf project retrieve start --target-org <target_org> --metadata "CustomObject:MyObject__c"
|
|
266
|
+
sf project retrieve start --target-org <target_org> --metadata "ApexClass:*"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Create Scratch Org (for package dev)
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
sf org create scratch --definition-file config/project-scratch-def.json \
|
|
273
|
+
--alias scratch-dev --duration-days 7 --set-default
|
|
274
|
+
sf project deploy start --target-org scratch-dev --source-dir force-app
|
|
275
|
+
sf apex run test --target-org scratch-dev --test-level RunLocalTests
|
|
276
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Context Snapshot — YYYY-MM-DD
|
|
2
|
+
|
|
3
|
+
> Point-in-time capture of project state for agent session continuity.
|
|
4
|
+
> Copy this template and fill in at the start of each major session.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Session Info
|
|
9
|
+
|
|
10
|
+
| Field | Value |
|
|
11
|
+
|-------|-------|
|
|
12
|
+
| **Date** | |
|
|
13
|
+
| **Agent** | [Claude / Copilot / Cursor / etc.] |
|
|
14
|
+
| **Session Goal** | |
|
|
15
|
+
| **Branch** | |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What Was Done
|
|
20
|
+
|
|
21
|
+
<!-- List what was accomplished in this session -->
|
|
22
|
+
|
|
23
|
+
-
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## What's Next
|
|
28
|
+
|
|
29
|
+
<!-- What should the next session pick up -->
|
|
30
|
+
|
|
31
|
+
-
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Key Decisions Made
|
|
36
|
+
|
|
37
|
+
<!-- Any architectural or design decisions -->
|
|
38
|
+
|
|
39
|
+
-
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Open Questions
|
|
44
|
+
|
|
45
|
+
<!-- Unresolved questions for the developer -->
|
|
46
|
+
|
|
47
|
+
-
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Files Changed
|
|
52
|
+
|
|
53
|
+
<!-- List modified files for quick reference -->
|
|
54
|
+
|
|
55
|
+
| File | Change |
|
|
56
|
+
|------|--------|
|
|
57
|
+
| | |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Notes
|
|
62
|
+
|
|
63
|
+
<!-- Any additional context for future sessions -->
|
|
64
|
+
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Coding Conventions
|
|
2
|
+
|
|
3
|
+
> Enforced standards for all Salesforce development in this project.
|
|
4
|
+
> These rules are NON-NEGOTIABLE — agents must follow them without exception.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Apex Standards
|
|
9
|
+
|
|
10
|
+
### Bulkification (Non-Negotiable)
|
|
11
|
+
|
|
12
|
+
- **NEVER** put SOQL or DML inside loops. No exceptions.
|
|
13
|
+
- All triggers and services must handle **200+ records** per transaction.
|
|
14
|
+
- Use `Map<Id, SObject>` patterns for efficient lookups.
|
|
15
|
+
- Aggregate queries (`COUNT()`, `SUM()`, `GROUP BY`) over iterative counting.
|
|
16
|
+
|
|
17
|
+
```apex
|
|
18
|
+
// ✅ CORRECT — Bulkified
|
|
19
|
+
Map<Id, Account> accountMap = new Map<Id, Account>(
|
|
20
|
+
[SELECT Id, Name FROM Account WHERE Id IN :accountIds WITH USER_MODE]
|
|
21
|
+
);
|
|
22
|
+
for (Contact c : contacts) {
|
|
23
|
+
Account a = accountMap.get(c.AccountId);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ❌ WRONG — SOQL in loop
|
|
27
|
+
for (Contact c : contacts) {
|
|
28
|
+
Account a = [SELECT Id, Name FROM Account WHERE Id = :c.AccountId];
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Security (Non-Negotiable)
|
|
33
|
+
|
|
34
|
+
- **`WITH USER_MODE`** on ALL SOQL queries (API v60.0+). Fall back to `WITH SECURITY_ENFORCED` for older versions.
|
|
35
|
+
- **`with sharing`** by default on ALL classes. Use `without sharing` ONLY with documented justification in a code comment.
|
|
36
|
+
- **`Security.stripInaccessible()`** before DML when handling external/untrusted data.
|
|
37
|
+
- **Never** hardcode IDs, credentials, or endpoints.
|
|
38
|
+
|
|
39
|
+
```apex
|
|
40
|
+
// ✅ CORRECT
|
|
41
|
+
public with sharing class AccountService {
|
|
42
|
+
public static List<Account> getAccounts(Set<Id> ids) {
|
|
43
|
+
return [SELECT Id, Name FROM Account WHERE Id IN :ids WITH USER_MODE];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ❌ WRONG — missing sharing, missing USER_MODE
|
|
48
|
+
public class AccountService {
|
|
49
|
+
public static List<Account> getAccounts(Set<Id> ids) {
|
|
50
|
+
return [SELECT Id, Name FROM Account WHERE Id IN :ids];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Architecture Pattern
|
|
56
|
+
|
|
57
|
+
All code follows a **strict layered architecture**:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Trigger → TriggerHandler → Service → Selector
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Layer | Responsibility | Naming |
|
|
64
|
+
|-------|---------------|--------|
|
|
65
|
+
| **Trigger** | Entry point only. Delegates immediately. | `ObjectNameTrigger` |
|
|
66
|
+
| **Trigger Handler** | Routes events (before/after insert/update/delete). | `ObjectNameTriggerHandler` |
|
|
67
|
+
| **Service** | ALL business logic. Stateless methods. | `ObjectNameService` |
|
|
68
|
+
| **Selector** | ALL SOQL queries. Reusable, testable. | `ObjectNameSelector` |
|
|
69
|
+
| **Domain** | Complex object-specific logic (optional). | `ObjectNames` (plural) |
|
|
70
|
+
|
|
71
|
+
**Rules:**
|
|
72
|
+
- **One trigger per object** — no exceptions.
|
|
73
|
+
- **Zero business logic in triggers** — handlers delegate to services.
|
|
74
|
+
- **Zero SOQL in services** — services call selectors.
|
|
75
|
+
- **Unit of Work** — batch DML at end of transaction, not scattered throughout.
|
|
76
|
+
|
|
77
|
+
### Error Handling
|
|
78
|
+
|
|
79
|
+
```apex
|
|
80
|
+
// ✅ CORRECT
|
|
81
|
+
try {
|
|
82
|
+
HttpResponse response = new Http().send(request);
|
|
83
|
+
if (response.getStatusCode() != 200) {
|
|
84
|
+
throw new IntegrationException(
|
|
85
|
+
'Callout failed: ' + response.getStatusCode() + ' ' + response.getBody()
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
} catch (CalloutException e) {
|
|
89
|
+
Logger.error('Callout to ' + endpoint + ' failed', e);
|
|
90
|
+
throw new IntegrationException('External service unavailable', e);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- Wrap callouts and integrations in `try/catch` with structured logging.
|
|
95
|
+
- Use **custom exceptions** extending `Exception` for domain errors.
|
|
96
|
+
- **NEVER** swallow exceptions silently (empty catch blocks).
|
|
97
|
+
- Include meaningful context in error messages.
|
|
98
|
+
|
|
99
|
+
### Testing Standards
|
|
100
|
+
|
|
101
|
+
| Requirement | Standard |
|
|
102
|
+
|------------|----------|
|
|
103
|
+
| Minimum coverage | **75%** (target **90%+**) |
|
|
104
|
+
| `SeeAllData=true` | **BANNED** — never use |
|
|
105
|
+
| Test data | `@TestSetup` for shared data |
|
|
106
|
+
| Assertions | Meaningful messages: `System.assertEquals(expected, actual, 'Context')` |
|
|
107
|
+
| Bulk testing | Test with **200+ records** |
|
|
108
|
+
| Negative paths | Test error conditions and exceptions |
|
|
109
|
+
| Permission testing | Test with restricted user profiles |
|
|
110
|
+
|
|
111
|
+
```apex
|
|
112
|
+
@IsTest
|
|
113
|
+
private class AccountServiceTest {
|
|
114
|
+
|
|
115
|
+
@TestSetup
|
|
116
|
+
static void setupTestData() {
|
|
117
|
+
List<Account> accounts = TestDataFactory.createAccounts(200);
|
|
118
|
+
insert accounts;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@IsTest
|
|
122
|
+
static void testGetAccounts_BulkPositive() {
|
|
123
|
+
List<Account> accounts = [SELECT Id FROM Account];
|
|
124
|
+
System.assertEquals(200, accounts.size(), 'Should have 200 test accounts');
|
|
125
|
+
|
|
126
|
+
Test.startTest();
|
|
127
|
+
List<Account> result = AccountService.getAccounts(
|
|
128
|
+
new Map<Id, Account>(accounts).keySet()
|
|
129
|
+
);
|
|
130
|
+
Test.stopTest();
|
|
131
|
+
|
|
132
|
+
System.assertEquals(200, result.size(), 'Service should return all 200 accounts');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Naming Conventions
|
|
140
|
+
|
|
141
|
+
### Apex Classes
|
|
142
|
+
|
|
143
|
+
| Type | Pattern | Example |
|
|
144
|
+
|------|---------|---------|
|
|
145
|
+
| Trigger | `{Object}Trigger` | `AccountTrigger` |
|
|
146
|
+
| Trigger Handler | `{Object}TriggerHandler` | `AccountTriggerHandler` |
|
|
147
|
+
| Service | `{Object}Service` | `AccountService` |
|
|
148
|
+
| Selector | `{Object}Selector` | `AccountSelector` |
|
|
149
|
+
| Domain | `{Objects}` (plural) | `Accounts` |
|
|
150
|
+
| Controller | `{Feature}Controller` | `AccountSearchController` |
|
|
151
|
+
| Batch | `{Purpose}Batch` | `AccountCleanupBatch` |
|
|
152
|
+
| Queueable | `{Purpose}Queueable` | `SyncAccountsQueueable` |
|
|
153
|
+
| Schedulable | `{Purpose}Scheduler` | `DailyCleanupScheduler` |
|
|
154
|
+
| Test | `{ClassName}Test` | `AccountServiceTest` |
|
|
155
|
+
| Exception | `{Domain}Exception` | `IntegrationException` |
|
|
156
|
+
| Utility | `{Purpose}Util` | `DateUtil` |
|
|
157
|
+
| Wrapper/DTO | `{Purpose}DTO` | `AccountResponseDTO` |
|
|
158
|
+
|
|
159
|
+
### Apex Methods
|
|
160
|
+
|
|
161
|
+
- **camelCase** — `getAccountsByIds()`, `validateContacts()`
|
|
162
|
+
- Prefix with verb — `get`, `create`, `update`, `delete`, `validate`, `process`, `sync`
|
|
163
|
+
- Boolean methods — prefix with `is`, `has`, `can`, `should`
|
|
164
|
+
|
|
165
|
+
### Variables
|
|
166
|
+
|
|
167
|
+
- **camelCase** — `accountMap`, `contactList`, `isActive`
|
|
168
|
+
- Collections — use plural or descriptive suffix: `accountIds`, `contactsByAccountId`
|
|
169
|
+
- Constants — `UPPER_SNAKE_CASE`: `MAX_RETRY_COUNT`
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## LWC Standards
|
|
174
|
+
|
|
175
|
+
### Component Structure
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
lwc/
|
|
179
|
+
accountSummary/
|
|
180
|
+
accountSummary.html
|
|
181
|
+
accountSummary.js
|
|
182
|
+
accountSummary.css
|
|
183
|
+
accountSummary.js-meta.xml
|
|
184
|
+
__tests__/
|
|
185
|
+
accountSummary.test.js
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Rules
|
|
189
|
+
|
|
190
|
+
- **camelCase** folder names: `accountSummary/`, NOT `AccountSummary/`
|
|
191
|
+
- Use **`@wire`** for reactive data. Imperative Apex only when wire won't work.
|
|
192
|
+
- **No inline styles** — use CSS custom properties and component CSS files.
|
|
193
|
+
- Always handle **loading**, **error**, and **empty** states in the template.
|
|
194
|
+
- Use **Lightning Design System (SLDS)** classes — don't reinvent the wheel.
|
|
195
|
+
|
|
196
|
+
```html
|
|
197
|
+
<!-- ✅ CORRECT — handles all states -->
|
|
198
|
+
<template>
|
|
199
|
+
<template if:true={isLoading}>
|
|
200
|
+
<lightning-spinner alternative-text="Loading"></lightning-spinner>
|
|
201
|
+
</template>
|
|
202
|
+
<template if:true={error}>
|
|
203
|
+
<c-error-panel errors={error}></c-error-panel>
|
|
204
|
+
</template>
|
|
205
|
+
<template if:true={hasData}>
|
|
206
|
+
<!-- Component content -->
|
|
207
|
+
</template>
|
|
208
|
+
<template if:false={hasData}>
|
|
209
|
+
<c-empty-state message="No records found"></c-empty-state>
|
|
210
|
+
</template>
|
|
211
|
+
</template>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### LWC Naming
|
|
215
|
+
|
|
216
|
+
| Type | Pattern | Example |
|
|
217
|
+
|------|---------|---------|
|
|
218
|
+
| Component | `camelCase` | `accountSummary` |
|
|
219
|
+
| Event | `camelCase` verb | `recordselected` |
|
|
220
|
+
| Property | `camelCase` | `@api recordId` |
|
|
221
|
+
| Method | `camelCase` verb | `handleSearch()` |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Flow Standards
|
|
226
|
+
|
|
227
|
+
- **Record-Triggered Flows** over Process Builder (deprecated) and Workflow Rules.
|
|
228
|
+
- **Before-Save** flows for field updates (no DML consumed).
|
|
229
|
+
- **After-Save** flows only when related record updates or platform events needed.
|
|
230
|
+
- Add **fault paths** to every DML and callout element.
|
|
231
|
+
- **Never hardcode IDs** — use Custom Labels or Custom Metadata Types.
|
|
232
|
+
- Flow naming: `{Object}_{Trigger}_{Purpose}` — e.g., `Account_AfterSave_UpdateContacts`
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## General Rules
|
|
237
|
+
|
|
238
|
+
- **No hardcoded IDs** anywhere — use Custom Labels, Custom Metadata, Custom Settings.
|
|
239
|
+
- **No hardcoded credentials** — use Named Credentials exclusively.
|
|
240
|
+
- **API version**: v66.0 (unless project specifies otherwise).
|
|
241
|
+
- **One class per file** — no inner classes except for private DTOs/wrappers.
|
|
242
|
+
- **Comments**: Meaningful Javadoc on all public methods. Don't state the obvious.
|