@salesforce/afv-skills 1.45.0 → 1.46.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/package.json +1 -1
- package/skills/automation-flow-generate/SKILL.md +11 -5
- package/skills/dx-code-analyzer-configure/scripts/validate-config.sh +14 -10
- package/skills/dx-code-analyzer-run/scripts/apply-fixes.js +45 -4
- package/skills/dx-code-analyzer-run/scripts/describe-rule.js +52 -32
- package/skills/platform-apex-logs-debug/SKILL.md +7 -7
- package/skills/platform-custom-application-generate/SKILL.md +4 -4
- package/skills/platform-custom-object-generate/SKILL.md +7 -7
- package/skills/platform-custom-tab-generate/SKILL.md +1 -1
- package/skills/platform-flexipage-generate/SKILL.md +4 -0
- package/skills/platform-list-view-generate/SKILL.md +1 -0
- package/skills/platform-soql-query/SKILL.md +8 -8
- package/skills/platform-value-set-generate/SKILL.md +2 -2
package/package.json
CHANGED
|
@@ -78,11 +78,10 @@ Generates flow metadata element by element. This step is **mandatory** and must
|
|
|
78
78
|
- **result** (STRING): Result of the flow element generation. Contains the final flow metadata **only when `isComplete` is `true`**.
|
|
79
79
|
|
|
80
80
|
**MANDATORY: Loop until complete. NEVER pause or ask the user to confirm continuation.**
|
|
81
|
-
- A flow can have **any number of elements
|
|
81
|
+
- A flow can have **any number of elements**. Each call generates one element at a time, so you may need **many** iterations.
|
|
82
82
|
- Call `flowElementGeneration` with the `operationId` from Step 2 and `requestSource` (use `"A4V"` for XML output, empty string or other value for JSON).
|
|
83
83
|
- Check the `isComplete` output and the `result` field after each call.
|
|
84
|
-
- If `isComplete` is `false` **and no errors are returned**, you **MUST** call `flowElementGeneration` again with the **same `operationId`** from Step 2. **Do NOT ask the user if they want to continue. Do NOT pause. Do NOT summarize progress mid-loop. Just keep calling
|
|
85
|
-
- **Do NOT stop** until `isComplete` is `true` **or** the invocable action returns errors. There is **no maximum** number of iterations — keep going regardless of how many calls it takes.
|
|
84
|
+
- If `isComplete` is `false` **and no errors are returned**, you **MUST** call `flowElementGeneration` again with the **same `operationId`** from Step 2. **Do NOT ask the user if they want to continue. Do NOT pause. Do NOT summarize progress mid-loop. Just keep calling** — there is **no maximum** number of iterations.
|
|
86
85
|
- When `isComplete` is `true`, extract the flow metadata from the `result` field.
|
|
87
86
|
- If errors are returned, stop the loop and surface the error to the user.
|
|
88
87
|
|
|
@@ -91,6 +90,8 @@ Generates flow metadata element by element. This step is **mandatory** and must
|
|
|
91
90
|
- DO NOT add new nodes, tags, attributes, or text (do not add missing labels, X/Y coordinates, etc.).
|
|
92
91
|
- DO NOT remove any existing nodes.
|
|
93
92
|
|
|
93
|
+
**Canvas mode.** A net-new canvas-based flow needs a `<processMetadataValues>` `CanvasMode`=`AUTO_LAYOUT_CANVAS` entry (independent of `BuilderType`) or it opens in **Free-Form**. STRICT CONSTRAINTS forbid hand-editing the XML, so this must come from the generation pipeline — if the returned XML lacks it, surface a pipeline gap; do NOT hand-edit. (Non-canvas types like `CustomerLifecycle` carry none — don't flag those.)
|
|
94
|
+
|
|
94
95
|
## inflightMetadata Format
|
|
95
96
|
**DATA TYPE: ARRAY (not string)**
|
|
96
97
|
|
|
@@ -208,6 +209,10 @@ To select records for a scheduled flow, put the record criteria in the **start e
|
|
|
208
209
|
|
|
209
210
|
When the prompt says "runs once a week / every Sunday / daily at a time", the `userPrompt` should state: scheduled trigger, the frequency, the start time, and the record filter on the triggering object.
|
|
210
211
|
|
|
212
|
+
### "Autolaunched" is literal
|
|
213
|
+
|
|
214
|
+
An **"autolaunched"**/"no-trigger" request builds a **no-trigger** flow (no `triggerType`, `object`, or `<schedule>` on start) — put "for each…/when X"/timing intent in the **body**, not the trigger; add one only if asked.
|
|
215
|
+
|
|
211
216
|
### Counting related records ("count all related X", "number of X")
|
|
212
217
|
|
|
213
218
|
To store a count of records, use a **single** assignment element with `<operator>AssignCount</operator>`, assigning from the collection (the record-lookup result) into a Number variable. Do NOT emit two assignment elements for one count, and do NOT give two assignment elements the same `name` — duplicate assignment names, or two assignments doing one logical count, fail deployment. One lookup → one `AssignCount` assignment → one record update.
|
|
@@ -299,7 +304,7 @@ Then call Step 2 and Step 3 for this flow.
|
|
|
299
304
|
"requestSource": "A4V"
|
|
300
305
|
}
|
|
301
306
|
```
|
|
302
|
-
|
|
307
|
+
Loop as in Step 3 (same `operationId`, `requestSource: "A4V"`) until `isComplete` is `true` or errors return; extract the XML from `result` then.
|
|
303
308
|
|
|
304
309
|
**Example 2: With custom objects from local sfdx project**
|
|
305
310
|
|
|
@@ -348,7 +353,7 @@ Call repeatedly with the same `operationId` until `isComplete` is `true` or erro
|
|
|
348
353
|
"requestSource": "A4V"
|
|
349
354
|
}
|
|
350
355
|
```
|
|
351
|
-
|
|
356
|
+
Loop exactly as in Example 1's Step 3.
|
|
352
357
|
|
|
353
358
|
## CRITICAL Verification Checklist (MUST VERIFY BEFORE AND AFTER EVERY FLOW GENERATION)
|
|
354
359
|
|
|
@@ -361,3 +366,4 @@ Call repeatedly with the same `operationId` until `isComplete` is `true` or erro
|
|
|
361
366
|
- [ ] **groundingMetadata** from Step 1 output is passed directly to Step 2 input (already a string — do NOT serialize it again).
|
|
362
367
|
- [ ] **Step 3** is called in a loop with the same `operationId` from Step 2, `requestSource` always `"A4V"`, until `isComplete` is `true` or errors are returned — no pausing, no asking the user to continue, no matter how many iterations. Extract the XML from `result` only when `isComplete` is `true`.
|
|
363
368
|
- [ ] **Multi-flow**: Each flow's full pipeline is completed before starting the next, SEQUENTIALLY (never in parallel, never interleaved), and ALL requested flows are generated — do NOT stop after the first.
|
|
369
|
+
- [ ] **Canvas mode (read-only)**: a canvas-based net-new flow's XML carries a `<processMetadataValues>` `CanvasMode`=`AUTO_LAYOUT_CANVAS` entry. If absent, surface a pipeline gap — do NOT hand-edit (STRICT CONSTRAINTS forbid it). Non-canvas types like `CustomerLifecycle` carry none — don't flag those.
|
|
@@ -37,10 +37,11 @@ fi
|
|
|
37
37
|
# Basic YAML syntax check (if python3 available)
|
|
38
38
|
if command -v python3 &> /dev/null; then
|
|
39
39
|
echo "Checking YAML syntax..."
|
|
40
|
-
if python3 -
|
|
40
|
+
if python3 - "$CONFIG_FILE" 2>&1 <<'PYEOF'; then
|
|
41
41
|
import yaml, sys
|
|
42
|
+
config_file = sys.argv[1]
|
|
42
43
|
try:
|
|
43
|
-
with open(
|
|
44
|
+
with open(config_file, 'r') as f:
|
|
44
45
|
config = yaml.safe_load(f)
|
|
45
46
|
if config is None:
|
|
46
47
|
print('WARNING: Config file is empty or contains only comments')
|
|
@@ -50,10 +51,10 @@ try:
|
|
|
50
51
|
sys.exit(1)
|
|
51
52
|
print('YAML syntax: OK')
|
|
52
53
|
except yaml.YAMLError as e:
|
|
53
|
-
print(
|
|
54
|
+
print('ERROR: Invalid YAML syntax')
|
|
54
55
|
print(f' {e}')
|
|
55
56
|
sys.exit(1)
|
|
56
|
-
|
|
57
|
+
PYEOF
|
|
57
58
|
echo ""
|
|
58
59
|
else
|
|
59
60
|
echo ""
|
|
@@ -69,16 +70,19 @@ echo "Checking known fields..."
|
|
|
69
70
|
VALID_TOP_LEVEL="config_root log_folder log_level rules engines ignores suppressions"
|
|
70
71
|
|
|
71
72
|
if command -v python3 &> /dev/null; then
|
|
72
|
-
python3 -
|
|
73
|
+
python3 - "$CONFIG_FILE" "$VALID_TOP_LEVEL" 2>&1 <<'PYEOF'
|
|
73
74
|
import yaml, sys
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
config_file = sys.argv[1]
|
|
77
|
+
valid_top_level = sys.argv[2]
|
|
78
|
+
|
|
79
|
+
with open(config_file, 'r') as f:
|
|
76
80
|
config = yaml.safe_load(f)
|
|
77
81
|
|
|
78
82
|
if config is None:
|
|
79
83
|
sys.exit(0)
|
|
80
84
|
|
|
81
|
-
valid_fields = set(
|
|
85
|
+
valid_fields = set(valid_top_level.split())
|
|
82
86
|
unknown = set(config.keys()) - valid_fields
|
|
83
87
|
if unknown:
|
|
84
88
|
print(f'WARNING: Unknown top-level fields: {sorted(unknown)}')
|
|
@@ -100,12 +104,12 @@ if 'engines' in config and config['engines']:
|
|
|
100
104
|
# Check ignores section
|
|
101
105
|
if 'ignores' in config and config['ignores']:
|
|
102
106
|
if 'files' not in config['ignores']:
|
|
103
|
-
print('WARNING: ignores section should contain a
|
|
107
|
+
print('WARNING: ignores section should contain a "files" list')
|
|
104
108
|
elif not isinstance(config['ignores']['files'], list):
|
|
105
109
|
print('ERROR: ignores.files must be a list of glob patterns')
|
|
106
110
|
sys.exit(1)
|
|
107
111
|
else:
|
|
108
|
-
print(f'Ignore patterns: {len(config[
|
|
112
|
+
print(f'Ignore patterns: {len(config["ignores"]["files"])} patterns configured')
|
|
109
113
|
|
|
110
114
|
# Check rules section
|
|
111
115
|
if 'rules' in config and config['rules']:
|
|
@@ -120,7 +124,7 @@ if 'rules' in config and config['rules']:
|
|
|
120
124
|
print(f'WARNING: rules.{engine}.{rule_name}.severity = {sev} (expected 1-5 or Critical/High/Moderate/Low/Info)')
|
|
121
125
|
|
|
122
126
|
print('')
|
|
123
|
-
|
|
127
|
+
PYEOF
|
|
124
128
|
fi
|
|
125
129
|
|
|
126
130
|
# Run sf code-analyzer config validation (if sf CLI available)
|
|
@@ -15,14 +15,55 @@ const filePath = process.argv[2];
|
|
|
15
15
|
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
16
16
|
const runDir = data.runDir || "";
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// Fix targets are confined to the current working directory (the project being
|
|
19
|
+
// fixed). loc.file and runDir come from the results JSON and are untrusted;
|
|
20
|
+
// resolving each target against cwd and rejecting anything that escapes it
|
|
21
|
+
// stops a crafted results file from writing arbitrary paths (e.g. /etc/passwd
|
|
22
|
+
// or ../../outside-project).
|
|
23
|
+
const baseDir = fs.realpathSync(process.cwd());
|
|
24
|
+
|
|
25
|
+
function isOutside(base, target) {
|
|
26
|
+
const rel = path.relative(base, target);
|
|
27
|
+
return rel === "" || rel.startsWith("..") || path.isAbsolute(rel);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function safeResolve(candidate) {
|
|
31
|
+
if (typeof candidate !== "string" || !candidate) return null;
|
|
32
|
+
let rel = candidate;
|
|
33
|
+
// Engine output often uses absolute paths under the scan runDir; normalize
|
|
34
|
+
// those to project-relative before confinement.
|
|
35
|
+
if (runDir && rel.startsWith(runDir)) rel = rel.substring(runDir.length + 1);
|
|
36
|
+
const resolved = path.resolve(baseDir, rel);
|
|
37
|
+
// Lexical guard: rejects absolute paths and `..` traversal.
|
|
38
|
+
if (isOutside(baseDir, resolved)) return null;
|
|
39
|
+
// Symlink-aware guard: path.resolve/relative do not follow symlinks, so a
|
|
40
|
+
// symlinked path component (e.g. `link/victim` where `link` -> outside the
|
|
41
|
+
// tree) would pass the lexical check and let writeFileSync escape. Fix
|
|
42
|
+
// targets are read before being written, so the file exists — resolve its
|
|
43
|
+
// real path and re-check. Writing to the realpath also avoids following a
|
|
44
|
+
// symlink at write time. A missing target (realpathSync throws) is skipped.
|
|
45
|
+
let real;
|
|
46
|
+
try {
|
|
47
|
+
real = fs.realpathSync(resolved);
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (isOutside(baseDir, real)) return null;
|
|
52
|
+
return real;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Group fixes by confined, absolute file path
|
|
19
56
|
const fileFixesMap = new Map();
|
|
57
|
+
let fixesSkippedUnsafe = 0;
|
|
20
58
|
data.violations.forEach(v => {
|
|
21
59
|
if (v.fixes && v.fixes.length > 0) {
|
|
22
60
|
v.fixes.forEach(fix => {
|
|
23
61
|
const loc = fix.location;
|
|
24
|
-
|
|
25
|
-
if (
|
|
62
|
+
const filePath = safeResolve(loc.file);
|
|
63
|
+
if (!filePath) {
|
|
64
|
+
fixesSkippedUnsafe++;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
26
67
|
|
|
27
68
|
if (!fileFixesMap.has(filePath)) fileFixesMap.set(filePath, []);
|
|
28
69
|
fileFixesMap.get(filePath).push({
|
|
@@ -83,4 +124,4 @@ fileFixesMap.forEach((fixes, filePath) => {
|
|
|
83
124
|
}
|
|
84
125
|
});
|
|
85
126
|
|
|
86
|
-
console.log(JSON.stringify({ success: true, filesModified, fixesApplied, fixesSkipped, totalFixableFiles: fileFixesMap.size }));
|
|
127
|
+
console.log(JSON.stringify({ success: true, filesModified, fixesApplied, fixesSkipped, fixesSkippedUnsafe, totalFixableFiles: fileFixesMap.size }));
|
|
@@ -15,7 +15,32 @@
|
|
|
15
15
|
// resource: https://...
|
|
16
16
|
// description: Some description text
|
|
17
17
|
|
|
18
|
-
const {
|
|
18
|
+
const { execFileSync } = require("child_process");
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Run `sf code-analyzer rules ...` safely.
|
|
22
|
+
*
|
|
23
|
+
* Arguments are passed as an argv array (NOT a shell string), so a rule name
|
|
24
|
+
* or --engine value can never be interpreted as shell syntax. Returns the
|
|
25
|
+
* command's stdout as text; never throws (a non-zero exit still yields the
|
|
26
|
+
* output the caller wants to parse).
|
|
27
|
+
*/
|
|
28
|
+
function runCodeAnalyzerRules(extraArgs) {
|
|
29
|
+
try {
|
|
30
|
+
return execFileSync("sf", ["code-analyzer", "rules", ...extraArgs], {
|
|
31
|
+
encoding: "utf8",
|
|
32
|
+
timeout: 60000,
|
|
33
|
+
maxBuffer: 2 * 1024 * 1024,
|
|
34
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
35
|
+
});
|
|
36
|
+
} catch (err) {
|
|
37
|
+
// Preserve the original `2>&1` behavior: a non-zero exit that emitted
|
|
38
|
+
// partial stdout plus a diagnostic on stderr must surface BOTH streams, in
|
|
39
|
+
// order, so a genuine failure isn't misparsed as "rule not found".
|
|
40
|
+
const combined = String(err.stdout || "") + String(err.stderr || "");
|
|
41
|
+
return combined || (err.output && err.output.filter(Boolean).join("")) || "";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
19
44
|
|
|
20
45
|
function printUsage() {
|
|
21
46
|
console.error(`Usage: node describe-rule.js <rule-name> [--engine <engine>]
|
|
@@ -52,25 +77,21 @@ for (let i = 1; i < args.length; i++) {
|
|
|
52
77
|
// Build the rule selector for the lookup
|
|
53
78
|
const selector = engine ? `${engine}:${ruleName}` : ruleName;
|
|
54
79
|
|
|
55
|
-
// Run `sf code-analyzer rules` with --view detail to get full rule info
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
message: `Failed to run sf code-analyzer rules: ${err.message}`,
|
|
71
|
-
}));
|
|
72
|
-
process.exit(0);
|
|
73
|
-
}
|
|
80
|
+
// Run `sf code-analyzer rules` with --view detail to get full rule info.
|
|
81
|
+
// `selector` is passed as a single argv element, so it cannot inject shell
|
|
82
|
+
// commands even if it contains metacharacters.
|
|
83
|
+
const rawOutput = runCodeAnalyzerRules([
|
|
84
|
+
"--rule-selector",
|
|
85
|
+
selector,
|
|
86
|
+
"--view",
|
|
87
|
+
"detail",
|
|
88
|
+
]);
|
|
89
|
+
if (!rawOutput) {
|
|
90
|
+
console.log(JSON.stringify({
|
|
91
|
+
status: "error",
|
|
92
|
+
message: `Failed to run sf code-analyzer rules for selector "${selector}"`,
|
|
93
|
+
}));
|
|
94
|
+
process.exit(0);
|
|
74
95
|
}
|
|
75
96
|
|
|
76
97
|
// Parse the detail view output
|
|
@@ -224,12 +245,16 @@ function parseDetailOutput(output) {
|
|
|
224
245
|
function tryGrepFallback(ruleName, engine) {
|
|
225
246
|
try {
|
|
226
247
|
const sel = engine || "Recommended";
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
248
|
+
const output = runCodeAnalyzerRules(["--rule-selector", sel]);
|
|
249
|
+
|
|
250
|
+
// Case-insensitive substring filter done in JS. Previously this shelled out
|
|
251
|
+
// to `... | grep -i "${ruleName}"`, interpolating the untrusted rule name
|
|
252
|
+
// into a shell command; matching here removes that injection sink.
|
|
253
|
+
const needle = ruleName.toLowerCase();
|
|
254
|
+
const grepOutput = output
|
|
255
|
+
.split("\n")
|
|
256
|
+
.filter((line) => line.toLowerCase().includes(needle))
|
|
257
|
+
.join("\n");
|
|
233
258
|
|
|
234
259
|
if (!grepOutput.trim()) return null;
|
|
235
260
|
|
|
@@ -273,12 +298,7 @@ function tryGrepFallback(ruleName, engine) {
|
|
|
273
298
|
function tryFuzzyFallback(ruleName, engine) {
|
|
274
299
|
try {
|
|
275
300
|
const sel = engine || "Recommended";
|
|
276
|
-
const
|
|
277
|
-
const output = execSync(cmd, {
|
|
278
|
-
encoding: "utf8",
|
|
279
|
-
timeout: 60000,
|
|
280
|
-
maxBuffer: 2 * 1024 * 1024,
|
|
281
|
-
});
|
|
301
|
+
const output = runCodeAnalyzerRules(["--rule-selector", sel]);
|
|
282
302
|
|
|
283
303
|
// Extract rule names from table output
|
|
284
304
|
// Lines with rule data have: index name engine severity tags
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
name: platform-apex-logs-debug
|
|
3
3
|
description: "Salesforce debug log analysis and troubleshooting with 100-point scoring. TRIGGER when: user analyzes debug logs, hits governor limits, reads stack traces, or touches .log files from Salesforce orgs. DO NOT TRIGGER when: running Apex tests (use platform-apex-test-run), generating or fixing Apex code (use platform-apex-generate), or Agentforce session tracing (use agentforce-observe)."
|
|
4
4
|
metadata:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
semver: ">=1.6.0"
|
|
8
|
-
- tool: ["sf"]
|
|
9
|
-
semver: ">=2.0.0"
|
|
5
|
+
version: "1.1"
|
|
6
|
+
domains: ["Platform"]
|
|
10
7
|
relatedSkills:
|
|
11
8
|
- "agentforce-observe"
|
|
12
9
|
- "platform-apex-generate"
|
|
@@ -14,8 +11,11 @@ metadata:
|
|
|
14
11
|
- "platform-data-manage"
|
|
15
12
|
- "platform-metadata-deploy"
|
|
16
13
|
- "platform-soql-query"
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
cliTools:
|
|
15
|
+
- tool: ["sf"]
|
|
16
|
+
semver: ">=2.0.0"
|
|
17
|
+
- tool: ["jq"]
|
|
18
|
+
semver: ">=1.6.0"
|
|
19
19
|
---
|
|
20
20
|
|
|
21
21
|
# platform-apex-logs-debug: Salesforce Debug Log Analysis & Troubleshooting
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
name: platform-custom-application-generate
|
|
3
3
|
description: "Use this skill when users need to create or configure tab-based Salesforce Custom Applications with navigation, branding, and action overrides. Trigger when users mention custom apps, application metadata, app navigation, or organizing tabs into applications. Use when users want to create app containers for tabs and pages. Do NOT use when the goal is hosting a React UI bundle in the App Launcher — use experience-ui-bundle-custom-app-generate for that case."
|
|
4
4
|
metadata:
|
|
5
|
-
relatedSkills:
|
|
6
|
-
- "experience-ui-bundle-custom-app-generate"
|
|
7
5
|
version: "1.0"
|
|
8
6
|
domains: ["Platform"]
|
|
9
7
|
minApiVersion: "60.0"
|
|
8
|
+
relatedSkills:
|
|
9
|
+
- "experience-ui-bundle-custom-app-generate"
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
## When to Use This Skill
|
|
@@ -48,8 +48,8 @@ Custom applications (Lightning Apps) that group tabs and functionality to provid
|
|
|
48
48
|
- **description**: Brief description of the application's purpose
|
|
49
49
|
- **tabs**: Array of tab names to include
|
|
50
50
|
- **utilityBar**: API name of the Utility Bar configuration
|
|
51
|
-
- **brand**:
|
|
52
|
-
- **actionOverrides**:
|
|
51
|
+
- **brand**: HIGHLY RECOMMENDED - Branding configuration object (headerColor, shouldOverrideOrgTheme, footerColor)
|
|
52
|
+
- **actionOverrides**: REQUIRED when custom record pages exist - Action override configuration (actionName, content, formFactor, type, pageOrSobjectType)
|
|
53
53
|
- **profileActionOverrides**: Profile-specific action overrides (actionName, content, formFactor, pageOrSobjectType, type, profile)
|
|
54
54
|
- **isNavAutoTempTabsDisabled**: Navigation behavior setting (default: false)
|
|
55
55
|
- **isNavPersonalizationDisabled**: Personalization setting (default: false)
|
|
@@ -25,7 +25,7 @@ This document defines the mandatory constraints for generating CustomObject meta
|
|
|
25
25
|
|
|
26
26
|
**File extension:** `.object-meta.xml`
|
|
27
27
|
|
|
28
|
-
>
|
|
28
|
+
> **Description freshness — applies to EVERY object change, fields AND validation rules:** Whenever you add, update, or delete a field **or a validation rule** on an object, the `<description>` may now be stale. Before finishing, refresh it per **Section 3.B** (propose, confirm with the user, write). A validation-rule change counts exactly like a field change — the change is **not** done until the description has been reconciled. This is easy to forget on validation-rule edits/deletes — don't.
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
@@ -57,7 +57,7 @@ The following constraints must be true for the XML body to deploy successfully.
|
|
|
57
57
|
- IF object has Master-Detail field → use `ControlledByParent`
|
|
58
58
|
- IF a Master-Detail field is being added to an existing child object → that existing object's `<sharingModel>` must also be updated to `ControlledByParent`
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
**INCORRECT** — Will cause error: `Cannot set sharingModel to ReadWrite on a CustomObject with a MasterDetail relationship field`
|
|
61
61
|
```xml
|
|
62
62
|
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
63
63
|
<label>Order Line Item</label>
|
|
@@ -67,7 +67,7 @@ The following constraints must be true for the XML body to deploy successfully.
|
|
|
67
67
|
</CustomObject>
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
**CORRECT:**
|
|
71
71
|
```xml
|
|
72
72
|
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
73
73
|
<label>Order Line Item</label>
|
|
@@ -191,7 +191,7 @@ Do not create more than **2 Master-Detail relationships** for a single object. I
|
|
|
191
191
|
|
|
192
192
|
Do NOT include the `<fullName>` tag at the root of the `.object-meta.xml` file. The API name is derived from the filename.
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
**INCORRECT:**
|
|
195
195
|
```xml
|
|
196
196
|
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
197
197
|
<fullName>Vehicle__c</fullName> <!-- WRONG: Remove this -->
|
|
@@ -199,7 +199,7 @@ Do NOT include the `<fullName>` tag at the root of the `.object-meta.xml` file.
|
|
|
199
199
|
</CustomObject>
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
**CORRECT:**
|
|
203
203
|
```xml
|
|
204
204
|
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
205
205
|
<label>Vehicle</label>
|
|
@@ -218,7 +218,7 @@ Validation rule names follow different conventions than custom fields.
|
|
|
218
218
|
- Cannot contain two consecutive underscores
|
|
219
219
|
- **Must NOT end with `__c`** (unlike custom fields)
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
**INCORRECT:**
|
|
222
222
|
```xml
|
|
223
223
|
<validationRules>
|
|
224
224
|
<fullName>Require_Start_Date__c</fullName> <!-- WRONG: Has __c suffix -->
|
|
@@ -229,7 +229,7 @@ Validation rule names follow different conventions than custom fields.
|
|
|
229
229
|
```
|
|
230
230
|
**Error:** `The validation name can only contain alphanumeric characters, must begin with a letter, cannot end with an underscore...`
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
**CORRECT:**
|
|
233
233
|
```xml
|
|
234
234
|
<validationRules>
|
|
235
235
|
<fullName>Require_Start_Date</fullName> <!-- CORRECT: No __c suffix -->
|
|
@@ -81,7 +81,7 @@ Also forbidden:
|
|
|
81
81
|
<motif>Custom98: Truck</motif>
|
|
82
82
|
</CustomTab>
|
|
83
83
|
```
|
|
84
|
-
-
|
|
84
|
+
- **WRONG** — do NOT add `<sobjectName>`, `<name>`, `<fullName>`, or `<label>`:
|
|
85
85
|
```xml
|
|
86
86
|
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
87
87
|
<sobjectName>Space_Station__c</sobjectName> <!-- DEPLOYMENT ERROR -->
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
name: platform-soql-query
|
|
3
3
|
description: "SOQL query generation, optimization, and analysis with 100-point scoring. Use this skill when the user needs SOQL/SOSL authoring or optimization: natural-language-to-query generation, relationship queries, aggregates, query-plan analysis, and performance or safety improvements for Salesforce queries. TRIGGER when: user writes, optimizes, or debugs SOQL/SOSL queries, touches .soql files, or asks about relationship queries, aggregates, or query performance. DO NOT TRIGGER when: bulk data operations (use platform-data-manage), Apex DML logic (use platform-apex-generate), or report/dashboard queries."
|
|
4
4
|
metadata:
|
|
5
|
+
version: "1.1"
|
|
6
|
+
domains: ["Platform"]
|
|
7
|
+
relatedSkills:
|
|
8
|
+
- "experience-lwc-generate"
|
|
9
|
+
- "platform-apex-generate"
|
|
10
|
+
- "platform-apex-logs-debug"
|
|
11
|
+
- "platform-apex-test-run"
|
|
12
|
+
- "platform-data-manage"
|
|
5
13
|
cliTools:
|
|
6
14
|
- tool: ["jq"]
|
|
7
15
|
semver: ">=1.6.0"
|
|
@@ -9,14 +17,6 @@ metadata:
|
|
|
9
17
|
semver: ">=3.10.0"
|
|
10
18
|
- tool: ["sf"]
|
|
11
19
|
semver: ">=2.0.0"
|
|
12
|
-
relatedSkills:
|
|
13
|
-
- "experience-lwc-generate"
|
|
14
|
-
- "platform-apex-generate"
|
|
15
|
-
- "platform-apex-logs-debug"
|
|
16
|
-
- "platform-apex-test-run"
|
|
17
|
-
- "platform-data-manage"
|
|
18
|
-
version: "1.1"
|
|
19
|
-
domains: ["Platform"]
|
|
20
20
|
---
|
|
21
21
|
|
|
22
22
|
# platform-soql-query: Salesforce SOQL Query Expert
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
name: platform-value-set-generate
|
|
3
3
|
description: "Use this skill when users need to create, generate, or validate a Salesforce global value set or customize a standard value set. Trigger when users mention a global value set, GlobalValueSet, standard value set, StandardValueSet, a reusable picklist, a picklist value set shared across fields, or customizing standard picklists like Industry, Lead Source, or Opportunity Stage. Also use when users hit deployment errors adding values to a standard picklist, referencing a value set from a custom field, or working with .globalValueSet-meta.xml or .standardValueSet-meta.xml files. DO NOT TRIGGER for an inline one-off picklist on a single field with no reuse, or for general custom field metadata work that does not involve a GlobalValueSet or StandardValueSet — use platform-custom-field-generate instead."
|
|
4
4
|
metadata:
|
|
5
|
-
relatedSkills:
|
|
6
|
-
- "platform-custom-field-generate"
|
|
7
5
|
version: "1.0"
|
|
8
6
|
domains: ["Platform"]
|
|
9
7
|
minApiVersion: "60.0"
|
|
8
|
+
relatedSkills:
|
|
9
|
+
- "platform-custom-field-generate"
|
|
10
10
|
cliTools:
|
|
11
11
|
- tool: ["sf"]
|
|
12
12
|
semver: ">=2.0.0"
|