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,269 @@
|
|
|
1
|
+
# Test Class Generation Rules
|
|
2
|
+
|
|
3
|
+
> Instructions for AI agents when generating Apex test classes.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Absolute Rules
|
|
8
|
+
|
|
9
|
+
| Rule | Enforcement |
|
|
10
|
+
|------|------------|
|
|
11
|
+
| `SeeAllData=true` | **BANNED** — never use, no exceptions |
|
|
12
|
+
| Minimum coverage | **75%** (target **90%+**) |
|
|
13
|
+
| Test data | Always use `@TestSetup` or `TestDataFactory` |
|
|
14
|
+
| Assertions | Every test must have `System.assert*` with meaningful message |
|
|
15
|
+
| Bulk testing | At least one test with **200+ records** |
|
|
16
|
+
| Negative paths | At least one test for error/exception scenarios |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Test Class Structure
|
|
21
|
+
|
|
22
|
+
```apex
|
|
23
|
+
@IsTest
|
|
24
|
+
private class {ClassName}Test {
|
|
25
|
+
|
|
26
|
+
// ─── Shared Test Data ───────────────────────────────────────
|
|
27
|
+
@TestSetup
|
|
28
|
+
static void setupTestData() {
|
|
29
|
+
// Create all shared test data here
|
|
30
|
+
// This runs once, data is rolled back per test method
|
|
31
|
+
List<Account> accounts = TestDataFactory.createAccounts(200);
|
|
32
|
+
insert accounts;
|
|
33
|
+
|
|
34
|
+
List<Contact> contacts = TestDataFactory.createContacts(accounts, 2); // 2 per account
|
|
35
|
+
insert contacts;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Positive Tests ─────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
@IsTest
|
|
41
|
+
static void testMethodName_PositiveScenario() {
|
|
42
|
+
// Arrange
|
|
43
|
+
List<Account> accounts = [SELECT Id, Name FROM Account];
|
|
44
|
+
|
|
45
|
+
// Act
|
|
46
|
+
Test.startTest();
|
|
47
|
+
List<Account> result = AccountService.processAccounts(accounts);
|
|
48
|
+
Test.stopTest();
|
|
49
|
+
|
|
50
|
+
// Assert
|
|
51
|
+
System.assertEquals(200, result.size(), 'Should process all 200 accounts');
|
|
52
|
+
System.assertNotEquals(null, result[0].Name, 'Account name should not be null');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─── Bulk Tests ─────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
@IsTest
|
|
58
|
+
static void testMethodName_Bulk200Records() {
|
|
59
|
+
// Arrange
|
|
60
|
+
List<Account> accounts = [SELECT Id FROM Account];
|
|
61
|
+
System.assertEquals(200, accounts.size(), 'Test setup should create 200 accounts');
|
|
62
|
+
|
|
63
|
+
// Act
|
|
64
|
+
Test.startTest();
|
|
65
|
+
AccountService.bulkOperation(accounts);
|
|
66
|
+
Test.stopTest();
|
|
67
|
+
|
|
68
|
+
// Assert — verify no governor limit exceptions occurred
|
|
69
|
+
List<Account> updated = [SELECT Id, Status__c FROM Account WHERE Id IN :accounts];
|
|
70
|
+
for (Account a : updated) {
|
|
71
|
+
System.assertEquals('Processed', a.Status__c, 'All accounts should be processed');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── Negative Tests ─────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
@IsTest
|
|
78
|
+
static void testMethodName_NullInput() {
|
|
79
|
+
Boolean exceptionThrown = false;
|
|
80
|
+
|
|
81
|
+
Test.startTest();
|
|
82
|
+
try {
|
|
83
|
+
AccountService.processAccounts(null);
|
|
84
|
+
} catch (AccountService.AccountProcessingException e) {
|
|
85
|
+
exceptionThrown = true;
|
|
86
|
+
System.assert(e.getMessage().contains('cannot be null'),
|
|
87
|
+
'Exception message should indicate null input');
|
|
88
|
+
}
|
|
89
|
+
Test.stopTest();
|
|
90
|
+
|
|
91
|
+
System.assert(exceptionThrown, 'Should throw exception for null input');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@IsTest
|
|
95
|
+
static void testMethodName_InvalidData() {
|
|
96
|
+
// Arrange
|
|
97
|
+
Account invalidAccount = new Account(); // Missing required fields
|
|
98
|
+
|
|
99
|
+
// Act & Assert
|
|
100
|
+
Test.startTest();
|
|
101
|
+
try {
|
|
102
|
+
insert invalidAccount;
|
|
103
|
+
System.assert(false, 'Should have thrown DmlException');
|
|
104
|
+
} catch (DmlException e) {
|
|
105
|
+
System.assert(e.getMessage().contains('REQUIRED_FIELD_MISSING'),
|
|
106
|
+
'Should fail on required field validation');
|
|
107
|
+
}
|
|
108
|
+
Test.stopTest();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ─── Permission Tests ───────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
@IsTest
|
|
114
|
+
static void testMethodName_RestrictedUser() {
|
|
115
|
+
// Create a user with minimal permissions
|
|
116
|
+
User restrictedUser = TestDataFactory.createStandardUser();
|
|
117
|
+
insert restrictedUser;
|
|
118
|
+
|
|
119
|
+
System.runAs(restrictedUser) {
|
|
120
|
+
Test.startTest();
|
|
121
|
+
try {
|
|
122
|
+
AccountService.processAccounts([SELECT Id FROM Account]);
|
|
123
|
+
// If security is properly enforced, this should throw
|
|
124
|
+
} catch (System.NoAccessException e) {
|
|
125
|
+
System.assert(true, 'Restricted user should not have access');
|
|
126
|
+
}
|
|
127
|
+
Test.stopTest();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ─── Callout Mock Tests ─────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
@IsTest
|
|
134
|
+
static void testCallout_Success() {
|
|
135
|
+
Test.setMock(HttpCalloutMock.class, new ExternalApiMockSuccess());
|
|
136
|
+
|
|
137
|
+
Test.startTest();
|
|
138
|
+
ApiResponseDTO response = ExternalApiService.callExternalApi('/endpoint', 'GET', null);
|
|
139
|
+
Test.stopTest();
|
|
140
|
+
|
|
141
|
+
System.assertNotEquals(null, response, 'Response should not be null');
|
|
142
|
+
System.assertEquals('success', response.status, 'API call should succeed');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@IsTest
|
|
146
|
+
static void testCallout_Failure() {
|
|
147
|
+
Test.setMock(HttpCalloutMock.class, new ExternalApiMockFailure());
|
|
148
|
+
|
|
149
|
+
Boolean exceptionThrown = false;
|
|
150
|
+
Test.startTest();
|
|
151
|
+
try {
|
|
152
|
+
ExternalApiService.callExternalApi('/endpoint', 'GET', null);
|
|
153
|
+
} catch (IntegrationException e) {
|
|
154
|
+
exceptionThrown = true;
|
|
155
|
+
}
|
|
156
|
+
Test.stopTest();
|
|
157
|
+
|
|
158
|
+
System.assert(exceptionThrown, 'Should throw IntegrationException on API failure');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## TestDataFactory Pattern
|
|
166
|
+
|
|
167
|
+
```apex
|
|
168
|
+
@IsTest
|
|
169
|
+
public class TestDataFactory {
|
|
170
|
+
|
|
171
|
+
public static List<Account> createAccounts(Integer count) {
|
|
172
|
+
List<Account> accounts = new List<Account>();
|
|
173
|
+
for (Integer i = 0; i < count; i++) {
|
|
174
|
+
accounts.add(new Account(
|
|
175
|
+
Name = 'Test Account ' + i,
|
|
176
|
+
Industry = 'Technology'
|
|
177
|
+
));
|
|
178
|
+
}
|
|
179
|
+
return accounts; // Don't insert — let caller decide
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
public static List<Contact> createContacts(List<Account> accounts, Integer perAccount) {
|
|
183
|
+
List<Contact> contacts = new List<Contact>();
|
|
184
|
+
for (Account a : accounts) {
|
|
185
|
+
for (Integer i = 0; i < perAccount; i++) {
|
|
186
|
+
contacts.add(new Contact(
|
|
187
|
+
FirstName = 'Test',
|
|
188
|
+
LastName = 'Contact ' + i,
|
|
189
|
+
AccountId = a.Id,
|
|
190
|
+
Email = 'test' + i + '@' + a.Id + '.test'
|
|
191
|
+
));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return contacts;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public static User createStandardUser() {
|
|
198
|
+
Profile p = [SELECT Id FROM Profile WHERE Name = 'Standard User' LIMIT 1];
|
|
199
|
+
return new User(
|
|
200
|
+
FirstName = 'Test',
|
|
201
|
+
LastName = 'User',
|
|
202
|
+
Email = 'testuser@test.salesforce.com',
|
|
203
|
+
Username = 'testuser' + DateTime.now().getTime() + '@test.salesforce.com',
|
|
204
|
+
Alias = 'tuser',
|
|
205
|
+
TimeZoneSidKey = 'America/Los_Angeles',
|
|
206
|
+
LocaleSidKey = 'en_US',
|
|
207
|
+
EmailEncodingKey = 'UTF-8',
|
|
208
|
+
LanguageLocaleKey = 'en_US',
|
|
209
|
+
ProfileId = p.Id
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## HttpCalloutMock Pattern
|
|
218
|
+
|
|
219
|
+
```apex
|
|
220
|
+
@IsTest
|
|
221
|
+
public class ExternalApiMockSuccess implements HttpCalloutMock {
|
|
222
|
+
public HTTPResponse respond(HTTPRequest req) {
|
|
223
|
+
HttpResponse res = new HttpResponse();
|
|
224
|
+
res.setStatusCode(200);
|
|
225
|
+
res.setHeader('Content-Type', 'application/json');
|
|
226
|
+
res.setBody('{"status":"success","data":[]}');
|
|
227
|
+
return res;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@IsTest
|
|
232
|
+
public class ExternalApiMockFailure implements HttpCalloutMock {
|
|
233
|
+
public HTTPResponse respond(HTTPRequest req) {
|
|
234
|
+
HttpResponse res = new HttpResponse();
|
|
235
|
+
res.setStatusCode(500);
|
|
236
|
+
res.setBody('{"error":"Internal Server Error"}');
|
|
237
|
+
return res;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Assertion Best Practices
|
|
245
|
+
|
|
246
|
+
```apex
|
|
247
|
+
// ✅ GOOD — Meaningful messages
|
|
248
|
+
System.assertEquals(200, accounts.size(), 'Should return 200 accounts for bulk test');
|
|
249
|
+
System.assertNotEquals(null, account.Name, 'Account name should be populated after processing');
|
|
250
|
+
System.assert(result.isSuccess(), 'DML should succeed for valid records: ' + result.getErrors());
|
|
251
|
+
|
|
252
|
+
// ❌ BAD — No context
|
|
253
|
+
System.assertEquals(200, accounts.size());
|
|
254
|
+
System.assert(result.isSuccess());
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Checklist Before Returning Test Code
|
|
260
|
+
|
|
261
|
+
- [ ] `@IsTest` annotation on class (private class)
|
|
262
|
+
- [ ] `@TestSetup` for shared data
|
|
263
|
+
- [ ] NO `SeeAllData=true` anywhere
|
|
264
|
+
- [ ] `Test.startTest()` / `Test.stopTest()` wrapping the operation under test
|
|
265
|
+
- [ ] At least one bulk test (200+ records)
|
|
266
|
+
- [ ] At least one negative/error path test
|
|
267
|
+
- [ ] All assertions have descriptive messages
|
|
268
|
+
- [ ] HttpCalloutMock used for callout tests
|
|
269
|
+
- [ ] Follows naming convention: `{ClassName}Test`
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Agent Rules — READ THIS FIRST
|
|
2
|
+
|
|
3
|
+
> This is the master instruction file for any AI agent working in this Salesforce project.
|
|
4
|
+
> Read this file BEFORE doing anything else. Follow every rule without exception.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Read Order
|
|
9
|
+
|
|
10
|
+
When starting a new session, read files by tier. Read T0 always. Read T1–T3 as needed.
|
|
11
|
+
|
|
12
|
+
### T0 — Always (Every Session)
|
|
13
|
+
|
|
14
|
+
1. **This file** (`rules.md`) — Agent behavior rules
|
|
15
|
+
2. **`org-context.local.md`** (or **`org-context.md`** if local does not exist) — Active Org details, CLI config, limits
|
|
16
|
+
3. **`inventory.md`** — What actually exists in this project (objects, classes, components)
|
|
17
|
+
4. **`current-state.md`** — What's happening NOW (session log, active work, blockers, next steps)
|
|
18
|
+
|
|
19
|
+
### T1 — Before Writing Code
|
|
20
|
+
|
|
21
|
+
5. **`conventions.md`** — Coding standards (Apex, LWC, Flow)
|
|
22
|
+
6. **`source-of-truth.md`** — How to verify before you generate
|
|
23
|
+
7. **`prompts/{relevant}.md`** — Generation rules for the specific task
|
|
24
|
+
|
|
25
|
+
### T2 — When Task Involves Design, Tests, or Performance
|
|
26
|
+
|
|
27
|
+
8. **`architecture.md`** — Data model, layers, integrations
|
|
28
|
+
9. **`testing-strategy.md`** — Test philosophy, patterns, coverage rules
|
|
29
|
+
10. **`performance.md`** — Governor limits, SOQL optimization, async patterns
|
|
30
|
+
|
|
31
|
+
### T3 — Reference (Read When Specifically Needed)
|
|
32
|
+
|
|
33
|
+
11. **`commands.md`** — sf CLI cookbook
|
|
34
|
+
12. **`deployment.md`** — Deploy/retrieve workflows, CI/CD
|
|
35
|
+
13. **`integrations.md`** — External systems, callout patterns
|
|
36
|
+
14. **`debugging-notes.md`** — Debug techniques, log levels
|
|
37
|
+
15. **`known-issues.md`** — Platform bugs, workarounds
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Auto-Update Protocol (MANDATORY)
|
|
42
|
+
|
|
43
|
+
You MUST keep `current-state.md` and `inventory.md` updated as you work. These files are the project's living memory — they ensure continuity across sessions and agents.
|
|
44
|
+
|
|
45
|
+
### When to Update `current-state.md`
|
|
46
|
+
|
|
47
|
+
| Trigger | What to Update |
|
|
48
|
+
|---------|---------------|
|
|
49
|
+
| **Session start** | Add entry to `## Session Log` with date, agent, goal |
|
|
50
|
+
| **Starting a task** | Move item to `In Progress` in `## Active Work`, mark `[/]` |
|
|
51
|
+
| **Completing a task** | Move item to `Recently Completed`, mark `[x]`, note files changed |
|
|
52
|
+
| **Hitting a blocker** | Add to `## Blockers` with impact and context |
|
|
53
|
+
| **Resolving a blocker** | Remove from `## Blockers`, note resolution in session log |
|
|
54
|
+
| **Making a design decision** | Add to `## Decisions` with rationale |
|
|
55
|
+
| **Discovering an issue** | Add to `## Blockers` or update `known-issues.md` |
|
|
56
|
+
| **Changing files** | Append to `## Files Changed This Session` |
|
|
57
|
+
| **Session end** | Write summary in session log, update status |
|
|
58
|
+
|
|
59
|
+
### When to Update `inventory.md`
|
|
60
|
+
|
|
61
|
+
| Trigger | What to Update |
|
|
62
|
+
|---------|---------------|
|
|
63
|
+
| **First session on project** | Run populate commands, fill all sections |
|
|
64
|
+
| **Creating a new class** | Add to the appropriate layer section (Service, Selector, etc.) |
|
|
65
|
+
| **Creating a new LWC** | Add to LWC Components section |
|
|
66
|
+
| **Creating a new Flow** | Add to Flows section |
|
|
67
|
+
| **Creating a new object/field** | Add to Custom Objects section |
|
|
68
|
+
| **Deleting anything** | Remove from inventory |
|
|
69
|
+
|
|
70
|
+
### How to Update
|
|
71
|
+
|
|
72
|
+
- **Method A (Recommended):** Use the helper script `.ai/scripts/update_state.py`. This ensures perfect formatting and saves time:
|
|
73
|
+
- `python3 .ai/scripts/update_state.py scan` (scans project and updates `inventory.md` automatically)
|
|
74
|
+
- `python3 .ai/scripts/update_state.py session-start --agent "AgentName" --goal "Goal description"`
|
|
75
|
+
- `python3 .ai/scripts/update_state.py task-start "Task description"`
|
|
76
|
+
- `python3 .ai/scripts/update_state.py task-complete "Task description" --files "comma,separated,files"`
|
|
77
|
+
- `python3 .ai/scripts/update_state.py session-end --summary "Summary of work" --files "comma,separated,files"`
|
|
78
|
+
- **Method B (Manual):** If the script is unavailable or you have no command execution capabilities, edit the files manually using file replacement tools. Follow the format guidelines:
|
|
79
|
+
- Keep entries concise — one line per item.
|
|
80
|
+
- Use ISO dates: `2026-05-22`
|
|
81
|
+
- Use status emojis: 🟢 On Track, 🟡 At Risk, 🔴 Blocked, ✅ Done
|
|
82
|
+
- Prepend new entries (newest first) in the session log.
|
|
83
|
+
|
|
84
|
+
### Example Session Log Entry
|
|
85
|
+
|
|
86
|
+
```markdown
|
|
87
|
+
### 2026-05-22 | Claude | Implement Account dedup service
|
|
88
|
+
- ✅ Created `AccountSelector.findDuplicates()` with fuzzy matching
|
|
89
|
+
- ✅ Created `AccountDedupService` with merge logic
|
|
90
|
+
- ✅ Test class with 200+ records, 94% coverage
|
|
91
|
+
- 🟡 Open question: Should we auto-merge or flag for review?
|
|
92
|
+
- **Files:** AccountSelector.cls, AccountDedupService.cls, AccountDedupServiceTest.cls
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 3. Anti-Hallucination Rules (NON-NEGOTIABLE)
|
|
98
|
+
|
|
99
|
+
> **NEVER GUESS. ALWAYS VERIFY.**
|
|
100
|
+
> Hallucinated field names, fake API methods, and invented CLI flags are the #1 cause of broken Salesforce code from AI agents.
|
|
101
|
+
|
|
102
|
+
### The Verification Mandate
|
|
103
|
+
|
|
104
|
+
Before writing code that references ANY of the following, you MUST verify it exists:
|
|
105
|
+
|
|
106
|
+
| What | How to Verify |
|
|
107
|
+
|------|--------------|
|
|
108
|
+
| **Custom field** (`*__c`) | Check `inventory.md` → or run `FieldDefinition` SOQL (see `source-of-truth.md`) |
|
|
109
|
+
| **Custom object** (`*__c`) | Check `inventory.md` → or run `EntityDefinition` SOQL |
|
|
110
|
+
| **Apex class** | Check `inventory.md` → or `ls force-app/main/default/classes/` |
|
|
111
|
+
| **LWC component** | Check `inventory.md` → or `ls force-app/main/default/lwc/` |
|
|
112
|
+
| **Named Credential** | Check `inventory.md` → or query `NamedCredential` via Tooling API |
|
|
113
|
+
| **Custom Label** | Check `inventory.md` → or query `ExternalString` via Tooling API |
|
|
114
|
+
| **Custom Metadata** | Check `inventory.md` → or query the CMDT object directly |
|
|
115
|
+
| **Standard Apex method** | Check `source-of-truth.md` standard library table |
|
|
116
|
+
| **CLI flag** | Run the command with `--help` first |
|
|
117
|
+
| **Governor limit number** | Check `org-context.local.md` (or `org-context.md`) — don't guess the numbers |
|
|
118
|
+
|
|
119
|
+
### Verification Workflow
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
About to reference something?
|
|
123
|
+
│
|
|
124
|
+
├── Is it in inventory.md?
|
|
125
|
+
│ ├── YES → Safe to use ✅
|
|
126
|
+
│ └── NO ──→ Can you verify it?
|
|
127
|
+
│ ├── YES (run CLI/SOQL/ls) → Verify, then use ✅
|
|
128
|
+
│ │ Update inventory.md
|
|
129
|
+
│ └── NO (no org access) → Flag it ⚠️
|
|
130
|
+
│ Tell the user:
|
|
131
|
+
│ "⚠️ Assumed: X exists — please verify"
|
|
132
|
+
│
|
|
133
|
+
└── Is it a standard Apex class/method?
|
|
134
|
+
├── In source-of-truth.md table? → Safe to use ✅
|
|
135
|
+
└── Not in table? → Don't invent it ❌
|
|
136
|
+
Search documentation or ask the user
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Confidence Signals (REQUIRED)
|
|
140
|
+
|
|
141
|
+
When referencing Salesforce metadata in your response, prefix with:
|
|
142
|
+
|
|
143
|
+
| Signal | Meaning | When to Use |
|
|
144
|
+
|--------|---------|-------------|
|
|
145
|
+
| ✅ **Verified** | Confirmed it exists via query, file check, or inventory | You checked and it's real |
|
|
146
|
+
| ⚠️ **Assumed** | Reasonable assumption, not verified | You think it exists but didn't check |
|
|
147
|
+
| ❓ **Uncertain** | You don't know — ask the developer | Don't fake it |
|
|
148
|
+
|
|
149
|
+
**Example in code comments:**
|
|
150
|
+
```apex
|
|
151
|
+
// ✅ Verified: Account.Industry (standard field)
|
|
152
|
+
// ⚠️ Assumed: Account.Health_Score__c — listed in architecture.md but not verified in org
|
|
153
|
+
// ❓ Uncertain: Account.Risk_Rating__c — need to confirm this field exists
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Absolute Don'ts
|
|
157
|
+
|
|
158
|
+
- ❌ **NEVER invent a field name** — If you're not sure `MyObject__c.MyField__c` exists, say so
|
|
159
|
+
- ❌ **NEVER fabricate an Apex method** — If `Database.upsertImmediate()` isn't in the standard library table, it doesn't exist
|
|
160
|
+
- ❌ **NEVER guess a CLI flag** — Run `--help` first
|
|
161
|
+
- ❌ **NEVER assume org features** — Check `org-context.local.md` (or `org-context.md` if local does not exist) for enabled features
|
|
162
|
+
- ❌ **NEVER make up governor limit numbers** — Reference `org-context.local.md` (or `org-context.md` if local does not exist)
|
|
163
|
+
- ❌ **NEVER invent trigger events** — `before undelete` does not exist
|
|
164
|
+
|
|
165
|
+
### When in Doubt
|
|
166
|
+
|
|
167
|
+
Say this instead of guessing:
|
|
168
|
+
|
|
169
|
+
> "I'm referencing `Account.Health_Score__c` based on the architecture doc, but I haven't verified this field exists in the org. Please confirm before deploying, or I can run a Tooling API query to check."
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 4. Next-Session Handoff Protocol
|
|
174
|
+
|
|
175
|
+
At end of every session, update `current-state.md` → `## Next Session` section with:
|
|
176
|
+
|
|
177
|
+
1. **What to do first** — The single most important task for the next agent
|
|
178
|
+
2. **Context needed** — Files, decisions, or blockers the next agent must know
|
|
179
|
+
3. **What NOT to do** — Anything that's blocked, waiting on human input, or risky
|
|
180
|
+
|
|
181
|
+
This replaces the need for a separate `next-session.md` file.
|
|
182
|
+
|
|
183
|
+
## 5. Context Snapshot Protocol
|
|
184
|
+
|
|
185
|
+
At the end of long sessions or before a major context switch:
|
|
186
|
+
|
|
187
|
+
1. Copy `context-snapshots/TEMPLATE.md` → `context-snapshots/YYYY-MM-DD.md`
|
|
188
|
+
2. Fill in what was done, what's next, and key decisions
|
|
189
|
+
3. This preserves deep context that `current-state.md` summarizes
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 6. Code Generation Rules
|
|
194
|
+
|
|
195
|
+
Before generating ANY Salesforce code:
|
|
196
|
+
|
|
197
|
+
1. **Check `inventory.md`** — Does the class/object/field you're referencing actually exist?
|
|
198
|
+
2. **Check `source-of-truth.md`** — Are the Apex methods you're using real?
|
|
199
|
+
3. Read the relevant `prompts/*.md` file for the code type
|
|
200
|
+
4. Follow `conventions.md` — especially:
|
|
201
|
+
- `with sharing` on every class
|
|
202
|
+
- `WITH USER_MODE` on every SOQL query
|
|
203
|
+
- No SOQL/DML in loops
|
|
204
|
+
- One trigger per object → Handler → Service → Selector
|
|
205
|
+
5. After generating code:
|
|
206
|
+
- Update `current-state.md` with files changed
|
|
207
|
+
- Update `inventory.md` with new classes/components created
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 7. Performance Rules
|
|
212
|
+
|
|
213
|
+
Before writing code that processes data:
|
|
214
|
+
|
|
215
|
+
1. **Check `performance.md`** — Know governor limits for the operation type
|
|
216
|
+
2. **Choose the right async pattern** — See the selection table in `performance.md`
|
|
217
|
+
3. **For 50K+ records** — Use Batch Apex, not collections in memory
|
|
218
|
+
4. **For CPU-heavy logic** — Use Map lookups, not nested loops
|
|
219
|
+
5. **Monitor in code** — Add `Limits.getQueries()` / `Limits.getCpuTime()` checks for critical methods
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 8. Pre-Deploy Self-Check
|
|
224
|
+
|
|
225
|
+
Before telling the user code is ready to deploy:
|
|
226
|
+
|
|
227
|
+
- [ ] All referenced fields/objects verified (via `inventory.md` or Tooling API)
|
|
228
|
+
- [ ] All Apex methods used are from the standard library (no invented methods)
|
|
229
|
+
- [ ] All classes use `with sharing`
|
|
230
|
+
- [ ] All SOQL uses `WITH USER_MODE`
|
|
231
|
+
- [ ] No SOQL or DML inside loops
|
|
232
|
+
- [ ] Handles 200+ records (bulkified)
|
|
233
|
+
- [ ] Error handling with meaningful messages (no empty catches)
|
|
234
|
+
- [ ] No hardcoded IDs or credentials
|
|
235
|
+
- [ ] Test coverage target ≥ 90% with meaningful assertions
|
|
236
|
+
- [ ] `current-state.md` is updated with what was done
|
|
237
|
+
- [ ] `inventory.md` is updated with any new metadata created
|
|
238
|
+
- [ ] All assumptions flagged with ⚠️ for human review
|