acek-skills 1.2.0 → 1.3.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/README.md +14 -7
- package/package.json +1 -1
- package/skills/sf-admin/SKILL.md +4 -1
- package/skills/sf-architect/SKILL.md +33 -11
- package/skills/sf-ba/SKILL.md +8 -0
- package/skills/sf-data-migration/SKILL.md +4 -1
- package/skills/sf-dev/SKILL.md +23 -32
- package/skills/sf-devops/SKILL.md +6 -1
- package/skills/sf-ideation/SKILL.md +8 -0
- package/skills/sf-security-review/SKILL.md +56 -1
- package/skills/sf-testing/SKILL.md +116 -6
package/README.md
CHANGED
|
@@ -149,7 +149,9 @@ metadata is touched: runs Discovery (project scan + choice-based clarifying ques
|
|
|
149
149
|
text), proposes Decisions with trade-offs for data model / automation layer / integration pattern
|
|
150
150
|
/ security model / Permission Set strategy, then breaks approved work into tasks dispatched to the
|
|
151
151
|
right skill (`sf-admin`, `sf-dev`, `sf-testing`, `sf-devops`, `sf-security-review`,
|
|
152
|
-
`sf-data-migration`)
|
|
152
|
+
`sf-data-migration`) — always including a `sf-security-review` task when the plan touches a
|
|
153
|
+
PII-bearing object/field, an integration, or a sharing/OWD change. Plans are saved to
|
|
154
|
+
`instructions/architecture/` and are resumable across
|
|
153
155
|
sessions without re-scanning the project or re-reading chat history. Complements — does not
|
|
154
156
|
replace — `sf-ba` (PRDs) and `sf-ideation` (open-ended brainstorming), and can consume either as
|
|
155
157
|
input.
|
|
@@ -177,7 +179,10 @@ data management (import/export/data loader) — anything declarative, no code in
|
|
|
177
179
|
|
|
178
180
|
Writes PRDs, user stories, feature specs, and functional requirements — structured Markdown docs
|
|
179
181
|
intended to hand off to an admin or developer. Triggers on "write a PRD", "document requirements",
|
|
180
|
-
"write user stories", or when a business problem needs to become executable documentation.
|
|
182
|
+
"write user stories", or when a business problem needs to become executable documentation. Its
|
|
183
|
+
Admin Spec / Dev Spec templates are for work that will **not** go through `sf-architect`; for
|
|
184
|
+
open-ended brainstorming on a specific existing component instead of net-new requirements, it
|
|
185
|
+
hands off to `sf-ideation`.
|
|
181
186
|
|
|
182
187
|
</details>
|
|
183
188
|
|
|
@@ -230,8 +235,9 @@ opportunities.
|
|
|
230
235
|
|
|
231
236
|
CRUD/FLS checks, sharing model review, `with sharing` enforcement, SOQL injection, XSS in LWC,
|
|
232
237
|
hardcoded credential detection, and a production sign-off checklist. Also covers Shield,
|
|
233
|
-
encryption, audit trail,
|
|
234
|
-
|
|
238
|
+
encryption, audit trail, a dedicated PII & Data Privacy section — field classification, sandbox
|
|
239
|
+
data masking, and UU PDP-aware review practices — plus Guest User/Experience Cloud access review
|
|
240
|
+
and Connected App/External Client App (OAuth) review checklists.
|
|
235
241
|
|
|
236
242
|
</details>
|
|
237
243
|
|
|
@@ -239,8 +245,9 @@ sandbox data masking, and UU PDP-aware review practices.
|
|
|
239
245
|
<summary><strong>sf-testing</strong> — Apex test strategy</summary>
|
|
240
246
|
|
|
241
247
|
Test classes, `TestDataFactory` patterns, mocking HTTP callouts and platform events, test
|
|
242
|
-
assertions, coverage reports,
|
|
243
|
-
floor
|
|
248
|
+
assertions, coverage reports, debugging org-only test failures, and LWC Jest unit tests
|
|
249
|
+
(`@salesforce/sfdx-lwc-jest`). Enforces the 85% Apex coverage floor, `AuraHandledException`
|
|
250
|
+
assertion conventions, and a 3-scenario minimum (happy/empty/interaction) for every LWC component.
|
|
244
251
|
|
|
245
252
|
</details>
|
|
246
253
|
|
|
@@ -310,7 +317,7 @@ responds, and doesn't touch any org, sandbox, or production data on its own.
|
|
|
310
317
|
Yes — deselect any skill in the interactive wizard, or use `install <name>` for a single one.
|
|
311
318
|
|
|
312
319
|
**Can I re-run install to update after a new version is published?**
|
|
313
|
-
Yes, `npx` always resolves the latest version unless you pin one (`npx acek-skills@1.
|
|
320
|
+
Yes, `npx` always resolves the latest version unless you pin one (`npx acek-skills@1.3.0 install`).
|
|
314
321
|
Re-running overwrites previously installed files for the skills/targets you select.
|
|
315
322
|
|
|
316
323
|
**Why do only Claude Code skills auto-trigger?**
|
package/package.json
CHANGED
package/skills/sf-admin/SKILL.md
CHANGED
|
@@ -14,7 +14,10 @@ description: >
|
|
|
14
14
|
|
|
15
15
|
## Environment Context
|
|
16
16
|
|
|
17
|
-
- API Version: **
|
|
17
|
+
- API Version: **not hardcoded** — new files inherit whatever `sourceApiVersion` is set in the
|
|
18
|
+
project's `sfdx-project.json` when scaffolded via SF CLI. When touching an **existing**
|
|
19
|
+
class/component/metadata item, check its `apiVersion`: below **62.0** → bump to **67.0** as part
|
|
20
|
+
of the change; 62.0+ → leave as-is unless the task needs 67.0+ behavior specifically.
|
|
18
21
|
- Org Type: Enterprise (single org: sandbox + production)
|
|
19
22
|
- Tooling: **SF CLI** for metadata retrieval; most admin tasks done declaratively in Setup UI
|
|
20
23
|
- Any metadata changes intended for production must go through the DevOps deployment process
|
|
@@ -141,12 +141,19 @@ Nothing is built yet. This phase only produces decisions with rationale.
|
|
|
141
141
|
**Touches** list (exact file(s)/component(s) it will create or modify), a dependency list, and
|
|
142
142
|
status `P` (Pending). `Touches` is what Phase 4 uses to decide which tasks are safe to run in
|
|
143
143
|
parallel — be specific and accurate here, not vague.
|
|
144
|
-
3. **
|
|
144
|
+
3. **Always add a `sf-security-review` task** when the plan creates or modifies access to any
|
|
145
|
+
PII-bearing object/field (see `sf-security-review`'s PII & Data Privacy categories), introduces
|
|
146
|
+
a new integration/callout, or changes the sharing model — never skip it because the feature
|
|
147
|
+
"seems simple," the same standard already applied to Permission Set Strategy above. Depend it
|
|
148
|
+
on the relevant `sf-admin`/`sf-dev` tasks and put it before the `sf-devops` deploy task — its
|
|
149
|
+
sign-off is what fills the CR's "PII Impact" section. Skip only when Project Context confirms
|
|
150
|
+
none of those three surfaces are touched.
|
|
151
|
+
4. **If any task's Owner Skill is `sf-devops` or `sf-data-migration`, resolve org aliases before
|
|
145
152
|
finalizing** — see [Org Alias Resolution](#org-alias-resolution) below. Skip this step
|
|
146
153
|
entirely if no task touches those two skills; don't ask about aliases a plan doesn't need.
|
|
147
|
-
|
|
154
|
+
5. Present the plan summary to the user and ask for explicit approval — options: **Approve**,
|
|
148
155
|
**Revise a section**, **Cancel**. Do not proceed on silence or an ambiguous reply.
|
|
149
|
-
|
|
156
|
+
6. Only after explicit approval: write the plan file to
|
|
150
157
|
`instructions/architecture/YYYYMMDD-<feature-slug>-plan.md`.
|
|
151
158
|
|
|
152
159
|
This is the hard gate. Nothing before this point touches the org or the repo's buildable code.
|
|
@@ -185,15 +192,22 @@ dispatched to parallel subagents; tasks that depend on each other still run in s
|
|
|
185
192
|
1. **Compute the ready set.** A task is ready when every task in its `Depends On` list is `D`.
|
|
186
193
|
2. **Split the ready set into parallel-safe groups.** Two ready tasks can run in the same batch
|
|
187
194
|
only if their `Touches` values don't overlap (no shared file/component). If a task's `Touches`
|
|
188
|
-
is unclear or ambiguous, treat it as not parallel-safe — run it alone.
|
|
195
|
+
is unclear or ambiguous, treat it as not parallel-safe — run it alone. **This applies
|
|
196
|
+
regardless of Owner Skill** — two tasks both owned by `sf-dev` (e.g. two independent LWC
|
|
197
|
+
components, or two unrelated Apex classes) are just as eligible for the same parallel batch as
|
|
198
|
+
two tasks owned by different skills, as long as their `Touches` don't overlap. Parallel
|
|
199
|
+
eligibility is decided by `Touches`, never by which skill owns the task.
|
|
189
200
|
3. **Dispatch the batch:**
|
|
190
201
|
- **Batch of 1** → run directly in the main thread, exactly as before (no subagent overhead
|
|
191
202
|
for solo work).
|
|
192
203
|
- **Batch of 2+** → dispatch each task to a separate subagent via the Task tool, in the same
|
|
193
|
-
turn.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
204
|
+
turn. This holds even when two or more tasks in the batch share the same Owner Skill (e.g.
|
|
205
|
+
two `sf-dev` tasks) — each still gets its own subagent, invoked independently; never route
|
|
206
|
+
two tasks through one subagent just because they'd trigger the same skill. Each subagent
|
|
207
|
+
prompt must state explicitly: the task description, the exact file(s) in `Touches`, and
|
|
208
|
+
"follow `sf-[owner skill]`'s conventions exactly as if that skill were invoked directly" —
|
|
209
|
+
never duplicate that skill's rules into the prompt, just name it and let it trigger in the
|
|
210
|
+
subagent's own context.
|
|
197
211
|
- **Assign each subagent in a batch a random name from the pool below, unique within that
|
|
198
212
|
batch** — used only as a tracking label in the orchestrator's own narration and Execution Log
|
|
199
213
|
("Dispatched to Sasha"), never written into code, commits, or file content.
|
|
@@ -313,9 +327,11 @@ Permission Set Group `[PSG Name]`
|
|
|
313
327
|
**Status codes:** `P` Pending · `IP` In Progress · `B` Blocked · `D` Done
|
|
314
328
|
|
|
315
329
|
**Touches** is what makes parallel dispatch safe: TASK-02, TASK-03, and TASK-04 all depend only on
|
|
316
|
-
TASK-01 and touch different things — they're a valid parallel batch
|
|
317
|
-
|
|
318
|
-
|
|
330
|
+
TASK-01 and touch different things — they're a valid parallel batch, dispatched as three separate
|
|
331
|
+
subagents. Note TASK-03 and TASK-04 share the same Owner Skill (`sf-dev`) but still run as two
|
|
332
|
+
independent subagents in that batch — parallel eligibility is decided by `Touches`, not by which
|
|
333
|
+
skill owns the task. TASK-05 depends on TASK-03 and must wait for it. TASK-06 depends on both
|
|
334
|
+
TASK-04 and TASK-05, so it waits for the slower of the two.
|
|
319
335
|
|
|
320
336
|
---
|
|
321
337
|
|
|
@@ -398,8 +414,14 @@ best if users need to review before submitting (D) Other — describe your own a
|
|
|
398
414
|
- ✕ Skipping the Permission Set Strategy decision because the feature "seems simple" — access
|
|
399
415
|
control is part of every feature that touches an object, field, Apex class, or LWC
|
|
400
416
|
- ✕ Assuming a new Permission Set is needed without first scanning for an existing one that fits
|
|
417
|
+
- ✕ Omitting a `sf-security-review` task from Task Breakdown when the plan touches a PII-bearing
|
|
418
|
+
field/object, adds a new integration/callout, or changes the sharing model
|
|
401
419
|
- ✕ Dispatching two tasks in parallel when their `Touches` overlap, or when either one's
|
|
402
420
|
`Touches` is too vague to be sure it doesn't
|
|
421
|
+
- ✕ Merging two or more same-Owner-Skill tasks into a single subagent because they'd trigger the
|
|
422
|
+
same skill — each task still gets its own subagent when their `Touches` don't overlap
|
|
423
|
+
- ✕ Treating parallel-safe dispatch as only available across *different* Owner Skills — two tasks
|
|
424
|
+
under the same skill (e.g. two `sf-dev` tasks) are equally eligible when `Touches` don't overlap
|
|
403
425
|
- ✕ Letting a subagent write directly to the plan file — only the orchestrator writes status/log
|
|
404
426
|
updates, and only after the batch reports back
|
|
405
427
|
- ✕ Reusing the same agent name for two subagents within the same batch
|
package/skills/sf-ba/SKILL.md
CHANGED
|
@@ -29,6 +29,14 @@ running `sf-architect` first, or right after — it consumes the PRD as input an
|
|
|
29
29
|
technical execution plan. `sf-ba` decides WHAT to build; `sf-architect` decides HOW and in what
|
|
30
30
|
order. Don't try to make the PRD carry both.
|
|
31
31
|
|
|
32
|
+
**Admin Spec and Dev Spec are for work that will NOT go through `sf-architect`** — e.g. handing
|
|
33
|
+
instructions to a human admin/dev directly, or documenting a declarative/code change after the
|
|
34
|
+
fact for the record. If the work is going to be executed via an `sf-architect` plan, skip writing
|
|
35
|
+
these specs: `sf-architect`'s Task Breakdown (with each task's Owner Skill and exact `Touches`)
|
|
36
|
+
already carries that execution detail, and a separately-written spec would just be a second copy
|
|
37
|
+
of the same decisions to keep in sync by hand. Write the PRD, hand it to `sf-architect`, and let
|
|
38
|
+
the plan carry the rest.
|
|
39
|
+
|
|
32
40
|
---
|
|
33
41
|
|
|
34
42
|
## Document Types
|
|
@@ -14,7 +14,10 @@ description: >
|
|
|
14
14
|
|
|
15
15
|
## Environment Context
|
|
16
16
|
|
|
17
|
-
- API Version: **
|
|
17
|
+
- API Version: **not hardcoded** — new files inherit whatever `sourceApiVersion` is set in the
|
|
18
|
+
project's `sfdx-project.json` when scaffolded via SF CLI. When touching an **existing**
|
|
19
|
+
class/component/metadata item, check its `apiVersion`: below **62.0** → bump to **67.0** as part
|
|
20
|
+
of the change; 62.0+ → leave as-is unless the task needs 67.0+ behavior specifically.
|
|
18
21
|
- Tooling: **SF CLI** (`sf data` commands) for dev/test volumes; Data Loader for production volumes
|
|
19
22
|
- Always migrate to **sandbox first**, verify, then production
|
|
20
23
|
- Single org: sandbox → production
|
package/skills/sf-dev/SKILL.md
CHANGED
|
@@ -13,7 +13,12 @@ description: >
|
|
|
13
13
|
|
|
14
14
|
## Environment Context
|
|
15
15
|
|
|
16
|
-
- API Version: **
|
|
16
|
+
- API Version: **not hardcoded** — new files inherit whatever `sourceApiVersion` is set in the
|
|
17
|
+
project's `sfdx-project.json` when scaffolded via SF CLI (see [File
|
|
18
|
+
Creation](#-file-creation--always-via-sf-cli-never-manual) below — this is one more reason to
|
|
19
|
+
always generate, never hand-create). When touching an **existing** class/component, check its
|
|
20
|
+
`apiVersion`: below **62.0** → bump to **67.0** as part of the change; 62.0+ → leave as-is unless
|
|
21
|
+
the task needs 67.0+ behavior specifically.
|
|
17
22
|
- Tooling: **SF CLI** (`sf` commands)
|
|
18
23
|
- Apex LSP: **offline** — never use compile-time SObject type references that require org metadata
|
|
19
24
|
- Version control: **GitHub** (branching: `main → staging → develop → feature/*`)
|
|
@@ -174,10 +179,11 @@ public static Result myMethod(String param) {
|
|
|
174
179
|
|
|
175
180
|
| Type | Convention | Example |
|
|
176
181
|
| ------------ | -------------------------- | ------------------------------------- |
|
|
177
|
-
| Apex Class | PascalCase
|
|
178
|
-
| Test Class | PascalCase + `Test` suffix
|
|
179
|
-
|
|
|
180
|
-
|
|
|
182
|
+
| Apex Class | PascalCase | `AccountHelper`, `OpportunityService` |
|
|
183
|
+
| Test Class | PascalCase + `Test` suffix | `AccountHelperTest` |
|
|
184
|
+
| Apex Trigger | `<Object>Trigger`, one per object | `AccountTrigger`, `OpportunityTrigger` |
|
|
185
|
+
| Custom Field | PascalCase\_\_c | `ProjectStatus__c` |
|
|
186
|
+
| Variable | camelCase | `accountList`, `oppMap` |
|
|
181
187
|
|
|
182
188
|
### ApexDoc (required on all public methods)
|
|
183
189
|
|
|
@@ -194,34 +200,17 @@ public static List<Result__c> processRecord(Id recordId) { ... }
|
|
|
194
200
|
|
|
195
201
|
### Test Class Pattern
|
|
196
202
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
static void makeData() {
|
|
202
|
-
// create test records once for all test methods
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
@isTest
|
|
206
|
-
static void testPositivePath() {
|
|
207
|
-
Test.startTest();
|
|
208
|
-
// call method
|
|
209
|
-
Test.stopTest();
|
|
210
|
-
// assert
|
|
211
|
-
}
|
|
203
|
+
Full test class structure, `TestDataFactory` usage, callout/platform-event mocking, and assertion
|
|
204
|
+
conventions are owned by `sf-testing` — follow that skill's pattern exactly rather than
|
|
205
|
+
improvising a competing one here. Two things worth remembering while writing the code being
|
|
206
|
+
tested:
|
|
212
207
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
} catch (AuraHandledException e) {
|
|
220
|
-
Assert.areEqual('Script-thrown exception', e.getMessage(), 'AuraHandledException message is masked in test context by design');
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
208
|
+
- `AuraHandledException.getMessage()` always returns `'Script-thrown exception'` in test context
|
|
209
|
+
(Salesforce by design) — the test asserting your exception path is expected to check this exact
|
|
210
|
+
string, not your real error message
|
|
211
|
+
- Structure the method so `Test.startTest()` / `Test.stopTest()` around the call actually resets
|
|
212
|
+
governor limits and forces async paths (Queueable, `@future`, Platform Events) to run — don't
|
|
213
|
+
write logic that fights that boundary
|
|
225
214
|
|
|
226
215
|
---
|
|
227
216
|
|
|
@@ -442,4 +431,6 @@ sf apex run --target-org <alias> --file scripts/apex/myScript.apex
|
|
|
442
431
|
- [ ] No `WITH SECURITY_ENFORCED` in SOQL — removed in API 67.0
|
|
443
432
|
- [ ] All `@AuraEnabled` methods wrapped in try-catch → `AuraHandledException`
|
|
444
433
|
- [ ] LWC uses SLDS 2 hooks (`var(--slds-g-*, fallback)`) — no hardcoded hex/px, no `.slds-*` overrides
|
|
434
|
+
- [ ] New/modified LWC has a Jest test file under `__tests__/` — see `sf-testing`'s LWC Jest
|
|
435
|
+
Testing section for the pattern; don't improvise a competing one
|
|
445
436
|
- [ ] Version badge present _if the project has adopted that convention_ (not required by default)
|
|
@@ -14,7 +14,10 @@ description: >
|
|
|
14
14
|
|
|
15
15
|
## Environment Context
|
|
16
16
|
|
|
17
|
-
- API Version: **
|
|
17
|
+
- API Version: **not hardcoded** — new files inherit whatever `sourceApiVersion` is set in the
|
|
18
|
+
project's `sfdx-project.json` when scaffolded via SF CLI. When touching an **existing**
|
|
19
|
+
class/component/metadata item, check its `apiVersion`: below **62.0** → bump to **67.0** as part
|
|
20
|
+
of the change; 62.0+ → leave as-is unless the task needs 67.0+ behavior specifically.
|
|
18
21
|
- Branching: `main → staging → develop → feature/*`
|
|
19
22
|
- Production org alias: **`{{PROD_ORG_ALIAS}}`**
|
|
20
23
|
- Sandbox/dev org alias: **`{{DEV_ORG_ALIAS}}`**
|
|
@@ -64,6 +67,8 @@ Never skip steps. Never deploy to `{{PROD_ORG_ALIAS}}` without a CR.
|
|
|
64
67
|
an LWC missing its `js-meta.xml` will fail to deploy or stay invisible in the org
|
|
65
68
|
- [ ] No `@wire` adapter inside a loop
|
|
66
69
|
- [ ] All public `@api` properties have JSDoc
|
|
70
|
+
- [ ] Jest test file exists under `__tests__/` covering at least happy path, empty state, and one
|
|
71
|
+
interaction/error path (see `sf-testing`'s LWC Jest Testing section) — no LWC ships untested
|
|
67
72
|
|
|
68
73
|
> **Out of scope for this checklist:** CSS/styling conventions and design system compliance.
|
|
69
74
|
> That belongs to whichever skill built the component — `sf-dev` (standard SLDS 2 styling) or
|
|
@@ -161,6 +161,13 @@ using the project's actual stack, referencing the anchor by name.
|
|
|
161
161
|
- **Be opinionated** — don't just list options, recommend the best one and explain why.
|
|
162
162
|
- **Think Salesforce seasons** — flag if an idea aligns with an upcoming Salesforce release
|
|
163
163
|
feature that could reduce build effort.
|
|
164
|
+
- **Hand off, don't build.** This skill's job stops at generating and prioritizing ideas — it
|
|
165
|
+
never writes code, metadata, or a plan file itself, and its output is intentionally **not
|
|
166
|
+
persisted** to a file. For a **Big Bet** or any idea that needs a real architecture decision
|
|
167
|
+
(data model, Flow vs Apex, integration pattern, security model), hand off to `sf-architect` — its
|
|
168
|
+
plan file is where the idea becomes durable. For an idea that should become a formally scoped
|
|
169
|
+
feature with requirements and acceptance criteria first, hand off to `sf-ba` to write a PRD.
|
|
170
|
+
Don't try to make this skill's output do either job.
|
|
164
171
|
|
|
165
172
|
---
|
|
166
173
|
|
|
@@ -175,3 +182,4 @@ using the project's actual stack, referencing the anchor by name.
|
|
|
175
182
|
| "apa next step dari PRD [name]?" | Anchor = that PRD (confirm it's closed/approved). Run Extension Questions |
|
|
176
183
|
| "prioritas enhancement mana yang paling worth it?" | Requires a prior anchored ideation output in this conversation to rank — if none exists, ask which anchor to run first |
|
|
177
184
|
| "ada yang bisa diimprove dari sisi performance?" (no name given) | No anchor — scan project, ask which component to focus the Performance lens on |
|
|
185
|
+
| "let's build idea #[N]" / "jalankan ide nomor [N]" | Hand off — don't execute here. Route to `sf-architect` if it needs a technical decision, or `sf-ba` if it needs a PRD first |
|
|
@@ -14,7 +14,12 @@ description: >
|
|
|
14
14
|
|
|
15
15
|
## Environment Context
|
|
16
16
|
|
|
17
|
-
- API Version: **
|
|
17
|
+
- API Version: **not hardcoded** — new files inherit whatever `sourceApiVersion` is set in the
|
|
18
|
+
project's `sfdx-project.json` when scaffolded via SF CLI. When reviewing an **existing**
|
|
19
|
+
class/component, check its `apiVersion`: below **62.0** → recommend bumping to **67.0** as part
|
|
20
|
+
of the change; 62.0+ → confirm whether the [API 67.0+ Default Security
|
|
21
|
+
Model](#-api-670-default-security-model-summer-26) applies before deciding whether a missing
|
|
22
|
+
manual check is a real gap.
|
|
18
23
|
- Apex LSP: **offline** — use `Schema.getGlobalDescribe()` pattern (not compile-time SObject reference)
|
|
19
24
|
- All security decisions must be documented in the relevant CR before production deploy
|
|
20
25
|
|
|
@@ -370,6 +375,56 @@ When PII-bearing objects/fields are part of the change being reviewed, add to th
|
|
|
370
375
|
|
|
371
376
|
---
|
|
372
377
|
|
|
378
|
+
## Guest User & Experience Cloud Access
|
|
379
|
+
|
|
380
|
+
Guest User (unauthenticated Experience Cloud / site visitor) access is one of the highest-risk
|
|
381
|
+
misconfigurations in Salesforce — a single overly-broad Guest User sharing rule or OWD setting can
|
|
382
|
+
expose record data to anyone on the internet, no login required.
|
|
383
|
+
|
|
384
|
+
### Review Checklist
|
|
385
|
+
|
|
386
|
+
- [ ] Guest User profile has **no** object permissions beyond what the public-facing page
|
|
387
|
+
genuinely needs to render — verify against the actual Experience Cloud page, not assumption
|
|
388
|
+
- [ ] No Guest User sharing rule or "Public Read/Write" OWD grants access to a PII-bearing object
|
|
389
|
+
(see [PII & Data Privacy](#pii--data-privacy)) unless explicitly required and documented
|
|
390
|
+
- [ ] "Secure Guest User Record Access" org setting is enabled (Setup → Sharing Settings) — this
|
|
391
|
+
caps Guest User access to explicit sharing rules rather than the older default-access model
|
|
392
|
+
- [ ] Guest User cannot create/edit/delete records unless the page's actual function requires it
|
|
393
|
+
(e.g. a public intake form) — CRUD checked per object, not granted broadly
|
|
394
|
+
- [ ] No Apex class exposed to Guest User (`without sharing` + `global`/`@AuraEnabled` reachable
|
|
395
|
+
from a public page) returns fields beyond what the page displays — treat any Guest-reachable
|
|
396
|
+
Apex method as a public API surface, not an internal helper
|
|
397
|
+
- [ ] Community/site URLs referencing record IDs are validated server-side, not trusted as proof
|
|
398
|
+
of access — a guessable/enumerable ID in a public URL is not an access control
|
|
399
|
+
|
|
400
|
+
Treat any change that touches a Guest User profile, an Experience Cloud page, or a
|
|
401
|
+
Guest-reachable Apex method as requiring this checklist, in addition to the standard Apex/LWC
|
|
402
|
+
review above.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Connected App & External Client App Review
|
|
407
|
+
|
|
408
|
+
Relevant whenever a change introduces or modifies a Connected App, External Client App, or any
|
|
409
|
+
OAuth-based integration.
|
|
410
|
+
|
|
411
|
+
### Review Checklist
|
|
412
|
+
|
|
413
|
+
- [ ] OAuth scopes requested are the **minimum** needed for the integration's actual purpose —
|
|
414
|
+
flag broad scopes (`full`, `api` when something narrower would do) without a documented
|
|
415
|
+
reason
|
|
416
|
+
- [ ] Refresh token policy is explicit (expire on inactivity / never expire) — "never expire" needs
|
|
417
|
+
a documented justification, not a default
|
|
418
|
+
- [ ] IP restriction is "Enforce IP restrictions" + Trusted IP Ranges unless the integration
|
|
419
|
+
genuinely runs from unpredictable IPs
|
|
420
|
+
- [ ] Callback URL(s) are exact, not wildcarded, unless the integration pattern requires it
|
|
421
|
+
- [ ] The integration/running user for the app has the minimum Permission Set needed — never
|
|
422
|
+
"System Administrator" as a convenience default
|
|
423
|
+
- [ ] Client secret / certificate lives in a secrets manager or Named Credential — never committed
|
|
424
|
+
to source control or hardcoded in an integration script
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
373
428
|
## Security Review Checklist
|
|
374
429
|
|
|
375
430
|
### Apex
|
|
@@ -3,11 +3,12 @@ name: sf-testing
|
|
|
3
3
|
description: >
|
|
4
4
|
Use this skill for ANY Salesforce test-related task: writing Apex test classes, building
|
|
5
5
|
TestDataFactory, mocking HTTP callouts, mocking platform events, writing test assertions,
|
|
6
|
-
generating test coverage reports, debugging test failures,
|
|
7
|
-
Trigger when the user asks to "write a test class", "fix test
|
|
8
|
-
"write a TestDataFactory", "test fails in org but passes locally",
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
generating test coverage reports, debugging test failures, designing test strategies, and
|
|
7
|
+
writing LWC Jest unit tests. Trigger when the user asks to "write a test class", "fix test
|
|
8
|
+
coverage", "mock a callout", "write a TestDataFactory", "test fails in org but passes locally",
|
|
9
|
+
"assert this method", "write a jest test", "test this component", or anything about Apex unit
|
|
10
|
+
testing, LWC unit testing, test data, or code coverage. Also use when coverage drops below 85%
|
|
11
|
+
or when AuraHandledException assertions are involved.
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
# Salesforce Testing Skill
|
|
@@ -15,7 +16,10 @@ description: >
|
|
|
15
16
|
## Environment Context
|
|
16
17
|
|
|
17
18
|
- Minimum coverage: **85%** (aim for 90%+)
|
|
18
|
-
- API Version: **
|
|
19
|
+
- API Version: **not hardcoded** — new files inherit whatever `sourceApiVersion` is set in the
|
|
20
|
+
project's `sfdx-project.json` when scaffolded via SF CLI. When touching an **existing** test
|
|
21
|
+
class, check its `apiVersion`: below **62.0** → bump to **67.0** as part of the change; 62.0+ →
|
|
22
|
+
leave as-is unless the task needs 67.0+ behavior specifically.
|
|
19
23
|
- Assertions: prefer the **`Assert` class** (`Assert.areEqual`, `Assert.isTrue`, etc. — API 56.0+)
|
|
20
24
|
over legacy `System.assertEquals`/`System.assert` in new tests
|
|
21
25
|
- `AuraHandledException.getMessage()` in test context **always** returns `'Script-thrown exception'` — this is Salesforce by design, not a bug
|
|
@@ -372,6 +376,112 @@ sf apex run test \
|
|
|
372
376
|
|
|
373
377
|
---
|
|
374
378
|
|
|
379
|
+
## LWC Jest Testing
|
|
380
|
+
|
|
381
|
+
Every new or modified LWC component needs a Jest test file — same discipline as Apex coverage,
|
|
382
|
+
just not gated by a governor limit. This is separate from `sf-devops`'s LWC review checklist
|
|
383
|
+
(structural: naming, wire usage, JSDoc) and from `shaiden`'s design critique (visual/SLDS
|
|
384
|
+
compliance) — this section owns whether the component's *behavior* is actually tested.
|
|
385
|
+
|
|
386
|
+
### File Location
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
force-app/main/default/lwc/myComponent/
|
|
390
|
+
├── myComponent.html
|
|
391
|
+
├── myComponent.js
|
|
392
|
+
├── myComponent.css
|
|
393
|
+
├── myComponent.js-meta.xml
|
|
394
|
+
└── __tests__/
|
|
395
|
+
└── myComponent.test.js
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Uses `@salesforce/sfdx-lwc-jest` — run via `npm run test:unit` (or the project's configured Jest
|
|
399
|
+
script), not SF CLI.
|
|
400
|
+
|
|
401
|
+
### Test Structure — Standard Pattern
|
|
402
|
+
|
|
403
|
+
```javascript
|
|
404
|
+
import { createElement } from 'lwc';
|
|
405
|
+
import MyComponent from 'c/myComponent';
|
|
406
|
+
import getRelatedItems from '@salesforce/apex/MyController.getRelatedItems';
|
|
407
|
+
|
|
408
|
+
// Mock the wired Apex method — never let a Jest test hit a real Apex method
|
|
409
|
+
jest.mock(
|
|
410
|
+
'@salesforce/apex/MyController.getRelatedItems',
|
|
411
|
+
() => ({ default: jest.fn() }),
|
|
412
|
+
{ virtual: true }
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
describe('c-my-component', () => {
|
|
416
|
+
afterEach(() => {
|
|
417
|
+
while (document.body.firstChild) {
|
|
418
|
+
document.body.removeChild(document.body.firstChild);
|
|
419
|
+
}
|
|
420
|
+
jest.clearAllMocks();
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// ─── Happy Path ────────────────────────────────────────────────────────
|
|
424
|
+
it('renders items after the wire resolves', async () => {
|
|
425
|
+
const element = createElement('c-my-component', { is: MyComponent });
|
|
426
|
+
document.body.appendChild(element);
|
|
427
|
+
|
|
428
|
+
getRelatedItems.emit([{ Id: '001', Name: 'Test Item' }]);
|
|
429
|
+
await Promise.resolve();
|
|
430
|
+
|
|
431
|
+
const items = element.shadowRoot.querySelectorAll('.my-component__item');
|
|
432
|
+
expect(items.length).toBe(1);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// ─── Empty State ───────────────────────────────────────────────────────
|
|
436
|
+
it('shows the empty state when there is no data', async () => {
|
|
437
|
+
const element = createElement('c-my-component', { is: MyComponent });
|
|
438
|
+
document.body.appendChild(element);
|
|
439
|
+
|
|
440
|
+
getRelatedItems.emit([]);
|
|
441
|
+
await Promise.resolve();
|
|
442
|
+
|
|
443
|
+
expect(element.shadowRoot.querySelector('.my-component__empty')).not.toBeNull();
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// ─── Interaction / Error Path ──────────────────────────────────────────
|
|
447
|
+
it('fires the itemselected event on click', () => {
|
|
448
|
+
const element = createElement('c-my-component', { is: MyComponent });
|
|
449
|
+
document.body.appendChild(element);
|
|
450
|
+
|
|
451
|
+
const handler = jest.fn();
|
|
452
|
+
element.addEventListener('itemselected', handler);
|
|
453
|
+
element.shadowRoot.querySelector('button')?.click();
|
|
454
|
+
|
|
455
|
+
expect(handler).toHaveBeenCalled();
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Rules
|
|
461
|
+
|
|
462
|
+
- **Minimum 3 scenarios** per component — same bar as Apex: happy/rendered path, empty state, one
|
|
463
|
+
interaction or error path
|
|
464
|
+
- Mock every `@wire`/imperative Apex import — a Jest test that reaches a real Apex method is broken
|
|
465
|
+
- Query only through `element.shadowRoot` — never reach into the component's internals directly
|
|
466
|
+
- Assert on rendered output (DOM nodes, text, fired events), not on private component state
|
|
467
|
+
- Run `npm run test:unit` before every PR — same gate as Apex coverage, checked in `sf-devops`'s
|
|
468
|
+
review
|
|
469
|
+
|
|
470
|
+
### Run Commands
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
# Run all LWC Jest tests
|
|
474
|
+
npm run test:unit
|
|
475
|
+
|
|
476
|
+
# Run a single component's tests
|
|
477
|
+
npx sfdx-lwc-jest -- myComponent
|
|
478
|
+
|
|
479
|
+
# Watch mode during development
|
|
480
|
+
npx sfdx-lwc-jest --watch
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
375
485
|
## Common Test Failures & Fixes
|
|
376
486
|
|
|
377
487
|
| Error | Cause | Fix |
|