acek-skills 1.0.3 → 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 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`). Plans are saved to `instructions/architecture/` and are resumable across
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, and a dedicated PII & Data Privacy section — field classification,
234
- sandbox data masking, and UU PDP-aware review practices.
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, and debugging org-only test failures. Enforces the 85% coverage
243
- floor and `AuraHandledException` assertion conventions.
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.2.0 install`).
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?**
@@ -0,0 +1,165 @@
1
+ ---
2
+ description: Bootstrap or refresh the project's architecture.md baseline — org aliases, tech stack, data model, integrations, and Permission Set inventory that sf-architect reads before planning any feature.
3
+ ---
4
+
5
+ # /sf-init — Architecture Baseline Bootstrap
6
+
7
+ This command creates (or refreshes) `architecture.md` at the project root. It is a one-time setup
8
+ step per project — `sf-architect` reads this file on every plan afterward instead of re-scanning
9
+ the whole project from scratch each time.
10
+
11
+ **This command follows `sf-architect`'s conventions exactly:** scan before asking, never fabricate
12
+ a name that wasn't found, every clarification is a choice with an "Other" option — never a bare
13
+ open text prompt.
14
+
15
+ ---
16
+
17
+ ## Step 1 — Check for an existing baseline
18
+
19
+ Look for `architecture.md` at the project root.
20
+
21
+ - **Not found** → run the [Bootstrap Flow](#bootstrap-flow) below.
22
+ - **Found** → run the [Refresh Flow](#refresh-flow) below. Do not blindly overwrite or blindly
23
+ merge — always diff and confirm first.
24
+
25
+ ---
26
+
27
+ ## Bootstrap Flow
28
+
29
+ ### 1. Scan the project
30
+
31
+ Before asking anything, gather what can be found directly:
32
+
33
+ - **Data model** — custom objects under `force-app/main/default/objects/`, their purpose (from
34
+ field/label context, not guessed)
35
+ - **Components** — LWC folders and Apex classes/triggers present
36
+ - **LWC styling convention in use** — check existing component CSS: custom `:host` brand-token
37
+ block (`sf-dev` standard) vs `--slds-g-*` global hooks (`shaiden` convention) vs none yet
38
+ - **Automation layer balance** — rough split of Flow-triggered vs Apex-triggered automation found
39
+ - **Integrations** — Named Credentials, external callouts referenced in Apex
40
+ - **Permission Sets / Permission Set Groups** — existing inventory
41
+ - **API version** — from `sfdx-project.json` or existing metadata
42
+ - **Org aliases** — run `sf org list` to see what's actually configured locally
43
+
44
+ ### 2. Ask only what can't be scanned
45
+
46
+ Batch these into one round-trip, always as choices with an "Other" option:
47
+
48
+ - Which scanned org is **Production** vs **Sandbox/Dev**? (options = orgs found by `sf org list`)
49
+ - Is there an established **data classification / retention / masking policy** already in place?
50
+ (options: "Yes — describe it", "No, not yet documented", "Partially — describe what exists")
51
+ - Any **org-specific constraints** that always apply (naming conventions, governance rules,
52
+ deployment windows)? (options: "Yes — describe", "None beyond standard practice")
53
+
54
+ If `sf org list` returns nothing or the CLI isn't available, ask for aliases directly via the
55
+ choice format instead — never require typing from memory when a scan could have answered it.
56
+
57
+ ### 3. Write architecture.md
58
+
59
+ Compile everything into `architecture.md` at the project root using the
60
+ [Baseline Template](#baseline-template) below.
61
+
62
+ ### 4. Confirm
63
+
64
+ Tell the user the file was created and that `sf-architect` will now read it automatically on
65
+ future plans instead of re-scanning the whole project each time.
66
+
67
+ ---
68
+
69
+ ## Refresh Flow
70
+
71
+ ### 1. Re-scan the project
72
+
73
+ Same scan as [Bootstrap Step 1](#1-scan-the-project), fresh.
74
+
75
+ ### 2. Diff against the existing file
76
+
77
+ Compare the fresh scan to what's currently in `architecture.md`. Categorize:
78
+
79
+ - **Added** — new objects/components/Permission Sets/integrations found
80
+ - **Removed** — previously listed items no longer found
81
+ - **Changed** — e.g. org alias no longer resolves, API version bumped
82
+
83
+ Never touch manually-maintained sections (**Data Privacy Baseline** notes, **Org-Specific
84
+ Constraints**) as part of an automatic refresh — those are only updated if the user explicitly
85
+ asks to revise them.
86
+
87
+ ### 3. Present the diff and confirm before writing
88
+
89
+ Show what changed as a choice question:
90
+
91
+ - **Update all** — apply every detected change to the auto-scanned sections
92
+ - **Review each change** — walk through each addition/removal one at a time for confirmation
93
+ - **Cancel** — leave `architecture.md` untouched
94
+
95
+ Do not write anything until the user picks one.
96
+
97
+ ### 4. Write and log
98
+
99
+ Apply the confirmed changes, update the `Last Scanned` timestamp, and append one line to the
100
+ Changelog section (e.g. `2026-07-13 — Refreshed: +2 objects, org alias updated`).
101
+
102
+ ---
103
+
104
+ ## Baseline Template
105
+
106
+ ```markdown
107
+ # Architecture Baseline — [Project Name]
108
+
109
+ _Generated by /sf-init — YYYY-MM-DD_
110
+ _Last Scanned: YYYY-MM-DD HH:MM_
111
+
112
+ ## Org Aliases
113
+
114
+ - Production: `[alias]`
115
+ - Sandbox / Dev: `[alias]`
116
+
117
+ ## Tech Stack Baseline
118
+
119
+ - API Version: `[version]`
120
+ - LWC Styling Convention: [sf-dev standard SLDS 2 / shaiden / mixed — note which components use which]
121
+ - Primary Automation Layer: [Flow-first / Apex-heavy / mixed]
122
+
123
+ ## Data Model Overview
124
+
125
+ | Object | Type | Purpose |
126
+ | ------ | ----------------- | -------- |
127
+ | [Name] | Standard / Custom | [1 line] |
128
+
129
+ ## Existing Integrations
130
+
131
+ | System | Type | Notes |
132
+ | ------ | ------------------------------------------------ | -------- |
133
+ | [Name] | Named Credential / REST callout / Platform Event | [1 line] |
134
+
135
+ ## Permission Set / PSG Inventory
136
+
137
+ | Name | Type | Purpose |
138
+ | ------ | ------------------------------------- | -------- |
139
+ | [Name] | Permission Set / Permission Set Group | [1 line] |
140
+
141
+ ## Data Privacy Baseline
142
+
143
+ [Established classification/retention/masking policy, or "Not yet documented"]
144
+
145
+ ## Org-Specific Constraints
146
+
147
+ [Naming conventions, governance rules, non-negotiables — manually maintained, not auto-refreshed
148
+ by future /sf-init runs unless explicitly requested]
149
+
150
+ ## Changelog
151
+
152
+ - YYYY-MM-DD — Initial bootstrap
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Relationship to sf-architect
158
+
159
+ `sf-architect`'s Discovery phase checks for this file first and reads it instead of re-scanning
160
+ the whole project on every plan. Only feature-specific touchpoints not already covered by the
161
+ baseline get scanned fresh. If this file doesn't exist yet when `sf-architect` runs, it falls back
162
+ to its own full scan and may suggest running `/sf-init` first for future efficiency.
163
+
164
+ Re-run `/sf-init` periodically or after major structural changes (new integration, object model
165
+ overhaul, org alias change) — it's a refresh, not a one-time-only command.
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "acek-skills",
3
- "version": "1.0.3",
3
+ "version": "1.3.0",
4
4
  "description": "Installable Claude Code skills for Salesforce development (admin, dev, BA, testing, devops, security review, data migration, ideation)",
5
5
  "bin": {
6
6
  "acek-skills": "bin/cli.js"
7
7
  },
8
8
  "files": [
9
9
  "bin/",
10
- "skills/"
10
+ "skills/",
11
+ "commands"
11
12
  ],
12
13
  "keywords": [
13
14
  "claude",
@@ -14,7 +14,10 @@ description: >
14
14
 
15
15
  ## Environment Context
16
16
 
17
- - API Version: **67.0** (Summer '26)
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
@@ -72,7 +72,7 @@ unrelated features into one plan.
72
72
  ## Workflow
73
73
 
74
74
  ```
75
- DISCOVERY (grill me) → DECISIONS → PLAN (gate) → EXECUTE task-by-task → RESUME (new session)
75
+ DISCOVERY (grill me) → DECISIONS → PLAN (gate) → EXECUTE in batches (parallel where safe) → RESUME (new session)
76
76
  ```
77
77
 
78
78
  ### Phase 1 — Discovery ("Grill Me")
@@ -138,13 +138,22 @@ Nothing is built yet. This phase only produces decisions with rationale.
138
138
 
139
139
  1. Compile Discovery + Decisions into the full plan file using the [Plan Template](#plan-template).
140
140
  2. Break the work into tasks. Every task gets an ID, a description, an **Owner Skill**, a
141
- dependency list, and status `P` (Pending).
142
- 3. **If any task's Owner Skill is `sf-devops` or `sf-data-migration`, resolve org aliases before
141
+ **Touches** list (exact file(s)/component(s) it will create or modify), a dependency list, and
142
+ status `P` (Pending). `Touches` is what Phase 4 uses to decide which tasks are safe to run in
143
+ parallel — be specific and accurate here, not vague.
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
143
152
  finalizing** — see [Org Alias Resolution](#org-alias-resolution) below. Skip this step
144
153
  entirely if no task touches those two skills; don't ask about aliases a plan doesn't need.
145
- 4. Present the plan summary to the user and ask for explicit approval — options: **Approve**,
154
+ 5. Present the plan summary to the user and ask for explicit approval — options: **Approve**,
146
155
  **Revise a section**, **Cancel**. Do not proceed on silence or an ambiguous reply.
147
- 5. Only after explicit approval: write the plan file to
156
+ 6. Only after explicit approval: write the plan file to
148
157
  `instructions/architecture/YYYYMMDD-<feature-slug>-plan.md`.
149
158
 
150
159
  This is the hard gate. Nothing before this point touches the org or the repo's buildable code.
@@ -177,18 +186,49 @@ commands.
177
186
 
178
187
  ### Phase 4 — Execute
179
188
 
180
- Work through tasks in dependency order, one at a time:
181
-
182
- 1. Announce which task is starting and which Owner Skill's conventions apply.
183
- 2. Set that task's status to `IP` (In Progress) in the plan file.
184
- 3. Do the work following that owner skill's rules exactly as if that skill were invoked directly.
185
- 4. On completion, set status to `D` (Done) and append **one line** to the Execution Log — no
186
- narrative, just what changed and where.
187
- 5. If a task cannot proceed (missing decision, blocked dependency, discovered conflict), set
188
- status to `B` (Blocked), write a one-line reason, and surface it to the user immediately rather
189
- than guessing.
190
- 6. Move to the next unblocked task. If all remaining tasks are blocked, stop and ask the user how
191
- to proceed — using the choice format.
189
+ Tasks run in **batches**, not strictly one at a time. Within a batch, independent tasks are
190
+ dispatched to parallel subagents; tasks that depend on each other still run in sequence.
191
+
192
+ 1. **Compute the ready set.** A task is ready when every task in its `Depends On` list is `D`.
193
+ 2. **Split the ready set into parallel-safe groups.** Two ready tasks can run in the same batch
194
+ only if their `Touches` values don't overlap (no shared file/component). If a task's `Touches`
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.
200
+ 3. **Dispatch the batch:**
201
+ - **Batch of 1** → run directly in the main thread, exactly as before (no subagent overhead
202
+ for solo work).
203
+ - **Batch of 2+** → dispatch each task to a separate subagent via the Task tool, in the same
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.
211
+ - **Assign each subagent in a batch a random name from the pool below, unique within that
212
+ batch** — used only as a tracking label in the orchestrator's own narration and Execution Log
213
+ ("Dispatched to Sasha"), never written into code, commits, or file content.
214
+ 4. **Only the orchestrator (main thread) writes to the plan file.** Subagents report their result
215
+ back; they never edit `instructions/architecture/*.md` directly. Once every subagent in the
216
+ batch has reported back, the orchestrator updates each task's status serially — this is what
217
+ prevents concurrent writes to the same plan file.
218
+ 5. **Per task, on completion:** set status to `D` (Done) and append **one line** to the Execution
219
+ Log — no narrative, just what changed, where, and (if dispatched in a batch) which agent name
220
+ handled it.
221
+ 6. **Per task, if it can't proceed** (missing decision, blocked dependency, discovered conflict):
222
+ set status to `B` (Blocked), write a one-line reason, and surface it to the user — a Blocked
223
+ task in a batch does not stop the other tasks in that same batch from completing.
224
+ 7. **Recompute and repeat.** After a batch finishes, recompute the ready set (newly-unblocked
225
+ tasks may now qualify) and dispatch the next batch. Continue until every task is `D`, or every
226
+ remaining task is `B` — in which case stop and ask the user how to proceed via the choice
227
+ format.
228
+
229
+ **Subagent name pool** (pick unique names within a batch; reuse across different batches is fine):
230
+ Maya, Sasha, Nadia, Kirana, Alya, Cinta, Bunga, Dewi, Farah, Gita, Intan, Laras, Mira, Nita,
231
+ Putri, Rani, Sari, Tantri, Vina, Wulan
192
232
 
193
233
  ### Phase 5 — Resume
194
234
 
@@ -201,8 +241,12 @@ direct reference to a plan file.
201
241
  feature name and last-updated date as the option label).
202
242
  4. Read only that plan file. The **Resume Briefing** section is sufficient to continue — if it
203
243
  isn't, that's a defect in how the plan was written, not a reason to re-derive context elsewhere.
204
- 5. Find the first task that is not `D`, confirm with the user in one line ("Resuming at TASK-04:
205
- [description], owner sf-testingproceeding"), and continue Phase 4.
244
+ 5. Find every task that is not `D`. **Any task still marked `IP` from a previous session means
245
+ its batch was interrupted mid-flight**don't assume it actually finished or actually failed;
246
+ treat it as `P` again and re-verify its `Touches` before re-dispatching (check whether the file
247
+ actually reflects the change described in the task, since the interruption could have happened
248
+ before or after the underlying work landed). Recompute the ready/parallel-safe batch from
249
+ there and continue Phase 4 normally.
206
250
 
207
251
  ---
208
252
 
@@ -271,24 +315,35 @@ Permission Set Group `[PSG Name]`
271
315
 
272
316
  ## Task Breakdown
273
317
 
274
- | ID | Description | Owner Skill | Depends On | Status |
275
- | ------- | --------------------------------------------------------------------------- | ----------- | ---------- | ------ |
276
- | TASK-01 | [what] | sf-admin | — | D |
277
- | TASK-02 | Grant access via `[PS/PSG name from Decision 3]` — extend/create as decided | sf-admin | TASK-01 | P |
278
- | TASK-03 | [what] | sf-dev | TASK-01 | IP |
279
- | TASK-04 | [what] | sf-testing | TASK-03 | P |
280
- | TASK-05 | [what] | sf-devops | TASK-04 | P |
318
+ | ID | Description | Owner Skill | Touches | Depends On | Status |
319
+ | ------- | --------------------------------------------------------------------------- | ----------- | --------------------------- | ---------------- | ------ |
320
+ | TASK-01 | [what] | sf-admin | `Project__c` (object) | | D |
321
+ | TASK-02 | Grant access via `[PS/PSG name from Decision 3]` — extend/create as decided | sf-admin | `[PS/PSG name]` | TASK-01 | P |
322
+ | TASK-03 | [what] | sf-dev | `ProjectController.cls` | TASK-01 | P |
323
+ | TASK-04 | [what] | sf-dev | `projectCard` (LWC) | TASK-01 | P |
324
+ | TASK-05 | [what] | sf-testing | `ProjectControllerTest.cls` | TASK-03 | P |
325
+ | TASK-06 | [what] | sf-devops | manifest + deploy | TASK-04, TASK-05 | P |
281
326
 
282
327
  **Status codes:** `P` Pending · `IP` In Progress · `B` Blocked · `D` Done
283
328
 
329
+ **Touches** is what makes parallel dispatch safe: TASK-02, TASK-03, and TASK-04 all depend only on
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.
335
+
284
336
  ---
285
337
 
286
338
  ## Execution Log
287
339
 
288
- _One line per completed or blocked task. No narrative._
340
+ _One line per completed or blocked task. No narrative. Include the agent name if the task was
341
+ dispatched as part of a parallel batch — omit it for solo/main-thread tasks._
289
342
 
290
343
  - YYYY-MM-DD HH:MM — TASK-01 done — Created `Project__c` fields per Decision 1
291
- - YYYY-MM-DD HH:MM — TASK-02 blocked needs Decision on callout timeout, asked user
344
+ - YYYY-MM-DD HH:MM — TASK-02 done (agent: Nadia) Extended `Project Manager Access` PS
345
+ - YYYY-MM-DD HH:MM — TASK-03 done (agent: Sasha) — Added `getProjects()` to `ProjectController.cls`
346
+ - YYYY-MM-DD HH:MM — TASK-04 blocked (agent: Kirana) — needs Decision on empty-state copy, asked user
292
347
  ```
293
348
 
294
349
  ---
@@ -339,6 +394,9 @@ best if users need to review before submitting (D) Other — describe your own a
339
394
  9. **Read `architecture.md` instead of re-scanning the whole project.** If the project baseline
340
395
  exists (from `/sf-init`), it already has the tech stack, data model, Permission Set inventory,
341
396
  and org aliases — reuse it. Only scan for what's specific to the current feature.
397
+ 10. **Don't parallelize trivial batches.** A batch of 2+ is only worth subagent overhead when the
398
+ tasks are substantial (real file edits, not one-liners). If a "parallel-safe" batch is tiny,
399
+ running it directly in the main thread is often cheaper than the dispatch overhead.
342
400
 
343
401
  ---
344
402
 
@@ -356,5 +414,18 @@ best if users need to review before submitting (D) Other — describe your own a
356
414
  - ✕ Skipping the Permission Set Strategy decision because the feature "seems simple" — access
357
415
  control is part of every feature that touches an object, field, Apex class, or LWC
358
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
419
+ - ✕ Dispatching two tasks in parallel when their `Touches` overlap, or when either one's
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
425
+ - ✕ Letting a subagent write directly to the plan file — only the orchestrator writes status/log
426
+ updates, and only after the batch reports back
427
+ - ✕ Reusing the same agent name for two subagents within the same batch
428
+ - ✕ Treating an `IP` task found on resume as already finished — it means the batch was
429
+ interrupted, not that the work landed; re-verify before re-dispatching
359
430
  - ✕ Asking the user to type an org alias from memory when `sf org list` can just be run
360
431
  - ✕ Resolving org aliases for a plan that has no `sf-devops`/`sf-data-migration` task at all
@@ -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: **67.0** (Summer '26)
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
@@ -13,7 +13,12 @@ description: >
13
13
 
14
14
  ## Environment Context
15
15
 
16
- - API Version: **67.0** (Summer '26)
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 | `AccountHelper`, `OpportunityTrigger` |
178
- | Test Class | PascalCase + `Test` suffix | `AccountHelperTest` |
179
- | Custom Field | PascalCase\_\_c | `ProjectStatus__c` |
180
- | Variable | camelCase | `accountList`, `oppMap` |
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
- ```apex
198
- @isTest
199
- private class AccountHelperTest {
200
- @TestSetup
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
- @isTest
214
- static void testNegativePath_insufficientPermission() {
215
- // test exception path
216
- try {
217
- AccountHelper.myMethod(null);
218
- Assert.fail('Expected exception not thrown');
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: **67.0** (Summer '26)
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: **67.0** (Summer '26)
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, and designing test strategies.
7
- Trigger when the user asks to "write a test class", "fix test coverage", "mock a callout",
8
- "write a TestDataFactory", "test fails in org but passes locally", "assert this method",
9
- or anything about Apex unit testing, test data, or code coverage. Also use when coverage
10
- drops below 85% or when AuraHandledException assertions are involved.
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: **67.0** (Summer '26)
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 |